Browse Source

Vk: enumerating instance extension properties.

Device extension properties will reuse a large part of this API.
pull/234/head
Vladimír Vondruš 6 years ago
parent
commit
64ba1e6732
  1. 2
      doc/vulkan-mapping.dox
  2. 2
      src/Magnum/Vk/CMakeLists.txt
  3. 182
      src/Magnum/Vk/ExtensionProperties.cpp
  4. 263
      src/Magnum/Vk/ExtensionProperties.h
  5. 2
      src/Magnum/Vk/Test/CMakeLists.txt
  6. 82
      src/Magnum/Vk/Test/ExtensionPropertiesTest.cpp
  7. 221
      src/Magnum/Vk/Test/ExtensionPropertiesVkTest.cpp
  8. 2
      src/Magnum/Vk/Vk.h

2
doc/vulkan-mapping.dox

@ -166,7 +166,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{EnumerateInstanceExtensionProperties} | @ref enumerateInstanceExtensionProperties()
@fn_vk{EnumerateInstanceLayerProperties} | @ref enumerateLayerProperties()
@fn_vk{EnumerateInstanceVersion} @m_class{m-label m-flat m-success} **1.1** | @ref enumerateInstanceVersion()
@fn_vk{EnumeratePhysicalDevices} | |

2
src/Magnum/Vk/CMakeLists.txt

