mirror of https://github.com/mosra/magnum.git
14 changed files with 1452 additions and 6 deletions
@ -0,0 +1,335 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Device.h" |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <Corrade/Containers/Optional.h> |
||||||
|
#include <Corrade/Containers/GrowableArray.h> |
||||||
|
#include <Corrade/Containers/StaticArray.h> |
||||||
|
#include <Corrade/Containers/String.h> |
||||||
|
#include <Corrade/Containers/StringView.h> |
||||||
|
#include <Corrade/Utility/Algorithms.h> |
||||||
|
#include <Corrade/Utility/Arguments.h> |
||||||
|
|
||||||
|
#include "Magnum/Math/Functions.h" |
||||||
|
#include "Magnum/Vk/Handle.h" |
||||||
|
#include "Magnum/Vk/Instance.h" |
||||||
|
#include "Magnum/Vk/DeviceProperties.h" |
||||||
|
#include "Magnum/Vk/Extensions.h" |
||||||
|
#include "Magnum/Vk/ExtensionProperties.h" |
||||||
|
#include "Magnum/Vk/Result.h" |
||||||
|
#include "Magnum/Vk/Version.h" |
||||||
|
#include "Magnum/Vk/Implementation/Arguments.h" |
||||||
|
#include "Magnum/Vk/Implementation/InstanceState.h" |
||||||
|
#include "Magnum/Vk/Implementation/DeviceState.h" |
||||||
|
#include "MagnumExternal/Vulkan/flextVkGlobal.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Vk { |
||||||
|
|
||||||
|
struct DeviceCreateInfo::State { |
||||||
|
Containers::Array<Containers::String> ownedStrings; |
||||||
|
Containers::Array<const char*> extensions; |
||||||
|
|
||||||
|
Containers::String disabledExtensionsStorage; |
||||||
|
Containers::Array<Containers::StringView> disabledExtensions; |
||||||
|
Containers::Array<VkDeviceQueueCreateInfo> queues; |
||||||
|
Containers::StaticArray<32, Float> queuePriorities; |
||||||
|
|
||||||
|
std::size_t nextQueuePriority = 0; |
||||||
|
bool quietLog = false; |
||||||
|
Version version = Version::None; |
||||||
|
}; |
||||||
|
|
||||||
|
DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const ExtensionProperties* extensionProperties, const Flags flags): _physicalDevice{deviceProperties}, _info{}, _state{Containers::InPlaceInit} { |
||||||
|
Utility::Arguments args = Implementation::arguments(); |
||||||
|
args.parse(deviceProperties._instance->state().argc, deviceProperties._instance->state().argv); |
||||||
|
|
||||||
|
if(args.value("log") == "quiet") |
||||||
|
_state->quietLog = true; |
||||||
|
|
||||||
|
_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; |
||||||
|
_info.flags = VkDeviceCreateFlags(flags & ~Flag::NoImplicitExtensions); |
||||||
|
|
||||||
|
/* Take the minimum of instance and device version. Instance version being
|
||||||
|
smaller than a device version happens mainly if there's a forced Vulkan |
||||||
|
version via --magnum-vulkan version, which will be later used to cap available features. */ |
||||||
|
_state->version = Version(Math::min(UnsignedInt(deviceProperties._instance->version()), UnsignedInt(deviceProperties.apiVersion()))); |
||||||
|
|
||||||
|
/* If there are any disabled extensions, sort them and save for later --
|
||||||
|
we'll use them to filter the ones added by the app */ |
||||||
|
Containers::String disabledExtensions = args.value<Containers::String>("disable-extensions"); |
||||||
|
if(!disabledExtensions.isEmpty()) { |
||||||
|
_state->disabledExtensionsStorage = std::move(disabledExtensions); |
||||||
|
_state->disabledExtensions = Containers::StringView{_state->disabledExtensionsStorage}.splitWithoutEmptyParts(); |
||||||
|
std::sort(_state->disabledExtensions.begin(), _state->disabledExtensions.end()); |
||||||
|
} |
||||||
|
|
||||||
|
/* Add all extensions enabled on command-line. The blacklist is applied on
|
||||||
|
those as well. */ |
||||||
|
/** @todo use a generator split() so we can avoid the growing allocation
|
||||||
|
of the output array */ |
||||||
|
/** @todo unfortunately even though the split and value retrieval is mostly
|
||||||
|
allocation-free, the strings will be turned into owning copies because |
||||||
|
none of them is null-terminated or global -- could be a better idea to |
||||||
|
just grow one giant string internally (once we have growable strings) */ |
||||||
|
addEnabledExtensions(args.value<Containers::StringView>("enable-extensions").splitWithoutEmptyParts()); |
||||||
|
|
||||||
|
/* Enable implicit extensions unless that's forbidden */ |
||||||
|
/** @todo move this somewhere else as this will grow significantly? */ |
||||||
|
if(!(flags & Flag::NoImplicitExtensions)) { |
||||||
|
/* Fetch searchable extension properties if not already */ |
||||||
|
Containers::Optional<ExtensionProperties> extensionPropertiesStorage; |
||||||
|
if(!extensionProperties) { |
||||||
|
/** @todo i'd like to know which layers are enabled so i can list
|
||||||
|
the exts from those .. but how? */ |
||||||
|
extensionPropertiesStorage = deviceProperties.enumerateExtensionProperties(); |
||||||
|
extensionProperties = &*extensionPropertiesStorage; |
||||||
|
} |
||||||
|
|
||||||
|
/* No extensions at the moment */ |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
DeviceCreateInfo::DeviceCreateInfo(Instance& instance, const Flags flags): DeviceCreateInfo{pickDevice(instance), flags} {} |
||||||
|
|
||||||
|
DeviceCreateInfo::DeviceCreateInfo(NoInitT) noexcept {} |
||||||
|
|
||||||
|
DeviceCreateInfo::DeviceCreateInfo(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo& info): _physicalDevice{physicalDevice}, _info{info} {} |
||||||
|
|
||||||
|
DeviceCreateInfo::~DeviceCreateInfo() = default; |
||||||
|
|
||||||
|
DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const Containers::ArrayView<const Containers::StringView> extensions) { |
||||||
|
if(extensions.empty()) return *this; |
||||||
|
/* This can happen in case we used the NoInit or VkDeviceCreateInfo
|
||||||
|
constructor */ |
||||||
|
if(!_state) _state.emplace(); |
||||||
|
|
||||||
|
/* Add null-terminated strings to the extension array */ |
||||||
|
arrayReserve(_state->extensions, _state->extensions.size() + extensions.size()); |
||||||
|
for(const Containers::StringView extension: extensions) { |
||||||
|
/* If the extension is blacklisted, skip it */ |
||||||
|
if(std::binary_search(_state->disabledExtensions.begin(), _state->disabledExtensions.end(), extension)) continue; |
||||||
|
|
||||||
|
/* Keep an owned *allocated* copy of the string if it's not global or
|
||||||
|
null-terminated -- ideally, if people use string view literals, |
||||||
|
those will be, so this won't allocate. Allocated so the pointers |
||||||
|
don't get invalidated when the array gets reallocated. */ |
||||||
|
const char* data; |
||||||
|
if(!(extension.flags() >= (Containers::StringViewFlag::NullTerminated|Containers::StringViewFlag::Global))) |
||||||
|
data = arrayAppend(_state->ownedStrings, Containers::InPlaceInit, |
||||||
|
Containers::AllocatedInit, extension).data(); |
||||||
|
else data = extension.data(); |
||||||
|
|
||||||
|
arrayAppend(_state->extensions, data); |
||||||
|
} |
||||||
|
|
||||||
|
/* Update the extension count, re-route the pointer to the layers array in
|
||||||
|
case it got reallocated */ |
||||||
|
_info.enabledExtensionCount = _state->extensions.size(); |
||||||
|
_info.ppEnabledExtensionNames = _state->extensions.data(); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const std::initializer_list<Containers::StringView> extensions) { |
||||||
|
return addEnabledExtensions(Containers::arrayView(extensions)); |
||||||
|
} |
||||||
|
|
||||||
|
DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const Containers::ArrayView<const Extension> extensions) { |
||||||
|
if(extensions.empty()) return *this; |
||||||
|
/* This can happen in case we used the NoInit or VkDeviceCreateInfo
|
||||||
|
constructor */ |
||||||
|
if(!_state) _state.emplace(); |
||||||
|
|
||||||
|
arrayReserve(_state->extensions, _state->extensions.size() + extensions.size()); |
||||||
|
for(const Extension& extension: extensions) { |
||||||
|
/* If the extension is blacklisted, skip it */ |
||||||
|
if(std::binary_search(_state->disabledExtensions.begin(), _state->disabledExtensions.end(), extension.string())) continue; |
||||||
|
|
||||||
|
arrayAppend(_state->extensions, extension.string().data()); |
||||||
|
} |
||||||
|
|
||||||
|
/* Update the extension count, re-route the pointer to the layers array in
|
||||||
|
case it got reallocated */ |
||||||
|
_info.enabledExtensionCount = _state->extensions.size(); |
||||||
|
_info.ppEnabledExtensionNames = _state->extensions.data(); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const std::initializer_list<Extension> extensions) { |
||||||
|
return addEnabledExtensions(Containers::arrayView(extensions)); |
||||||
|
} |
||||||
|
|
||||||
|
DeviceCreateInfo& DeviceCreateInfo::addQueues(UnsignedInt family, Containers::ArrayView<const Float> priorities) { |
||||||
|
CORRADE_ASSERT(!priorities.empty(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified", *this); |
||||||
|
|
||||||
|
/* This can happen in case we used the NoInit or VkDeviceCreateInfo
|
||||||
|
constructor */ |
||||||
|
if(!_state) _state.emplace(); |
||||||
|
|
||||||
|
VkDeviceQueueCreateInfo info{}; |
||||||
|
info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
||||||
|
info.queueFamilyIndex = family; |
||||||
|
info.queueCount = priorities.size(); |
||||||
|
info.pQueuePriorities = _state->queuePriorities + _state->nextQueuePriority; |
||||||
|
|
||||||
|
/* Copy the passed queue priorities to an internal storage that never
|
||||||
|
reallocates. If this blows up, see the definition of queuePriorities for |
||||||
|
details. We can't easily reallocate if this grows too big as all |
||||||
|
pointers would need to be patched, so there's a static limit. */ |
||||||
|
CORRADE_INTERNAL_ASSERT(_state->nextQueuePriority + priorities.size() <= _state->queuePriorities.size()); |
||||||
|
Utility::copy(priorities, _state->queuePriorities.suffix(_state->nextQueuePriority).prefix(priorities.size())); |
||||||
|
_state->nextQueuePriority += priorities.size(); |
||||||
|
|
||||||
|
return addQueues(info); |
||||||
|
} |
||||||
|
|
||||||
|
DeviceCreateInfo& DeviceCreateInfo::addQueues(UnsignedInt family, std::initializer_list<Float> priorities) { |
||||||
|
return addQueues(family, Containers::arrayView(priorities)); |
||||||
|
} |
||||||
|
|
||||||
|
DeviceCreateInfo& DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo& info) { |
||||||
|
/* This can happen in case we used the NoInit or VkDeviceCreateInfo
|
||||||
|
constructor */ |
||||||
|
if(!_state) _state.emplace(); |
||||||
|
|
||||||
|
/* Copy the info to an internal storage and re-route the pointer to it.
|
||||||
|
This handles a potential reallocation and also the case of replacing the |
||||||
|
default queue on the first call to addQueues(). */ |
||||||
|
arrayAppend(_state->queues, info); |
||||||
|
_info.pQueueCreateInfos = _state->queues; |
||||||
|
_info.queueCreateInfoCount = _state->queues.size(); |
||||||
|
|
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
Device Device::wrap(Instance& instance, const VkDevice handle, const Version version, const Containers::ArrayView<const Containers::StringView> enabledExtensions, const HandleFlags flags) { |
||||||
|
/* Compared to the constructor nothing is printed here as it would be just
|
||||||
|
repeating what was passed to the constructor */ |
||||||
|
Device out{NoCreate}; |
||||||
|
out._handle = handle; |
||||||
|
out._flags = flags; |
||||||
|
out.initializeExtensions(enabledExtensions); |
||||||
|
out.initialize(instance, version); |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
Device Device::wrap(Instance& instance, const VkDevice handle, const Version version, const std::initializer_list<Containers::StringView> enabledExtensions, const HandleFlags flags) { |
||||||
|
return wrap(instance, handle, version, Containers::arrayView(enabledExtensions), flags); |
||||||
|
} |
||||||
|
|
||||||
|
Device::Device(Instance& instance, const DeviceCreateInfo& info): |
||||||
|
#ifdef CORRADE_GRACEFUL_ASSERT |
||||||
|
_handle{}, /* Otherwise the destructor dies if we hit the queue assert */ |
||||||
|
#endif |
||||||
|
_flags{HandleFlag::DestroyOnDestruction} |
||||||
|
{ |
||||||
|
CORRADE_ASSERT(info._info.queueCreateInfoCount, |
||||||
|
"Vk::Device: needs to be created with at least one queue", ); |
||||||
|
|
||||||
|
const Version version = info._state->version != Version::None ? info._state->version : DeviceProperties::wrap(instance, info._physicalDevice).apiVersion(); |
||||||
|
|
||||||
|
/* Print all enabled extensions if we're not told to be quiet */ |
||||||
|
if(!info._state || !info._state->quietLog) { |
||||||
|
Debug{} << "Device version:" << version; |
||||||
|
|
||||||
|
if(info->enabledExtensionCount) { |
||||||
|
Debug{} << "Enabled device extensions:"; |
||||||
|
for(std::size_t i = 0, max = info->enabledExtensionCount; i != max; ++i) |
||||||
|
Debug{} << " " << info->ppEnabledExtensionNames[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MAGNUM_VK_INTERNAL_ASSERT_RESULT(instance->CreateDevice(info._physicalDevice, info, nullptr, &_handle)); |
||||||
|
|
||||||
|
initializeExtensions<const char*>({info->ppEnabledExtensionNames, info->enabledExtensionCount}); |
||||||
|
initialize(instance, version); |
||||||
|
} |
||||||
|
|
||||||
|
Device::Device(NoCreateT): _handle{}, _functionPointers{} {} |
||||||
|
|
||||||
|
Device::Device(Device&& other) noexcept: _handle{other._handle}, _flags{other._flags}, _version{other._version}, _extensionStatus{other._extensionStatus}, _state{std::move(other._state)}, _functionPointers{other._functionPointers} { |
||||||
|
other._handle = nullptr; |
||||||
|
other._functionPointers = {}; |
||||||
|
} |
||||||
|
|
||||||
|
Device::~Device() { |
||||||
|
if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) |
||||||
|
_functionPointers.DestroyDevice(_handle, nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
Device& Device::operator=(Device&& other) noexcept { |
||||||
|
using std::swap; |
||||||
|
swap(other._handle, _handle); |
||||||
|
swap(other._flags, _flags); |
||||||
|
swap(other._version, _version); |
||||||
|
swap(other._extensionStatus, _extensionStatus); |
||||||
|
swap(other._state, _state); |
||||||
|
swap(other._functionPointers, _functionPointers); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
template<class T> void Device::initializeExtensions(const Containers::ArrayView<const T> enabledExtensions) { |
||||||
|
/* Mark all known extensions as enabled */ |
||||||
|
for(const T extension: enabledExtensions) { |
||||||
|
for(Containers::ArrayView<const Extension> knownExtensions: { |
||||||
|
Extension::extensions(Version::None), |
||||||
|
/*Extension::extensions(Version::Vk10), is empty */ |
||||||
|
Extension::extensions(Version::Vk11), |
||||||
|
Extension::extensions(Version::Vk12) |
||||||
|
}) { |
||||||
|
auto found = std::lower_bound(knownExtensions.begin(), knownExtensions.end(), extension, [](const Extension& a, const T& b) { |
||||||
|
return a.string() < static_cast<const Containers::StringView&>(b); |
||||||
|
}); |
||||||
|
if(found->string() != extension) continue; |
||||||
|
_extensionStatus.set(found->index(), true); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Device::initialize(Instance& instance, const Version version) { |
||||||
|
/* Init version, function pointers */ |
||||||
|
_version = version; |
||||||
|
flextVkInitDevice(_handle, &_functionPointers, instance->GetDeviceProcAddr); |
||||||
|
|
||||||
|
/* Set up extension-dependent functionality */ |
||||||
|
_state.emplace(*this); |
||||||
|
} |
||||||
|
|
||||||
|
bool Device::isExtensionEnabled(const Extension& extension) const { |
||||||
|
return _extensionStatus[extension.index()]; |
||||||
|
} |
||||||
|
|
||||||
|
VkDevice Device::release() { |
||||||
|
const VkDevice handle = _handle; |
||||||
|
_handle = nullptr; |
||||||
|
return handle; |
||||||
|
} |
||||||
|
|
||||||
|
void Device::populateGlobalFunctionPointers() { |
||||||
|
flextVkDevice = _functionPointers; |
||||||
|
} |
||||||
|
|
||||||
|
}} |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "DeviceState.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Vk { namespace Implementation { |
||||||
|
|
||||||
|
DeviceState::DeviceState(Device&) {} |
||||||
|
|
||||||
|
}}} |
||||||
@ -0,0 +1,40 @@ |
|||||||
|
#ifndef Magnum_Vk_Implementation_DeviceState_h |
||||||
|
#define Magnum_Vk_Implementation_DeviceState_h |
||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Magnum/Vk/Vk.h" |
||||||
|
#include "Magnum/Vk/Vulkan.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Vk { namespace Implementation { |
||||||
|
|
||||||
|
struct DeviceState { |
||||||
|
explicit DeviceState(Device& instance); |
||||||
|
}; |
||||||
|
|
||||||
|
}}} |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
@ -0,0 +1,90 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <new> |
||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
|
||||||
|
#include "Magnum/Vk/Device.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Vk { namespace Test { namespace { |
||||||
|
|
||||||
|
struct DeviceTest: TestSuite::Tester { |
||||||
|
explicit DeviceTest(); |
||||||
|
|
||||||
|
void createInfoConstructNoInit(); |
||||||
|
void createInfoConstructFromVk(); |
||||||
|
|
||||||
|
void constructNoCreate(); |
||||||
|
void constructCopy(); |
||||||
|
}; |
||||||
|
|
||||||
|
DeviceTest::DeviceTest() { |
||||||
|
addTests({&DeviceTest::createInfoConstructNoInit, |
||||||
|
&DeviceTest::createInfoConstructFromVk, |
||||||
|
|
||||||
|
&DeviceTest::constructNoCreate, |
||||||
|
&DeviceTest::constructCopy}); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceTest::createInfoConstructNoInit() { |
||||||
|
DeviceCreateInfo info{NoInit}; |
||||||
|
info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; |
||||||
|
new(&info) DeviceCreateInfo{NoInit}; |
||||||
|
CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); |
||||||
|
|
||||||
|
CORRADE_VERIFY((std::is_nothrow_constructible<DeviceCreateInfo, NoInitT>::value)); |
||||||
|
|
||||||
|
/* Implicit construction is not allowed */ |
||||||
|
CORRADE_VERIFY(!(std::is_convertible<NoInitT, DeviceCreateInfo>::value)); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceTest::createInfoConstructFromVk() { |
||||||
|
VkDeviceCreateInfo vkInfo; |
||||||
|
vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; |
||||||
|
|
||||||
|
DeviceCreateInfo info{VkPhysicalDevice{}, vkInfo}; |
||||||
|
CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceTest::constructNoCreate() { |
||||||
|
{ |
||||||
|
Device device{NoCreate}; |
||||||
|
CORRADE_VERIFY(!device.handle()); |
||||||
|
/* Device function pointers should be null */ |
||||||
|
CORRADE_VERIFY(!device->CreateBuffer); |
||||||
|
} |
||||||
|
|
||||||
|
/* Implicit construction is not allowed */ |
||||||
|
CORRADE_VERIFY(!(std::is_convertible<NoCreateT, Device>::value)); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceTest::constructCopy() { |
||||||
|
CORRADE_VERIFY(!(std::is_constructible<Device, const Device&>{})); |
||||||
|
CORRADE_VERIFY(!(std::is_assignable<Device, const Device&>{})); |
||||||
|
} |
||||||
|
|
||||||
|
}}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::Vk::Test::DeviceTest) |
||||||
@ -0,0 +1,546 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <sstream> |
||||||
|
#include <Corrade/Containers/StringStl.h> |
||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
#include <Corrade/TestSuite/Compare/Numeric.h> |
||||||
|
#include <Corrade/Utility/DebugStl.h> |
||||||
|
#include <Corrade/Utility/FormatStl.h> |
||||||
|
|
||||||
|
#include "Magnum/Vk/Device.h" |
||||||
|
#include "Magnum/Vk/DeviceProperties.h" |
||||||
|
#include "Magnum/Vk/Extensions.h" |
||||||
|
#include "Magnum/Vk/ExtensionProperties.h" |
||||||
|
#include "Magnum/Vk/Handle.h" |
||||||
|
#include "Magnum/Vk/Instance.h" |
||||||
|
#include "Magnum/Vk/LayerProperties.h" |
||||||
|
#include "Magnum/Vk/Result.h" |
||||||
|
#include "Magnum/Vk/Version.h" |
||||||
|
|
||||||
|
#include "MagnumExternal/Vulkan/flextVkGlobal.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Vk { namespace Test { namespace { |
||||||
|
|
||||||
|
struct DeviceVkTest: TestSuite::Tester { |
||||||
|
explicit DeviceVkTest(); |
||||||
|
|
||||||
|
void createInfoConstruct(); |
||||||
|
void createInfoConstructImplicitDevice(); |
||||||
|
void createInfoConstructNoImplicitExtensions(); |
||||||
|
void createInfoExtensions(); |
||||||
|
void createInfoCopiedStrings(); |
||||||
|
void createInfoNoQueuePriorities(); |
||||||
|
|
||||||
|
void construct(); |
||||||
|
void constructExtensions(); |
||||||
|
void constructExtensionsCommandLineDisable(); |
||||||
|
void constructExtensionsCommandLineEnable(); |
||||||
|
void constructMove(); |
||||||
|
void constructUnknownExtension(); |
||||||
|
void constructNoQueue(); |
||||||
|
void wrap(); |
||||||
|
void populateGlobalFunctionPointers(); |
||||||
|
|
||||||
|
Instance _instance; |
||||||
|
}; |
||||||
|
|
||||||
|
struct { |
||||||
|
const char* nameDisable; |
||||||
|
const char* nameEnable; |
||||||
|
Containers::Array<const char*> argsDisable, argsEnable; |
||||||
|
bool driverVersionSupported, debugMarkerEnabled, maintenance1Enabled; |
||||||
|
const char* log; |
||||||
|
} ConstructCommandLineData[] { |
||||||
|
/* Shouldn't print anything about version, enabled layers/exts if quier
|
||||||
|
output is enabled. */ |
||||||
|
{"quiet", "quiet, enabled extensions", |
||||||
|
Containers::array({"", "--magnum-log", "quiet"}), |
||||||
|
Containers::array({"", "--magnum-log", "quiet", |
||||||
|
"--magnum-enable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}), |
||||||
|
true, true, true, |
||||||
|
""}, |
||||||
|
{"", "enabled extensions", nullptr, |
||||||
|
Containers::array({"", |
||||||
|
"--magnum-enable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}), |
||||||
|
true, true, true, |
||||||
|
"Device version: Vulkan {}.{}{}\n" |
||||||
|
"Enabled device extensions:\n" |
||||||
|
" VK_EXT_debug_marker\n" |
||||||
|
" VK_KHR_maintenance1\n"}, |
||||||
|
{"forced version", "forced version, enabled extensions", |
||||||
|
Containers::array({"", |
||||||
|
"--magnum-vulkan-version", "1.0"}), |
||||||
|
Containers::array({"", |
||||||
|
"--magnum-vulkan-version", "1.0", |
||||||
|
"--magnum-enable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}), |
||||||
|
false, true, true, |
||||||
|
"Device version: Vulkan 1.0\n" |
||||||
|
"Enabled device extensions:\n" |
||||||
|
" VK_EXT_debug_marker\n" |
||||||
|
" VK_KHR_maintenance1\n"}, |
||||||
|
{"disabled one extension", "enabled one extension", |
||||||
|
Containers::array({"", |
||||||
|
"--magnum-disable-extensions", "VK_EXT_debug_marker"}), |
||||||
|
Containers::array({"", |
||||||
|
"--magnum-enable-extensions", "VK_KHR_maintenance1"}), |
||||||
|
true, false, true, |
||||||
|
"Device version: Vulkan {}.{}{}\n" |
||||||
|
"Enabled device extensions:\n" |
||||||
|
" VK_KHR_maintenance1\n"}, |
||||||
|
{"disabled extensions", "", |
||||||
|
Containers::array({"", |
||||||
|
"--magnum-disable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}), |
||||||
|
nullptr, |
||||||
|
true, false, false, |
||||||
|
"Device version: Vulkan {}.{}{}\n"}, |
||||||
|
}; |
||||||
|
|
||||||
|
DeviceVkTest::DeviceVkTest(): _instance{InstanceCreateInfo{arguments().first, arguments().second}} { |
||||||
|
addTests({&DeviceVkTest::createInfoConstruct, |
||||||
|
&DeviceVkTest::createInfoConstructImplicitDevice, |
||||||
|
&DeviceVkTest::createInfoConstructNoImplicitExtensions, |
||||||
|
&DeviceVkTest::createInfoExtensions, |
||||||
|
&DeviceVkTest::createInfoCopiedStrings, |
||||||
|
&DeviceVkTest::createInfoNoQueuePriorities, |
||||||
|
|
||||||
|
&DeviceVkTest::construct, |
||||||
|
&DeviceVkTest::constructExtensions}); |
||||||
|
|
||||||
|
addInstancedTests({&DeviceVkTest::constructExtensionsCommandLineDisable, |
||||||
|
&DeviceVkTest::constructExtensionsCommandLineEnable}, |
||||||
|
Containers::arraySize(ConstructCommandLineData)); |
||||||
|
|
||||||
|
addTests({&DeviceVkTest::constructMove, |
||||||
|
&DeviceVkTest::constructUnknownExtension, |
||||||
|
&DeviceVkTest::constructNoQueue, |
||||||
|
|
||||||
|
&DeviceVkTest::wrap, |
||||||
|
&DeviceVkTest::populateGlobalFunctionPointers}); |
||||||
|
} |
||||||
|
|
||||||
|
using namespace Containers::Literals; |
||||||
|
|
||||||
|
void DeviceVkTest::createInfoConstruct() { |
||||||
|
DeviceCreateInfo info{pickDevice(_instance)}; |
||||||
|
CORRADE_VERIFY(info->sType); |
||||||
|
CORRADE_VERIFY(!info->pNext); |
||||||
|
/* Extensions might or might not be enabled */ |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::createInfoConstructImplicitDevice() { |
||||||
|
DeviceCreateInfo info{_instance}; |
||||||
|
CORRADE_VERIFY(info->sType); |
||||||
|
CORRADE_VERIFY(!info->pNext); |
||||||
|
/* Extensions might or might not be enabled */ |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::createInfoConstructNoImplicitExtensions() { |
||||||
|
DeviceCreateInfo info{_instance, DeviceCreateInfo::Flag::NoImplicitExtensions}; |
||||||
|
CORRADE_VERIFY(info->sType); |
||||||
|
CORRADE_VERIFY(!info->pNext); |
||||||
|
/* No extensions enabled as we explicitly disabled that */ |
||||||
|
CORRADE_VERIFY(!info->ppEnabledExtensionNames); |
||||||
|
CORRADE_COMPARE(info->enabledExtensionCount, 0); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::createInfoExtensions() { |
||||||
|
if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) |
||||||
|
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); |
||||||
|
|
||||||
|
DeviceCreateInfo info{_instance, DeviceCreateInfo::Flag::NoImplicitExtensions}; |
||||||
|
CORRADE_VERIFY(!info->ppEnabledExtensionNames); |
||||||
|
CORRADE_COMPARE(info->enabledExtensionCount, 0); |
||||||
|
|
||||||
|
info.addEnabledExtensions<Extensions::KHR::maintenance1>(); |
||||||
|
CORRADE_VERIFY(info->ppEnabledExtensionNames); |
||||||
|
CORRADE_COMPARE(info->enabledExtensionCount, 1); |
||||||
|
/* The pointer should be to the global data */ |
||||||
|
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledExtensionNames[0]), |
||||||
|
Extensions::KHR::maintenance1::string().data()); |
||||||
|
|
||||||
|
info.addEnabledExtensions({ |
||||||
|
Extensions::KHR::draw_indirect_count{}, |
||||||
|
Extensions::KHR::get_memory_requirements2{} |
||||||
|
}); |
||||||
|
CORRADE_COMPARE(info->enabledExtensionCount, 3); |
||||||
|
/* The pointer should be to the global data */ |
||||||
|
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledExtensionNames[0]), |
||||||
|
Extensions::KHR::maintenance1::string().data()); |
||||||
|
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledExtensionNames[1]), |
||||||
|
Extensions::KHR::draw_indirect_count::string().data()); |
||||||
|
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledExtensionNames[2]), |
||||||
|
Extensions::KHR::get_memory_requirements2::string().data()); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::createInfoCopiedStrings() { |
||||||
|
Containers::StringView globalButNotNullTerminated = "VK_KHR_maintenance25"_s.except(1); |
||||||
|
Containers::String localButNullTerminated = Extensions::KHR::draw_indirect_count::string(); |
||||||
|
|
||||||
|
DeviceCreateInfo info{_instance, DeviceCreateInfo::Flag::NoImplicitExtensions}; |
||||||
|
info.addEnabledExtensions({ |
||||||
|
globalButNotNullTerminated, |
||||||
|
localButNullTerminated |
||||||
|
}); |
||||||
|
CORRADE_COMPARE(info->enabledExtensionCount, 2); |
||||||
|
|
||||||
|
CORRADE_COMPARE(info->ppEnabledExtensionNames[0], globalButNotNullTerminated); |
||||||
|
CORRADE_VERIFY(info->ppEnabledExtensionNames[0] != globalButNotNullTerminated.data()); |
||||||
|
|
||||||
|
CORRADE_COMPARE(info->ppEnabledExtensionNames[1], localButNullTerminated); |
||||||
|
CORRADE_VERIFY(info->ppEnabledExtensionNames[1] != localButNullTerminated.data()); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::createInfoNoQueuePriorities() { |
||||||
|
#ifdef CORRADE_NO_ASSERT |
||||||
|
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); |
||||||
|
#endif |
||||||
|
|
||||||
|
std::ostringstream out; |
||||||
|
Error redirectError{&out}; |
||||||
|
DeviceCreateInfo{_instance}.addQueues(0, {}); |
||||||
|
CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::construct() { |
||||||
|
if(std::getenv("MAGNUM_VULKAN_VERSION")) |
||||||
|
CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); |
||||||
|
|
||||||
|
{ |
||||||
|
DeviceProperties deviceProperties = pickDevice(_instance); |
||||||
|
Device device{_instance, DeviceCreateInfo{deviceProperties} |
||||||
|
.addQueues(0, {0.0f}) |
||||||
|
}; |
||||||
|
CORRADE_VERIFY(device.handle()); |
||||||
|
/* Device function pointers should be populated */ |
||||||
|
CORRADE_VERIFY(device->CreateBuffer); |
||||||
|
CORRADE_COMPARE(device.handleFlags(), HandleFlag::DestroyOnDestruction); |
||||||
|
CORRADE_COMPARE(device.version(), deviceProperties.apiVersion()); |
||||||
|
/* Device version is supported */ |
||||||
|
CORRADE_VERIFY(device.isVersionSupported(deviceProperties.apiVersion())); |
||||||
|
CORRADE_VERIFY(!device.isVersionSupported(Version::None)); |
||||||
|
/* No extensions are enabled by default ... */ |
||||||
|
CORRADE_VERIFY(!device.isExtensionEnabled<Extensions::EXT::debug_marker>()); |
||||||
|
/* ... and thus also no function pointers loaded */ |
||||||
|
CORRADE_VERIFY(!device->CmdDebugMarkerInsertEXT); |
||||||
|
} |
||||||
|
|
||||||
|
/* Shouldn't crash or anything */ |
||||||
|
CORRADE_VERIFY(true); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::constructExtensions() { |
||||||
|
if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) |
||||||
|
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); |
||||||
|
|
||||||
|
if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) |
||||||
|
CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); |
||||||
|
|
||||||
|
/* Creating a dedicated instance so we can pass custom args and enable
|
||||||
|
layers & exts independently */ |
||||||
|
Instance instance{InstanceCreateInfo{} |
||||||
|
.addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) |
||||||
|
/* Needed by VK_EXT_debug_marker */ |
||||||
|
.addEnabledExtensions<Extensions::EXT::debug_report>() |
||||||
|
}; |
||||||
|
|
||||||
|
DeviceProperties deviceProperties = pickDevice(instance); |
||||||
|
ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); |
||||||
|
if(!extensions.isSupported<Extensions::EXT::debug_marker>()) |
||||||
|
CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); |
||||||
|
if(!extensions.isSupported<Extensions::KHR::maintenance1>()) |
||||||
|
CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); |
||||||
|
|
||||||
|
Device device{instance, DeviceCreateInfo{deviceProperties} |
||||||
|
.addQueues(0, {0.0f}) |
||||||
|
.addEnabledExtensions({ |
||||||
|
Extensions::EXT::debug_marker::string(), |
||||||
|
"VK_KHR_maintenance1"_s |
||||||
|
})}; |
||||||
|
CORRADE_VERIFY(device.handle()); |
||||||
|
|
||||||
|
/* Extensions should be reported as enabled ... */ |
||||||
|
CORRADE_VERIFY(device.isExtensionEnabled<Extensions::EXT::debug_marker>()); |
||||||
|
CORRADE_VERIFY(device.isExtensionEnabled(Extensions::KHR::maintenance1{})); |
||||||
|
/* ... and function pointers loaded */ |
||||||
|
CORRADE_VERIFY(device->CmdDebugMarkerInsertEXT); |
||||||
|
CORRADE_VERIFY(device->TrimCommandPoolKHR); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::constructExtensionsCommandLineDisable() { |
||||||
|
auto&& data = ConstructCommandLineData[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.nameDisable); |
||||||
|
|
||||||
|
if(std::getenv("MAGNUM_VULKAN_VERSION")) |
||||||
|
CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); |
||||||
|
if(std::getenv("MAGNUM_DISABLE_LAYERS")) |
||||||
|
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_LAYERS environment variable set"); |
||||||
|
if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) |
||||||
|
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); |
||||||
|
|
||||||
|
if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) |
||||||
|
CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); |
||||||
|
|
||||||
|
/* Creating a dedicated instance so we can pass custom args and enable
|
||||||
|
layers independently */ |
||||||
|
Instance instance{InstanceCreateInfo{Int(data.argsDisable.size()), data.argsDisable} |
||||||
|
.addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) |
||||||
|
/* Needed by VK_EXT_debug_marker */ |
||||||
|
.addEnabledExtensions<Extensions::EXT::debug_report>() |
||||||
|
}; |
||||||
|
|
||||||
|
DeviceProperties deviceProperties = pickDevice(instance); |
||||||
|
ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); |
||||||
|
if(!extensions.isSupported<Extensions::EXT::debug_marker>()) |
||||||
|
CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); |
||||||
|
if(!extensions.isSupported<Extensions::KHR::maintenance1>()) |
||||||
|
CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); |
||||||
|
|
||||||
|
std::ostringstream out; |
||||||
|
Debug redirectOutput{&out}; |
||||||
|
Device device{instance, DeviceCreateInfo{deviceProperties, DeviceCreateInfo::Flag::NoImplicitExtensions} |
||||||
|
.addQueues(0, {0.0f}) |
||||||
|
.addEnabledExtensions< |
||||||
|
Extensions::EXT::debug_marker, |
||||||
|
Extensions::KHR::maintenance1 |
||||||
|
>()}; |
||||||
|
CORRADE_VERIFY(device.handle()); |
||||||
|
CORRADE_COMPARE(device.isVersionSupported(deviceProperties.apiVersion()), data.driverVersionSupported); |
||||||
|
CORRADE_COMPARE(device.isExtensionEnabled<Extensions::EXT::debug_marker>(), data.debugMarkerEnabled); |
||||||
|
CORRADE_COMPARE(device.isExtensionEnabled<Extensions::KHR::maintenance1>(), data.maintenance1Enabled); |
||||||
|
|
||||||
|
/** @todo cleanup when Debug::toString() or some similar utility exists */ |
||||||
|
UnsignedInt major = versionMajor(deviceProperties.apiVersion()); |
||||||
|
UnsignedInt minor = versionMinor(deviceProperties.apiVersion()); |
||||||
|
UnsignedInt patch = versionPatch(deviceProperties.apiVersion()); |
||||||
|
/* SwiftShader reports just 1.1 with no patch version, special-case that */ |
||||||
|
CORRADE_COMPARE(out.str(), Utility::formatString(data.log, major, minor, patch ? Utility::formatString(".{}", patch) : "")); |
||||||
|
|
||||||
|
/* Verify that the entrypoint is actually (not) loaded as expected, to
|
||||||
|
avoid all the above reporting being just smoke & mirrors */ |
||||||
|
CORRADE_COMPARE(!!device->CmdDebugMarkerInsertEXT, data.debugMarkerEnabled); |
||||||
|
CORRADE_COMPARE(!!device->TrimCommandPoolKHR, data.maintenance1Enabled); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::constructExtensionsCommandLineEnable() { |
||||||
|
auto&& data = ConstructCommandLineData[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.nameEnable); |
||||||
|
|
||||||
|
if(std::getenv("MAGNUM_VULKAN_VERSION")) |
||||||
|
CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); |
||||||
|
if(std::getenv("MAGNUM_DISABLE_LAYERS")) |
||||||
|
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_LAYERS environment variable set"); |
||||||
|
if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) |
||||||
|
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); |
||||||
|
|
||||||
|
if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) |
||||||
|
CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); |
||||||
|
|
||||||
|
/* Creating a dedicated instance so we can pass custom args and enable
|
||||||
|
layers independently */ |
||||||
|
Instance instance{InstanceCreateInfo{Int(data.argsEnable.size()), data.argsEnable} |
||||||
|
.addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) |
||||||
|
/* Needed by VK_EXT_debug_marker */ |
||||||
|
.addEnabledExtensions<Extensions::EXT::debug_report>() |
||||||
|
}; |
||||||
|
|
||||||
|
DeviceProperties deviceProperties = pickDevice(instance); |
||||||
|
ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); |
||||||
|
if(!extensions.isSupported<Extensions::EXT::debug_marker>()) |
||||||
|
CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); |
||||||
|
if(!extensions.isSupported<Extensions::KHR::maintenance1>()) |
||||||
|
CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); |
||||||
|
|
||||||
|
std::ostringstream out; |
||||||
|
Debug redirectOutput{&out}; |
||||||
|
Device device{instance, DeviceCreateInfo{instance, DeviceCreateInfo::Flag::NoImplicitExtensions} |
||||||
|
.addQueues(0, {0.0f}) |
||||||
|
/* Nothing enabled by the application */ |
||||||
|
}; |
||||||
|
CORRADE_VERIFY(device.handle()); |
||||||
|
CORRADE_COMPARE(device.isVersionSupported(deviceProperties.apiVersion()), data.driverVersionSupported); |
||||||
|
CORRADE_COMPARE(device.isExtensionEnabled<Extensions::EXT::debug_marker>(), data.debugMarkerEnabled); |
||||||
|
CORRADE_COMPARE(device.isExtensionEnabled<Extensions::KHR::maintenance1>(), data.maintenance1Enabled); |
||||||
|
|
||||||
|
/** @todo cleanup when Debug::toString() or some similar utility exists */ |
||||||
|
UnsignedInt major = versionMajor(deviceProperties.apiVersion()); |
||||||
|
UnsignedInt minor = versionMinor(deviceProperties.apiVersion()); |
||||||
|
UnsignedInt patch = versionPatch(deviceProperties.apiVersion()); |
||||||
|
/* SwiftShader reports just 1.1 with no patch version, special-case that */ |
||||||
|
CORRADE_COMPARE(out.str(), Utility::formatString(data.log, major, minor, patch ? Utility::formatString(".{}", patch) : "")); |
||||||
|
|
||||||
|
/* Verify that the entrypoint is actually (not) loaded as expected, to
|
||||||
|
avoid all the above reporting being just smoke & mirrors */ |
||||||
|
CORRADE_COMPARE(!!device->CmdDebugMarkerInsertEXT, data.debugMarkerEnabled); |
||||||
|
CORRADE_COMPARE(!!device->TrimCommandPoolKHR, data.maintenance1Enabled); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::constructMove() { |
||||||
|
DeviceProperties deviceProperties = pickDevice(_instance); |
||||||
|
ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties(); |
||||||
|
if(!extensions.isSupported<Extensions::KHR::maintenance1>()) |
||||||
|
CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); |
||||||
|
|
||||||
|
Device a{_instance, DeviceCreateInfo{deviceProperties} |
||||||
|
.addQueues(0, {0.0f}) |
||||||
|
.addEnabledExtensions<Extensions::KHR::maintenance1>() |
||||||
|
}; |
||||||
|
VkDevice handle = a.handle(); |
||||||
|
Version version = a.version(); |
||||||
|
CORRADE_VERIFY(handle); |
||||||
|
CORRADE_VERIFY(version != Version{}); |
||||||
|
CORRADE_VERIFY(version != Version::None); |
||||||
|
|
||||||
|
Device b = std::move(a); |
||||||
|
CORRADE_VERIFY(!a.handle()); |
||||||
|
CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); |
||||||
|
CORRADE_COMPARE(b.handle(), handle); |
||||||
|
CORRADE_COMPARE(b.version(), version); |
||||||
|
CORRADE_VERIFY(b.isExtensionEnabled<Extensions::KHR::maintenance1>()); |
||||||
|
/* Function pointers in a are left in whatever state they were before, as
|
||||||
|
that doesn't matter */ |
||||||
|
CORRADE_VERIFY(b->CreateBuffer); |
||||||
|
|
||||||
|
Device c{NoCreate}; |
||||||
|
c = std::move(b); |
||||||
|
CORRADE_VERIFY(!b.handle()); |
||||||
|
CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); |
||||||
|
CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); |
||||||
|
CORRADE_COMPARE(c.handle(), handle); |
||||||
|
CORRADE_COMPARE(c.version(), version); |
||||||
|
CORRADE_VERIFY(c.isExtensionEnabled<Extensions::KHR::maintenance1>()); |
||||||
|
/* Everything is swapped, including function pointers */ |
||||||
|
CORRADE_VERIFY(!b->CreateBuffer); |
||||||
|
CORRADE_VERIFY(c->CreateBuffer); |
||||||
|
|
||||||
|
CORRADE_VERIFY(std::is_nothrow_move_constructible<Device>::value); |
||||||
|
CORRADE_VERIFY(std::is_nothrow_move_assignable<Device>::value); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::constructUnknownExtension() { |
||||||
|
CORRADE_SKIP("Currently this hits an internal assert, which can't be tested."); |
||||||
|
|
||||||
|
std::ostringstream out; |
||||||
|
Error redirectError{&out}; |
||||||
|
Device device{_instance, DeviceCreateInfo{_instance} |
||||||
|
.addQueues(0, {0.0f}) |
||||||
|
.addEnabledExtensions({"VK_this_doesnt_exist"_s})}; |
||||||
|
CORRADE_COMPARE(out.str(), "TODO"); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::constructNoQueue() { |
||||||
|
#ifdef CORRADE_NO_ASSERT |
||||||
|
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); |
||||||
|
#endif |
||||||
|
|
||||||
|
std::ostringstream out; |
||||||
|
Error redirectError{&out}; |
||||||
|
Device device{_instance, DeviceCreateInfo{_instance}}; |
||||||
|
CORRADE_COMPARE(out.str(), "Vk::Device: needs to be created with at least one queue\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::wrap() { |
||||||
|
if(std::getenv("MAGNUM_VULKAN_VERSION")) |
||||||
|
CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); |
||||||
|
if(std::getenv("MAGNUM_DISABLE_LAYERS")) |
||||||
|
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_LAYERS environment variable set"); |
||||||
|
if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) |
||||||
|
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); |
||||||
|
|
||||||
|
if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) |
||||||
|
CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); |
||||||
|
|
||||||
|
/* Creating a dedicated instance so we can enable layers independently */ |
||||||
|
Instance instance{InstanceCreateInfo{} |
||||||
|
.addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) |
||||||
|
/* Needed by VK_EXT_debug_marker */ |
||||||
|
.addEnabledExtensions<Extensions::EXT::debug_report>() |
||||||
|
}; |
||||||
|
|
||||||
|
DeviceProperties deviceProperties = pickDevice(instance); |
||||||
|
ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); |
||||||
|
if(!extensions.isSupported<Extensions::EXT::debug_marker>()) |
||||||
|
CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); |
||||||
|
if(!extensions.isSupported<Extensions::KHR::maintenance1>()) |
||||||
|
CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); |
||||||
|
|
||||||
|
VkDevice device; |
||||||
|
CORRADE_COMPARE(Result(instance->CreateDevice(deviceProperties, |
||||||
|
DeviceCreateInfo{instance} |
||||||
|
.addQueues(0, {0.0f}) |
||||||
|
.addEnabledExtensions< |
||||||
|
Extensions::EXT::debug_marker, |
||||||
|
Extensions::KHR::maintenance1 |
||||||
|
>(), |
||||||
|
nullptr, &device)), Result::Success); |
||||||
|
CORRADE_VERIFY(device); |
||||||
|
|
||||||
|
{ |
||||||
|
/* Wrapping should load the basic function pointers */ |
||||||
|
auto wrapped = Device::wrap(instance, device, Version::Vk11, { |
||||||
|
Extensions::EXT::debug_marker::string() |
||||||
|
}, HandleFlag::DestroyOnDestruction); |
||||||
|
CORRADE_VERIFY(wrapped->DestroyDevice); |
||||||
|
|
||||||
|
/* Specified version should be reported as supported but higher not
|
||||||
|
regardless of the actual driver version */ |
||||||
|
CORRADE_VERIFY(wrapped.isVersionSupported(Version::Vk11)); |
||||||
|
CORRADE_VERIFY(!wrapped.isVersionSupported(Version::Vk12)); |
||||||
|
|
||||||
|
/* Listed extensions should be reported as enabled and function
|
||||||
|
pointers loaded as well */ |
||||||
|
CORRADE_VERIFY(wrapped.isExtensionEnabled<Extensions::EXT::debug_marker>()); |
||||||
|
CORRADE_VERIFY(wrapped->CmdDebugMarkerInsertEXT); |
||||||
|
|
||||||
|
/* Unlisted not, but function pointers should still be loaded as the
|
||||||
|
actual instance does have the extension enabled */ |
||||||
|
CORRADE_VERIFY(!wrapped.isExtensionEnabled<Extensions::KHR::maintenance1>()); |
||||||
|
CORRADE_VERIFY(wrapped->TrimCommandPoolKHR); |
||||||
|
|
||||||
|
/* Releasing won't destroy anything ... */ |
||||||
|
CORRADE_COMPARE(wrapped.release(), device); |
||||||
|
} |
||||||
|
|
||||||
|
/* ...so we can wrap it again, non-owned, and then destroy it manually */ |
||||||
|
auto wrapped = Device::wrap(instance, device, Version::Vk10, {}); |
||||||
|
CORRADE_VERIFY(wrapped->DestroyDevice); |
||||||
|
wrapped->DestroyDevice(device, nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
void DeviceVkTest::populateGlobalFunctionPointers() { |
||||||
|
vkDestroyDevice = nullptr; |
||||||
|
|
||||||
|
Device device{_instance, DeviceCreateInfo{_instance} |
||||||
|
.addQueues(0, {0.0f}) |
||||||
|
}; |
||||||
|
CORRADE_VERIFY(!vkDestroyDevice); |
||||||
|
device.populateGlobalFunctionPointers(); |
||||||
|
CORRADE_VERIFY(vkDestroyDevice); |
||||||
|
CORRADE_VERIFY(vkDestroyDevice == device->DestroyDevice); |
||||||
|
} |
||||||
|
|
||||||
|
}}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::Vk::Test::DeviceVkTest) |
||||||
Loading…
Reference in new issue