Browse Source

Vk: device creation.

pull/234/head
Vladimír Vondruš 6 years ago
parent
commit
374f816762
  1. 33
      doc/snippets/MagnumVk.cpp
  2. 13
      doc/vulkan-mapping.dox
  3. 3
      src/Magnum/Vk/CMakeLists.txt
  4. 335
      src/Magnum/Vk/Device.cpp
  5. 351
      src/Magnum/Vk/Device.h
  6. 5
      src/Magnum/Vk/DeviceProperties.h
  7. 4
      src/Magnum/Vk/Implementation/Arguments.cpp
  8. 32
      src/Magnum/Vk/Implementation/DeviceState.cpp
  9. 40
      src/Magnum/Vk/Implementation/DeviceState.h
  10. 2
      src/Magnum/Vk/Instance.h
  11. 2
      src/Magnum/Vk/Test/CMakeLists.txt
  12. 90
      src/Magnum/Vk/Test/DeviceTest.cpp
  13. 546
      src/Magnum/Vk/Test/DeviceVkTest.cpp
  14. 2
      src/Magnum/Vk/Vk.h

33
doc/snippets/MagnumVk.cpp

@ -27,6 +27,7 @@
#include "Magnum/Magnum.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Vk/Device.h"
#include "Magnum/Vk/Extensions.h"
#include "Magnum/Vk/Instance.h"
#include "Magnum/Vk/Integration.h"
@ -34,7 +35,39 @@
using namespace Magnum;
#define DOXYGEN_IGNORE(...) __VA_ARGS__
int main() {
{
Vk::Device device{NoCreate};
/* [Device-isExtensionEnabled] */
if(device.isExtensionEnabled<Vk::Extensions::EXT::index_type_uint8>()) {
// keep mesh indices 8bit
} else {
// convert them to 16bit
}
/* [Device-isExtensionEnabled] */
}
{
Vk::Instance instance;
/* Header included again inside a function, but it's fine as the guards will
make it empty */
/* [Device-global-function-pointers] */
#include <MagnumExternal/Vulkan/flextVkGlobal.h>
// …
Vk::Device device{DOXYGEN_IGNORE(instance, Vk::DeviceCreateInfo{instance})};
device.populateGlobalFunctionPointers();
VkCommandPool commandPool;
VkCommandPoolCreateInfo info{};
// …
vkCreateCommandPool(device, &info, nullptr, &commandPool);
/* [Device-global-function-pointers] */
}
{
Vk::Instance instance;
/* [Instance-isExtensionEnabled] */

13
doc/vulkan-mapping.dox

@ -130,7 +130,7 @@ Vulkan function | Matching API
@fn_vk{CreateDescriptorPool}, \n @fn_vk{DestroyDescriptorPool} | |
@fn_vk{CreateDescriptorSetLayout}, \n @fn_vk{DestroyDescriptorSetLayout} | |
@fn_vk{CreateDescriptorUpdateTemplate} @m_class{m-label m-flat m-success} **KHR, 1.1**, \n @fn_vk{DestroyDescriptorUpdateTemplate} @m_class{m-label m-flat m-success} **KHR, 1.1** | |
@fn_vk{CreateDevice}, \n @fn_vk{DestroyDevice} | |
@fn_vk{CreateDevice}, \n @fn_vk{DestroyDevice} | @ref Device constructor and destructor
@fn_vk{CreateEvent}, \n @fn_vk{DestroyEvent} | |
@fn_vk{CreateFence}, \n @fn_vk{DestroyFence} | |
@fn_vk{CreateFramebuffer}, \n @fn_vk{DestroyFramebuffer} | |
@ -193,7 +193,7 @@ Vulkan function | Matching API
@fn_vk{GetDescriptorSetLayoutSupport} @m_class{m-label m-flat m-success} **KHR, 1.1** | |
@fn_vk{GetDeviceGroupPeerMemoryFeatures} @m_class{m-label m-flat m-success} **KHR, 1.1** | |
@fn_vk{GetDeviceMemoryCommitment} | |
@fn_vk{GetDeviceProcAddr} | |
@fn_vk{GetDeviceProcAddr} | @ref Device constructor
@fn_vk{GetDeviceQueue}, \n @fn_vk{GetDeviceQueue2} @m_class{m-label m-flat m-success} **1.1** | |
@fn_vk{GetEventStatus} | |
@fn_vk{GetFenceStatus} | |
@ -304,6 +304,15 @@ Vulkan structure | Matching API
--------------------------------------- | ------------
@type_vk{ApplicationInfo} | @ref InstanceCreateInfo
@subsection vulkan-mapping-structures-d D
@m_class{m-fullwidth}
Vulkan structure | Matching API
--------------------------------------- | ------------
@type_vk{DeviceCreateInfo} | @ref DeviceCreateInfo
@type_vk{DeviceQueueCreateInfo} | not exposed, but you can pass a custom instance to @ref DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo&)
@subsection vulkan-mapping-structures-i I
@m_class{m-fullwidth}

3
src/Magnum/Vk/CMakeLists.txt

@ -34,9 +34,11 @@ set(MagnumVk_SRCS
Version.cpp
Implementation/Arguments.cpp
Implementation/DeviceState.cpp
Implementation/InstanceState.cpp)
set(MagnumVk_GracefulAssert_SRCS
Device.cpp
DeviceProperties.cpp
Enums.cpp
ExtensionProperties.cpp
@ -62,6 +64,7 @@ set(MagnumVk_HEADERS
set(MagnumVk_PRIVATE_HEADERS
Implementation/Arguments.h
Implementation/DeviceState.h
Implementation/InstanceState.h
Implementation/compressedPixelFormatMapping.hpp

335
src/Magnum/Vk/Device.cpp

@ -0,0 +1,335 @@
/*
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 "Device.h"
#include <algorithm>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/StaticArray.h>
#include <Corrade/Containers/String.h>
#include <Corrade/Containers/StringView.h>
#include <Corrade/Utility/Algorithms.h>
#include <Corrade/Utility/Arguments.h>
#include "Magnum/Math/Functions.h"
#include "Magnum/Vk/Handle.h"
#include "Magnum/Vk/Instance.h"
#include "Magnum/Vk/DeviceProperties.h"
#include "Magnum/Vk/Extensions.h"
#include "Magnum/Vk/ExtensionProperties.h"
#include "Magnum/Vk/Result.h"
#include "Magnum/Vk/Version.h"
#include "Magnum/Vk/Implementation/Arguments.h"
#include "Magnum/Vk/Implementation/InstanceState.h"
#include "Magnum/Vk/Implementation/DeviceState.h"
#include "MagnumExternal/Vulkan/flextVkGlobal.h"
namespace Magnum { namespace Vk {
struct DeviceCreateInfo::State {
Containers::Array<Containers::String> ownedStrings;
Containers::Array<const char*> extensions;
Containers::String disabledExtensionsStorage;
Containers::Array<Containers::StringView> disabledExtensions;
Containers::Array<VkDeviceQueueCreateInfo> queues;
Containers::StaticArray<32, Float> queuePriorities;
std::size_t nextQueuePriority = 0;
bool quietLog = false;
Version version = Version::None;
};
DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const ExtensionProperties* extensionProperties, const Flags flags): _physicalDevice{deviceProperties}, _info{}, _state{Containers::InPlaceInit} {
Utility::Arguments args = Implementation::arguments();
args.parse(deviceProperties._instance->state().argc, deviceProperties._instance->state().argv);
if(args.value("log") == "quiet")
_state->quietLog = true;
_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
_info.flags = VkDeviceCreateFlags(flags & ~Flag::NoImplicitExtensions);
/* Take the minimum of instance and device version. Instance version being
smaller than a device version happens mainly if there's a forced Vulkan
version via --magnum-vulkan version, which will be later used to cap available features. */
_state->version = Version(Math::min(UnsignedInt(deviceProperties._instance->version()), UnsignedInt(deviceProperties.apiVersion())));
/* If there are any disabled extensions, sort them and save for later --
we'll use them to filter the ones added by the app */
Containers::String disabledExtensions = args.value<Containers::String>("disable-extensions");
if(!disabledExtensions.isEmpty()) {
_state->disabledExtensionsStorage = std::move(disabledExtensions);
_state->disabledExtensions = Containers::StringView{_state->disabledExtensionsStorage}.splitWithoutEmptyParts();
std::sort(_state->disabledExtensions.begin(), _state->disabledExtensions.end());
}
/* Add all 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) */
addEnabledExtensions(args.value<Containers::StringView>("enable-extensions").splitWithoutEmptyParts());
/* Enable implicit extensions unless that's forbidden */
/** @todo move this somewhere else as this will grow significantly? */
if(!(flags & Flag::NoImplicitExtensions)) {
/* Fetch searchable extension properties if not already */
Containers::Optional<ExtensionProperties> extensionPropertiesStorage;
if(!extensionProperties) {
/** @todo i'd like to know which layers are enabled so i can list
the exts from those .. but how? */
extensionPropertiesStorage = deviceProperties.enumerateExtensionProperties();
extensionProperties = &*extensionPropertiesStorage;
}
/* No extensions at the moment */
}
}
DeviceCreateInfo::DeviceCreateInfo(Instance& instance, const Flags flags): DeviceCreateInfo{pickDevice(instance), flags} {}
DeviceCreateInfo::DeviceCreateInfo(NoInitT) noexcept {}
DeviceCreateInfo::DeviceCreateInfo(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo& info): _physicalDevice{physicalDevice}, _info{info} {}
DeviceCreateInfo::~DeviceCreateInfo() = default;
DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const Containers::ArrayView<const Containers::StringView> extensions) {
if(extensions.empty()) return *this;
/* This can happen in case we used the NoInit or VkDeviceCreateInfo
constructor */
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;
}
DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const std::initializer_list<Containers::StringView> extensions) {
return addEnabledExtensions(Containers::arrayView(extensions));
}
DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const Containers::ArrayView<const Extension> extensions) {
if(extensions.empty()) return *this;
/* This can happen in case we used the NoInit or VkDeviceCreateInfo
constructor */
if(!_state) _state.emplace();
arrayReserve(_state->extensions, _state->extensions.size() + extensions.size());
for(const Extension& 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;
}
DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const std::initializer_list<Extension> extensions) {
return addEnabledExtensions(Containers::arrayView(extensions));
}
DeviceCreateInfo& DeviceCreateInfo::addQueues(UnsignedInt family, Containers::ArrayView<const Float> priorities) {
CORRADE_ASSERT(!priorities.empty(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified", *this);
/* This can happen in case we used the NoInit or VkDeviceCreateInfo
constructor */
if(!_state) _state.emplace();
VkDeviceQueueCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
info.queueFamilyIndex = family;
info.queueCount = priorities.size();
info.pQueuePriorities = _state->queuePriorities + _state->nextQueuePriority;
/* Copy the passed queue priorities to an internal storage that never
reallocates. If this blows up, see the definition of queuePriorities for
details. We can't easily reallocate if this grows too big as all
pointers would need to be patched, so there's a static limit. */
CORRADE_INTERNAL_ASSERT(_state->nextQueuePriority + priorities.size() <= _state->queuePriorities.size());
Utility::copy(priorities, _state->queuePriorities.suffix(_state->nextQueuePriority).prefix(priorities.size()));
_state->nextQueuePriority += priorities.size();
return addQueues(info);
}
DeviceCreateInfo& DeviceCreateInfo::addQueues(UnsignedInt family, std::initializer_list<Float> priorities) {
return addQueues(family, Containers::arrayView(priorities));
}
DeviceCreateInfo& DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo& info) {
/* This can happen in case we used the NoInit or VkDeviceCreateInfo
constructor */
if(!_state) _state.emplace();
/* Copy the info to an internal storage and re-route the pointer to it.
This handles a potential reallocation and also the case of replacing the
default queue on the first call to addQueues(). */
arrayAppend(_state->queues, info);
_info.pQueueCreateInfos = _state->queues;
_info.queueCreateInfoCount = _state->queues.size();
return *this;
}
Device Device::wrap(Instance& instance, const VkDevice 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 */
Device out{NoCreate};
out._handle = handle;
out._flags = flags;
out.initializeExtensions(enabledExtensions);
out.initialize(instance, version);
return out;
}
Device Device::wrap(Instance& instance, const VkDevice handle, const Version version, const std::initializer_list<Containers::StringView> enabledExtensions, const HandleFlags flags) {
return wrap(instance, handle, version, Containers::arrayView(enabledExtensions), flags);
}
Device::Device(Instance& instance, const DeviceCreateInfo& info):
#ifdef CORRADE_GRACEFUL_ASSERT
_handle{}, /* Otherwise the destructor dies if we hit the queue assert */
#endif
_flags{HandleFlag::DestroyOnDestruction}
{
CORRADE_ASSERT(info._info.queueCreateInfoCount,
"Vk::Device: needs to be created with at least one queue", );
const Version version = info._state->version != Version::None ? info._state->version : DeviceProperties::wrap(instance, info._physicalDevice).apiVersion();
/* Print all enabled extensions if we're not told to be quiet */
if(!info._state || !info._state->quietLog) {
Debug{} << "Device version:" << version;
if(info->enabledExtensionCount) {
Debug{} << "Enabled device extensions:";
for(std::size_t i = 0, max = info->enabledExtensionCount; i != max; ++i)
Debug{} << " " << info->ppEnabledExtensionNames[i];
}
}
MAGNUM_VK_INTERNAL_ASSERT_RESULT(instance->CreateDevice(info._physicalDevice, info, nullptr, &_handle));
initializeExtensions<const char*>({info->ppEnabledExtensionNames, info->enabledExtensionCount});
initialize(instance, version);
}
Device::Device(NoCreateT): _handle{}, _functionPointers{} {}
Device::Device(Device&& 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 = {};
}
Device::~Device() {
if(_handle && (_flags & HandleFlag::DestroyOnDestruction))
_functionPointers.DestroyDevice(_handle, nullptr);
}
Device& Device::operator=(Device&& 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 Device::initializeExtensions(const Containers::ArrayView<const T> enabledExtensions) {
/* Mark all known extensions as enabled */
for(const T extension: enabledExtensions) {
for(Containers::ArrayView<const Extension> knownExtensions: {
Extension::extensions(Version::None),
/*Extension::extensions(Version::Vk10), is empty */
Extension::extensions(Version::Vk11),
Extension::extensions(Version::Vk12)
}) {
auto found = std::lower_bound(knownExtensions.begin(), knownExtensions.end(), extension, [](const Extension& a, const T& b) {
return a.string() < static_cast<const Containers::StringView&>(b);
});
if(found->string() != extension) continue;
_extensionStatus.set(found->index(), true);
}
}
}
void Device::initialize(Instance& instance, const Version version) {
/* Init version, function pointers */
_version = version;
flextVkInitDevice(_handle, &_functionPointers, instance->GetDeviceProcAddr);
/* Set up extension-dependent functionality */
_state.emplace(*this);
}
bool Device::isExtensionEnabled(const Extension& extension) const {
return _extensionStatus[extension.index()];
}
VkDevice Device::release() {
const VkDevice handle = _handle;
_handle = nullptr;
return handle;
}
void Device::populateGlobalFunctionPointers() {
flextVkDevice = _functionPointers;
}
}}

