Browse Source

Vk: instance creation.

pull/234/head
Vladimír Vondruš 6 years ago
parent
commit
0392aeb878
  1. 35
      doc/snippets/MagnumVk.cpp
  2. 22
      doc/vulkan-mapping.dox
  3. 9
      src/Magnum/Vk/CMakeLists.txt
  4. 6
      src/Magnum/Vk/Extensions.cpp
  5. 3
      src/Magnum/Vk/Extensions.h
  6. 49
      src/Magnum/Vk/Implementation/Arguments.cpp
  7. 40
      src/Magnum/Vk/Implementation/Arguments.h
  8. 32
      src/Magnum/Vk/Implementation/InstanceState.cpp
  9. 42
      src/Magnum/Vk/Implementation/InstanceState.h
  10. 348
      src/Magnum/Vk/Instance.cpp
  11. 342
      src/Magnum/Vk/Instance.h
  12. 2
      src/Magnum/Vk/Test/CMakeLists.txt
  13. 93
      src/Magnum/Vk/Test/InstanceTest.cpp
  14. 508
      src/Magnum/Vk/Test/InstanceVkTest.cpp
  15. 2
      src/Magnum/Vk/Vk.h

35
doc/snippets/MagnumVk.cpp

@ -23,13 +23,48 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include <Corrade/Containers/ArrayView.h>
#include "Magnum/Magnum.h" #include "Magnum/Magnum.h"
#include "Magnum/Math/Color.h" #include "Magnum/Math/Color.h"
#include "Magnum/Vk/Extensions.h"
#include "Magnum/Vk/Instance.h"
#include "Magnum/Vk/Integration.h" #include "Magnum/Vk/Integration.h"
#include "MagnumExternal/Vulkan/flextVkGlobal.h"
using namespace Magnum; using namespace Magnum;
int main() { int main() {
{
Vk::Instance instance;
/* [Instance-isExtensionEnabled] */
if(instance.isExtensionEnabled<Vk::Extensions::EXT::debug_utils>()) {
// use the fancy debugging APIs
} else if(instance.isExtensionEnabled<Vk::Extensions::EXT::debug_report>()) {
// use the non-fancy and deprecated debugging APIs
} else {
// well, tough luck
}
/* [Instance-isExtensionEnabled] */
}
{
/* Header included again inside a function, but it's fine as the guards will
make it empty */
/* [Instance-global-function-pointers] */
#include <MagnumExternal/Vulkan/flextVkGlobal.h>
// …
Vk::Instance instance;
instance.populateGlobalFunctionPointers();
VkPhysicalDeviceGroupProperties properties[10];
UnsignedInt count = Containers::arraySize(properties);
vkEnumeratePhysicalDeviceGroupsKHR(instance, &count, properties);
/* [Instance-global-function-pointers] */
}
{ {
/* [Integration] */ /* [Integration] */
VkOffset2D a{64, 32}; VkOffset2D a{64, 32};

22
doc/vulkan-mapping.dox

@ -136,7 +136,7 @@ Vulkan function | Matching API
@fn_vk{CreateFramebuffer}, \n @fn_vk{DestroyFramebuffer} | | @fn_vk{CreateFramebuffer}, \n @fn_vk{DestroyFramebuffer} | |
@fn_vk{CreateImage}, \n @fn_vk{DestroyImage} | | @fn_vk{CreateImage}, \n @fn_vk{DestroyImage} | |
@fn_vk{CreateImageView}, \n @fn_vk{DestroyImageView} | | @fn_vk{CreateImageView}, \n @fn_vk{DestroyImageView} | |
@fn_vk{CreateInstance}, \n @fn_vk{DestroyInstance} | | @fn_vk{CreateInstance}, \n @fn_vk{DestroyInstance} | @ref Instance constructor and destructor
@fn_vk{CreatePipeline}, \n @fn_vk{DestroyPipeline} | | @fn_vk{CreatePipeline}, \n @fn_vk{DestroyPipeline} | |
@fn_vk{CreatePipelineCache}, \n @fn_vk{DestroyPipelineCache} | | @fn_vk{CreatePipelineCache}, \n @fn_vk{DestroyPipelineCache} | |
@fn_vk{CreatePipelineLayout}, \n @fn_vk{DestroyPipelineLayout} | | @fn_vk{CreatePipelineLayout}, \n @fn_vk{DestroyPipelineLayout} | |
@ -200,7 +200,7 @@ Vulkan function | Matching API
@fn_vk{GetImageMemoryRequirements}, \n @fn_vk{GetImageMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetImageMemoryRequirements}, \n @fn_vk{GetImageMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | |
@fn_vk{GetImageSparseMemoryRequirements}, \n @fn_vk{GetImageSparseMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetImageSparseMemoryRequirements}, \n @fn_vk{GetImageSparseMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | |
@fn_vk{GetImageSubresourceLayout} | | @fn_vk{GetImageSubresourceLayout} | |
@fn_vk{GetInstanceProcAddr} | | @fn_vk{GetInstanceProcAddr} | @ref Instance constructor
@fn_vk{GetPhysicalDeviceExternalBufferProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPhysicalDeviceExternalBufferProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | |
@fn_vk{GetPhysicalDeviceExternalFenceProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPhysicalDeviceExternalFenceProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | |
@fn_vk{GetPhysicalDeviceExternalSemaphoreProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPhysicalDeviceExternalSemaphoreProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | |
@ -294,6 +294,24 @@ Vulkan function | Matching API
--------------------------------------- | ------------ --------------------------------------- | ------------
@fn_vk{WaitForFences} | | @fn_vk{WaitForFences} | |
@section vulkan-mapping-structures Info structures
@subsection vulkan-mapping-structures-a A
@m_class{m-fullwidth}
Vulkan structure | Matching API
--------------------------------------- | ------------
@type_vk{ApplicationInfo} | @ref InstanceCreateInfo
@subsection vulkan-mapping-structures-i I
@m_class{m-fullwidth}
Vulkan structure | Matching API
--------------------------------------- | ------------
@type_vk{InstanceCreateInfo} | @ref InstanceCreateInfo
*/ */
}} }}

9
src/Magnum/Vk/CMakeLists.txt

@ -29,8 +29,12 @@ find_package(Vulkan REQUIRED)
set(MagnumVk_SRCS set(MagnumVk_SRCS
Extensions.cpp Extensions.cpp
Handle.cpp Handle.cpp
Instance.cpp
Result.cpp Result.cpp
Version.cpp) Version.cpp
Implementation/Arguments.cpp
Implementation/InstanceState.cpp)
set(MagnumVk_GracefulAssert_SRCS set(MagnumVk_GracefulAssert_SRCS
Enums.cpp Enums.cpp
@ -55,6 +59,9 @@ set(MagnumVk_HEADERS
visibility.h) visibility.h)
set(MagnumVk_PRIVATE_HEADERS set(MagnumVk_PRIVATE_HEADERS
Implementation/Arguments.h
Implementation/InstanceState.h
Implementation/compressedPixelFormatMapping.hpp Implementation/compressedPixelFormatMapping.hpp
Implementation/pixelFormatMapping.hpp Implementation/pixelFormatMapping.hpp
Implementation/vertexFormatMapping.hpp) Implementation/vertexFormatMapping.hpp)

6
src/Magnum/Vk/Extensions.cpp

@ -31,9 +31,9 @@ namespace Magnum { namespace Vk {
namespace { namespace {
/* When adding a new list, InstanceExtension::extensions() needs to be adapted. /* When adding a new list, InstanceExtension::extensions() and
Binary search is performed on the extensions, thus they have to be sorted Instance::initialize() needs to be adapted. Binary search is performed on
alphabetically. */ the extensions, thus they have to be sorted alphabetically. */
constexpr InstanceExtension InstanceExtensions[] { constexpr InstanceExtension InstanceExtensions[] {
Extensions::EXT::debug_report{}, Extensions::EXT::debug_report{},
Extensions::EXT::debug_utils{}, Extensions::EXT::debug_utils{},

3
src/Magnum/Vk/Extensions.h

@ -50,7 +50,8 @@ Each struct has the same public methods as the @ref InstanceExtension /
@ref Extension class (@ref Extension::requiredVersion() "requiredVersion()", @ref Extension class (@ref Extension::requiredVersion() "requiredVersion()",
@ref Extension::coreVersion() "coreVersion()" and @ref Extension::string() "string()"), @ref Extension::coreVersion() "coreVersion()" and @ref Extension::string() "string()"),
but these structs are better suited for compile-time decisions rather than but these structs are better suited for compile-time decisions rather than
@ref Extension instances. @ref Extension instances. See @ref Instance::isExtensionEnabled() for example
usage.
This library is built if `WITH_VK` is enabled when building Magnum. To use this This library is built if `WITH_VK` is enabled when building Magnum. To use this
library with CMake, you need to request the `Vk` component of the `Magnum` library with CMake, you need to request the `Vk` component of the `Magnum`

49
src/Magnum/Vk/Implementation/Arguments.cpp

@ -0,0 +1,49 @@
/*
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 "Arguments.h"
#include <Corrade/Utility/Arguments.h>
namespace Magnum { namespace Vk { namespace Implementation {
Utility::Arguments arguments() {
Utility::Arguments args{"magnum"};
args.addOption("disable-layers").setHelp("disable-layers", "Vulkan layers to disable", "LIST")
.addOption("disable-extensions").setHelp("disable-extensions", "Vulkan extensions to disable", "LIST")
.addOption("enable-layers").setHelp("enable-layers", "Vulkan layers to enable in addition to the defaults and what the application requests", "LIST")
.addOption("enable-instance-extensions").setHelp("enable-instance-extensions", "Vulkan instance extensions to enable in addition to the defaults and what the application requests", "LIST")
.addOption("vulkan-version").setHelp("vulkan-version", "force Vulkan version", "X.Y")
.addOption("log", "default").setHelp("log", "console logging", "default|quiet|verbose")
.setFromEnvironment("disable-layers")
.setFromEnvironment("disable-extensions")
.setFromEnvironment("enable-layers")
.setFromEnvironment("enable-instance-extensions")
.setFromEnvironment("vulkan-version")
.setFromEnvironment("log");
return args;
}
}}}

40
src/Magnum/Vk/Implementation/Arguments.h

@ -0,0 +1,40 @@
#ifndef Magnum_Vk_Implementation_Arguments_h
#define Magnum_Vk_Implementation_Arguments_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.
*/
#include <Corrade/Utility/Utility.h>
#include "Magnum/Magnum.h"
namespace Magnum { namespace Vk { namespace Implementation {
/* Used by both InstanceCreateInfo and DeviceCreateInfo, each takes a subset
of the arguments */
Utility::Arguments arguments();
}}}
#endif

32
src/Magnum/Vk/Implementation/InstanceState.cpp

@ -0,0 +1,32 @@
/*
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 "InstanceState.h"
namespace Magnum { namespace Vk { namespace Implementation {
InstanceState::InstanceState(Instance&, Int argc, const char** argv): argc{argc}, argv{argv} {}
}}}

42
src/Magnum/Vk/Implementation/InstanceState.h

@ -0,0 +1,42 @@
#ifndef Magnum_Vk_Implementation_InstanceState_h
#define Magnum_Vk_Implementation_InstanceState_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.
*/
#include "Magnum/Vk/Vk.h"
#include "Magnum/Vk/Vulkan.h"
namespace Magnum { namespace Vk { namespace Implementation {
struct InstanceState {
explicit InstanceState(Instance& instance, Int argc, const char** argv);
Int argc;
const char** argv;
};
}}}
#endif

348
src/Magnum/Vk/Instance.cpp

@ -0,0 +1,348 @@
/*
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 "Instance.h"
#include <algorithm>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/String.h>
#include <Corrade/Containers/StringView.h>
#include <Corrade/Utility/Arguments.h>
#include "Magnum/Vk/Extensions.h"
#include "Magnum/Vk/Handle.h"
#include "Magnum/Vk/Result.h"
#include "Magnum/Vk/Version.h"
#include "Magnum/Vk/Implementation/Arguments.h"
#include "Magnum/Vk/Implementation/InstanceState.h"
#include "MagnumExternal/Vulkan/flextVkGlobal.h"
namespace Magnum { namespace Vk {
struct InstanceCreateInfo::State {
Containers::String applicationName;
Containers::Array<Containers::String> ownedStrings;
Containers::Array<const char*> layers;
Containers::Array<const char*> extensions;
Containers::String disabledLayersStorage, disabledExtensionsStorage;
Containers::Array<Containers::StringView> disabledLayers, disabledExtensions;
bool quietLog = false;
Version version = Version::None;
Int argc;
const char** argv;
};
InstanceCreateInfo::InstanceCreateInfo(const Int argc, const char** const argv, const LayerProperties* const layerProperties, const InstanceExtensionProperties* const extensionProperties, const Flags flags): _info{}, _applicationInfo{} {
Utility::Arguments args = Implementation::arguments();
args.parse(argc, argv);
if(args.value("log") == "quiet")
_state.emplace().quietLog = true;
if(argc && argv) {
if(!_state) _state.emplace();
_state->argc = argc;
_state->argv = argv;
}
_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
/** @todo filter out magnum-specific flags once there are any */
_info.flags = VkInstanceCreateFlags(flags);
_info.pApplicationInfo = &_applicationInfo;
_applicationInfo.pEngineName = "Magnum";
/** @todo magnum version? 2020 can't fit into Vulkan's version
representation, sigh */
/* If there's a forced Vulkan version, use that, otherwise use the reported
instance version */
Containers::StringView version = args.value<Containers::StringView>("vulkan-version");
if(!version.isEmpty()) {
if(!_state) _state.emplace();
if((_state->version = args.value<Version>("vulkan-version")) == Version::None)
Warning{} << "Invalid --magnum-vulkan-version" << args.value<Containers::StringView>("vulkan-version") << Debug::nospace << ", ignoring";
}
if(_state && _state->version == Version::None)
_state->version = enumerateInstanceVersion();
_applicationInfo.apiVersion = UnsignedInt(_state ? _state->version : enumerateInstanceVersion());
/* If there are any disabled layers or extensions, sort them and save for
later -- we'll use them to filter the ones added by the app */
Containers::String disabledLayers = args.value<Containers::String>("disable-layers");
Containers::String disabledExtensions = args.value<Containers::String>("disable-extensions");
if(!disabledLayers.isEmpty()) {
if(!_state) _state.emplace();
_state->disabledLayersStorage = std::move(disabledLayers);
_state->disabledLayers = Containers::StringView{_state->disabledLayersStorage}.splitWithoutEmptyParts();
std::sort(_state->disabledLayers.begin(), _state->disabledLayers.end());
}
if(!disabledExtensions.isEmpty()) {
if(!_state) _state.emplace();
_state->disabledExtensionsStorage = std::move(disabledExtensions);
_state->disabledExtensions = Containers::StringView{_state->disabledExtensionsStorage}.splitWithoutEmptyParts();
std::sort(_state->disabledExtensions.begin(), _state->disabledExtensions.end());
}
/* Add all layers and extensions enabled on command-line. The blacklist is
applied on those as well. */
/** @todo use a generator split() so we can avoid the growing allocation
of the output array */
/** @todo unfortunately even though the split and value retrieval is mostly
allocation-free, the strings will be turned into owning copies because
none of them is null-terminated or global -- could be a better idea to
just grow one giant string internally (once we have growable strings) */
addEnabledLayers(args.value<Containers::StringView>("enable-layers").splitWithoutEmptyParts());
addEnabledExtensions(args.value<Containers::StringView>("enable-instance-extensions").splitWithoutEmptyParts());
/** @todo use this (enabling debug layers etc.) */
static_cast<void>(layerProperties);
static_cast<void>(extensionProperties);
}
InstanceCreateInfo::InstanceCreateInfo(NoInitT) noexcept {}
InstanceCreateInfo::InstanceCreateInfo(const VkInstanceCreateInfo& info): _info{info} {}
InstanceCreateInfo::~InstanceCreateInfo() = default;
InstanceCreateInfo& InstanceCreateInfo::setApplicationInfo(const Containers::StringView name, const Version version) {
/* Keep an owned copy of the name if it's not global / null-terminated;
Use nullptr if the view is empty */
if(!name.isEmpty()) {
if(!_state) _state.emplace();
_state->applicationName = Containers::String::nullTerminatedGlobalView(name);
_applicationInfo.pApplicationName = _state->applicationName.data();
} else {
if(_state) _state->applicationName = nullptr;
_applicationInfo.pApplicationName = nullptr;
}
_applicationInfo.applicationVersion = UnsignedInt(version);
return *this;
}
InstanceCreateInfo& InstanceCreateInfo::addEnabledLayers(const Containers::ArrayView<const Containers::StringView> layers) {
if(layers.empty()) return *this;
if(!_state) _state.emplace();
/* Add null-terminated strings to the layer array */
arrayReserve(_state->layers, _state->layers.size() + layers.size());
for(const Containers::StringView layer: layers) {
/* If the layer is blacklisted, skip it */
if(std::binary_search(_state->disabledLayers.begin(), _state->disabledLayers.end(), layer)) continue;
/* Keep an owned *allocated* copy of the string if it's not global or
null-terminated -- ideally, if people use string view literals,
those will be, so this won't allocate. Allocated so the pointers
don't get invalidated when the array gets reallocated. */
const char* data;
if(!(layer.flags() >= (Containers::StringViewFlag::NullTerminated|Containers::StringViewFlag::Global)))
data = arrayAppend(_state->ownedStrings, Containers::InPlaceInit,
Containers::AllocatedInit, layer).data();
else data = layer.data();
arrayAppend(_state->layers, data);
}
/* Update the layer count, re-route the pointer to the layers array in case
it got reallocated */
_info.enabledLayerCount = _state->layers.size();
_info.ppEnabledLayerNames = _state->layers.data();
return *this;
}
InstanceCreateInfo& InstanceCreateInfo::addEnabledLayers(const std::initializer_list<Containers::StringView> layers) {
return addEnabledLayers(Containers::arrayView(layers));
}
InstanceCreateInfo& InstanceCreateInfo::addEnabledExtensions(const Containers::ArrayView<const Containers::StringView> extensions) {
if(extensions.empty()) return *this;
if(!_state) _state.emplace();
/* Add null-terminated strings to the extension array */
arrayReserve(_state->extensions, _state->extensions.size() + extensions.size());
for(const Containers::StringView extension: extensions) {
/* If the extension is blacklisted, skip it */
if(std::binary_search(_state->disabledExtensions.begin(), _state->disabledExtensions.end(), extension)) continue;
/* Keep an owned *allocated* copy of the string if it's not global or
null-terminated -- ideally, if people use string view literals,
those will be, so this won't allocate. Allocated so the pointers
don't get invalidated when the array gets reallocated. */
const char* data;
if(!(extension.flags() >= (Containers::StringViewFlag::NullTerminated|Containers::StringViewFlag::Global)))
data = arrayAppend(_state->ownedStrings, Containers::InPlaceInit,
Containers::AllocatedInit, extension).data();
else data = extension.data();
arrayAppend(_state->extensions, data);
}
/* Update the extension count, re-route the pointer to the layers array in
case it got reallocated */
_info.enabledExtensionCount = _state->extensions.size();
_info.ppEnabledExtensionNames = _state->extensions.data();
return *this;
}
InstanceCreateInfo& InstanceCreateInfo::addEnabledExtensions(const std::initializer_list<Containers::StringView> extensions) {
return addEnabledExtensions(Containers::arrayView(extensions));
}
InstanceCreateInfo& InstanceCreateInfo::addEnabledExtensions(const Containers::ArrayView<const InstanceExtension> extensions) {
if(extensions.empty()) return *this;
if(!_state) _state.emplace();
arrayReserve(_state->extensions, _state->extensions.size() + extensions.size());
for(const InstanceExtension& extension: extensions) {
/* If the extension is blacklisted, skip it */
if(std::binary_search(_state->disabledExtensions.begin(), _state->disabledExtensions.end(), extension.string())) continue;
arrayAppend(_state->extensions, extension.string().data());
}
/* Update the extension count, re-route the pointer to the layers array in
case it got reallocated */
_info.enabledExtensionCount = _state->extensions.size();
_info.ppEnabledExtensionNames = _state->extensions.data();
return *this;
}
InstanceCreateInfo& InstanceCreateInfo::addEnabledExtensions(const std::initializer_list<InstanceExtension> extensions) {
return addEnabledExtensions(Containers::arrayView(extensions));
}
Instance Instance::wrap(const VkInstance handle, const Version version, const Containers::ArrayView<const Containers::StringView> enabledExtensions, const HandleFlags flags) {
/* Compared to the constructor nothing is printed here as it would be just
repeating what was passed to the constructor */
Instance out{NoCreate};
out._handle = handle;
out._flags = flags;
out.initializeExtensions(enabledExtensions);
out.initialize(version, 0, nullptr);
return out;
}
Instance Instance::wrap(const VkInstance handle, const Version version, const std::initializer_list<Containers::StringView> enabledExtensions, const HandleFlags flags) {
return wrap(handle, version, Containers::arrayView(enabledExtensions), flags);
}
Instance::Instance(const InstanceCreateInfo& info): _flags{HandleFlag::DestroyOnDestruction} {
const Version version = info._state && info._state->version != Version::None ? info._state->version : enumerateInstanceVersion();
/* Print all enabled layers and extensions if we're not told to be quiet */
if(!info._state || !info._state->quietLog) {
Debug{} << "Instance version:" << version;
if(info->enabledLayerCount) {
Debug{} << "Enabled layers:";
for(std::size_t i = 0, max = info->enabledLayerCount; i != max; ++i)
Debug{} << " " << info->ppEnabledLayerNames[i];
}
if(info->enabledExtensionCount) {
Debug{} << "Enabled instance extensions:";
for(std::size_t i = 0, max = info->enabledExtensionCount; i != max; ++i)
Debug{} << " " << info->ppEnabledExtensionNames[i];
}
}
MAGNUM_VK_INTERNAL_ASSERT_RESULT(vkCreateInstance(info, nullptr, &_handle));
initializeExtensions<const char*>({info->ppEnabledExtensionNames, info->enabledExtensionCount});
if(info._state)
initialize(version, info._state->argc, info._state->argv);
else
initialize(version, 0, nullptr);
}
Instance::Instance(NoCreateT): _handle{}, _functionPointers{} {}
Instance::Instance(Instance&& other) noexcept: _handle{other._handle}, _flags{other._flags}, _version{other._version}, _extensionStatus{other._extensionStatus}, _state{std::move(other._state)}, _functionPointers{other._functionPointers} {
other._handle = nullptr;
other._functionPointers = {};
}
Instance::~Instance() {
if(_handle && (_flags & HandleFlag::DestroyOnDestruction))
_functionPointers.DestroyInstance(_handle, nullptr);
}
Instance& Instance::operator=(Instance&& other) noexcept {
using std::swap;
swap(other._handle, _handle);
swap(other._flags, _flags);
swap(other._version, _version);
swap(other._extensionStatus, _extensionStatus);
swap(other._state, _state);
swap(other._functionPointers, _functionPointers);
return *this;
}
template<class T> void Instance::initializeExtensions(const Containers::ArrayView<const T> enabledExtensions) {
/* Mark all known extensions as enabled */
for(const T extension: enabledExtensions) {
for(Containers::ArrayView<const InstanceExtension> knownExtensions: {
InstanceExtension::extensions(Version::None),
/*InstanceExtension::extensions(Version::Vk10), is empty */
InstanceExtension::extensions(Version::Vk11),
/*InstanceExtension::extensions(Version::Vk12) is empty */
}) {
auto found = std::lower_bound(knownExtensions.begin(), knownExtensions.end(), extension, [](const InstanceExtension& a, const T& b) {
return a.string() < static_cast<const Containers::StringView&>(b);
});
if(found->string() != extension) continue;
_extensionStatus.set(found->index(), true);
}
}
}
void Instance::initialize(const Version version, const Int argc, const char** const argv) {
/* Init version, function pointers */
_version = version;
flextVkInitInstance(_handle, &_functionPointers);
/* Set up extension-dependent functionality */
_state.emplace(*this, argc, argv);
}
VkInstance Instance::release() {
const VkInstance handle = _handle;
_handle = nullptr;
return handle;
}
bool Instance::isExtensionEnabled(const InstanceExtension& extension) const {
return _extensionStatus[extension.index()];
}
void Instance::populateGlobalFunctionPointers() {
flextVkInstance = _functionPointers;
}
}}

342
src/Magnum/Vk/Instance.h

@ -26,18 +26,356 @@
*/ */
/** @file /** @file
* @brief Nothing, haha * @brief Class @ref Magnum::Vk::InstanceCreateInfo, @ref Magnum::Vk::Instance
* @m_since_latest * @m_since_latest
*/ */
#include <cstddef> #include <Corrade/Containers/Pointer.h>
#include "Magnum/Tags.h"
#include "Magnum/Math/BoolVector.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 { namespace Magnum { namespace Vk {
namespace Implementation { namespace Implementation {
enum: std::size_t { InstanceExtensionCount = 16 }; enum: std::size_t { InstanceExtensionCount = 16 };
struct InstanceState;
} }
/**
@brief Instance creation info
@m_since_latest
Wraps a @type_vk_keyword{InstanceCreateInfo} and
@type_vk_keyword{ApplicationInfo}.
@see @ref Instance
*/
class MAGNUM_VK_EXPORT InstanceCreateInfo {
public:
/**
* @brief Instance creation flag
*
* Wraps @type_vk_keyword{InstanceCreateFlagBits}.
* @see @ref Flags, @ref InstanceCreateInfo(Int, const char**, const LayerProperties*, const InstanceExtensionProperties*, Flags)
*/
enum class Flag: UnsignedInt {};
/**
* @brief Instance creation flags
*
* Type-safe wrapper for @type_vk_keyword{InstanceCreateFlags}.
* @see @ref InstanceCreateInfo(Int, const char**, const LayerProperties*, const InstanceExtensionProperties*, Flags)
*/
typedef Containers::EnumSet<Flag> Flags;
/**
* @brief Constructor
* @param argc Command-line argument count. Can be @cpp 0 @ce.
* @param argv Command-line argument values. Can be
* @cpp nullptr @ce. If set, is expected to stay in scope for the
* whole instance lifetime.
* @param layerProperties Existing @ref LayerProperties instance for
* querying available Vulkan layers. If @cpp nullptr @ce, a new
* instance may be created internally if needed.
* @param extensionProperties Existing @ref InstanceExtensionProperties
* instance for querying available Vulkan extensions. If
* @cpp nullptr @ce, a new instance may be created internally if
* needed.
* @param flags Instance creation flags
*
* The following @type_vk{InstanceCreateInfo} fields are pre-filled in
* addition to `sType`, everything else is zero-filled:
*
* - `pApplicationInfo`
* - @cpp pApplicationInfo->apiVersion @ce to
* @ref enumerateInstanceVersion()
* - @cpp pApplicationInfo->engineName @ce to @cpp "Magnum" @ce
*/
explicit InstanceCreateInfo(Int argc, const char** argv, const LayerProperties* layerProperties, const InstanceExtensionProperties* const extensionProperties, Flags flags = {});
/** @overload */
explicit InstanceCreateInfo(Int argc, char** argv, const LayerProperties* layerProperties, const InstanceExtensionProperties* extensionProperties, Flags flags = {}): InstanceCreateInfo{argc, const_cast<const char**>(argv), layerProperties, extensionProperties, flags} {}
/** @overload */
explicit InstanceCreateInfo(Int argc, std::nullptr_t argv, const LayerProperties* layerProperties, const InstanceExtensionProperties* extensionProperties, Flags flags = {}): InstanceCreateInfo{argc, static_cast<const char**>(argv), layerProperties, extensionProperties, flags} {}
/** @overload */
explicit InstanceCreateInfo(Int argc, const char** argv, Flags flags = {}): InstanceCreateInfo{argc, argv, nullptr, nullptr, flags} {}
/** @overload */
explicit InstanceCreateInfo(Int argc, char** argv, Flags flags = {}): InstanceCreateInfo{argc, argv, nullptr, nullptr, flags} {}
/** @overload */
explicit InstanceCreateInfo(Int argc, std::nullptr_t argv, Flags flags = {}): InstanceCreateInfo{argc, argv, nullptr, nullptr, flags} {}
/** @overload */
explicit InstanceCreateInfo(Flags flags = {}): InstanceCreateInfo{0, nullptr, flags} {}
/**
* @brief Construct without initializing the contents
*
* Note that not even the `sType` field is set --- the structure has to
* be fully initialized afterwards in order to be usable.
*/
explicit InstanceCreateInfo(NoInitT) noexcept;
/**
* @brief Construct from existing data
*
* Copies the existing values verbatim, pointers are kept unchanged
* without taking over the ownership. Modifying the newly created
* instance will not modify the original data nor the pointed-to data.
*/
explicit InstanceCreateInfo(const VkInstanceCreateInfo& info);
~InstanceCreateInfo();
/**
* @brief Set application info
* @return Reference to self (for method chaining)
*
* Use the @ref version() helper to create the @p version value. The
* name is @cpp nullptr @ce by default.
*/
InstanceCreateInfo& setApplicationInfo(Containers::StringView name, Version version);
/**
* @brief Add enabled layers
* @return Reference to self (for method chaining)
*
* All listed layers are expected be supported, use
* @ref LayerProperties::isSupported() to check for their presence.
*
* The function makes copies of string views that are not owning or
* null-terminated, use the @link Containers::Literals::operator""_s() @endlink
* literal to prevent that where possible.
*/
InstanceCreateInfo& addEnabledLayers(Containers::ArrayView<const Containers::StringView> layers);
/** @overload */
InstanceCreateInfo& addEnabledLayers(std::initializer_list<Containers::StringView> layers);
/**
* @brief Add enabled instance extensions
* @return Reference to self (for method chaining)
*
* All listed extensions are expected to be supported either globally
* or in at least one of the enabled layers, use
* @ref InstanceExtensionProperties::isSupported() to check for their
* presence.
*
* The function makes copies of string views that are not owning or
* null-terminated, use the @link Containers::Literals::operator""_s() @endlink
* literal to prevent that where possible.
*/
InstanceCreateInfo& addEnabledExtensions(Containers::ArrayView<const Containers::StringView> extensions);
/** @overload */
InstanceCreateInfo& addEnabledExtensions(std::initializer_list<Containers::StringView> extension);
/** @overload */
InstanceCreateInfo& addEnabledExtensions(Containers::ArrayView<const InstanceExtension> extensions);
/** @overload */
InstanceCreateInfo& addEnabledExtensions(std::initializer_list<InstanceExtension> extension);
/** @overload */
template<class ...E> InstanceCreateInfo& addEnabledExtensions() {
static_assert(Implementation::IsInstanceExtension<E...>::value, "expected only Vulkan instance extensions");
return addEnabledExtensions({E{}...});
}
/** @brief Underlying @type_vk{InstanceCreateInfo} structure */
VkInstanceCreateInfo& operator*() { return _info; }
/** @overload */
const VkInstanceCreateInfo& operator*() const { return _info; }
/** @overload */
VkInstanceCreateInfo* operator->() { return &_info; }
/** @overload */
const VkInstanceCreateInfo* operator->() const { return &_info; }
/** @overload */
operator const VkInstanceCreateInfo*() const { return &_info; }
private:
friend Instance;
VkInstanceCreateInfo _info;
VkApplicationInfo _applicationInfo;
struct State;
Containers::Pointer<State> _state;
};
CORRADE_ENUMSET_OPERATORS(InstanceCreateInfo::Flags)
/**
@brief Instance
@m_since_latest
Wraps a @type_vk_keyword{Instance} and stores instance-specific Vulkan function
pointers.
@see @ref vulkan-wrapping
*/
class MAGNUM_VK_EXPORT Instance {
public:
/**
* @brief Wrap existing Vulkan handle
* @param handle The @type_vk{Instance} handle
* @param version Vulkan version that's assumed to be used on the
* instance
* @param enabledExtensions Extensions that are assumed to be enabled
* on the instance
* @param flags Handle flags
*
* The @p handle is expected to be of an existing Vulkan instance. The
* @p version and @p enabledExtensions parameters populate internal
* info about supported version and extensions and will be reflected in
* @ref isVersionSupported() and @ref isExtensionEnabled(), among other
* things. If @p enabledExtensions empty, the instance will behave as
* if no extensions were enabled.
*
* Unlike an instance created using a constructor, the Vulkan instance
* is by default not deleted on destruction, use @p flags for different
* behavior.
* @see @ref release()
*/
static Instance wrap(VkInstance handle, Version version, Containers::ArrayView<const Containers::StringView> enabledExtensions, HandleFlags flags = {});
/** @overload */
static Instance wrap(VkInstance handle, Version version, std::initializer_list<Containers::StringView> enabledExtensions, HandleFlags flags = {});
/**
* @brief Constructor
*
* @see @fn_vk_keyword{CreateInstance}
*/
explicit Instance(const InstanceCreateInfo& info = InstanceCreateInfo{});
/**
* @brief Construct without creating the instance
*
* The constructed instance is equivalent to moved-from state. Useful
* in cases where you will overwrite the instance later anyway. Move
* another object over it to make it useful.
*/
explicit Instance(NoCreateT);
/** @brief Copying is not allowed */
Instance(const Instance&) = delete;
/** @brief Move constructor */
Instance(Instance&& other) noexcept;
/**
* @brief Destructor
*
* Destroys associated @type_vk{Instance} handle, unless the instance
* was created using @ref wrap() without @ref HandleFlag::DestroyOnDestruction
* specified.
* @see @fn_vk_keyword{DestroyInstance}, @ref release()
*/
~Instance();
/** @brief Copying is not allowed */
Instance& operator=(const Instance&) = delete;
/** @brief Move assignment */
Instance& operator=(Instance&& other) noexcept;
/** @brief Underlying @type_vk{Instance} handle */
VkInstance handle() { return _handle; }
/** @overload */
operator VkInstance() { return _handle; }
/** @brief Handle flags */
HandleFlags handleFlags() const { return _flags; }
/** @brief Instance version */
Version version() const { return _version; }
/** @brief Whether given version is supported on the instance */
bool isVersionSupported(Version version) const {
return _version >= version;
}
/**
* @brief Whether given extension is enabled
*
* Accepts instance extensions from the @ref Extensions namespace,
* listed also in the @ref vulkan-support "Vulkan support tables".
* Search complexity is @f$ \mathcal{O}(1) @f$. Example usage:
*
* @snippet MagnumVk.cpp Instance-isExtensionEnabled
*
* Note that this returns @cpp true @ce only if given extension is
* supported by the driver *and* it was enabled in
* @ref InstanceCreateInfo when creating the @ref Instance. For
* querying extension support before creating an instance use
* @ref InstanceExtensionProperties::isSupported().
*/
template<class E> bool isExtensionEnabled() const {
static_assert(Implementation::IsInstanceExtension<E>::value, "expected a Vulkan instance extension");
return _extensionStatus[E::InstanceIndex];
}
/** @overload */
bool isExtensionEnabled(const InstanceExtension& extension) const;
/**
* @brief Instance-specific Vulkan function pointers
*
* Function pointers are implicitly stored per-instance, use
* @ref populateGlobalFunctionPointers() to populate the global `vk*`
* functions.
*/
const FlextVkInstance& operator*() const { return _functionPointers; }
/** @overload */
const FlextVkInstance* operator->() const { return &_functionPointers; }
/**
* @brief Release the underlying Vulkan instance
*
* Releases ownership of the Vulkan instance and returns its handle so
* @fn_vk{DestroyInstance} is not called on destruction. The internal
* state is then equivalent to moved-from state.
* @see @ref wrap()
*/
VkInstance release();
/**
* @brief Populate global instance-level function pointers to be used with third-party code
*
* Populates instance-level global function pointers so third-party
* code is able to call global instance-level `vk*` functions:
*
* @snippet MagnumVk.cpp Instance-global-function-pointers
*
* @attention This operation is changing global state. You need to
* ensure that this function is not called simultaenously from
* multiple threads and code using those function points is
* calling them with the same instance as the one returned by
* @ref handle().
*/
void populateGlobalFunctionPointers();
#ifdef DOXYGEN_GENERATING_OUTPUT
private:
#endif
Implementation::InstanceState& state() { return *_state; }
private:
template<class T> MAGNUM_VK_LOCAL void initializeExtensions(Containers::ArrayView<const T> enabledExtensions);
MAGNUM_VK_LOCAL void initialize(Version version, Int argc, const char** argv);
VkInstance _handle;
HandleFlags _flags;
Version _version;
Math::BoolVector<Implementation::InstanceExtensionCount> _extensionStatus;
Containers::Pointer<Implementation::InstanceState> _state;
/* This member is bigger than you might think */
FlextVkInstance _functionPointers;
};
}} }}
#endif #endif

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

@ -28,6 +28,7 @@ corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkExtensionPropertiesTest ExtensionPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkExtensionPropertiesTest ExtensionPropertiesTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkHandleTest HandleTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkHandleTest HandleTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkInstanceTest InstanceTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk)
@ -36,5 +37,6 @@ corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk)
if(BUILD_VK_TESTS) if(BUILD_VK_TESTS)
corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk)
endif() endif()

93
src/Magnum/Vk/Test/InstanceTest.cpp

@ -0,0 +1,93 @@
/*
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 <new>
#include <Corrade/TestSuite/Tester.h>
#include "Magnum/Vk/Instance.h"
namespace Magnum { namespace Vk { namespace Test { namespace {
struct InstanceTest: TestSuite::Tester {
explicit InstanceTest();
void createInfoConstructNoInit();
void createInfoConstructFromVk();
void constructNoCreate();
void constructCopy();
};
InstanceTest::InstanceTest() {
addTests({&InstanceTest::createInfoConstructNoInit,
&InstanceTest::createInfoConstructFromVk,
&InstanceTest::constructNoCreate,
&InstanceTest::constructCopy});
}
void InstanceTest::createInfoConstructNoInit() {
InstanceCreateInfo info;
info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
new(&info) InstanceCreateInfo{NoInit};
CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2);
CORRADE_VERIFY((std::is_nothrow_constructible<InstanceCreateInfo, NoInitT>::value));
/* Implicit construction is not allowed */
CORRADE_VERIFY(!(std::is_convertible<NoInitT, InstanceCreateInfo>::value));
}
void InstanceTest::createInfoConstructFromVk() {
VkInstanceCreateInfo vkInfo;
vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
InstanceCreateInfo info{vkInfo};
CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2);
/* Implicit construction is not allowed */
CORRADE_VERIFY(!(std::is_convertible<VkInstanceCreateInfo, InstanceCreateInfo>::value));
}
void InstanceTest::constructNoCreate() {
{
Instance instance{NoCreate};
CORRADE_VERIFY(!instance.handle());
/* Instance function pointers should be null */
CORRADE_VERIFY(!instance->CreateDevice);
}
/* Implicit construction is not allowed */
CORRADE_VERIFY(!(std::is_convertible<NoCreateT, Instance>::value));
}
void InstanceTest::constructCopy() {
CORRADE_VERIFY(!(std::is_constructible<Instance, const Instance&>{}));
CORRADE_VERIFY(!(std::is_assignable<Instance, const Instance&>{}));
}
}}}}
CORRADE_TEST_MAIN(Magnum::Vk::Test::InstanceTest)