@ -33,12 +33,14 @@ set(MagnumVk_SRCS
set(MagnumVk_GracefulAssert_SRCS
Enums.cpp
ExtensionProperties.cpp
LayerProperties.cpp)
set(MagnumVk_HEADERS
Device.h
Enums.h
Extensions.h
ExtensionProperties.h
Instance.h
Integration.h
LayerProperties.h

182
src/Magnum/Vk/ExtensionProperties.cpp

@ -0,0 +1,182 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020 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 "ExtensionProperties.h"
#include <algorithm>
#include <Corrade/Containers/String.h>
#include <Corrade/Containers/StringView.h>
#include <Corrade/Utility/Assert.h>
#include "Magnum/Vk/Extensions.h"
#include "Magnum/Vk/Result.h"
#include "Magnum/Vk/Version.h"
namespace Magnum { namespace Vk {
ExtensionProperties::ExtensionProperties(NoCreateT) {}
ExtensionProperties::ExtensionProperties(ExtensionProperties&&) noexcept = default;
ExtensionProperties::~ExtensionProperties() = default;
ExtensionProperties& ExtensionProperties::operator=(ExtensionProperties&&) noexcept = default;
ExtensionProperties::ExtensionProperties(const Containers::ArrayView<const Containers::StringView> layers, VkResult(*const enumerator)(void*, const char*, UnsignedInt*, VkExtensionProperties*), void* const state) {
/* Retrieve total extension count for all layers + the global extensions */
std::size_t totalCount = 0;
for(std::size_t i = 0; i <= layers.size(); ++i) {
UnsignedInt count;
MAGNUM_VK_INTERNAL_ASSERT_RESULT(enumerator(state,
i == 0 ? nullptr :
Containers::String::nullTerminatedView(layers[i - 1]).data(),
&count, nullptr));
totalCount += count;
}
/* Allocate extra for a list of string views that we'll use to sort &
search the values and a layer index so we can map the extensions back
to which layer they come from */
_extensions = Containers::Array<VkExtensionProperties>{
reinterpret_cast<VkExtensionProperties*>(new char[totalCount*(sizeof(VkExtensionProperties) + sizeof(Containers::StringView) + sizeof(UnsignedInt))]),
totalCount,
[](VkExtensionProperties* data, std::size_t) {
delete[] reinterpret_cast<char*>(data);
}};
Containers::ArrayView<Containers::StringView> extensionNames{reinterpret_cast<Containers::StringView*>(_extensions.end()), totalCount};
Containers::ArrayView<UnsignedInt> extensionLayers{reinterpret_cast<UnsignedInt*>(extensionNames.end()), totalCount};
/* Query the extensions, save layer ID for each */
std::size_t offset = 0;
for(std::size_t i = 0; i <= layers.size(); ++i) {
UnsignedInt count = totalCount - offset;
MAGNUM_VK_INTERNAL_ASSERT_RESULT(enumerator(state,
i == 0 ? nullptr :
Containers::String::nullTerminatedView(layers[i - 1]).data(),
&count, reinterpret_cast<VkExtensionProperties*>(_extensions.data()) + offset));
for(std::size_t j = 0; j != count; ++j) extensionLayers[offset + j] = i;
offset += count;
}
/* Expect the total extension count didn't change between calls */
CORRADE_INTERNAL_ASSERT(offset == totalCount);
/* Populate the views, sort them and remove duplicates so we can search in
O(log n) later */
for(std::size_t i = 0; i != extensionNames.size(); ++i)
extensionNames[i] = _extensions[i].extensionName;
std::sort(extensionNames.begin(), extensionNames.end());
_uniqueExtensionCount = std::unique(extensionNames.begin(), extensionNames.end()) - extensionNames.begin();
}
Containers::ArrayView<const Containers::StringView> ExtensionProperties::names() const {
return {reinterpret_cast<const Containers::StringView*>(_extensions.end()), _uniqueExtensionCount};
}
bool ExtensionProperties::isSupported(const Containers::StringView extension) const {
return std::binary_search(
reinterpret_cast<const Containers::StringView*>(_extensions.end()),
reinterpret_cast<const Containers::StringView*>(_extensions.end()) + _uniqueExtensionCount,
extension);
}
bool ExtensionProperties::isSupported(const Extension& extension) const {
return isSupported(extension.string());
}
Containers::StringView ExtensionProperties::name(const UnsignedInt id) const {
CORRADE_ASSERT(id < _extensions.size(),
"Vk::ExtensionProperties::name(): index" << id << "out of range for" << _extensions.size() << "entries", {});
/* Not returning the string views at the end because those are in a
different order */
return _extensions[id].extensionName;
}
UnsignedInt ExtensionProperties::revision(const Extension& extension) const {
return revision(extension.string());
}
UnsignedInt ExtensionProperties::revision(const UnsignedInt id) const {
CORRADE_ASSERT(id < _extensions.size(),
"Vk::ExtensionProperties::revision(): index" << id << "out of range for" << _extensions.size() << "entries", {});
/* WTF, why VkLayerProperties::specVersion is an actual Vulkan version and
here it is a revision number?! Consistency my ass. */
return _extensions[id].specVersion;
}
UnsignedInt ExtensionProperties::revision(const Containers::StringView extension) const {
/* Thanks, C++, for forcing me to do one more comparison than strictly
necessary */
auto found = std::lower_bound(
reinterpret_cast<const Containers::StringView*>(_extensions.end()),
reinterpret_cast<const Containers::StringView*>(_extensions.end()) + _uniqueExtensionCount,
extension);
if(*found != extension) return 0;
/* The view target is contents of the VkExtensionProperties structure,
the revision is stored nearby */
return reinterpret_cast<const VkExtensionProperties*>(found->data() - offsetof(VkExtensionProperties, extensionName))->specVersion;
}
UnsignedInt ExtensionProperties::layer(const UnsignedInt id) const {
CORRADE_ASSERT(id < _extensions.size(),
"Vk::xtensionProperties::layer(): index" << id << "out of range for" << _extensions.size() << "entries", {});
return reinterpret_cast<const UnsignedInt*>(reinterpret_cast<const Containers::StringView*>(_extensions.end()) + _extensions.size())[id];
}
InstanceExtensionProperties::InstanceExtensionProperties(InstanceExtensionProperties&&) noexcept = default;
InstanceExtensionProperties::~InstanceExtensionProperties() = default;
InstanceExtensionProperties& InstanceExtensionProperties::operator=(InstanceExtensionProperties&&) noexcept = default;
bool InstanceExtensionProperties::isSupported(Containers::StringView extension) const {
return ExtensionProperties::isSupported(extension);
}
bool InstanceExtensionProperties::isSupported(const InstanceExtension& extension) const {
return isSupported(extension.string());
}
UnsignedInt InstanceExtensionProperties::revision(Containers::StringView extension) const {
return ExtensionProperties::revision(extension);
}
UnsignedInt InstanceExtensionProperties::revision(const InstanceExtension& extension) const {
return revision(extension.string());
}
InstanceExtensionProperties enumerateInstanceExtensionProperties(const Containers::ArrayView<const Containers::StringView> layers) {
return InstanceExtensionProperties{layers, [](void*, const char* const layer, UnsignedInt* count, VkExtensionProperties* properties) {
return vkEnumerateInstanceExtensionProperties(layer, count, properties);
}, nullptr};
}
InstanceExtensionProperties enumerateInstanceExtensionProperties(const std::initializer_list<Containers::StringView> layers) {
return enumerateInstanceExtensionProperties(Containers::arrayView(layers));
}
}}

263
src/Magnum/Vk/ExtensionProperties.h

@ -0,0 +1,263 @@
#ifndef Magnum_Vk_ExtensionProperties_h
#define Magnum_Vk_ExtensionProperties_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020 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.
*/
/** @file
* @brief Class @ref Magnum::Vk::ExtensionProperties, @ref Magnum::Vk::InstanceExtensionProperties, function @ref enumerateInstanceExtensionProperties()
* @m_since_latest
*/
#include <Corrade/Containers/Array.h>
#include "Magnum/Tags.h"
#include "Magnum/Magnum.h"
#include "Magnum/Vk/TypeTraits.h"
#include "Magnum/Vk/Vk.h"
#include "Magnum/Vk/Vulkan.h"
#include "Magnum/Vk/visibility.h"
namespace Magnum { namespace Vk {
/**
@brief Extension properties
@m_since_latest
@see @ref InstanceExtensionProperties, @type_vk_keyword{ExtensionProperties}
*/
class MAGNUM_VK_EXPORT ExtensionProperties {
public:
/**
* @brief Construct without populating the contents
*
* Equivalent to a moved-from state.
*/
explicit ExtensionProperties(NoCreateT);
/** @brief Copying is not allowed */
ExtensionProperties(const ExtensionProperties&) = delete;
/** @brief Move constructor */
ExtensionProperties(ExtensionProperties&&) noexcept;
~ExtensionProperties();
/** @brief Copying is not allowed */
ExtensionProperties& operator=(const ExtensionProperties&) = delete;
/** @brief Move assignment */
ExtensionProperties& operator=(ExtensionProperties&&) noexcept;
/**
* @brief Instance extensions
*
* A list of all extension strings reported by the driver for all
* layers passed to the constructor, with duplicates removed. Use
* @ref isSupported() to query support of a particular
* extension. Note that the list is sorted and thus may be different
* than the order in which the @ref name() and @ref revision()
* accessors return values.
*
* The returned views are owned by the
* @ref ExtensionProperties instance (i.e., *not* a global
* memory).
*/
Containers::ArrayView<const Containers::StringView> names() const;
/**
* @brief Whether given extension is supported
*
* Accepts extensions from the @ref Extension namespace as a template
* parameter. Use the other overloads to query support of a runtime
* extension or a plain extension string.
*
* Search complexity is @f$ \mathcal{O}(\log n) @f$ in the total
* extension count; in contrast extension queries on a created instance
* are @f$ \mathcal{O}(1) @f$.
* @see @ref revision()
*/
bool isSupported(Containers::StringView extension) const;
/** @overload */
bool isSupported(const Extension& extension) const;
/** @overload */
template<class E> bool isSupported() const {
static_assert(Implementation::IsExtension<E>::value, "expected a Vulkan deviceension");
return isSupported(E::string());
}
/**
* @brief Count of extensions reported by the driver for all layers
*
* The count includes potential duplicates when an extension is both
* available globally and through a particular layer.
*/
UnsignedInt count() const { return _extensions.size(); }
/**
* @brief Extension name
* @param id Extension index, expected to be smaller than @ref count()
*
* The returned view is owned by the
* @ref ExtensionProperties instance (i.e., *not* a global
* memory).
*/
Containers::StringView name(UnsignedInt id) const;
/**
* @brief Extension revision
* @param id Extension index, expected to be smaller than @ref count()
*/
UnsignedInt revision(UnsignedInt id) const;
/**
* @brief Revision of a particular extension name
*
* If the extension is not supported, returns @cpp 0 @ce, supported
* extensions always have a non-zero revision. If the extension is
* implemented by more than one layer, returns revision of the first
* layer implementing it --- use @ref revision(UnsignedInt) const
* to get revision of a concrete extension in a concrete layer.
* @see @ref isSupported()
*/
UnsignedInt revision(Containers::StringView extension) const;
/** @overload */
UnsignedInt revision(const Extension& extension) const;
/** @overload */
template<class E> UnsignedInt revision() const {
static_assert(Implementation::IsExtension<E>::value, "expected a Vulkan device extension");
return revision(E::string());
}
/**
* @brief Extension layer index
* @param id Extension index, expected to be smaller than @ref count()
*
* Returns ID of the layer the extension comes from. @cpp 0 @ce is
* global extensions, @cpp 1 @ce is the first layer passed to
* @ref enumerateInstanceExtensionProperties() and so on.
*/
UnsignedInt layer(UnsignedInt id) const;
private:
friend class InstanceExtensionProperties;
explicit ExtensionProperties(const Containers::ArrayView<const Containers::StringView> layers, VkResult(*enumerator)(void*, const char*, UnsignedInt*, VkExtensionProperties*), void* state);
Containers::Array<VkExtensionProperties> _extensions;
std::size_t _uniqueExtensionCount;
};
/**
@brief Instance extension properties
@m_since_latest
Provides a searchable container of Vulkan device extensions enumerated with
@ref enumerateInstanceExtensionProperties().
@see @ref ExtensionProperties, @ref LayerProperties
*/
class MAGNUM_VK_EXPORT InstanceExtensionProperties: public ExtensionProperties {
public:
/**
* @brief Construct without populating the contents
*
* Equivalent to a moved-from state. Move over the result of
* @ref enumerateInstanceExtensionProperties() to make it usable.
*/
explicit InstanceExtensionProperties(NoCreateT): ExtensionProperties{NoCreate} {}
/** @brief Copying is not allowed */
InstanceExtensionProperties(const InstanceExtensionProperties&) = delete;
/** @brief Move constructor */
InstanceExtensionProperties(InstanceExtensionProperties&&) noexcept;
~InstanceExtensionProperties();
/** @brief Copying is not allowed */
InstanceExtensionProperties& operator=(const InstanceExtensionProperties&) = delete;
/** @brief Move assignment */
InstanceExtensionProperties& operator=(InstanceExtensionProperties&&) noexcept;
/** @copydoc ExtensionProperties::isSupported(Containers::StringView) const */
bool isSupported(Containers::StringView extension) const;
/** @overload */
bool isSupported(const InstanceExtension& extension) const;
/** @overload */
template<class E> bool isSupported() const {
static_assert(Implementation::IsInstanceExtension<E>::value, "expected a Vulkan instance extension");
return isSupported(E::string());
}
/** @copydoc ExtensionProperties::revision(UnsignedInt) const */
UnsignedInt revision(UnsignedInt id) const {
return ExtensionProperties::revision(id);
}
/** @copydoc ExtensionProperties::revision(Containers::StringView) const */
UnsignedInt revision(Containers::StringView extension) const;
/** @overload */
UnsignedInt revision(const InstanceExtension& extension) const;
/** @overload */
template<class E> UnsignedInt revision() const {
static_assert(Implementation::IsInstanceExtension<E>::value, "expected a Vulkan instance extension");
return revision(E::string());
}
private:
#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 MAGNUM_VK_EXPORT InstanceExtensionProperties enumerateInstanceExtensionProperties(Containers::ArrayView<const Containers::StringView>);
#endif
explicit InstanceExtensionProperties(const Containers::ArrayView<const Containers::StringView> layers, VkResult(*enumerator)(void*, const char*, UnsignedInt*, VkExtensionProperties*), void* state): ExtensionProperties{layers, enumerator, state} {}
};
/**
@brief Enumerate instance extension properties
@param layers Additional layers to list extensions from
@m_since_latest
Expects that all listed layers are supported.
@see @ref LayerProperties::isSupported(),
@fn_vk_keyword{EnumerateExtensionProperties}
*/
MAGNUM_VK_EXPORT InstanceExtensionProperties enumerateInstanceExtensionProperties(Containers::ArrayView<const Containers::StringView> layers = {});
/** @overload */
MAGNUM_VK_EXPORT InstanceExtensionProperties enumerateInstanceExtensionProperties(std::initializer_list<Containers::StringView> layers);
}}
#endif

2
src/Magnum/Vk/Test/CMakeLists.txt

@ -26,12 +26,14 @@
corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkExtensionPropertiesTest ExtensionPropertiesTest.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(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk)
endif()

82
src/Magnum/Vk/Test/ExtensionPropertiesTest.cpp

@ -0,0 +1,82 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020 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 <Corrade/TestSuite/Tester.h>
#include "Magnum/Vk/ExtensionProperties.h"
namespace Magnum { namespace Vk { namespace Test { namespace {
struct ExtensionPropertiesTest: TestSuite::Tester {
explicit ExtensionPropertiesTest();
void constructNoCreate();
void constructInstanceNoCreate();
void constructCopy();
void constructInstanceCopy();
};
ExtensionPropertiesTest::ExtensionPropertiesTest() {
addTests({&ExtensionPropertiesTest::constructNoCreate,
&ExtensionPropertiesTest::constructInstanceNoCreate,
&ExtensionPropertiesTest::constructCopy,
&ExtensionPropertiesTest::constructInstanceCopy});
}
void ExtensionPropertiesTest::constructNoCreate() {
{
ExtensionProperties properties{NoCreate};
CORRADE_COMPARE(properties.count(), 0);
}
/* Implicit construction is not allowed */
CORRADE_VERIFY(!(std::is_convertible<NoCreateT, ExtensionProperties>::value));
}
void ExtensionPropertiesTest::constructInstanceNoCreate() {
{
InstanceExtensionProperties properties{NoCreate};
CORRADE_COMPARE(properties.count(), 0);
}
/* Implicit construction is not allowed */
CORRADE_VERIFY(!(std::is_convertible<NoCreateT, InstanceExtensionProperties>::value));
}
void ExtensionPropertiesTest::constructCopy() {
CORRADE_VERIFY(!(std::is_constructible<ExtensionProperties, const ExtensionProperties&>{}));
CORRADE_VERIFY(!(std::is_assignable<ExtensionProperties, const ExtensionProperties&>{}));
}
void ExtensionPropertiesTest::constructInstanceCopy() {
CORRADE_VERIFY(!(std::is_constructible<InstanceExtensionProperties, const InstanceExtensionProperties&>{}));
CORRADE_VERIFY(!(std::is_assignable<InstanceExtensionProperties, const InstanceExtensionProperties&>{}));
}
}}}}
CORRADE_TEST_MAIN(Magnum::Vk::Test::ExtensionPropertiesTest)

221
src/Magnum/Vk/Test/ExtensionPropertiesVkTest.cpp

@ -0,0 +1,221 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020 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/StringStl.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Numeric.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/FormatStl.h>
#include "Magnum/Vk/Extensions.h"
#include "Magnum/Vk/LayerProperties.h"
#include "Magnum/Vk/ExtensionProperties.h"
#include "Magnum/Vk/Version.h"
namespace Magnum { namespace Vk { namespace Test { namespace {
struct ExtensionPropertiesVkTest: TestSuite::Tester {
explicit ExtensionPropertiesVkTest();
void constructMove();
/* Device extension move tested in DevicePropertiesVkTest */
void enumerateInstance();
void enumerateInstanceWithKhronosValidationLayer();
void enumerateInstanceNonexistentLayer();
void instanceExtensionIsSupported();
/* Device extensions tested in DevicePropertiesVkTest */
void outOfRange();
void namedRevision();
};
ExtensionPropertiesVkTest::ExtensionPropertiesVkTest() {
addTests({&ExtensionPropertiesVkTest::constructMove,
&ExtensionPropertiesVkTest::enumerateInstance,
&ExtensionPropertiesVkTest::enumerateInstanceWithKhronosValidationLayer,
&ExtensionPropertiesVkTest::enumerateInstanceNonexistentLayer,
&ExtensionPropertiesVkTest::instanceExtensionIsSupported,
&ExtensionPropertiesVkTest::outOfRange,
&ExtensionPropertiesVkTest::namedRevision});
}
using namespace Containers::Literals;
void ExtensionPropertiesVkTest::constructMove() {
InstanceExtensionProperties a = enumerateInstanceExtensionProperties();
const UnsignedInt count = a.count();
if(!count) CORRADE_SKIP("No extensions reported, can't test");
InstanceExtensionProperties b = std::move(a);
CORRADE_COMPARE(b.count(), count);
InstanceExtensionProperties c{NoCreate};
c = std::move(b);
CORRADE_COMPARE(c.count(), count);
CORRADE_VERIFY(std::is_nothrow_move_constructible<InstanceExtensionProperties>::value);
CORRADE_VERIFY(std::is_nothrow_move_assignable<InstanceExtensionProperties>::value);
}
void ExtensionPropertiesVkTest::enumerateInstance() {
InstanceExtensionProperties properties = enumerateInstanceExtensionProperties();
Debug{} << "Available instance extension count:" << properties.names().size();
CORRADE_COMPARE_AS(properties.count(), 0, TestSuite::Compare::Greater);
for(std::size_t i = 0; i != properties.count(); ++i) {
using namespace Containers::Literals;
CORRADE_ITERATION(properties.name(i));
CORRADE_COMPARE_AS(properties.name(i).size(), "VK_"_s.size(),
TestSuite::Compare::Greater);
CORRADE_COMPARE_AS(properties.revision(i), 0,
TestSuite::Compare::Greater);
/* All extensions are from the global layer */
CORRADE_COMPARE(properties.layer(i), 0);
}
/* The extension list should be sorted and unique (so Less, not
LessOrEqual) */
Containers::ArrayView<const Containers::StringView> 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 ExtensionPropertiesVkTest::enumerateInstanceWithKhronosValidationLayer() {
if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation"))
CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test");
/* There should be more extensions with this layer enabled */
InstanceExtensionProperties global = enumerateInstanceExtensionProperties();
InstanceExtensionProperties withKhronosValidation = enumerateInstanceExtensionProperties({"VK_LAYER_KHRONOS_validation"});
CORRADE_COMPARE_AS(global.count(),
withKhronosValidation.count(),
TestSuite::Compare::Less);
/* The extension list should be sorted even including the extra layers, and
unique (so Less, not LessOrEqual) */
Containers::ArrayView<const Containers::StringView> extensions = withKhronosValidation.names();
for(std::size_t i = 1; i != extensions.size(); ++i) {
CORRADE_COMPARE_AS(extensions[i - 1], extensions[i],
TestSuite::Compare::Less);
}
/* The VK_LAYER_KHRONOS_validation adds extensions that are supported
globally, which means extensionCount() should be larger than
extensions.size() as it has some duplicates */
CORRADE_COMPARE_AS(withKhronosValidation.count(), extensions.size(),
TestSuite::Compare::Greater);
/* The last extension should be from the validation layer */
CORRADE_COMPARE(withKhronosValidation.layer(0), 0);
CORRADE_COMPARE(withKhronosValidation.layer(withKhronosValidation.count() - 1), 1);
/* VK_EXT_validation_features is only in the layer */
CORRADE_VERIFY(!global.isSupported("VK_EXT_validation_features"));
CORRADE_VERIFY(withKhronosValidation.isSupported("VK_EXT_validation_features"));
}
void ExtensionPropertiesVkTest::enumerateInstanceNonexistentLayer() {
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 ExtensionPropertiesVkTest::instanceExtensionIsSupported() {
InstanceExtensionProperties properties = enumerateInstanceExtensionProperties();
CORRADE_COMPARE_AS(properties.count(), 0, TestSuite::Compare::Greater);
for(UnsignedInt i = 0; i != properties.count(); ++i) {
CORRADE_ITERATION(properties.name(i));
CORRADE_VERIFY(properties.isSupported(properties.name(i)));
}
CORRADE_VERIFY(!properties.isSupported("VK_this_doesnt_exist"));
/* Verify that we're not just comparing a prefix */
const std::string extension = std::string(properties.name(0)) + "_hello";
CORRADE_VERIFY(!properties.isSupported(extension));
/* This extension should be available almost always */
if(!properties.isSupported("VK_KHR_get_physical_device_properties2"))
CORRADE_SKIP("VK_KHR_get_physical_device_properties2 not supported, can't fully test");
/* Verify the overloads that take our extension wrappers work as well */
CORRADE_VERIFY(properties.isSupported<Extensions::KHR::get_physical_device_properties2>());
CORRADE_VERIFY(properties.isSupported(Extensions::KHR::get_physical_device_properties2{}));
}
void ExtensionPropertiesVkTest::outOfRange() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
InstanceExtensionProperties properties = enumerateInstanceExtensionProperties();
const UnsignedInt count = properties.count();
std::ostringstream out;
Error redirectError{&out};
properties.name(count);
properties.revision(count);
CORRADE_COMPARE(out.str(), Utility::formatString(
"Vk::ExtensionProperties::name(): index {0} out of range for {0} entries\n"
"Vk::ExtensionProperties::revision(): index {0} out of range for {0} entries\n", count));
}
void ExtensionPropertiesVkTest::namedRevision() {
InstanceExtensionProperties properties = enumerateInstanceExtensionProperties();
/** @todo use Extensions::KHR::surface once the extension is recognized */
if(!properties.isSupported("VK_KHR_surface"))
CORRADE_SKIP("VK_KHR_surface not supported, can't test");
if(!properties.isSupported<Extensions::KHR::get_physical_device_properties2>())
CORRADE_SKIP("VK_KHR_get_physical_device_properties2 not supported, can't test");
/* It was at revision 25 in January 2016, which is four months before
Vulkan first came out, so it's safe to assume all drivers have this
revision by now */
CORRADE_COMPARE_AS(properties.revision("VK_KHR_surface"), 25,
TestSuite::Compare::GreaterOrEqual);
/* Unknown extensions return 0 */
CORRADE_COMPARE(properties.revision("VK_this_doesnt_exist"), 0);
/* Verify the overloads that take our extension wrappers work as well */
CORRADE_COMPARE_AS(properties.revision<Extensions::KHR::get_physical_device_properties2>(), 0,
TestSuite::Compare::Greater);
CORRADE_COMPARE_AS(properties.revision(Extensions::KHR::get_physical_device_properties2{}), 0,
TestSuite::Compare::Greater);
}
}}}}
CORRADE_TEST_MAIN(Magnum::Vk::Test::ExtensionPropertiesVkTest)

2
src/Magnum/Vk/Vk.h

@ -35,7 +35,9 @@ namespace Magnum { namespace Vk {
#ifndef DOXYGEN_GENERATING_OUTPUT
class Extension;
class ExtensionProperties;
class InstanceExtension;
class InstanceExtensionProperties;
class LayerProperties;
enum class Result: Int;

Loading…
Cancel
Save