351
src/Magnum/Vk/Device.h

@ -26,18 +26,367 @@
*/
/** @file
* @brief Nothing, haha
* @brief Class @ref Magnum::Vk::DeviceCreateInfo, @ref Magnum::Vk::Device
* @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 Implementation {
enum: std::size_t { ExtensionCount = 72 };
struct DeviceState;
}
/**
@brief Device creation info
@m_since_latest
Wraps a @type_vk_keyword{DeviceCreateInfo}.
@see @ref Device
*/
class MAGNUM_VK_EXPORT DeviceCreateInfo {
public:
/**
* @brief Device creation flag
*
* Wraps @type_vk_keyword{DeviceCreateFlagBits}.
* @see @ref Flags, @ref DeviceCreateInfo(DeviceProperties&, const ExtensionProperties*, Flags)
*/
enum class Flag: UnsignedInt {
/* Any magnum-specific flags added here have to be filtered out
when passing them to _info.flags in the constructor. Using the
highest bits in a hope to prevent conflicts with Vulkan instance
flags added in the future. */
/**
* Don't implicitly enable any extensions.
*
* By default, the engine enables various extensions such as
* @vk_extension{KHR,get_memory_requirements2} to provide a broader
* functionality. If you want to have a complete control over what
* gets enabled, set this flag.
*/
NoImplicitExtensions = 1u << 31
};
/**
* @brief Device creation flags
*
* Type-safe wrapper for @type_vk_keyword{DeviceCreateFlags}.
* @see @ref DeviceCreateInfo(DeviceProperties&, const ExtensionProperties*, Flags)
*/
typedef Containers::EnumSet<Flag> Flags;
/**
* @brief Constructor
* @param deviceProperties A device to use
* @param extensionProperties Existing @ref ExtensionProperties
* instance for querying available Vulkan extensions. If
* @cpp nullptr @ce, a new instance may be created internally if
* needed.
* @param flags Device creation flags
*
* The following @type_vk{DeviceCreateInfo} fields are pre-filled in
* addition to `sType`:
*
* - `flags`
*
* You need to call at least @ref addQueues() for a valid setup.
*/
explicit DeviceCreateInfo(DeviceProperties& deviceProperties, const ExtensionProperties* extensionProperties, Flags flags = {});
/** @overload */
explicit DeviceCreateInfo(DeviceProperties&& deviceProperties, const ExtensionProperties* extensionProperties, Flags flags = {}): DeviceCreateInfo{deviceProperties, extensionProperties, flags} {}
/** @overload */
explicit DeviceCreateInfo(DeviceProperties& deviceProperties, Flags flags = {}): DeviceCreateInfo{deviceProperties, nullptr, flags} {}
/** @overload */
explicit DeviceCreateInfo(DeviceProperties&& deviceProperties, Flags flags = {}): DeviceCreateInfo{std::move(deviceProperties), nullptr, flags} {}
/**
* @brief Construct for an implicitly picked device
*
* Calls @ref DeviceCreateInfo(DeviceProperties&, const ExtensionProperties*, Flags)
* with a device picked from @p instance using @ref pickDevice().
*/
explicit DeviceCreateInfo(Instance& instance, Flags 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 DeviceCreateInfo(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 DeviceCreateInfo(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo& info);
~DeviceCreateInfo();
/**
* @brief Add enabled device 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 ExtensionProperties::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.
*/
DeviceCreateInfo& addEnabledExtensions(Containers::ArrayView<const Containers::StringView> extensions);
/** @overload */
DeviceCreateInfo& addEnabledExtensions(std::initializer_list<Containers::StringView> extension);
/** @overload */
DeviceCreateInfo& addEnabledExtensions(Containers::ArrayView<const Extension> extensions);
/** @overload */
DeviceCreateInfo& addEnabledExtensions(std::initializer_list<Extension> extension);
/** @overload */
template<class ...E> DeviceCreateInfo& addEnabledExtensions() {
static_assert(Implementation::IsExtension<E...>::value, "expected only Vulkan device extensions");
return addEnabledExtensions({E{}...});
}
/**
* @brief Add queues
* @param family Family index, smaller than
* @ref DeviceProperties::queueFamilyCount()
* @param priorities Queue priorities. Size of the array implies how
* many queues to add and has to be at least one.
* @return Reference to self (for method chaining)
*
* At least one queue has to be added.
* @see @ref DeviceProperties::pickQueueFamily()
*/
DeviceCreateInfo& addQueues(UnsignedInt family, Containers::ArrayView<const Float> priorities);
/** @overload */
DeviceCreateInfo& addQueues(UnsignedInt family, std::initializer_list<Float> priorities);
/**
* @brief Add queues using raw info
* @return Reference to self (for method chaining)
*
* Compared to @ref addQueues() this allows you to specify additional
* queue properties using the `pNext` chain. The info is uses as-is,
* with all pointers expected to stay in scope until device creation.
*/
DeviceCreateInfo& addQueues(const VkDeviceQueueCreateInfo& info);
/** @brief Underlying @type_vk{DeviceCreateInfo} structure */
VkDeviceCreateInfo& operator*() { return _info; }
/** @overload */
const VkDeviceCreateInfo& operator*() const { return _info; }
/** @overload */
VkDeviceCreateInfo* operator->() { return &_info; }
/** @overload */
const VkDeviceCreateInfo* operator->() const { return &_info; }
/** @overload */
operator const VkDeviceCreateInfo*() const { return &_info; }
private:
friend Device;
VkPhysicalDevice _physicalDevice;
VkDeviceCreateInfo _info;
struct State;
Containers::Pointer<State> _state;
};
CORRADE_ENUMSET_OPERATORS(DeviceCreateInfo::Flags)
/**
@brief Device
@m_since_latest
Wraps a @type_vk_keyword{Device} and stores all device-specific function
pointers.
*/
class MAGNUM_VK_EXPORT Device {
public:
/**
* @brief Wrap existing Vulkan handle
* @param instance Vulkan instance the device is created on
* @param handle The @type_vk{Device} 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 originating from @p 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 is empty, the device will behave as
* if no extensions were enabled.
*
* Unlike a device created using a constructor, the Vulkan device is by
* default not deleted on destruction, use @p flags for different
* behavior.
* @see @ref release()
*/
static Device wrap(Instance& instance, VkDevice handle, Version version, Containers::ArrayView<const Containers::StringView> enabledExtensions, HandleFlags flags = {});
/** @overload */
static Device wrap(Instance& instance, VkDevice handle, Version version, std::initializer_list<Containers::StringView> enabledExtensions, HandleFlags flags = {});
/**
* @brief Constructor
* @param instance Vulkan instance to create the device on
* @param info Device creation info
*
* @see @fn_vk_keyword{CreateDevice}
*/
explicit Device(Instance& instance, const DeviceCreateInfo& info);
/**
* @brief Construct without creating the device
*
* 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 Device(NoCreateT);
/** @brief Copying is not allowed */
Device(const Device&) = delete;
/** @brief Move constructor */
Device(Device&& other) noexcept;
/**
* @brief Destructor
*
* Destroys associated @type_vk{Device} handle, unless the instance
* was created using @ref wrap() without @ref HandleFlag::DestroyOnDestruction
* specified.
* @see @fn_vk_keyword{DestroyDevice}, @ref release()
*/
~Device();
/** @brief Copying is not allowed */
Device& operator=(const Device&) = delete;
/** @brief Move assignment */
Device& operator=(Device&& other) noexcept;
/** @brief Underlying @type_vk{Device} handle */
VkDevice handle() { return _handle; }
/** @overload */
operator VkDevice() { return _handle; }
/** @brief Handle flags */
HandleFlags handleFlags() const { return _flags; }
/** @brief Device version */
Version version() const { return _version; }
/** @brief Whether given version is supported on the device */
bool isVersionSupported(Version version) const {
return _version >= version;
}
/**
* @brief Whether given extension is enabled
*
* Accepts device 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 Device-isExtensionEnabled
*
* Note that this returns @cpp true @ce only if given extension is
* supported by the driver *and* it was enabled in
* @ref DeviceCreateInfo when creating the @ref Device. For querying
* extension support before creating a device use
* @ref ExtensionProperties::isSupported().
*/
template<class E> bool isExtensionEnabled() const {
static_assert(Implementation::IsExtension<E>::value, "expected a Vulkan device extension");
return _extensionStatus[E::Index];
}
/** @overload */
bool isExtensionEnabled(const Extension& extension) const;
/**
* @brief Device-specific Vulkan function pointers
*
* Function pointers are implicitly stored per-device, use
* @ref populateGlobalFunctionPointers() to populate the global `vk*`
* functions.
*/
const FlextVkDevice& operator*() const { return _functionPointers; }
/** @overload */
const FlextVkDevice* operator->() const { return &_functionPointers; }
/**
* @brief Release the underlying Vulkan device
*
* Releases ownership of the Vulkan device and returns its handle so
* @fn_vk{DestroyDevice} is not called on destruction. The internal
* state is then equivalent to moved-from state.
* @see @ref wrap()
*/
VkDevice release();
/**
* @brief Populate global device-level function pointers to be used with third-party code
*
* Populates device-level global function pointers so third-party
* code is able to call global device-level `vk*` functions:
*
* @snippet MagnumVk.cpp Device-global-function-pointers
*
* Use @ref Instance::populateGlobalFunctionPointers() to populate
* instance-level 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 device as the one returned by
* @ref handle().
*/
void populateGlobalFunctionPointers();
#ifdef DOXYGEN_GENERATING_OUTPUT
private:
#endif
Implementation::DeviceState& state() { return *_state; }
private:
template<class T> MAGNUM_VK_LOCAL void initializeExtensions(Containers::ArrayView<const T> enabledExtensions);
MAGNUM_VK_LOCAL void initialize(Instance& instance, Version version);
VkDevice _handle;
HandleFlags _flags;
Version _version;
Math::BoolVector<Implementation::ExtensionCount> _extensionStatus;
Containers::Pointer<Implementation::DeviceState> _state;
/* This member is bigger than you might think */
FlextVkDevice _functionPointers;
};
}}
#endif