508
src/Magnum/Vk/Test/InstanceVkTest.cpp

@ -0,0 +1,508 @@
/*
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/ExtensionProperties.h"
#include "Magnum/Vk/Handle.h"
#include "Magnum/Vk/LayerProperties.h"
#include "Magnum/Vk/Instance.h"
#include "Magnum/Vk/Result.h"
#include "Magnum/Vk/Version.h"
#include "MagnumExternal/Vulkan/flextVkGlobal.h"
namespace Magnum { namespace Vk { namespace Test { namespace {
struct InstanceVkTest: TestSuite::Tester {
explicit InstanceVkTest();
void createInfoConstructDefault();
void createInfoApplicationInfo();
void createInfoLayers();
void createInfoExtensions();
void createInfoCopiedStrings();
void construct();
void constructLayerExtension();
void constructCommandLineDisable();
void constructCommandLineEnable();
void constructMove();
void constructUnknownLayer();
void constructUnknownExtension();
void wrap();
void populateGlobalFunctionPointers();
};
struct {
const char* nameDisable;
const char* nameEnable;
Containers::Array<const char*> argsDisable, argsEnable;
bool driverVersionSupported, debugReportEnabled, validationFeaturesEnabled;
const char* log;
} ConstructCommandLineData[] {
/* Shouldn't print anything about version, enabled layers/exts if quiet
output is enabled. */
{"quiet", "quiet, enabled layer + both extensions",
Containers::array({"", "--magnum-log", "quiet"}),
Containers::array({"", "--magnum-log", "quiet",
"--magnum-enable-layers", "VK_LAYER_KHRONOS_validation",
"--magnum-enable-instance-extensions", "VK_EXT_debug_report VK_EXT_validation_features"}),
true, true, true,
""},
{"", "enabled layer + both extensions", nullptr,
Containers::array({"",
"--magnum-enable-layers", "VK_LAYER_KHRONOS_validation",
"--magnum-enable-instance-extensions", "VK_EXT_debug_report VK_EXT_validation_features"}),
true, true, true,
"Instance version: Vulkan {}.{}{}\n"
"Enabled layers:\n"
" VK_LAYER_KHRONOS_validation\n"
"Enabled instance extensions:\n"
" VK_EXT_debug_report\n"
" VK_EXT_validation_features\n"},
{"forced invalid version", "forced invalid version, enabled layer + both extensions",
Containers::array({"", "--magnum-vulkan-version", "eh"}),
Containers::array({"", "--magnum-vulkan-version", "eh",
"--magnum-enable-layers", "VK_LAYER_KHRONOS_validation",
"--magnum-enable-instance-extensions", "VK_EXT_debug_report VK_EXT_validation_features"}),
true, true, true,
"Invalid --magnum-vulkan-version eh, ignoring\n"
"Instance version: Vulkan {}.{}{}\n"
"Enabled layers:\n"
" VK_LAYER_KHRONOS_validation\n"
"Enabled instance extensions:\n"
" VK_EXT_debug_report\n"
" VK_EXT_validation_features\n"},
{"forced version", "forced version, enabled layer + both extensions",
Containers::array({"", "--magnum-vulkan-version", "1.0"}),
Containers::array({"", "--magnum-vulkan-version", "1.0",
"--magnum-enable-layers", "VK_LAYER_KHRONOS_validation",
"--magnum-enable-instance-extensions", "VK_EXT_debug_report VK_EXT_validation_features"}),
false, true, true,
"Instance version: Vulkan 1.0\n"
"Enabled layers:\n"
" VK_LAYER_KHRONOS_validation\n"
"Enabled instance extensions:\n"
" VK_EXT_debug_report\n"
" VK_EXT_validation_features\n"},
{"disabled layer + layer-only extension", "enabled extension",
Containers::array({"",
"--magnum-disable-layers", "VK_LAYER_KHRONOS_validation",
"--magnum-disable-extensions", "VK_EXT_validation_features"}),
Containers::array({"",
"--magnum-enable-instance-extensions", "VK_EXT_debug_report"}),
true, true, false,
"Instance version: Vulkan {}.{}{}\n"
"Enabled instance extensions:\n"
" VK_EXT_debug_report\n"},
{"disabled extension", "enabled layer + one extension",
Containers::array({"",
"--magnum-disable-extensions", "VK_EXT_debug_report"}),
Containers::array({"",
"--magnum-enable-layers", "VK_LAYER_KHRONOS_validation",
"--magnum-enable-instance-extensions", "VK_EXT_validation_features"}),
true, false, true,
"Instance version: Vulkan {}.{}{}\n"
"Enabled layers:\n"
" VK_LAYER_KHRONOS_validation\n"
"Enabled instance extensions:\n"
" VK_EXT_validation_features\n"},
{"disabled extensions + layer", "",
Containers::array({"",
"--magnum-disable-layers", "VK_LAYER_KHRONOS_validation",
"--magnum-disable-extensions", "VK_EXT_debug_report VK_EXT_validation_features"}),
nullptr,
true, false, false,
"Instance version: Vulkan {}.{}{}\n"},
};
InstanceVkTest::InstanceVkTest() {
addTests({&InstanceVkTest::createInfoConstructDefault,
&InstanceVkTest::createInfoApplicationInfo,
&InstanceVkTest::createInfoLayers,
&InstanceVkTest::createInfoExtensions,
&InstanceVkTest::createInfoCopiedStrings,
&InstanceVkTest::construct,
&InstanceVkTest::constructLayerExtension});
addInstancedTests({&InstanceVkTest::constructCommandLineDisable,
&InstanceVkTest::constructCommandLineEnable},
Containers::arraySize(ConstructCommandLineData));
addTests({&InstanceVkTest::constructMove,
&InstanceVkTest::constructUnknownLayer,
&InstanceVkTest::constructUnknownExtension,
&InstanceVkTest::wrap,
&InstanceVkTest::populateGlobalFunctionPointers});
}
using namespace Containers::Literals;
void InstanceVkTest::createInfoConstructDefault() {
InstanceCreateInfo info;
CORRADE_VERIFY(info->sType);
CORRADE_VERIFY(!info->pNext);
CORRADE_VERIFY(!info->ppEnabledLayerNames);
CORRADE_COMPARE(info->enabledLayerCount, 0);
CORRADE_VERIFY(!info->ppEnabledExtensionNames);
CORRADE_COMPARE(info->enabledExtensionCount, 0);
CORRADE_VERIFY(info->pApplicationInfo);
CORRADE_COMPARE(Version(info->pApplicationInfo->apiVersion), enumerateInstanceVersion());
CORRADE_COMPARE(info->pApplicationInfo->applicationVersion, 0);
CORRADE_COMPARE(info->pApplicationInfo->engineVersion, 0);
CORRADE_COMPARE(info->pApplicationInfo->pEngineName, "Magnum"_s);
}
void InstanceVkTest::createInfoApplicationInfo() {
Containers::StringView name = "Magnum::Vk::Test::InstanceVkTest"_s;
InstanceCreateInfo info;
CORRADE_VERIFY(info->pApplicationInfo);
CORRADE_VERIFY(!info->pApplicationInfo->pApplicationName);
CORRADE_COMPARE(Version(info->pApplicationInfo->applicationVersion), Version{});
/* Setting an empty name should do nothing */
info.setApplicationInfo({}, {});
CORRADE_VERIFY(!info->pApplicationInfo->pApplicationName);
CORRADE_COMPARE(Version(info->pApplicationInfo->applicationVersion), Version{});
info.setApplicationInfo(name, version(0, 0, 1));
/* The pointer should be to the global data */
CORRADE_COMPARE(static_cast<const void*>(info->pApplicationInfo->pApplicationName), name.data());
CORRADE_COMPARE(Version(info->pApplicationInfo->applicationVersion), version(0, 0, 1));
/* Setting an empty view should put nullptr back */
info.setApplicationInfo({}, {});
CORRADE_VERIFY(!info->pApplicationInfo->pApplicationName);
CORRADE_COMPARE(Version(info->pApplicationInfo->applicationVersion), Version{});
}
void InstanceVkTest::createInfoLayers() {
Containers::StringView layer = "VK_LAYER_KHRONOS_validation"_s;
Containers::StringView another = "VK_LAYER_this_doesnt_exist"_s;
InstanceCreateInfo info;
CORRADE_VERIFY(!info->ppEnabledLayerNames);
CORRADE_COMPARE(info->enabledLayerCount, 0);
info.addEnabledLayers({layer});
CORRADE_VERIFY(info->ppEnabledLayerNames);
CORRADE_COMPARE(info->enabledLayerCount, 1);
/* The pointer should be to the global data */
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledLayerNames[0]), layer.data());
info.addEnabledLayers({another, layer});
CORRADE_COMPARE(info->enabledLayerCount, 3);
/* The pointer should be to the global data */
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledLayerNames[0]), layer.data());
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledLayerNames[1]), another.data());
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledLayerNames[2]), layer.data());
}
void InstanceVkTest::createInfoExtensions() {
InstanceCreateInfo info;
CORRADE_VERIFY(!info->ppEnabledExtensionNames);
CORRADE_COMPARE(info->enabledExtensionCount, 0);
info.addEnabledExtensions<Extensions::KHR::external_fence_capabilities>();
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::external_fence_capabilities::string().data());
info.addEnabledExtensions(
{Extensions::KHR::external_semaphore_capabilities{},
Extensions::KHR::get_physical_device_properties2{}});
CORRADE_COMPARE(info->enabledExtensionCount, 3);
/* The pointer should be to the global data */
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledExtensionNames[0]),
Extensions::KHR::external_fence_capabilities::string().data());
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledExtensionNames[1]),
Extensions::KHR::external_semaphore_capabilities::string().data());
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledExtensionNames[2]),
Extensions::KHR::get_physical_device_properties2::string().data());
}
void InstanceVkTest::createInfoCopiedStrings() {
Containers::StringView globalButNotNullTerminated = "VK_LAYER_KHRONOS_validation3"_s.except(1);
Containers::String localButNullTerminated = Extensions::KHR::external_memory_capabilities::string();
InstanceCreateInfo info;
info.setApplicationInfo(localButNullTerminated, {})
.addEnabledLayers({globalButNotNullTerminated})
.addEnabledExtensions({localButNullTerminated});
CORRADE_COMPARE(info->enabledLayerCount, 1);
CORRADE_COMPARE(info->enabledExtensionCount, 1);
CORRADE_COMPARE(info->pApplicationInfo->pApplicationName, localButNullTerminated);
CORRADE_VERIFY(info->pApplicationInfo->pApplicationName != localButNullTerminated.data());
CORRADE_COMPARE(info->ppEnabledLayerNames[0], globalButNotNullTerminated);
CORRADE_VERIFY(info->ppEnabledLayerNames[0] != globalButNotNullTerminated.data());
CORRADE_COMPARE(info->ppEnabledExtensionNames[0], localButNullTerminated);
CORRADE_VERIFY(info->ppEnabledExtensionNames[0] != localButNullTerminated.data());
}
void InstanceVkTest::construct() {
{
Instance instance;
CORRADE_VERIFY(instance.handle());
/* Instance function pointers should be populated */
CORRADE_VERIFY(instance->CreateDevice);
CORRADE_COMPARE(instance.handleFlags(), HandleFlag::DestroyOnDestruction);
CORRADE_COMPARE(instance.version(), enumerateInstanceVersion());
/* Instance version is supported */
CORRADE_VERIFY(instance.isVersionSupported(enumerateInstanceVersion()));
CORRADE_VERIFY(!instance.isVersionSupported(Version::None));
/* No extensions are enabled by default ... */
CORRADE_VERIFY(!instance.isExtensionEnabled<Extensions::EXT::debug_report>());
/* ... and thus also no function pointers loaded */
CORRADE_VERIFY(!instance->CreateDebugReportCallbackEXT);
}
/* Shouldn't crash or anything */
CORRADE_VERIFY(true);
}
void InstanceVkTest::constructLayerExtension() {
if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation"))
CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test");
if(!enumerateInstanceExtensionProperties({"VK_LAYER_KHRONOS_validation"}).isSupported<Extensions::EXT::debug_report>())
CORRADE_SKIP("VK_EXT_debug_report not supported, can't test");
Instance instance{InstanceCreateInfo{}
.setApplicationInfo("InstanceVkTest", version(0, 0, 1))
.addEnabledLayers({"VK_LAYER_KHRONOS_validation"_s})
.addEnabledExtensions({
Extensions::EXT::debug_report::string(),
"VK_EXT_validation_features"_s
})};
CORRADE_VERIFY(instance.handle());
/* Extensions should be reported as enabled ... */
CORRADE_VERIFY(instance.isExtensionEnabled<Extensions::EXT::debug_report>());
CORRADE_VERIFY(instance.isExtensionEnabled(Extensions::EXT::validation_features{}));
/* ... and function pointers loaded */
CORRADE_VERIFY(instance->CreateDebugReportCallbackEXT);
/* no entrypoints to test for EXT_validation_features */
}
void InstanceVkTest::constructCommandLineDisable() {
auto&& data = ConstructCommandLineData[testCaseInstanceId()];
setTestCaseDescription(data.nameDisable);
if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation"))
CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test");
if(!enumerateInstanceExtensionProperties({"VK_LAYER_KHRONOS_validation"}).isSupported<Extensions::EXT::validation_features>())
CORRADE_SKIP("VK_EXT_validation_features not supported, can't test");
std::ostringstream out;
Warning redirectWarning{&out};
Debug redirectOutput{&out};
Instance instance{InstanceCreateInfo{Int(data.argsDisable.size()), data.argsDisable}
.setApplicationInfo("InstanceVkTest", version(0, 0, 1))
.addEnabledLayers({"VK_LAYER_KHRONOS_validation"_s})
.addEnabledExtensions<Extensions::EXT::debug_report,
Extensions::EXT::validation_features>()};
CORRADE_VERIFY(instance.handle());
CORRADE_COMPARE(instance.isVersionSupported(enumerateInstanceVersion()), data.driverVersionSupported);
CORRADE_COMPARE(instance.isExtensionEnabled<Extensions::EXT::debug_report>(), data.debugReportEnabled);
CORRADE_COMPARE(instance.isExtensionEnabled<Extensions::EXT::validation_features>(), data.validationFeaturesEnabled);
/** @todo cleanup when Debug::toString() or some similar utility exists */
UnsignedInt major = versionMajor(enumerateInstanceVersion());
UnsignedInt minor = versionMinor(enumerateInstanceVersion());
UnsignedInt patch = versionPatch(enumerateInstanceVersion());
/* Vulkan 1.0 instances report no patch version, special-case that */
CORRADE_COMPARE(out.str(), Utility::formatString(data.log, major, minor, patch ? Utility::formatString(".{}", patch) : ""));
/* Verify that the entrypoint is actually (not) loaded as expected, to
avoid all the above reporting being just smoke & mirrors */
CORRADE_COMPARE(!!instance->CreateDebugReportCallbackEXT, data.debugReportEnabled);
}
void InstanceVkTest::constructCommandLineEnable() {
auto&& data = ConstructCommandLineData[testCaseInstanceId()];
setTestCaseDescription(data.nameEnable);
if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation"))
CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test");
if(!enumerateInstanceExtensionProperties({"VK_LAYER_KHRONOS_validation"}).isSupported<Extensions::EXT::validation_features>())
CORRADE_SKIP("VK_EXT_validation_features not supported, can't test");
std::ostringstream out;
Warning redirectWarning{&out};
Debug redirectOutput{&out};
Instance instance{InstanceCreateInfo{Int(data.argsEnable.size()), data.argsEnable}
/* Nothing enabled by the application */
};
CORRADE_VERIFY(instance.handle());
CORRADE_COMPARE(instance.isVersionSupported(enumerateInstanceVersion()), data.driverVersionSupported);
CORRADE_COMPARE(instance.isExtensionEnabled<Extensions::EXT::debug_report>(), data.debugReportEnabled);
CORRADE_COMPARE(instance.isExtensionEnabled<Extensions::EXT::validation_features>(), data.validationFeaturesEnabled);
/** @todo cleanup when Debug::toString() or some similar utility exists */
UnsignedInt major = versionMajor(enumerateInstanceVersion());
UnsignedInt minor = versionMinor(enumerateInstanceVersion());
UnsignedInt patch = versionPatch(enumerateInstanceVersion());
/* Vulkan 1.0 instances report no patch version, special-case that */
CORRADE_COMPARE(out.str(), Utility::formatString(data.log, major, minor, patch ? Utility::formatString(".{}", patch) : ""));
/* Verify that the entrypoint is actually (not) loaded as expected, to
avoid all the above reporting being just smoke & mirrors */
CORRADE_COMPARE(!!instance->CreateDebugReportCallbackEXT, data.debugReportEnabled);
}
void InstanceVkTest::constructMove() {
InstanceExtensionProperties extensions = enumerateInstanceExtensionProperties();
if(!extensions.isSupported<Extensions::KHR::get_physical_device_properties2>())
CORRADE_SKIP("VK_KHR_get_physical_device_properties2 not supported, can't test");
Instance a{InstanceCreateInfo{}
.setApplicationInfo("InstanceVkTest", version(0, 0, 1))
.addEnabledExtensions<Extensions::KHR::get_physical_device_properties2>()};
VkInstance handle = a.handle();
Version version = a.version();
CORRADE_VERIFY(handle);
CORRADE_VERIFY(version != Version{});
Instance b = std::move(a);
CORRADE_VERIFY(!a.handle());
CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction);
CORRADE_COMPARE(b.handle(), handle);
CORRADE_COMPARE(b.version(), version);
CORRADE_VERIFY(b.isExtensionEnabled<Extensions::KHR::get_physical_device_properties2>());
/* Function pointers in a are left in whatever state they were before, as
that doesn't matter */
CORRADE_VERIFY(b->CreateDevice);
Instance c{NoCreate};
c = std::move(b);
CORRADE_VERIFY(!b.handle());
CORRADE_COMPARE(b.handleFlags(), HandleFlags{});
CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction);
CORRADE_COMPARE(c.handle(), handle);
CORRADE_COMPARE(c.version(), version);
CORRADE_VERIFY(c.isExtensionEnabled<Extensions::KHR::get_physical_device_properties2>());
/* Everything is swapped, including function pointers */
CORRADE_VERIFY(!b->CreateDevice);
CORRADE_VERIFY(c->CreateDevice);
CORRADE_VERIFY(std::is_nothrow_move_constructible<Instance>::value);
CORRADE_VERIFY(std::is_nothrow_move_assignable<Instance>::value);
}
void InstanceVkTest::constructUnknownLayer() {
CORRADE_SKIP("Currently this hits an internal assert, which can't be tested.");
std::ostringstream out;
Error redirectError{&out};
Instance instance{InstanceCreateInfo{}
.addEnabledLayers({"VK_LAYER_this_doesnt_exist"_s})};
CORRADE_COMPARE(out.str(), "TODO");
}
void InstanceVkTest::constructUnknownExtension() {
CORRADE_SKIP("Currently this hits an internal assert, which can't be tested.");
std::ostringstream out;
Error redirectError{&out};
Instance instance{InstanceCreateInfo{}
.addEnabledExtensions({"VK_this_doesnt_exist"_s})};
CORRADE_COMPARE(out.str(), "TODO");
}
void InstanceVkTest::wrap() {
InstanceExtensionProperties properties = enumerateInstanceExtensionProperties();
if(!properties.isSupported<Extensions::EXT::debug_report>())
CORRADE_SKIP("VK_EXT_debug_report 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");
InstanceCreateInfo info;
info.addEnabledExtensions<
Extensions::EXT::debug_report,
Extensions::KHR::get_physical_device_properties2>();
VkInstance instance;
CORRADE_COMPARE(Result(vkCreateInstance(info, nullptr, &instance)), Result::Success);
CORRADE_VERIFY(instance);
{
/* Wrapping should load the basic function pointers */
auto wrapped = Instance::wrap(instance, Version::Vk11, {
Extensions::EXT::debug_report::string()
}, HandleFlag::DestroyOnDestruction);
CORRADE_VERIFY(wrapped->DestroyInstance);
/* 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_report>());
CORRADE_VERIFY(wrapped->CreateDebugReportCallbackEXT);
/* Unlisted not, but function pointers should still be loaded as the
actual instance does have the extension enabled */
CORRADE_VERIFY(!wrapped.isExtensionEnabled<Extensions::KHR::get_physical_device_properties2>());
CORRADE_VERIFY(wrapped->GetPhysicalDeviceProperties2KHR);
/* Releasing won't destroy anything ... */
CORRADE_COMPARE(wrapped.release(), instance);
}
/* ...so we can wrap it again, non-owned, and then destroy it manually */
auto wrapped = Instance::wrap(instance, Version::Vk10, {});
CORRADE_VERIFY(wrapped->DestroyInstance);
wrapped->DestroyInstance(instance, nullptr);
}
void InstanceVkTest::populateGlobalFunctionPointers() {
vkDestroyInstance = nullptr;
Instance instance;
CORRADE_VERIFY(!vkDestroyInstance);
instance.populateGlobalFunctionPointers();
CORRADE_VERIFY(vkDestroyInstance);
CORRADE_VERIFY(vkDestroyInstance == instance->DestroyInstance);
}
}}}}
CORRADE_TEST_MAIN(Magnum::Vk::Test::InstanceVkTest)

2
src/Magnum/Vk/Vk.h

@ -38,6 +38,8 @@ class Extension;
class ExtensionProperties; class ExtensionProperties;
enum class HandleFlag: UnsignedByte; enum class HandleFlag: UnsignedByte;
typedef Containers::EnumSet<HandleFlag> HandleFlags; typedef Containers::EnumSet<HandleFlag> HandleFlags;
class Instance;
class InstanceCreateInfo;
class InstanceExtension; class InstanceExtension;
class InstanceExtensionProperties; class InstanceExtensionProperties;
class LayerProperties; class LayerProperties;

Loading…
Cancel
Save