/* This file is part of Magnum. Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Vladimír Vondruš 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 #include #include #include #include #include #include #include "Magnum/Vk/DeviceCreateInfo.h" #include "Magnum/Vk/DeviceFeatures.h" #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/InstanceCreateInfo.h" #include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Queue.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" #include "Magnum/Vk/VulkanTester.h" #include "Magnum/Vk/Implementation/DeviceFeatures.h" /* for deviceFeaturesPortabilitySubset() */ #include "MagnumExternal/Vulkan/flextVkGlobal.h" namespace Magnum { namespace Vk { namespace Test { namespace { struct DeviceVkTest: VulkanTester { explicit DeviceVkTest(); void createInfoConstruct(); void createInfoConstructNoImplicitExtensions(); void createInfoExtensions(); void createInfoExtensionsCopiedStrings(); void createInfoFeatures(); void createInfoFeaturesReplaceExternal(); void createInfoFeaturesReplacePrevious(); void createInfoFeaturesEnableAllResetAll(); void createInfoFeaturesNothingInCoreFeatures(); void createInfoNoQueuePriorities(); void createInfoWrongQueueOutputCount(); void createInfoConstructCopy(); void createInfoConstructMove(); void createInfoRvalue(); void construct(); void constructQueueFromFlags(); void constructExtensions(); void constructFeatures(); void constructFeaturesFromExtensions(); void constructDeviceCreateInfoConstReference(); void constructTransferDeviceProperties(); void constructExtensionsCommandLineDisable(); void constructExtensionsCommandLineEnable(); void constructWorkaroundsCommandLineDisable(); void constructMultipleQueues(); void constructRawQueue(); void constructFeatureNotSupported(); void constructFeatureWithoutExtension(); void constructNoQueue(); void constructNoPortability(); void constructNoPortabilityEnablePortabilityFeatures(); void constructPortability(); void tryCreateAlreadyCreated(); void tryCreateUnknownExtension(); void wrap(); void wrapAlreadyCreated(); void populateGlobalFunctionPointers(); }; struct { const char* nameDisable; const char* nameEnable; Containers::Array argsDisable, argsEnable; bool driverVersionSupported, debugMarkerEnabled, maintenance1Enabled; const char* log; } ConstructCommandLineData[] { /* Shouldn't print anything about device/version, enabled layers/exts if quiet 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: {}, Vulkan {}.{}{}\n" "Device driver: {}, {}\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: {}, Vulkan 1.0\n" "Device driver: {4}, {5}\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: {}, Vulkan {}.{}{}\n" "Device driver: {}, {}\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: {}, Vulkan {}.{}{}\n" "Device driver: {}, {}\n"}, }; struct { const char* name; bool shouldPassAlways; Containers::Array args; const char* log; } ConstructWorkaroundsCommandLineData[] { {"default", false, Containers::array({"", /* Have to force version to 1.0 because on 1.1 SwiftShader requires the "swiftshader-crashy-getdevicequeue2" workaround, they can't be all disabled */ "--magnum-vulkan-version", "1.0"}), "Device: {}, Vulkan 1.0\n" "Device driver: {}, {}\n" "Using device driver workarounds:\n" " swiftshader-image-copy-extent-instead-of-layers\n" " swiftshader-spirv-multi-entrypoint-conflicting-locations\n"}, /* Shouldn't print anything if quiet output is enabled */ {"quiet", true, Containers::array({"", "--magnum-log", "quiet"}), ""}, {"disabled workarounds", true, Containers::array({"", /* Have to force version to 1.0 because on 1.1 SwiftShader requires the "swiftshader-crashy-getdevicequeue2" workaround, they can't be all disabled */ "--magnum-vulkan-version", "1.0", "--magnum-disable-workarounds", "swiftshader-image-copy-extent-instead-of-layers swiftshader-spirv-multi-entrypoint-conflicting-locations"}), "Device: {}, Vulkan 1.0\n" "Device driver: {}, {}\n"} }; DeviceVkTest::DeviceVkTest(): VulkanTester{NoCreate} { addTests({&DeviceVkTest::createInfoConstruct, &DeviceVkTest::createInfoConstructNoImplicitExtensions, &DeviceVkTest::createInfoExtensions, &DeviceVkTest::createInfoExtensionsCopiedStrings, &DeviceVkTest::createInfoFeatures, &DeviceVkTest::createInfoFeaturesReplaceExternal, &DeviceVkTest::createInfoFeaturesReplacePrevious, &DeviceVkTest::createInfoFeaturesEnableAllResetAll, &DeviceVkTest::createInfoFeaturesNothingInCoreFeatures, &DeviceVkTest::createInfoNoQueuePriorities, &DeviceVkTest::createInfoWrongQueueOutputCount, &DeviceVkTest::createInfoConstructCopy, &DeviceVkTest::createInfoConstructMove, &DeviceVkTest::createInfoRvalue, &DeviceVkTest::construct, &DeviceVkTest::constructQueueFromFlags, &DeviceVkTest::constructExtensions, &DeviceVkTest::constructFeatures, &DeviceVkTest::constructFeaturesFromExtensions, &DeviceVkTest::constructDeviceCreateInfoConstReference, &DeviceVkTest::constructTransferDeviceProperties}); addInstancedTests({&DeviceVkTest::constructExtensionsCommandLineDisable, &DeviceVkTest::constructExtensionsCommandLineEnable}, Containers::arraySize(ConstructCommandLineData)); addInstancedTests({&DeviceVkTest::constructWorkaroundsCommandLineDisable}, Containers::arraySize(ConstructWorkaroundsCommandLineData)); addTests({&DeviceVkTest::constructMultipleQueues, &DeviceVkTest::constructRawQueue, &DeviceVkTest::constructFeatureNotSupported, &DeviceVkTest::constructFeatureWithoutExtension, &DeviceVkTest::constructNoQueue, &DeviceVkTest::constructNoPortability, &DeviceVkTest::constructNoPortabilityEnablePortabilityFeatures, &DeviceVkTest::constructPortability, &DeviceVkTest::tryCreateAlreadyCreated, &DeviceVkTest::tryCreateUnknownExtension, &DeviceVkTest::wrap, &DeviceVkTest::wrapAlreadyCreated, &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 */ CORRADE_VERIFY(!info->pEnabledFeatures); } void DeviceVkTest::createInfoConstructNoImplicitExtensions() { DeviceCreateInfo info{pickDevice(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); CORRADE_VERIFY(!info->pEnabledFeatures); } void DeviceVkTest::createInfoExtensions() { if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); DeviceCreateInfo info{pickDevice(instance()), DeviceCreateInfo::Flag::NoImplicitExtensions}; CORRADE_VERIFY(!info->ppEnabledExtensionNames); CORRADE_COMPARE(info->enabledExtensionCount, 0); info.addEnabledExtensions(); CORRADE_VERIFY(info->ppEnabledExtensionNames); CORRADE_COMPARE(info->enabledExtensionCount, 1); /* The pointer should be to the global data */ CORRADE_COMPARE(static_cast(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(info->ppEnabledExtensionNames[0]), Extensions::KHR::maintenance1::string().data()); CORRADE_COMPARE(static_cast(info->ppEnabledExtensionNames[1]), Extensions::KHR::draw_indirect_count::string().data()); CORRADE_COMPARE(static_cast(info->ppEnabledExtensionNames[2]), Extensions::KHR::get_memory_requirements2::string().data()); } void DeviceVkTest::createInfoExtensionsCopiedStrings() { if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); Containers::StringView globalButNotNullTerminated = "VK_KHR_maintenance25"_s.except(1); Containers::String localButNullTerminated = Extensions::KHR::draw_indirect_count::string(); DeviceCreateInfo info{pickDevice(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::createInfoFeatures() { DeviceProperties properties = pickDevice(instance()); /* We don't use the structure for anything, so we don't need to check if the structure is actually supported */ DeviceCreateInfo info{properties}; info.setEnabledFeatures(DeviceFeature::RobustBufferAccess|DeviceFeature::SamplerYcbcrConversion); /* If we have Vulkan 1.1 on both instance and the device or KHR_gpdp2 is enabled on the instance, pNext chain will be filled as appropriate */ if((instance().isVersionSupported(Version::Vk11) && properties.isVersionSupported(Version::Vk11)) || instance().isExtensionEnabled()) { CORRADE_VERIFY(!info->pEnabledFeatures); CORRADE_VERIFY(info->pNext); const auto& features2 = *static_cast(info->pNext); CORRADE_COMPARE(features2.sType, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2); CORRADE_VERIFY(features2.features.robustBufferAccess); CORRADE_VERIFY(features2.pNext); const auto& samplerYcbcrConversionFeatures = *static_cast(features2.pNext); CORRADE_COMPARE(samplerYcbcrConversionFeatures.sType, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES); CORRADE_VERIFY(samplerYcbcrConversionFeatures.samplerYcbcrConversion); /* Otherwise just the pEnabledFeatures will be enabled */ } else { CORRADE_VERIFY(!info->pNext); CORRADE_VERIFY(info->pEnabledFeatures); CORRADE_VERIFY(info->pEnabledFeatures->robustBufferAccess); } } void DeviceVkTest::createInfoFeaturesNothingInCoreFeatures() { DeviceProperties properties = pickDevice(instance()); DeviceCreateInfo info{properties}; info.setEnabledFeatures(DeviceFeature::SamplerYcbcrConversion|DeviceFeature::ImagelessFramebuffer); /* If we have Vulkan 1.1 on both instance and the device or KHR_gpdp2 is enabled on the instance, pNext chain will be filled as appropriate */ if((instance().isVersionSupported(Version::Vk11) && properties.isVersionSupported(Version::Vk11)) || instance().isExtensionEnabled()) { CORRADE_VERIFY(!info->pEnabledFeatures); CORRADE_VERIFY(info->pNext); const auto& imagelessFramebufferFeatures = *static_cast(info->pNext); CORRADE_COMPARE(imagelessFramebufferFeatures.sType, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES); CORRADE_VERIFY(imagelessFramebufferFeatures.imagelessFramebuffer); const auto& samplerYcbcrConversionFeatures = *static_cast(imagelessFramebufferFeatures.pNext); CORRADE_COMPARE(samplerYcbcrConversionFeatures.sType, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES); CORRADE_VERIFY(samplerYcbcrConversionFeatures.samplerYcbcrConversion); /* Otherwise nothing is enabled as there's nowhere to connect that */ } else { CORRADE_VERIFY(!info->pNext); CORRADE_VERIFY(!info->pEnabledFeatures); } } void DeviceVkTest::createInfoFeaturesReplaceExternal() { DeviceProperties properties = pickDevice(instance()); VkPhysicalDeviceFeatures2 features{}; features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; VkAttachmentReference2 somethingAfter{}; somethingAfter.pNext = &features; DeviceCreateInfo info{properties}; info->pNext = &somethingAfter; info->pEnabledFeatures = &features.features; info.setEnabledFeatures(DeviceFeature::RobustBufferAccess); /* Then, if we have Vulkan 1.1 on both instance and the device or KHR_gpdp2 is enabled on the instance, pNext will be filled and pEnabledFeatures reset */ if((instance().isVersionSupported(Version::Vk11) && properties.isVersionSupported(Version::Vk11)) || instance().isExtensionEnabled()) { CORRADE_VERIFY(!info->pEnabledFeatures); CORRADE_VERIFY(info->pNext); const auto& features2 = *static_cast(info->pNext); CORRADE_COMPARE(features2.sType, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2); CORRADE_VERIFY(features2.features.robustBufferAccess); /* The original chain should be preserved after */ CORRADE_COMPARE(features2.pNext, &somethingAfter); /* Otherwise the pNext chain will be preserved and pEnabledFeatures replaced with own instance */ } else { CORRADE_VERIFY(info->pEnabledFeatures); CORRADE_VERIFY(info->pEnabledFeatures != &features.features); CORRADE_VERIFY(info->pEnabledFeatures->robustBufferAccess); } /* No changes to the original chain, even though it has a features on its own (that's user error) */ CORRADE_COMPARE(somethingAfter.pNext, &features); } void DeviceVkTest::createInfoFeaturesReplacePrevious() { DeviceProperties properties = pickDevice(instance()); VkAttachmentReference2 somethingAfter{}; DeviceCreateInfo info{properties}; info->pNext = &somethingAfter; info.setEnabledFeatures(DeviceFeature::RobustBufferAccess); /* If we have Vulkan 1.1 on both instance and the device or KHR_gpdp2 is enabled on the instance, pNext chain will be filled as appropriate */ if((instance().isVersionSupported(Version::Vk11) && properties.isVersionSupported(Version::Vk11)) || instance().isExtensionEnabled()) { CORRADE_VERIFY(!info->pEnabledFeatures); CORRADE_VERIFY(info->pNext); const auto& features2 = *static_cast(info->pNext); CORRADE_COMPARE(features2.sType, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2); CORRADE_VERIFY(features2.features.robustBufferAccess); /* The original chain should be preserved after */ CORRADE_COMPARE(features2.pNext, &somethingAfter); /* Otherwise the pNext chain will be preserved and pEnabledFeatures replaced with own instance */ } else { CORRADE_COMPARE(info->pNext, &somethingAfter); CORRADE_VERIFY(info->pEnabledFeatures); CORRADE_VERIFY(info->pEnabledFeatures->robustBufferAccess); } /* Setting a different non-core feature */ info.setEnabledFeatures(DeviceFeature::ImagelessFramebuffer); if((instance().isVersionSupported(Version::Vk11) && properties.isVersionSupported(Version::Vk11)) || instance().isExtensionEnabled()) { CORRADE_VERIFY(!info->pEnabledFeatures); CORRADE_VERIFY(info->pNext); const auto& imagelessFramebufferFeatures = *static_cast(info->pNext); CORRADE_COMPARE(imagelessFramebufferFeatures.sType, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES); CORRADE_VERIFY(imagelessFramebufferFeatures.imagelessFramebuffer); /* The original chain should still be preserved after, without the structures from the previous case getting in the way */ CORRADE_COMPARE(imagelessFramebufferFeatures.pNext, &somethingAfter); /* Otherwise the pNext chain will still be preserved and pEnabledFeatures empty */ } else { CORRADE_COMPARE(info->pNext, &somethingAfter); CORRADE_VERIFY(!info->pEnabledFeatures); } /* Setting no features, everything should be fully discarded, and the original chain still kept. This doesn't have any difference between versions. */ info.setEnabledFeatures({}); CORRADE_COMPARE(info->pNext, &somethingAfter); CORRADE_VERIFY(!info->pEnabledFeatures); CORRADE_VERIFY(!somethingAfter.pNext); } void DeviceVkTest::createInfoFeaturesEnableAllResetAll() { DeviceProperties properties = pickDevice(instance()); if((!instance().isVersionSupported(Version::Vk11) || !properties.isVersionSupported(Version::Vk11)) && !instance().isExtensionEnabled()) CORRADE_SKIP("Neither Vulkan 1.1 nor KHR_get_physical_device_properties2 is supported, can't test"); VkAttachmentDescription2 somethingAfter{}; somethingAfter.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2; DeviceCreateInfo info{properties}; info->pNext = &somethingAfter; /* This should populate a huge chain of structures */ info.setEnabledFeatures(~DeviceFeatures{}); CORRADE_VERIFY(info->pNext != &somethingAfter); CORRADE_VERIFY(!somethingAfter.pNext); /* And this should disconnect them all again. If this fails, it means the resetting code path got out of sync with the structure list. Sorry, there's not really a better way how to show *where* it got wrong. */ info.setEnabledFeatures({}); CORRADE_COMPARE(info->pNext, &somethingAfter); CORRADE_VERIFY(!somethingAfter.pNext); } 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{pickDevice(instance())}.addQueues(0, {}, {}); CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified\n"); } void DeviceVkTest::createInfoWrongQueueOutputCount() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif std::ostringstream out; Error redirectError{&out}; Queue a{NoCreate}, b{NoCreate}; DeviceCreateInfo{pickDevice(instance())}.addQueues(0, {0.0f, 1.0f, 0.3f}, {a, b}); CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): expected 3 outuput queue references but got 2\n"); } void DeviceVkTest::createInfoConstructCopy() { CORRADE_VERIFY(!std::is_copy_constructible{}); CORRADE_VERIFY(!std::is_copy_assignable{}); } void DeviceVkTest::createInfoConstructMove() { if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); Queue queue{NoCreate}; DeviceCreateInfo a{pickDevice(instance()), DeviceCreateInfo::Flag::NoImplicitExtensions}; a.addQueues(0, {0.35f}, {queue}) .addEnabledExtensions(); DeviceCreateInfo b{std::move(a)}; CORRADE_COMPARE(a->enabledExtensionCount, 0); CORRADE_VERIFY(!a->ppEnabledExtensionNames); CORRADE_COMPARE(a->queueCreateInfoCount, 0); CORRADE_VERIFY(!a->pQueueCreateInfos); CORRADE_COMPARE(b->enabledExtensionCount, 2); CORRADE_VERIFY(b->ppEnabledExtensionNames); CORRADE_COMPARE(b->ppEnabledExtensionNames[1], "VK_KHR_bind_memory2"_s); CORRADE_COMPARE(b->queueCreateInfoCount, 1); CORRADE_VERIFY(b->pQueueCreateInfos); CORRADE_COMPARE(b->pQueueCreateInfos[0].pQueuePriorities[0], 0.35f); DeviceCreateInfo c{{}, {}}; c = std::move(b); CORRADE_COMPARE(b->enabledExtensionCount, 0); CORRADE_VERIFY(!b->ppEnabledExtensionNames); CORRADE_COMPARE(b->queueCreateInfoCount, 0); CORRADE_VERIFY(!b->pQueueCreateInfos); CORRADE_COMPARE(c->enabledExtensionCount, 2); CORRADE_VERIFY(c->ppEnabledExtensionNames); CORRADE_COMPARE(c->ppEnabledExtensionNames[1], "VK_KHR_bind_memory2"_s); CORRADE_COMPARE(c->queueCreateInfoCount, 1); CORRADE_VERIFY(c->pQueueCreateInfos); CORRADE_COMPARE(c->pQueueCreateInfos[0].pQueuePriorities[0], 0.35f); } void DeviceVkTest::createInfoRvalue() { /* Verify that there actually are graphics queues so we don't exit inside addQueues() */ CORRADE_VERIFY(pickDevice(instance()).tryPickQueueFamily(QueueFlag::Graphics)); Float zero[1]{}; Queue a{NoCreate}, b{NoCreate}, c{NoCreate}, d{NoCreate}; Containers::Reference referenceA[1]{a}; Containers::Reference referenceC[1]{c}; VkDeviceQueueCreateInfo rawQueueInfo{}; rawQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; rawQueueInfo.pQueuePriorities = zero; rawQueueInfo.queueFamilyIndex = 0; rawQueueInfo.queueCount = 1; DeviceCreateInfo&& info = DeviceCreateInfo{pickDevice(instance())} .addEnabledExtensions(Containers::ArrayView{}) .addEnabledExtensions(std::initializer_list{}) .addEnabledExtensions(Containers::ArrayView{}) .addEnabledExtensions(std::initializer_list{}) .addEnabledExtensions<>() .addQueues(0, zero, referenceA) .addQueues(0, {0.0f}, {b}) .addQueues(QueueFlag::Graphics, zero, referenceC) .addQueues(QueueFlag::Graphics, {0.0f}, {d}) .addQueues(rawQueueInfo); /* Just to test something, main point is that the above compiles, links and returns a &&. Can't test anything related to the contents because the destructor gets called at the end of the expression. */ CORRADE_VERIFY(&info); } 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()); Queue queue{NoCreate}; Device device{instance(), DeviceCreateInfo{deviceProperties} .addQueues(0, {0.0f}, {queue}) }; 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.version()); /* Device version is supported */ CORRADE_VERIFY(device.isVersionSupported(deviceProperties.version())); CORRADE_VERIFY(!device.isVersionSupported(Version::None)); /* No extensions are enabled by default ... */ CORRADE_VERIFY(!device.isExtensionEnabled()); /* ... and thus also no function pointers loaded */ CORRADE_VERIFY(!device->CmdDebugMarkerInsertEXT); /* The queue should be also filled in */ CORRADE_VERIFY(queue.handle()); /* Device properties should be lazy-populated and different from the above instances because we didn't transfer the ownership */ CORRADE_COMPARE(device.properties().name(), deviceProperties.name()); CORRADE_VERIFY(&device.properties().properties() != &deviceProperties.properties()); } /* Shouldn't crash or anything */ CORRADE_VERIFY(true); } void DeviceVkTest::constructQueueFromFlags() { DeviceProperties deviceProperties = pickDevice(instance()); /* Verify that there actually are graphics queues so we don't exit after */ CORRADE_VERIFY(deviceProperties.tryPickQueueFamily(QueueFlag::Graphics)); Queue queue{NoCreate}; Device device{instance(), DeviceCreateInfo{deviceProperties} .addQueues(QueueFlag::Graphics, {0.0f}, {queue})}; CORRADE_VERIFY(device.handle()); /* The queue should be filled in like usual */ CORRADE_VERIFY(queue.handle()); } 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() }; DeviceProperties deviceProperties = pickDevice(instance); ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); if(!extensions.isSupported()) CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); if(!extensions.isSupported()) CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); Queue queue{NoCreate}; Device device{instance, DeviceCreateInfo{deviceProperties} .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions({ Extensions::EXT::debug_marker::string(), "VK_KHR_maintenance1"_s })}; CORRADE_VERIFY(device.handle()); /* Extensions should be reported as enabled ... */ CORRADE_VERIFY(device.isExtensionEnabled()); CORRADE_VERIFY(device.isExtensionEnabled(Extensions::KHR::maintenance1{})); /* ... and function pointers loaded */ CORRADE_VERIFY(device->CmdDebugMarkerInsertEXT); CORRADE_VERIFY(device->TrimCommandPoolKHR); } void DeviceVkTest::constructFeatures() { Queue queue{NoCreate}; Device device{instance(), DeviceCreateInfo{pickDevice(instance())} .addQueues(0, {0.0f}, {queue}) /* RobustBufferAccess is guaranteed to be supported always, no need to check anything */ .setEnabledFeatures(DeviceFeature::RobustBufferAccess)}; CORRADE_VERIFY(device.handle()); /* Features should be reported as enabled. Exclude portability subset features that get implicitly marked as enabled on devices w/o KHR_portability_subset. */ CORRADE_COMPARE(device.enabledFeatures() & ~Implementation::deviceFeaturesPortabilitySubset(), DeviceFeature::RobustBufferAccess); } void DeviceVkTest::constructFeaturesFromExtensions() { DeviceProperties properties = pickDevice(instance()); if(!properties.enumerateExtensionProperties().isSupported()) CORRADE_SKIP("VK_KHR_sampler_ycbcr_conversion not supported, can't test"); if(!(properties.features() & DeviceFeature::SamplerYcbcrConversion)) CORRADE_SKIP("SamplerYcbcrConversion feature not supported, can't test"); Queue queue{NoCreate}; Device device{instance(), DeviceCreateInfo{properties} .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions() /* RobustBufferAccess is guaranteed to be supported always, no need to check anything */ .setEnabledFeatures(DeviceFeature::RobustBufferAccess|DeviceFeature::SamplerYcbcrConversion)}; CORRADE_VERIFY(device.handle()); /* Features should be reported as enabled. Exclude portability subset features that get implicitly marked as enabled on devices w/o KHR_portability_subset. */ CORRADE_COMPARE(device.enabledFeatures() & ~Implementation::deviceFeaturesPortabilitySubset(), DeviceFeature::RobustBufferAccess|DeviceFeature::SamplerYcbcrConversion); } void DeviceVkTest::constructDeviceCreateInfoConstReference() { Queue queue{NoCreate}; DeviceProperties deviceProperties = pickDevice(instance()); DeviceCreateInfo info{deviceProperties}; info.addQueues(0, {0.0f}, {queue}); /* Just to verify the overload taking const DeviceProperties& works as well (most of the above tests verified a move) */ Device device{instance(), info}; CORRADE_VERIFY(device.handle()); /* Device properties should be lazy-populated and different from the above instances because we didn't transfer the ownership here either */ CORRADE_COMPARE(device.properties().name(), deviceProperties.name()); CORRADE_VERIFY(&device.properties().properties() != &deviceProperties.properties()); } void DeviceVkTest::constructTransferDeviceProperties() { DeviceProperties deviceProperties = pickDevice(instance()); const void* vkProperties = &deviceProperties.properties(); Queue queue{NoCreate}; Device device{instance(), DeviceCreateInfo{std::move(deviceProperties)} .addQueues(0, {0.0f}, {queue}) }; /* Device properties should be the same address as in the original instance because the ownership got transferred through */ CORRADE_COMPARE(&device.properties().properties(), vkProperties); } 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 instance2{InstanceCreateInfo{Int(data.argsDisable.size()), data.argsDisable} .addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) /* Needed by VK_EXT_debug_marker */ .addEnabledExtensions() }; DeviceProperties deviceProperties = pickDevice(instance2); ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); if(!extensions.isSupported()) CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); if(!extensions.isSupported()) CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); std::ostringstream out; Debug redirectOutput{&out}; Queue queue{NoCreate}; Device device{instance2, DeviceCreateInfo{deviceProperties, DeviceCreateInfo::Flag::NoImplicitExtensions} .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions< Extensions::EXT::debug_marker, Extensions::KHR::maintenance1 >()}; CORRADE_VERIFY(device.handle()); CORRADE_COMPARE(device.isVersionSupported(deviceProperties.version()), data.driverVersionSupported); CORRADE_COMPARE(device.isExtensionEnabled(), data.debugMarkerEnabled); CORRADE_COMPARE(device.isExtensionEnabled(), data.maintenance1Enabled); /** @todo cleanup when Debug::toString() or some similar utility exists */ UnsignedInt major = versionMajor(deviceProperties.version()); UnsignedInt minor = versionMinor(deviceProperties.version()); UnsignedInt patch = versionPatch(deviceProperties.version()); /* SwiftShader reports just 1.1 with no patch version, special-case that */ std::string expected = Utility::formatString(data.log, deviceProperties.name(), major, minor, patch ? Utility::formatString(".{}", patch) : "", deviceProperties.driverName(), deviceProperties.driverInfo()); /* The output might contain a device workaround list, cut that away. That's tested thoroughly in constructWorkaroundsCommandLineDisable(). */ CORRADE_COMPARE(out.str().substr(0, expected.size()), expected); /* 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 instance2{InstanceCreateInfo{Int(data.argsEnable.size()), data.argsEnable} .addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) /* Needed by VK_EXT_debug_marker */ .addEnabledExtensions() }; DeviceProperties deviceProperties = pickDevice(instance2); ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); if(!extensions.isSupported()) CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); if(!extensions.isSupported()) CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); std::ostringstream out; Debug redirectOutput{&out}; Queue queue{NoCreate}; Device device{instance2, DeviceCreateInfo{pickDevice(instance2), DeviceCreateInfo::Flag::NoImplicitExtensions} .addQueues(0, {0.0f}, {queue}) /* Nothing enabled by the application */ }; CORRADE_VERIFY(device.handle()); CORRADE_COMPARE(device.isVersionSupported(deviceProperties.version()), data.driverVersionSupported); CORRADE_COMPARE(device.isExtensionEnabled(), data.debugMarkerEnabled); CORRADE_COMPARE(device.isExtensionEnabled(), data.maintenance1Enabled); /** @todo cleanup when Debug::toString() or some similar utility exists */ UnsignedInt major = versionMajor(deviceProperties.version()); UnsignedInt minor = versionMinor(deviceProperties.version()); UnsignedInt patch = versionPatch(deviceProperties.version()); /* SwiftShader reports just 1.1 with no patch version, special-case that */ std::string expected = Utility::formatString(data.log, deviceProperties.name(), major, minor, patch ? Utility::formatString(".{}", patch) : "", deviceProperties.driverName(), deviceProperties.driverInfo()); /* The output might contain a device workaround list, cut that away. That's tested thoroughly in constructWorkaroundsCommandLineDisable(). */ CORRADE_COMPARE(out.str().substr(0, expected.size()), expected); /* 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::constructWorkaroundsCommandLineDisable() { auto&& data = ConstructWorkaroundsCommandLineData[testCaseInstanceId()]; setTestCaseDescription(data.name); if(std::getenv("MAGNUM_VULKAN_VERSION")) CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); /* Creating a dedicated instance so we can pass custom args */ Instance instance2{InstanceCreateInfo{Int(data.args.size()), data.args}}; DeviceProperties deviceProperties = pickDevice(instance2); if(!deviceProperties.name().hasPrefix("SwiftShader"_s) && !data.shouldPassAlways) CORRADE_SKIP("Workarounds only available on SwiftShader, can't test."); std::ostringstream out; Debug redirectOutput{&out}; Queue queue{NoCreate}; Device device{instance2, DeviceCreateInfo{deviceProperties, DeviceCreateInfo::Flag::NoImplicitExtensions} .addQueues(0, {0.0f}, {queue}) }; CORRADE_VERIFY(device.handle()); CORRADE_COMPARE(out.str(), Utility::formatString(data.log, deviceProperties.name(), deviceProperties.driverName(), deviceProperties.driverInfo())); } void DeviceVkTest::constructMultipleQueues() { /* Find a GPU that has at least two queue families and at least four queues in one family */ Containers::Array deviceProperties = enumerateDevices(instance()); DeviceProperties* deviceWithMultipleQueues = nullptr; UnsignedInt largeFamily = ~UnsignedInt{}; for(DeviceProperties& i: deviceProperties) { if(i.queueFamilyCount() < 2) continue; for(UnsignedInt family = 0; family != i.queueFamilyCount(); ++family) { if(i.queueFamilySize(family) < 4) continue; largeFamily = family; break; } deviceWithMultipleQueues = &i; break; } if(!deviceWithMultipleQueues || largeFamily == ~UnsignedInt{}) CORRADE_SKIP("No device with at least two queue families and at least four queues in one family found, can't test"); const UnsignedInt otherFamily = largeFamily == 0 ? 1 : 0; constexpr Float zero = 0.0f; VkDeviceQueueCreateInfo rawQueueInfo{}; rawQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; rawQueueInfo.pQueuePriorities = &zero; rawQueueInfo.queueFamilyIndex = otherFamily; rawQueueInfo.queueCount = 1; Queue a{NoCreate}, b{NoCreate}, c{NoCreate}; Device device{instance(), DeviceCreateInfo{*deviceWithMultipleQueues} /* Request a raw queue in the middle of it all to test we skip it when populating the outputs, and correctly offset the next IDs. According to the spec we can request each family only once, which makes the implementation and testing slightly simpler. */ .addQueues(rawQueueInfo) /* Request multiple queues in a single family to test we correctly loop over these as well */ .addQueues(largeFamily, {0.5f, 0.75f, 1.0f}, {a, b, c})}; /* All queues should be found and different */ CORRADE_VERIFY(a.handle()); CORRADE_VERIFY(b.handle()); CORRADE_VERIFY(c.handle()); CORRADE_VERIFY(a.handle() != b.handle()); CORRADE_VERIFY(a.handle() != c.handle()); CORRADE_VERIFY(b.handle() != c.handle()); /* Fetching the same queue again should give the same handle */ VkQueue aAgain; device->GetDeviceQueue(device, largeFamily, 0, &aAgain); CORRADE_COMPARE(aAgain, a.handle()); /* Fetch the raw queue, should be different from everything else as well */ VkQueue rawQueue; device->GetDeviceQueue(device, otherFamily, 0, &rawQueue); CORRADE_VERIFY(rawQueue); CORRADE_VERIFY(rawQueue != a.handle()); CORRADE_VERIFY(rawQueue != b.handle()); CORRADE_VERIFY(rawQueue != c.handle()); } void DeviceVkTest::constructRawQueue() { /* Testing a subset of constructQueues() again because not all drivers have multiple queues and we want to have the coverage */ constexpr Float zero = 0.0f; VkDeviceQueueCreateInfo rawQueueInfo{}; rawQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; rawQueueInfo.pQueuePriorities = &zero; rawQueueInfo.queueFamilyIndex = 0; rawQueueInfo.queueCount = 1; Device device{instance(), DeviceCreateInfo{pickDevice(instance())} .addQueues(rawQueueInfo)}; /* Fetch the raw queue */ VkQueue rawQueue; device->GetDeviceQueue(device, 0, 0, &rawQueue); CORRADE_VERIFY(rawQueue); } void DeviceVkTest::constructFeatureNotSupported() { DeviceProperties properties = pickDevice(instance()); if(properties.features() & DeviceFeature::SparseBinding) CORRADE_SKIP("The SparseBinding feature is supported, can't test"); if(properties.features() & DeviceFeature::SparseResidency16Samples) CORRADE_SKIP("The SparseResidency16Samples feature is supported, can't test"); std::ostringstream out; Error redirectError{&out}; Queue queue{NoCreate}; Device device{instance(), DeviceCreateInfo{properties} .addQueues(0, {0.0f}, {queue}) .setEnabledFeatures(DeviceFeature::SparseBinding|DeviceFeature::SparseResidency16Samples)}; CORRADE_COMPARE(out.str(), "Vk::Device::tryCreate(): some enabled features are not supported: Vk::DeviceFeature::SparseBinding|Vk::DeviceFeature::SparseResidency16Samples\n"); } void DeviceVkTest::constructFeatureWithoutExtension() { DeviceProperties properties = pickDevice(instance()); if((!instance().isVersionSupported(Version::Vk11) || !properties.isVersionSupported(Version::Vk11)) && !instance().isExtensionEnabled()) CORRADE_SKIP("Neither Vulkan 1.1 nor KHR_get_physical_device_properties2 is supported, can't test"); if(!(properties.features() & DeviceFeature::SamplerYcbcrConversion)) CORRADE_SKIP("The SamplerYcbcrConversion feature is not supported, can't test"); Queue queue{NoCreate}; DeviceCreateInfo info{properties}; info.addQueues(0, {0.0f}, {queue}) .setEnabledFeatures(DeviceFeature::SamplerYcbcrConversion); std::ostringstream out; Error redirectError{&out}; Device device{instance(), info}; CORRADE_COMPARE(out.str(), "Vk::Device::tryCreate(): some enabled features need VK_KHR_sampler_ycbcr_conversion enabled\n"); } 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{pickDevice(instance())}}; CORRADE_COMPARE(out.str(), "Vk::Device::tryCreate(): needs at least one queue\n"); } void DeviceVkTest::constructNoPortability() { DeviceProperties properties = pickDevice(instance()); if(properties.enumerateExtensionProperties().isSupported()) CORRADE_SKIP("KHR_portability_subset supported, can't test"); Queue queue{NoCreate}; Device device{instance(), DeviceCreateInfo{properties} .addQueues(0, {0.0f}, {queue}) }; /* The extension shouldn't be registered as enabled */ CORRADE_VERIFY(!device.isExtensionEnabled()); /* All features should be marked as enabled */ CORRADE_COMPARE_AS(device.enabledFeatures(), Implementation::deviceFeaturesPortabilitySubset(), TestSuite::Compare::GreaterOrEqual); } void DeviceVkTest::constructNoPortabilityEnablePortabilityFeatures() { DeviceProperties properties = pickDevice(instance()); if(properties.enumerateExtensionProperties().isSupported()) CORRADE_SKIP("KHR_portability_subset supported, can't test"); Device device{NoCreate}; /* Explicitly enabling portability subset features shouldn't do anything when the portability extension isn't present */ Queue queue{NoCreate}; CORRADE_COMPARE(device.tryCreate(instance(), DeviceCreateInfo{properties} .addQueues(0, {0.0f}, {queue}) .setEnabledFeatures(Implementation::deviceFeaturesPortabilitySubset()) ), Result::Success); /* All features should be marked as enabled */ CORRADE_COMPARE_AS(device.enabledFeatures(), Implementation::deviceFeaturesPortabilitySubset(), TestSuite::Compare::GreaterOrEqual); } void DeviceVkTest::constructPortability() { DeviceProperties properties = pickDevice(instance()); if(!properties.enumerateExtensionProperties().isSupported()) CORRADE_SKIP("KHR_portability_subset not supported, can't test"); /* (Same as in DevicePropertiesVkTest.) Not all features should be marked as supported... */ CORRADE_VERIFY((properties.features() & Implementation::deviceFeaturesPortabilitySubset()) != Implementation::deviceFeaturesPortabilitySubset()); /* ... but there should be at least one feature */ CORRADE_VERIFY(properties.features() & Implementation::deviceFeaturesPortabilitySubset()); Queue queue{NoCreate}; Device device{instance(), DeviceCreateInfo{properties} .addQueues(0, {0.0f}, {queue}) .setEnabledFeatures(properties.features() & Implementation::deviceFeaturesPortabilitySubset()) }; /* All requested features should be marked as enabled */ CORRADE_COMPARE_AS(device.enabledFeatures(), properties.features() & Implementation::deviceFeaturesPortabilitySubset(), TestSuite::Compare::GreaterOrEqual); } void DeviceVkTest::tryCreateAlreadyCreated() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif Queue queue{NoCreate}; Device device{instance(), DeviceCreateInfo{pickDevice(instance())} .addQueues(0, {0.0f}, {queue}) }; CORRADE_VERIFY(device.handle()); std::ostringstream out; Error redirectError{&out}; device.tryCreate(instance(), DeviceCreateInfo{pickDevice(instance())}); CORRADE_COMPARE(out.str(), "Vk::Device::tryCreate(): device already created\n"); } void DeviceVkTest::tryCreateUnknownExtension() { Queue queue{NoCreate}; Device device{NoCreate}; std::ostringstream out; Error redirectError{&out}; CORRADE_COMPARE(device.tryCreate(instance(), DeviceCreateInfo{pickDevice(instance())} .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions({"VK_this_doesnt_exist"_s})), Result::ErrorExtensionNotPresent); CORRADE_COMPARE(out.str(), "Vk::Device::tryCreate(): device creation failed: Vk::Result::ErrorExtensionNotPresent\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 instance2{InstanceCreateInfo{} .addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) /* Needed by VK_EXT_debug_marker */ .addEnabledExtensions() }; DeviceProperties deviceProperties = pickDevice(instance2); ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); if(!extensions.isSupported()) CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); if(!extensions.isSupported()) CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); VkDevice device; Queue queue{NoCreate}; CORRADE_COMPARE(Result(instance2->CreateDevice(deviceProperties, DeviceCreateInfo{pickDevice(instance2)} .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions< Extensions::EXT::debug_marker, Extensions::KHR::maintenance1 >() /* RobustBufferAccess is guaranteed to be supported always, no need to check anything */ .setEnabledFeatures(DeviceFeature::RobustBufferAccess), nullptr, &device)), Result::Success); CORRADE_VERIFY(device); /* Populating the queue handle is done only from Device itself, so it won't happen here -- would need to call vkGetDeviceQueue[2] directly */ CORRADE_VERIFY(!queue.handle()); { /* Wrapping should load the basic function pointers */ Device wrapped{NoCreate}; wrapped.wrap(instance2, deviceProperties, device, Version::Vk11, { Extensions::EXT::debug_marker::string() }, DeviceFeature::RobustBufferAccess, 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()); 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()); CORRADE_VERIFY(wrapped->TrimCommandPoolKHR); /* Listed features should be reported as enabled */ CORRADE_COMPARE(wrapped.enabledFeatures(), DeviceFeature::RobustBufferAccess); /* Device properties should be available */ CORRADE_COMPARE(wrapped.properties().handle(), deviceProperties.handle()); /* Releasing won't destroy anything ... */ CORRADE_COMPARE(wrapped.release(), device); } /* ...so we can wrap it again, non-owned, and then destroy it manually */ Device wrapped{NoCreate}; wrapped.wrap(instance2, deviceProperties, device, Version::Vk10, {}, {}); CORRADE_VERIFY(wrapped->DestroyDevice); wrapped->DestroyDevice(device, nullptr); } void DeviceVkTest::wrapAlreadyCreated() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif Queue queue{NoCreate}; Device device{instance(), DeviceCreateInfo{pickDevice(instance())} .addQueues(0, {0.0f}, {queue}) }; CORRADE_VERIFY(device.handle()); std::ostringstream out; Error redirectError{&out}; device.wrap(instance(), {}, {}, {}, {}, {}); CORRADE_COMPARE(out.str(), "Vk::Device::wrap(): device already created\n"); } void DeviceVkTest::populateGlobalFunctionPointers() { vkDestroyDevice = nullptr; Queue queue{NoCreate}; Device device{instance(), DeviceCreateInfo{pickDevice(instance())} .addQueues(0, {0.0f}, {queue}) }; CORRADE_VERIFY(!vkDestroyDevice); device.populateGlobalFunctionPointers(); CORRADE_VERIFY(vkDestroyDevice); CORRADE_VERIFY(vkDestroyDevice == device->DestroyDevice); } }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::DeviceVkTest)