5
src/Magnum/Vk/DeviceProperties.h

@ -288,8 +288,8 @@ class MAGNUM_VK_EXPORT DeviceProperties {
/**
* @brief Pick a queue family satisfying given flags
* @return Queue family index for use in @ref queueFamilySize() and
* @ref queueFamilyFlags()
* @return Queue family index for use in @ref queueFamilySize(),
* @ref queueFamilyFlags() and @ref DeviceCreateInfo::addQueues()
*
* Queries queue family properties using @ref queueFamilyProperties()
* and tries to find the first that contains all @p flags. If it is not
@ -313,6 +313,7 @@ class MAGNUM_VK_EXPORT DeviceProperties {
#ifndef DOXYGEN_GENERATING_OUTPUT
/* The DAMN THING lists this among friends, which is AN IMPLEMENTATION
DETAIL */
friend DeviceCreateInfo;
friend MAGNUM_VK_EXPORT Containers::Array<DeviceProperties> enumerateDevices(Instance&);
#endif

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

@ -32,9 +32,10 @@ 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("disable-extensions").setHelp("disable-extensions", "Vulkan instance or device 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("enable-extensions").setHelp("enable-extensions", "Vulkan device 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")
.addOption("device").setHelp("device", "device to use", "ID|integrated|discrete|virtual|cpu")
@ -42,6 +43,7 @@ Utility::Arguments arguments() {
.setFromEnvironment("disable-extensions")
.setFromEnvironment("enable-layers")
.setFromEnvironment("enable-instance-extensions")
.setFromEnvironment("enable-extensions")
.setFromEnvironment("vulkan-version")
.setFromEnvironment("log")
.setFromEnvironment("device");

32
src/Magnum/Vk/Implementation/DeviceState.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 "DeviceState.h"
namespace Magnum { namespace Vk { namespace Implementation {
DeviceState::DeviceState(Device&) {}
}}}

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

@ -0,0 +1,40 @@
#ifndef Magnum_Vk_Implementation_DeviceState_h
#define Magnum_Vk_Implementation_DeviceState_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 DeviceState {
explicit DeviceState(Device& instance);
};
}}}
#endif

