mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1215 lines
54 KiB
1215 lines
54 KiB
/* |
|
This file is part of Magnum. |
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
|
2020, 2021, 2022 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/Array.h> |
|
#include <Corrade/Containers/Optional.h> |
|
#include <Corrade/Containers/StringStl.h> |
|
#include <Corrade/TestSuite/Compare/Numeric.h> |
|
#include <Corrade/Utility/DebugStl.h> |
|
#include <Corrade/Utility/FormatStl.h> |
|
|
|
#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<const char*> 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<const char*> 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<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::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.exceptSuffix(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<Extensions::KHR::get_physical_device_properties2>()) { |
|
CORRADE_VERIFY(!info->pEnabledFeatures); |
|
CORRADE_VERIFY(info->pNext); |
|
const auto& features2 = *static_cast<const VkPhysicalDeviceFeatures2*>(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<const VkPhysicalDeviceSamplerYcbcrConversionFeatures*>(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<Extensions::KHR::get_physical_device_properties2>()) { |
|
CORRADE_VERIFY(!info->pEnabledFeatures); |
|
CORRADE_VERIFY(info->pNext); |
|
const auto& imagelessFramebufferFeatures = *static_cast<const VkPhysicalDeviceImagelessFramebufferFeatures*>(info->pNext); |
|
CORRADE_COMPARE(imagelessFramebufferFeatures.sType, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES); |
|
CORRADE_VERIFY(imagelessFramebufferFeatures.imagelessFramebuffer); |
|
|
|
const auto& samplerYcbcrConversionFeatures = *static_cast<const VkPhysicalDeviceSamplerYcbcrConversionFeatures*>(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<Extensions::KHR::get_physical_device_properties2>()) { |
|
CORRADE_VERIFY(!info->pEnabledFeatures); |
|
CORRADE_VERIFY(info->pNext); |
|
const auto& features2 = *static_cast<const VkPhysicalDeviceFeatures2*>(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<Extensions::KHR::get_physical_device_properties2>()) { |
|
CORRADE_VERIFY(!info->pEnabledFeatures); |
|
CORRADE_VERIFY(info->pNext); |
|
const auto& features2 = *static_cast<const VkPhysicalDeviceFeatures2*>(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<Extensions::KHR::get_physical_device_properties2>()) { |
|
CORRADE_VERIFY(!info->pEnabledFeatures); |
|
CORRADE_VERIFY(info->pNext); |
|
const auto& imagelessFramebufferFeatures = *static_cast<const VkPhysicalDeviceImagelessFramebufferFeatures*>(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<Extensions::KHR::get_physical_device_properties2>()) |
|
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() { |
|
CORRADE_SKIP_IF_NO_ASSERT(); |
|
|
|
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() { |
|
CORRADE_SKIP_IF_NO_ASSERT(); |
|
|
|
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<DeviceCreateInfo>{}); |
|
CORRADE_VERIFY(!std::is_copy_assignable<DeviceCreateInfo>{}); |
|
} |
|
|
|
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<Extensions::KHR::get_memory_requirements2, |
|
Extensions::KHR::bind_memory2>(); |
|
|
|
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<Queue> referenceA[1]{a}; |
|
Containers::Reference<Queue> 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<const Containers::StringView>{}) |
|
.addEnabledExtensions(std::initializer_list<Containers::StringView>{}) |
|
.addEnabledExtensions(Containers::ArrayView<const Extension>{}) |
|
.addEnabledExtensions(std::initializer_list<Extension>{}) |
|
.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<Extensions::EXT::debug_marker>()); |
|
/* ... 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<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"); |
|
|
|
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<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::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<Extensions::KHR::sampler_ycbcr_conversion>()) |
|
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<Extensions::KHR::sampler_ycbcr_conversion>() |
|
/* 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<Extensions::EXT::debug_report>() |
|
}; |
|
|
|
DeviceProperties deviceProperties = pickDevice(instance2); |
|
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}; |
|
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<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.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<Extensions::EXT::debug_report>() |
|
}; |
|
|
|
DeviceProperties deviceProperties = pickDevice(instance2); |
|
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}; |
|
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<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.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> 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<Extensions::KHR::get_physical_device_properties2>()) |
|
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() { |
|
CORRADE_SKIP_IF_NO_ASSERT(); |
|
|
|
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<Extensions::KHR::portability_subset>()) |
|
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<Extensions::KHR::portability_subset>()); |
|
|
|
/* 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<Extensions::KHR::portability_subset>()) |
|
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<Extensions::KHR::portability_subset>()) |
|
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() { |
|
CORRADE_SKIP_IF_NO_ASSERT(); |
|
|
|
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<Extensions::EXT::debug_report>() |
|
}; |
|
|
|
DeviceProperties deviceProperties = pickDevice(instance2); |
|
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; |
|
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<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); |
|
|
|
/* 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() { |
|
CORRADE_SKIP_IF_NO_ASSERT(); |
|
|
|
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)
|
|
|