From 18e223e4d9ea69d8fc0ae2d100f5f6f3e1056307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 20 Jun 2020 00:06:55 +0200 Subject: [PATCH] Vk: enumerating layer properties. Instance layers, with the same interface designed to be reused for device-level layers. --- doc/vulkan-mapping.dox | 2 +- src/Magnum/Vk/CMakeLists.txt | 4 +- src/Magnum/Vk/LayerProperties.cpp | 118 ++++++++++++++ src/Magnum/Vk/LayerProperties.h | 157 +++++++++++++++++++ src/Magnum/Vk/Test/CMakeLists.txt | 2 + src/Magnum/Vk/Test/LayerPropertiesTest.cpp | 61 +++++++ src/Magnum/Vk/Test/LayerPropertiesVkTest.cpp | 142 +++++++++++++++++ src/Magnum/Vk/Vk.h | 1 + 8 files changed, 485 insertions(+), 2 deletions(-) create mode 100644 src/Magnum/Vk/LayerProperties.cpp create mode 100644 src/Magnum/Vk/LayerProperties.h create mode 100644 src/Magnum/Vk/Test/LayerPropertiesTest.cpp create mode 100644 src/Magnum/Vk/Test/LayerPropertiesVkTest.cpp diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 4cd61af6a..8586ce65a 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -167,7 +167,7 @@ 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{EnumerateInstanceExtensionProperties} | | -@fn_vk{EnumerateInstanceLayerProperties} | | +@fn_vk{EnumerateInstanceLayerProperties} | @ref enumerateLayerProperties() @fn_vk{EnumerateInstanceVersion} @m_class{m-label m-flat m-success} **1.1** | @ref enumerateInstanceVersion() @fn_vk{EnumeratePhysicalDevices} | | @fn_vk{EnumeratePhysicalDeviceGroups} @m_class{m-label m-flat m-success} **KHR, 1.1** | | diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 117450208..1debdbfe1 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -32,7 +32,8 @@ set(MagnumVk_SRCS Version.cpp) set(MagnumVk_GracefulAssert_SRCS - Enums.cpp) + Enums.cpp + LayerProperties.cpp) set(MagnumVk_HEADERS Device.h @@ -40,6 +41,7 @@ set(MagnumVk_HEADERS Extensions.h Instance.h Integration.h + LayerProperties.h Result.h TypeTraits.h Version.h diff --git a/src/Magnum/Vk/LayerProperties.cpp b/src/Magnum/Vk/LayerProperties.cpp new file mode 100644 index 000000000..717d16dd4 --- /dev/null +++ b/src/Magnum/Vk/LayerProperties.cpp @@ -0,0 +1,118 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "LayerProperties.h" + +#include +#include + +#include "Magnum/Vk/Result.h" + +namespace Magnum { namespace Vk { + +LayerProperties::LayerProperties(NoCreateT) {} + +LayerProperties::LayerProperties() = default; + +LayerProperties::LayerProperties(LayerProperties&&) noexcept = default; + +LayerProperties::~LayerProperties() = default; + +LayerProperties& LayerProperties::operator=(LayerProperties&&) noexcept = default; + +Containers::ArrayView LayerProperties::names() { + return {reinterpret_cast(_layers.end()), _layers.size()}; +} + +bool LayerProperties::isSupported(const Containers::StringView layer) { + return std::binary_search( + reinterpret_cast(_layers.end()), + reinterpret_cast(_layers.end()) + _layers.size(), + layer); +} + +UnsignedInt LayerProperties::count() { + return UnsignedInt(_layers.size()); +} + +Containers::StringView LayerProperties::name(const UnsignedInt id) { + CORRADE_ASSERT(id < _layers.size(), + "Vk::LayerProperties::name(): index" << id << "out of range for" << _layers.size() << "entries", {}); + /* Not returning the string views at the end because those are in a + different order */ + return _layers[id].layerName; +} + +UnsignedInt LayerProperties::revision(const UnsignedInt id) { + CORRADE_ASSERT(id < _layers.size(), + "Vk::LayerProperties::revision(): index" << id << "out of range for" << _layers.size() << "entries", {}); + return _layers[id].implementationVersion; +} + +Version LayerProperties::version(const UnsignedInt id) { + CORRADE_ASSERT(id < _layers.size(), + "Vk::LayerProperties::version(): index" << id << "out of range for" << _layers.size() << "entries", {}); + return Version(_layers[id].specVersion); +} + +Containers::StringView LayerProperties::description(const UnsignedInt id) { + CORRADE_ASSERT(id < _layers.size(), + "Vk::LayerProperties::description(): index" << id << "out of range for" << _layers.size() << "entries", {}); + return _layers[id].description; +} + +LayerProperties enumerateLayerProperties() { + LayerProperties out; + + /* Retrieve layer count */ + UnsignedInt count; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(vkEnumerateInstanceLayerProperties(&count, nullptr)); + + /* No layers, nothing to do */ + if(!count) return out; + + /* Allocate extra for a list of string views that we'll use to sort & + search the values; query the layers */ + out._layers = Containers::Array{ + reinterpret_cast(new char[count*(sizeof(VkLayerProperties) + sizeof(Containers::StringView))]), + count, + [](VkLayerProperties* data, std::size_t) { + delete[] reinterpret_cast(data); + }}; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(vkEnumerateInstanceLayerProperties(&count, out._layers.data())); + + /* Expect the layer count didn't change between calls */ + CORRADE_INTERNAL_ASSERT(count == out._layers.size()); + + /* Populate the views and sort them so we can search in O(log n) later */ + Containers::ArrayView layerNames{reinterpret_cast(out._layers.end()), count}; + for(std::size_t i = 0; i != layerNames.size(); ++i) + layerNames[i] = out._layers[i].layerName; + std::sort(layerNames.begin(), layerNames.end()); + + return out; +} + +}} diff --git a/src/Magnum/Vk/LayerProperties.h b/src/Magnum/Vk/LayerProperties.h new file mode 100644 index 000000000..98a6cc0e2 --- /dev/null +++ b/src/Magnum/Vk/LayerProperties.h @@ -0,0 +1,157 @@ +#ifndef Magnum_Vk_LayerProperties_h +#define Magnum_Vk_LayerProperties_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::LayerProperties, function @ref Magnum::Vk::enumerateLayerProperties() + * @m_since_latest + */ + +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Tags.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Vulkan layer properties +@m_since_latest + +Provides a searchable container of Vulkan layers enumerated with +@ref enumerateLayerProperties(). Only instance layers are enumerated, as device +layers are [deprecated since Vulkan 1.0.13](https://github.com/KhronosGroup/Vulkan-Docs/commit/2656f459333b3a1dc63619a9ebd83490eea22e93) +and the assumption is that no drivers currently use rely on these anymore. See +[§ 37.3.1](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap38.html#extendingvulkan-layers-devicelayerdeprecation) +for more information. +@see @ref ExtensionProperties, @ref enumerateInstanceExtensionProperties(), + @type_vk_keyword{LayerProperties} +*/ +class MAGNUM_VK_EXPORT LayerProperties { + public: + /** + * @brief Construct without populating the contents + * + * Equivalent to a moved-from state. Move over the result of + * @ref enumerateLayerProperties() to make it usable. + */ + explicit LayerProperties(NoCreateT); + + ~LayerProperties(); + + /** @brief Copying is not allowed */ + LayerProperties(const LayerProperties&) = delete; + + /** @brief Move constructor */ + LayerProperties(LayerProperties&&) noexcept; + + /** @brief Copying is not allowed */ + LayerProperties& operator=(const LayerProperties&) = delete; + + /** @brief Move assignment */ + LayerProperties& operator=(LayerProperties&&) noexcept; + + /** + * @brief Layer names + * + * A list of all layers reported by the driver. Use @ref isSupported() + * to query support of a particular layer name. Note that the list is + * sorted and thus may be different than the order in which the + * @ref name(), @ref revision(), @ref version() and @ref description() + * accessors return values. + * + * The returned views are owned by the @ref LayerProperties instance + * (i.e., *not* a global memory). + */ + Containers::ArrayView names(); + + /** + * @brief Whether given layer is supported + * + * Search complexity is @f$ \mathcal{O}(\log n) @f$ in the total layer + * count. + */ + bool isSupported(Containers::StringView layer); + + /** @brief Count of layers reported by the driver */ + UnsignedInt count(); + + /** + * @brief Layer name + * @param id Layer index, expected to be smaller than @ref count() + * + * The returned view is owned by the @ref LayerProperties instance + * (i.e., *not* a global memory). + */ + Containers::StringView name(UnsignedInt id); + + /** + * @brief Layer revision + * @param id Layer index, expected to be smaller than @ref count() + */ + UnsignedInt revision(UnsignedInt id); + + /** + * @brief Vulkan version the layer is implemented against + * @param id Layer index, expected to be smaller than @ref count() + */ + Version version(UnsignedInt id); + + /** + * @brief Layer description + * @param id Layer index, expected to be smaller than @ref count() + * + * The returned view is owned by the @ref LayerProperties instance + * (i.e., *not* a global memory). + */ + Containers::StringView description(UnsignedInt id); + + private: + #ifndef DOXYGEN_GENERATING_OUTPUT + /* The DAMN THING lists this among friends, which is AN IMPLEMENTATION + DETAIL */ + friend MAGNUM_VK_EXPORT LayerProperties enumerateLayerProperties(); + #endif + + explicit LayerProperties(); + + Containers::Array _layers; +}; + +/** +@brief Enumerate instance layers +@m_since_latest + +@see @fn_vk_keyword{EnumerateInstanceLayerProperties} +*/ +MAGNUM_VK_EXPORT LayerProperties enumerateLayerProperties(); + +}} + +#endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index f622bf76f..22b99e606 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -27,9 +27,11 @@ corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) + corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) endif() diff --git a/src/Magnum/Vk/Test/LayerPropertiesTest.cpp b/src/Magnum/Vk/Test/LayerPropertiesTest.cpp new file mode 100644 index 000000000..852d2b0bb --- /dev/null +++ b/src/Magnum/Vk/Test/LayerPropertiesTest.cpp @@ -0,0 +1,61 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include "Magnum/Vk/LayerProperties.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct LayerPropertiesTest: TestSuite::Tester { + explicit LayerPropertiesTest(); + + void constructNoCreate(); + void constructCopy(); +}; + +LayerPropertiesTest::LayerPropertiesTest() { + addTests({&LayerPropertiesTest::constructNoCreate, + &LayerPropertiesTest::constructCopy}); +} + +void LayerPropertiesTest::constructNoCreate() { + { + LayerProperties properties{NoCreate}; + CORRADE_COMPARE(properties.count(), 0); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void LayerPropertiesTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::LayerPropertiesTest) diff --git a/src/Magnum/Vk/Test/LayerPropertiesVkTest.cpp b/src/Magnum/Vk/Test/LayerPropertiesVkTest.cpp new file mode 100644 index 000000000..8c74f3bf9 --- /dev/null +++ b/src/Magnum/Vk/Test/LayerPropertiesVkTest.cpp @@ -0,0 +1,142 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/Version.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct LayerPropertiesVkTest: TestSuite::Tester { + explicit LayerPropertiesVkTest(); + + void constructMove(); + + void enumerate(); + void outOfRange(); + void isSupported(); +}; + +LayerPropertiesVkTest::LayerPropertiesVkTest() { + addTests({&LayerPropertiesVkTest::constructMove, + + &LayerPropertiesVkTest::enumerate, + &LayerPropertiesVkTest::outOfRange, + &LayerPropertiesVkTest::isSupported}); +} + +using namespace Containers::Literals; + +void LayerPropertiesVkTest::constructMove() { + LayerProperties a = enumerateLayerProperties(); + const UnsignedInt count = a.count(); + if(!count) CORRADE_SKIP("No extensions reported, can't test"); + + LayerProperties b = std::move(a); + CORRADE_COMPARE(b.count(), count); + + LayerProperties 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 LayerPropertiesVkTest::enumerate() { + LayerProperties properties = enumerateLayerProperties(); + + if(!properties.count()) + CORRADE_SKIP("The driver reported no instance layers, can't test."); + + CORRADE_COMPARE(properties.count(), properties.names().size()); + Debug{} << "Available instance layers:" << properties.names(); + + Containers::ArrayView names = properties.names(); + CORRADE_COMPARE_AS(names.size(), 0, TestSuite::Compare::Greater); + /* The list should be sorted */ + for(std::size_t i = 1; i != names.size(); ++i) { + CORRADE_COMPARE_AS(names[i - 1], names[i], + TestSuite::Compare::Less); + } + + CORRADE_COMPARE_AS(properties.name(0).size(), "VK_LAYER_"_s.size(), + TestSuite::Compare::Greater); + CORRADE_COMPARE_AS(properties.revision(0), 0, + TestSuite::Compare::Greater); + CORRADE_COMPARE_AS(properties.version(0), Version::Vk10, + TestSuite::Compare::GreaterOrEqual); + CORRADE_COMPARE_AS(properties.description(0).size(), 10, + TestSuite::Compare::Greater); +} + +void LayerPropertiesVkTest::outOfRange() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + LayerProperties properties = enumerateLayerProperties(); + const UnsignedInt count = properties.count(); + + std::ostringstream out; + Error redirectError{&out}; + properties.name(count); + properties.revision(count); + properties.version(count); + properties.description(count); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Vk::LayerProperties::name(): index {0} out of range for {0} entries\n" + "Vk::LayerProperties::revision(): index {0} out of range for {0} entries\n" + "Vk::LayerProperties::version(): index {0} out of range for {0} entries\n" + "Vk::LayerProperties::description(): index {0} out of range for {0} entries\n", count)); +} + +void LayerPropertiesVkTest::isSupported() { + LayerProperties properties = enumerateLayerProperties(); + + CORRADE_VERIFY(!properties.isSupported("this layer doesn't exist")); + + if(!properties.count()) + CORRADE_SKIP("The driver reported no instance layers, can't fully test."); + + for(UnsignedInt i = 0; i != properties.count(); ++i) { + CORRADE_ITERATION(properties.name(i)); + CORRADE_VERIFY(properties.isSupported(properties.name(i))); + } + + /* Verify that we're not just comparing a prefix */ + const std::string layer = std::string(properties.name(0)) + "_hello"; + CORRADE_VERIFY(!properties.isSupported(layer)); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::LayerPropertiesVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 4b7697e94..253176489 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -36,6 +36,7 @@ namespace Magnum { namespace Vk { #ifndef DOXYGEN_GENERATING_OUTPUT class Extension; class InstanceExtension; +class LayerProperties; enum class Result: Int; enum class Version: UnsignedInt;