2
src/Magnum/Vk/Instance.h

@ -364,6 +364,8 @@ class MAGNUM_VK_EXPORT Instance {
*
* @snippet MagnumVk.cpp Instance-global-function-pointers
*
* Use @ref Device::populateGlobalFunctionPointers() to populate
* device-level 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

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

@ -24,6 +24,7 @@
# DEALINGS IN THE SOFTWARE.
#
corrade_add_test(VkDeviceTest DeviceTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkDevicePropertiesTest DevicePropertiesTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk)
@ -36,6 +37,7 @@ corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk)
if(BUILD_VK_TESTS)
corrade_add_test(VkDeviceVkTest DeviceVkTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkDevicePropertiesVkTest DevicePropertiesVkTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib)

90
src/Magnum/Vk/Test/DeviceTest.cpp

@ -0,0 +1,90 @@
/*
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/Device.h"
namespace Magnum { namespace Vk { namespace Test { namespace {
struct DeviceTest: TestSuite::Tester {
explicit DeviceTest();
void createInfoConstructNoInit();
void createInfoConstructFromVk();
void constructNoCreate();
void constructCopy();
};
DeviceTest::DeviceTest() {
addTests({&DeviceTest::createInfoConstructNoInit,
&DeviceTest::createInfoConstructFromVk,
&DeviceTest::constructNoCreate,
&DeviceTest::constructCopy});
}
void DeviceTest::createInfoConstructNoInit() {
DeviceCreateInfo info{NoInit};
info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
new(&info) DeviceCreateInfo{NoInit};
CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2);
CORRADE_VERIFY((std::is_nothrow_constructible<DeviceCreateInfo, NoInitT>::value));
/* Implicit construction is not allowed */
CORRADE_VERIFY(!(std::is_convertible<NoInitT, DeviceCreateInfo>::value));
}
void DeviceTest::createInfoConstructFromVk() {
VkDeviceCreateInfo vkInfo;
vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
DeviceCreateInfo info{VkPhysicalDevice{}, vkInfo};
CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2);
}
void DeviceTest::constructNoCreate() {
{
Device device{NoCreate};
CORRADE_VERIFY(!device.handle());
/* Device function pointers should be null */
CORRADE_VERIFY(!device->CreateBuffer);
}
/* Implicit construction is not allowed */
CORRADE_VERIFY(!(std::is_convertible<NoCreateT, Device>::value));
}
void DeviceTest::constructCopy() {
CORRADE_VERIFY(!(std::is_constructible<Device, const Device&>{}));
CORRADE_VERIFY(!(std::is_assignable<Device, const Device&>{}));
}
}}}}
CORRADE_TEST_MAIN(Magnum::Vk::Test::DeviceTest)

