From d28442e9b1f3e78e2fd255da90e8b0af8e4e7497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 10 Jul 2020 15:17:46 +0200 Subject: [PATCH] Vk: device extension enumeration. --- doc/vulkan-mapping.dox | 2 +- src/Magnum/Vk/DeviceProperties.cpp | 13 ++ src/Magnum/Vk/DeviceProperties.h | 15 +++ src/Magnum/Vk/ExtensionProperties.h | 7 +- src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp | 124 +++++++++++++++++- 5 files changed, 158 insertions(+), 3 deletions(-) diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index e9b986a10..0475c0f96 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -165,7 +165,7 @@ Vulkan function | Matching API Vulkan function | Matching API --------------------------------------- | ------------ @fn_vk{EnumerateDeviceLayerProperties} @m_class{m-label m-danger} **deprecated in 1.0.13** | not exposed, [spec commit](https://github.com/KhronosGroup/Vulkan-Docs/commit/2656f459333b3a1dc63619a9ebd83490eea22e93) -@fn_vk{EnumerateDeviceExtensionProperties} | | +@fn_vk{EnumerateDeviceExtensionProperties} | @ref DeviceProperties::enumerateExtensionProperties() @fn_vk{EnumerateInstanceExtensionProperties} | @ref enumerateInstanceExtensionProperties() @fn_vk{EnumerateInstanceLayerProperties} | @ref enumerateLayerProperties() @fn_vk{EnumerateInstanceVersion} @m_class{m-label m-flat m-success} **1.1** | @ref enumerateInstanceVersion() diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index bed65c26e..7bbef6ea0 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -30,6 +30,8 @@ #include #include "Magnum/Vk/Instance.h" +#include "Magnum/Vk/ExtensionProperties.h" +#include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Implementation/InstanceState.h" @@ -80,6 +82,17 @@ void DeviceProperties::getPropertiesImplementation11(DeviceProperties& self, VkP return (**self._instance).GetPhysicalDeviceProperties2(self._handle, &properties); } +ExtensionProperties DeviceProperties::enumerateExtensionProperties(Containers::ArrayView layers) { + return InstanceExtensionProperties{layers, [](void* state, const char* const layer, UnsignedInt* count, VkExtensionProperties* properties) { + auto& deviceProperties = *static_cast(state); + return (**deviceProperties._instance).EnumerateDeviceExtensionProperties(deviceProperties._handle, layer, count, properties); + }, this}; +} + +ExtensionProperties DeviceProperties::enumerateExtensionProperties(std::initializer_list layers) { + return enumerateExtensionProperties(Containers::arrayView(layers)); +} + Containers::Array enumerateDevices(Instance& instance) { /* Retrieve total device count */ UnsignedInt count; diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 535700407..27523e128 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -30,6 +30,7 @@ * @m_since_latest */ +#include #include #include @@ -182,6 +183,20 @@ class MAGNUM_VK_EXPORT DeviceProperties { */ Containers::StringView name(); + /** + * @brief Enumerate device extensions + * @param layers Additional layers to list extensions from + * + * Expects that all listed layers are supported --- however they don't + * need to be enabled on the instance. + * @see @ref LayerProperties::isSupported(), + * @fn_vk_keyword{EnumerateDeviceExtensionProperties} + */ + ExtensionProperties enumerateExtensionProperties(Containers::ArrayView layers = {}); + + /** @overload */ + ExtensionProperties enumerateExtensionProperties(std::initializer_list layers); + private: friend Implementation::InstanceState; diff --git a/src/Magnum/Vk/ExtensionProperties.h b/src/Magnum/Vk/ExtensionProperties.h index 4c3bfe9cd..b2589ef9a 100644 --- a/src/Magnum/Vk/ExtensionProperties.h +++ b/src/Magnum/Vk/ExtensionProperties.h @@ -45,6 +45,8 @@ namespace Magnum { namespace Vk { @brief Extension properties @m_since_latest +Provides a searchable container of Vulkan device extensions enumerated with +@ref DeviceProperties::enumerateExtensionProperties(). @see @ref InstanceExtensionProperties, @type_vk_keyword{ExtensionProperties} */ class MAGNUM_VK_EXPORT ExtensionProperties { @@ -52,7 +54,9 @@ class MAGNUM_VK_EXPORT ExtensionProperties { /** * @brief Construct without populating the contents * - * Equivalent to a moved-from state. + * Equivalent to a moved-from state. Move over the result of + * @ref DeviceProperties::enumerateExtensionProperties() to make it + * usable. */ explicit ExtensionProperties(NoCreateT); @@ -238,6 +242,7 @@ class MAGNUM_VK_EXPORT InstanceExtensionProperties: public ExtensionProperties { #ifndef DOXYGEN_GENERATING_OUTPUT /* The DAMN THING forgets parameter name if this is present, FFS. It also lists this among friends, which is AN IMPLEMENTATION DETAIL */ + friend DeviceProperties; friend MAGNUM_VK_EXPORT InstanceExtensionProperties enumerateInstanceExtensionProperties(Containers::ArrayView); #endif diff --git a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp index 5e0b98ce9..f11848af8 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp @@ -23,13 +23,19 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include #include #include +#include #include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/Instance.h" +#include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" @@ -42,13 +48,31 @@ struct DevicePropertiesVkTest: TestSuite::Tester { void constructMove(); void wrap(); + /* Most of this tested already in ExtensionPropertiesVkTest, this only + covers what isn't there already */ + void enumerateExtensions(); + void enumerateExtensionsWithKhronosValidationLayer(); + void enumerateExtensionsNonexistentLayer(); + + void extensionConstructMove(); + void extensionIsSupported(); + void extensionNamedRevision(); + Instance _instance; }; DevicePropertiesVkTest::DevicePropertiesVkTest(): _instance{InstanceCreateInfo{arguments().first, arguments().second}} { addTests({&DevicePropertiesVkTest::enumerate, &DevicePropertiesVkTest::constructMove, - &DevicePropertiesVkTest::wrap}); + &DevicePropertiesVkTest::wrap, + + &DevicePropertiesVkTest::enumerateExtensions, + &DevicePropertiesVkTest::enumerateExtensionsWithKhronosValidationLayer, + &DevicePropertiesVkTest::enumerateExtensionsNonexistentLayer, + + &DevicePropertiesVkTest::extensionConstructMove, + &DevicePropertiesVkTest::extensionIsSupported, + &DevicePropertiesVkTest::extensionNamedRevision}); } void DevicePropertiesVkTest::enumerate() { @@ -105,6 +129,104 @@ void DevicePropertiesVkTest::wrap() { CORRADE_COMPARE(wrapped.name(), devices[0].name()); } +void DevicePropertiesVkTest::enumerateExtensions() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + ExtensionProperties properties = devices[0].enumerateExtensionProperties(); + Debug{} << "Available device extension count:" << properties.names().size(); + + CORRADE_COMPARE_AS(properties.count(), 0, TestSuite::Compare::Greater); + + /* The extension list should be sorted and unique (so Less, not + LessOrEqual) */ + Containers::ArrayView extensions = properties.names(); + for(std::size_t i = 1; i != extensions.size(); ++i) { + CORRADE_COMPARE_AS(extensions[i - 1], extensions[i], + TestSuite::Compare::Less); + } +} + +void DevicePropertiesVkTest::enumerateExtensionsWithKhronosValidationLayer() { + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) + CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); + + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + /* There should be more extensions with this layer enabled */ + ExtensionProperties global = devices[0].enumerateExtensionProperties(); + ExtensionProperties withKhronosValidation = devices[0].enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); + CORRADE_COMPARE_AS(global.count(), + withKhronosValidation.count(), + TestSuite::Compare::Less); + + /* VK_EXT_tooling_info is only in the layer */ + CORRADE_VERIFY(!global.isSupported("VK_EXT_tooling_info")); + CORRADE_VERIFY(withKhronosValidation.isSupported("VK_EXT_tooling_info")); +} + +void DevicePropertiesVkTest::enumerateExtensionsNonexistentLayer() { + CORRADE_SKIP("Currently this hits an internal assert, which can't be tested."); + + std::ostringstream out; + Error redirectError{&out}; + enumerateInstanceExtensionProperties({"VK_LAYER_this_doesnt_exist"}); + CORRADE_COMPARE(out.str(), "TODO"); +} + +void DevicePropertiesVkTest::extensionConstructMove() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + ExtensionProperties a = devices[0].enumerateExtensionProperties(); + const UnsignedInt count = a.count(); + if(!count) CORRADE_SKIP("No extensions reported, can't test"); + + ExtensionProperties b = std::move(a); + CORRADE_COMPARE(b.count(), count); + + ExtensionProperties c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.count(), count); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void DevicePropertiesVkTest::extensionIsSupported() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + ExtensionProperties properties = devices[0].enumerateExtensionProperties(); + + /* This extension should be available almost always */ + if(!properties.isSupported("VK_KHR_maintenance1")) + CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't fully test"); + + /* Verify the overloads that take our extension wrappers work as well */ + CORRADE_VERIFY(properties.isSupported()); + CORRADE_VERIFY(properties.isSupported(Extensions::KHR::maintenance1{})); +} + +void DevicePropertiesVkTest::extensionNamedRevision() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + ExtensionProperties properties = devices[0].enumerateExtensionProperties(); + + /* This extension should be available almost always */ + if(!properties.isSupported("VK_KHR_maintenance1")) + CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't fully test"); + + /* This isn't tested in ExtensionPropertiesVkTest because there's an + overload which takes only InstanceExtensions */ + CORRADE_COMPARE_AS(properties.revision(), 0, + TestSuite::Compare::GreaterOrEqual); + CORRADE_COMPARE_AS(properties.revision(Extensions::KHR::maintenance1{}), 0, + TestSuite::Compare::GreaterOrEqual); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::DevicePropertiesVkTest)