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

/*
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)