546
src/Magnum/Vk/Test/DeviceVkTest.cpp

@ -0,0 +1,546 @@
/*
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/Device.h"
#include "Magnum/Vk/DeviceProperties.h"
#include "Magnum/Vk/Extensions.h"
#include "Magnum/Vk/ExtensionProperties.h"
#include "Magnum/Vk/Handle.h"
#include "Magnum/Vk/Instance.h"
#include "Magnum/Vk/LayerProperties.h"
#include "Magnum/Vk/Result.h"
#include "Magnum/Vk/Version.h"
#include "MagnumExternal/Vulkan/flextVkGlobal.h"
namespace Magnum { namespace Vk { namespace Test { namespace {
struct DeviceVkTest: TestSuite::Tester {
explicit DeviceVkTest();
void createInfoConstruct();
void createInfoConstructImplicitDevice();
void createInfoConstructNoImplicitExtensions();
void createInfoExtensions();
void createInfoCopiedStrings();
void createInfoNoQueuePriorities();
void construct();
void constructExtensions();
void constructExtensionsCommandLineDisable();
void constructExtensionsCommandLineEnable();
void constructMove();
void constructUnknownExtension();
void constructNoQueue();
void wrap();
void populateGlobalFunctionPointers();
Instance _instance;
};
struct {
const char* nameDisable;
const char* nameEnable;
Containers::Array<const char*> argsDisable, argsEnable;
bool driverVersionSupported, debugMarkerEnabled, maintenance1Enabled;
const char* log;
} ConstructCommandLineData[] {
/* Shouldn't print anything about version, enabled layers/exts if quier
output is enabled. */
{"quiet", "quiet, enabled extensions",
Containers::array({"", "--magnum-log", "quiet"}),
Containers::array({"", "--magnum-log", "quiet",
"--magnum-enable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}),
true, true, true,
""},
{"", "enabled extensions", nullptr,
Containers::array({"",
"--magnum-enable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}),
true, true, true,
"Device version: Vulkan {}.{}{}\n"
"Enabled device extensions:\n"
" VK_EXT_debug_marker\n"
" VK_KHR_maintenance1\n"},
{"forced version", "forced version, enabled extensions",
Containers::array({"",
"--magnum-vulkan-version", "1.0"}),
Containers::array({"",
"--magnum-vulkan-version", "1.0",
"--magnum-enable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}),
false, true, true,
"Device version: Vulkan 1.0\n"
"Enabled device extensions:\n"
" VK_EXT_debug_marker\n"
" VK_KHR_maintenance1\n"},
{"disabled one extension", "enabled one extension",
Containers::array({"",
"--magnum-disable-extensions", "VK_EXT_debug_marker"}),
Containers::array({"",
"--magnum-enable-extensions", "VK_KHR_maintenance1"}),
true, false, true,
"Device version: Vulkan {}.{}{}\n"
"Enabled device extensions:\n"
" VK_KHR_maintenance1\n"},
{"disabled extensions", "",
Containers::array({"",
"--magnum-disable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}),
nullptr,
true, false, false,
"Device version: Vulkan {}.{}{}\n"},
};
DeviceVkTest::DeviceVkTest(): _instance{InstanceCreateInfo{arguments().first, arguments().second}} {
addTests({&DeviceVkTest::createInfoConstruct,
&DeviceVkTest::createInfoConstructImplicitDevice,
&DeviceVkTest::createInfoConstructNoImplicitExtensions,
&DeviceVkTest::createInfoExtensions,
&DeviceVkTest::createInfoCopiedStrings,
&DeviceVkTest::createInfoNoQueuePriorities,
&DeviceVkTest::construct,
&DeviceVkTest::constructExtensions});
addInstancedTests({&DeviceVkTest::constructExtensionsCommandLineDisable,
&DeviceVkTest::constructExtensionsCommandLineEnable},
Containers::arraySize(ConstructCommandLineData));
addTests({&DeviceVkTest::constructMove,
&DeviceVkTest::constructUnknownExtension,
&DeviceVkTest::constructNoQueue,
&DeviceVkTest::wrap,
&DeviceVkTest::populateGlobalFunctionPointers});
}
using namespace Containers::Literals;
void DeviceVkTest::createInfoConstruct() {
DeviceCreateInfo info{pickDevice(_instance)};
CORRADE_VERIFY(info->sType);
CORRADE_VERIFY(!info->pNext);
/* Extensions might or might not be enabled */
}
void DeviceVkTest::createInfoConstructImplicitDevice() {
DeviceCreateInfo info{_instance};
CORRADE_VERIFY(info->sType);
CORRADE_VERIFY(!info->pNext);
/* Extensions might or might not be enabled */
}
void DeviceVkTest::createInfoConstructNoImplicitExtensions() {
DeviceCreateInfo info{_instance, DeviceCreateInfo::Flag::NoImplicitExtensions};
CORRADE_VERIFY(info->sType);
CORRADE_VERIFY(!info->pNext);
/* No extensions enabled as we explicitly disabled that */
CORRADE_VERIFY(!info->ppEnabledExtensionNames);
CORRADE_COMPARE(info->enabledExtensionCount, 0);
}
void DeviceVkTest::createInfoExtensions() {
if(std::getenv("MAGNUM_DISABLE_EXTENSIONS"))
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set");
DeviceCreateInfo info{_instance, DeviceCreateInfo::Flag::NoImplicitExtensions};
CORRADE_VERIFY(!info->ppEnabledExtensionNames);
CORRADE_COMPARE(info->enabledExtensionCount, 0);
info.addEnabledExtensions<Extensions::KHR::maintenance1>();
CORRADE_VERIFY(info->ppEnabledExtensionNames);
CORRADE_COMPARE(info->enabledExtensionCount, 1);
/* The pointer should be to the global data */
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledExtensionNames[0]),
Extensions::KHR::maintenance1::string().data());
info.addEnabledExtensions({
Extensions::KHR::draw_indirect_count{},
Extensions::KHR::get_memory_requirements2{}
});
CORRADE_COMPARE(info->enabledExtensionCount, 3);
/* The pointer should be to the global data */
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledExtensionNames[0]),
Extensions::KHR::maintenance1::string().data());
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledExtensionNames[1]),
Extensions::KHR::draw_indirect_count::string().data());
CORRADE_COMPARE(static_cast<const void*>(info->ppEnabledExtensionNames[2]),
Extensions::KHR::get_memory_requirements2::string().data());
}
void DeviceVkTest::createInfoCopiedStrings() {
Containers::StringView globalButNotNullTerminated = "VK_KHR_maintenance25"_s.except(1);
Containers::String localButNullTerminated = Extensions::KHR::draw_indirect_count::string();
DeviceCreateInfo info{_instance, DeviceCreateInfo::Flag::NoImplicitExtensions};
info.addEnabledExtensions({
globalButNotNullTerminated,
localButNullTerminated
});
CORRADE_COMPARE(info->enabledExtensionCount, 2);
CORRADE_COMPARE(info->ppEnabledExtensionNames[0], globalButNotNullTerminated);
CORRADE_VERIFY(info->ppEnabledExtensionNames[0] != globalButNotNullTerminated.data());
CORRADE_COMPARE(info->ppEnabledExtensionNames[1], localButNullTerminated);
CORRADE_VERIFY(info->ppEnabledExtensionNames[1] != localButNullTerminated.data());
}
void DeviceVkTest::createInfoNoQueuePriorities() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
std::ostringstream out;
Error redirectError{&out};
DeviceCreateInfo{_instance}.addQueues(0, {});
CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified\n");
}
void DeviceVkTest::construct() {
if(std::getenv("MAGNUM_VULKAN_VERSION"))
CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set");
{
DeviceProperties deviceProperties = pickDevice(_instance);
Device device{_instance, DeviceCreateInfo{deviceProperties}
.addQueues(0, {0.0f})
};
CORRADE_VERIFY(device.handle());
/* Device function pointers should be populated */
CORRADE_VERIFY(device->CreateBuffer);
CORRADE_COMPARE(device.handleFlags(), HandleFlag::DestroyOnDestruction);
CORRADE_COMPARE(device.version(), deviceProperties.apiVersion());
/* Device version is supported */
CORRADE_VERIFY(device.isVersionSupported(deviceProperties.apiVersion()));
CORRADE_VERIFY(!device.isVersionSupported(Version::None));
/* No extensions are enabled by default ... */
CORRADE_VERIFY(!device.isExtensionEnabled<Extensions::EXT::debug_marker>());
/* ... and thus also no function pointers loaded */
CORRADE_VERIFY(!device->CmdDebugMarkerInsertEXT);
}
/* Shouldn't crash or anything */
CORRADE_VERIFY(true);
}
void DeviceVkTest::constructExtensions() {
if(std::getenv("MAGNUM_DISABLE_EXTENSIONS"))
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set");
if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation"))
CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test");
/* Creating a dedicated instance so we can pass custom args and enable
layers & exts independently */
Instance instance{InstanceCreateInfo{}
.addEnabledLayers({"VK_LAYER_KHRONOS_validation"})
/* Needed by VK_EXT_debug_marker */
.addEnabledExtensions<Extensions::EXT::debug_report>()
};
DeviceProperties deviceProperties = pickDevice(instance);
ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"});
if(!extensions.isSupported<Extensions::EXT::debug_marker>())
CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test");
if(!extensions.isSupported<Extensions::KHR::maintenance1>())
CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test");
Device device{instance, DeviceCreateInfo{deviceProperties}
.addQueues(0, {0.0f})
.addEnabledExtensions({
Extensions::EXT::debug_marker::string(),
"VK_KHR_maintenance1"_s
})};
CORRADE_VERIFY(device.handle());
/* Extensions should be reported as enabled ... */
CORRADE_VERIFY(device.isExtensionEnabled<Extensions::EXT::debug_marker>());
CORRADE_VERIFY(device.isExtensionEnabled(Extensions::KHR::maintenance1{}));
/* ... and function pointers loaded */
CORRADE_VERIFY(device->CmdDebugMarkerInsertEXT);
CORRADE_VERIFY(device->TrimCommandPoolKHR);
}
void DeviceVkTest::constructExtensionsCommandLineDisable() {
auto&& data = ConstructCommandLineData[testCaseInstanceId()];
setTestCaseDescription(data.nameDisable);
if(std::getenv("MAGNUM_VULKAN_VERSION"))
CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set");
if(std::getenv("MAGNUM_DISABLE_LAYERS"))
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_LAYERS environment variable set");
if(std::getenv("MAGNUM_DISABLE_EXTENSIONS"))
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set");
if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation"))
CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test");
/* Creating a dedicated instance so we can pass custom args and enable
layers independently */
Instance instance{InstanceCreateInfo{Int(data.argsDisable.size()), data.argsDisable}
.addEnabledLayers({"VK_LAYER_KHRONOS_validation"})
/* Needed by VK_EXT_debug_marker */
.addEnabledExtensions<Extensions::EXT::debug_report>()
};
DeviceProperties deviceProperties = pickDevice(instance);
ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"});
if(!extensions.isSupported<Extensions::EXT::debug_marker>())
CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test");
if(!extensions.isSupported<Extensions::KHR::maintenance1>())
CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test");
std::ostringstream out;
Debug redirectOutput{&out};
Device device{instance, DeviceCreateInfo{deviceProperties, DeviceCreateInfo::Flag::NoImplicitExtensions}
.addQueues(0, {0.0f})
.addEnabledExtensions<
Extensions::EXT::debug_marker,
Extensions::KHR::maintenance1
>()};
CORRADE_VERIFY(device.handle());
CORRADE_COMPARE(device.isVersionSupported(deviceProperties.apiVersion()), data.driverVersionSupported);
CORRADE_COMPARE(device.isExtensionEnabled<Extensions::EXT::debug_marker>(), data.debugMarkerEnabled);
CORRADE_COMPARE(device.isExtensionEnabled<Extensions::KHR::maintenance1>(), data.maintenance1Enabled);
/** @todo cleanup when Debug::toString() or some similar utility exists */
UnsignedInt major = versionMajor(deviceProperties.apiVersion());
UnsignedInt minor = versionMinor(deviceProperties.apiVersion());
UnsignedInt patch = versionPatch(deviceProperties.apiVersion());
/* SwiftShader reports just 1.1 with 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(!!device->CmdDebugMarkerInsertEXT, data.debugMarkerEnabled);
CORRADE_COMPARE(!!device->TrimCommandPoolKHR, data.maintenance1Enabled);
}
void DeviceVkTest::constructExtensionsCommandLineEnable() {
auto&& data = ConstructCommandLineData[testCaseInstanceId()];
setTestCaseDescription(data.nameEnable);
if(std::getenv("MAGNUM_VULKAN_VERSION"))
CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set");
if(std::getenv("MAGNUM_DISABLE_LAYERS"))
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_LAYERS environment variable set");
if(std::getenv("MAGNUM_DISABLE_EXTENSIONS"))
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set");
if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation"))
CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test");
/* Creating a dedicated instance so we can pass custom args and enable
layers independently */
Instance instance{InstanceCreateInfo{Int(data.argsEnable.size()), data.argsEnable}
.addEnabledLayers({"VK_LAYER_KHRONOS_validation"})
/* Needed by VK_EXT_debug_marker */
.addEnabledExtensions<Extensions::EXT::debug_report>()
};
DeviceProperties deviceProperties = pickDevice(instance);
ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"});
if(!extensions.isSupported<Extensions::EXT::debug_marker>())
CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test");
if(!extensions.isSupported<Extensions::KHR::maintenance1>())
CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test");
std::ostringstream out;
Debug redirectOutput{&out};
Device device{instance, DeviceCreateInfo{instance, DeviceCreateInfo::Flag::NoImplicitExtensions}
.addQueues(0, {0.0f})
/* Nothing enabled by the application */
};
CORRADE_VERIFY(device.handle());
CORRADE_COMPARE(device.isVersionSupported(deviceProperties.apiVersion()), data.driverVersionSupported);
CORRADE_COMPARE(device.isExtensionEnabled<Extensions::EXT::debug_marker>(), data.debugMarkerEnabled);
CORRADE_COMPARE(device.isExtensionEnabled<Extensions::KHR::maintenance1>(), data.maintenance1Enabled);
/** @todo cleanup when Debug::toString() or some similar utility exists */
UnsignedInt major = versionMajor(deviceProperties.apiVersion());
UnsignedInt minor = versionMinor(deviceProperties.apiVersion());
UnsignedInt patch = versionPatch(deviceProperties.apiVersion());
/* SwiftShader reports just 1.1 with 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(!!device->CmdDebugMarkerInsertEXT, data.debugMarkerEnabled);
CORRADE_COMPARE(!!device->TrimCommandPoolKHR, data.maintenance1Enabled);
}
void DeviceVkTest::constructMove() {
DeviceProperties deviceProperties = pickDevice(_instance);
ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties();
if(!extensions.isSupported<Extensions::KHR::maintenance1>())
CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test");
Device a{_instance, DeviceCreateInfo{deviceProperties}
.addQueues(0, {0.0f})
.addEnabledExtensions<Extensions::KHR::maintenance1>()
};
VkDevice handle = a.handle();
Version version = a.version();
CORRADE_VERIFY(handle);
CORRADE_VERIFY(version != Version{});
CORRADE_VERIFY(version != Version::None);
Device 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::maintenance1>());
/* Function pointers in a are left in whatever state they were before, as
that doesn't matter */
CORRADE_VERIFY(b->CreateBuffer);
Device 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::maintenance1>());
/* Everything is swapped, including function pointers */
CORRADE_VERIFY(!b->CreateBuffer);
CORRADE_VERIFY(c->CreateBuffer);
CORRADE_VERIFY(std::is_nothrow_move_constructible<Device>::value);
CORRADE_VERIFY(std::is_nothrow_move_assignable<Device>::value);
}
void DeviceVkTest::constructUnknownExtension() {
CORRADE_SKIP("Currently this hits an internal assert, which can't be tested.");
std::ostringstream out;
Error redirectError{&out};
Device device{_instance, DeviceCreateInfo{_instance}
.addQueues(0, {0.0f})
.addEnabledExtensions({"VK_this_doesnt_exist"_s})};
CORRADE_COMPARE(out.str(), "TODO");
}
void DeviceVkTest::constructNoQueue() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
std::ostringstream out;
Error redirectError{&out};
Device device{_instance, DeviceCreateInfo{_instance}};
CORRADE_COMPARE(out.str(), "Vk::Device: needs to be created with at least one queue\n");
}
void DeviceVkTest::wrap() {
if(std::getenv("MAGNUM_VULKAN_VERSION"))
CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set");
if(std::getenv("MAGNUM_DISABLE_LAYERS"))
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_LAYERS environment variable set");
if(std::getenv("MAGNUM_DISABLE_EXTENSIONS"))
CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set");
if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation"))
CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test");
/* Creating a dedicated instance so we can enable layers independently */
Instance instance{InstanceCreateInfo{}
.addEnabledLayers({"VK_LAYER_KHRONOS_validation"})
/* Needed by VK_EXT_debug_marker */
.addEnabledExtensions<Extensions::EXT::debug_report>()
};
DeviceProperties deviceProperties = pickDevice(instance);
ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"});
if(!extensions.isSupported<Extensions::EXT::debug_marker>())
CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test");
if(!extensions.isSupported<Extensions::KHR::maintenance1>())
CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test");
VkDevice device;
CORRADE_COMPARE(Result(instance->CreateDevice(deviceProperties,
DeviceCreateInfo{instance}
.addQueues(0, {0.0f})
.addEnabledExtensions<
Extensions::EXT::debug_marker,
Extensions::KHR::maintenance1
>(),
nullptr, &device)), Result::Success);
CORRADE_VERIFY(device);
{
/* Wrapping should load the basic function pointers */
auto wrapped = Device::wrap(instance, device, Version::Vk11, {
Extensions::EXT::debug_marker::string()
}, HandleFlag::DestroyOnDestruction);
CORRADE_VERIFY(wrapped->DestroyDevice);
/* Specified version should be reported as supported but higher not
regardless of the actual driver version */
CORRADE_VERIFY(wrapped.isVersionSupported(Version::Vk11));
CORRADE_VERIFY(!wrapped.isVersionSupported(Version::Vk12));
/* Listed extensions should be reported as enabled and function
pointers loaded as well */
CORRADE_VERIFY(wrapped.isExtensionEnabled<Extensions::EXT::debug_marker>());
CORRADE_VERIFY(wrapped->CmdDebugMarkerInsertEXT);
/* Unlisted not, but function pointers should still be loaded as the
actual instance does have the extension enabled */
CORRADE_VERIFY(!wrapped.isExtensionEnabled<Extensions::KHR::maintenance1>());
CORRADE_VERIFY(wrapped->TrimCommandPoolKHR);
/* Releasing won't destroy anything ... */
CORRADE_COMPARE(wrapped.release(), device);
}
/* ...so we can wrap it again, non-owned, and then destroy it manually */
auto wrapped = Device::wrap(instance, device, Version::Vk10, {});
CORRADE_VERIFY(wrapped->DestroyDevice);
wrapped->DestroyDevice(device, nullptr);
}
void DeviceVkTest::populateGlobalFunctionPointers() {
vkDestroyDevice = nullptr;
Device device{_instance, DeviceCreateInfo{_instance}
.addQueues(0, {0.0f})
};
CORRADE_VERIFY(!vkDestroyDevice);
device.populateGlobalFunctionPointers();
CORRADE_VERIFY(vkDestroyDevice);
CORRADE_VERIFY(vkDestroyDevice == device->DestroyDevice);
}
}}}}
CORRADE_TEST_MAIN(Magnum::Vk::Test::DeviceVkTest)

2
src/Magnum/Vk/Vk.h

@ -36,6 +36,8 @@
namespace Magnum { namespace Vk {
#ifndef DOXYGEN_GENERATING_OUTPUT
class Device;
class DeviceCreateInfo;
class DeviceProperties;
enum class DeviceType: Int;
class Extension;

Loading…
Cancel
Save