From a62e2b8f096b6997b13cd97ab60ed5bcbaf5367b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 15 Sep 2020 19:11:53 +0200 Subject: [PATCH] Vk: start writing some actual docs. Because I put this on hold for two months and almost forgot what design guidelines I wanted to follow and how the heck is the whole thing used. --- doc/features.dox | 1 + doc/snippets/MagnumVk.cpp | 203 ++++++++++++++++++++++++---- doc/vulkan-wrapping.dox | 102 ++++++++++++++ src/Magnum/Vk/CommandBuffer.h | 3 +- src/Magnum/Vk/CommandPool.h | 18 ++- src/Magnum/Vk/Device.h | 109 +++++++++++++-- src/Magnum/Vk/DeviceProperties.h | 15 +- src/Magnum/Vk/ExtensionProperties.h | 8 +- src/Magnum/Vk/Instance.h | 145 ++++++++++++++++++-- src/Magnum/Vk/LayerProperties.h | 4 + 10 files changed, 560 insertions(+), 48 deletions(-) create mode 100644 doc/vulkan-wrapping.dox diff --git a/doc/features.dox b/doc/features.dox index 44142a4b9..e4d6047f1 100644 --- a/doc/features.dox +++ b/doc/features.dox @@ -39,6 +39,7 @@ necessary to read through everything, pick only what you need. - @subpage plugins --- @copybrief plugins - @subpage file-formats --- @copybrief file-formats - @subpage opengl-wrapping --- @copybrief opengl-wrapping +- @subpage vulkan-wrapping --- @copybrief vulkan-wrapping - @subpage shaders --- @copybrief shaders - @subpage scenegraph --- @copybrief scenegraph - @subpage debug-tools --- @copybrief debug-tools diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index b209edeaa..2cce629f7 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -27,10 +27,16 @@ #include "Magnum/Magnum.h" #include "Magnum/Math/Color.h" +#include "Magnum/Vk/CommandBuffer.h" +#include "Magnum/Vk/CommandPool.h" #include "Magnum/Vk/Device.h" +#include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/Integration.h" +#include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/Queue.h" #include "MagnumExternal/Vulkan/flextVkGlobal.h" using namespace Magnum; @@ -38,65 +44,215 @@ using namespace Magnum; #define DOXYGEN_IGNORE(...) __VA_ARGS__ int main() { + { -Vk::Device device{NoCreate}; -/* [Device-isExtensionEnabled] */ -if(device.isExtensionEnabled()) { - // keep mesh indices 8bit -} else { - // convert them to 16bit +/* [wrapping-extending-create-info] */ +Vk::InstanceCreateInfo info{DOXYGEN_IGNORE()}; + +/* Add a custom validation features setup */ +VkValidationFeaturesEXT validationFeatures{}; +validationFeatures.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; +validationFeatures.enabledValidationFeatureCount = 1; +constexpr auto bestPractices = VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT; +validationFeatures.pEnabledValidationFeatures = &bestPractices; +CORRADE_INTERNAL_ASSERT(!info->pNext); // or find the end of the pNext chain +info->pNext = &validationFeatures; +/* [wrapping-extending-create-info] */ } -/* [Device-isExtensionEnabled] */ + +{ +Vk::Instance instance; +/* [CommandPool-usage] */ +Vk::DeviceProperties props = DOXYGEN_IGNORE(pickDevice(instance)); +Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; + +Vk::CommandPool graphicsCommandPool{device, Vk::CommandPoolCreateInfo{ + props.pickQueueFamily(Vk::QueueFlag::Graphics) +}}; +/* [CommandPool-usage] */ + +/* [CommandPool-usage-allocate] */ +Vk::CommandBuffer commandBuffer = graphicsCommandPool.allocate(); + +// fill the buffer, submit … +/* [CommandPool-usage-allocate] */ +} + +{ +Vk::Instance instance; +/* [Device-usage-pick] */ +Vk::DeviceProperties props = Vk::pickDevice(instance); +/* [Device-usage-pick] */ + +/* [Device-usage-construct-queue] */ +Vk::Queue queue{NoCreate}; +Vk::Device device{instance, Vk::DeviceCreateInfo{props} + .addQueues(props.pickQueueFamily(Vk::QueueFlag::Graphics), {0.0f}, {queue}) +}; +/* [Device-usage-construct-queue] */ } { +Vk::Instance instance; +Vk::DeviceProperties props{NoCreate}; +using namespace Containers::Literals; +/* [Device-usage-extensions] */ +Vk::Device device{instance, Vk::DeviceCreateInfo{props} + DOXYGEN_IGNORE() + .addEnabledExtensions< // predefined extensions + Vk::Extensions::EXT::index_type_uint8, + Vk::Extensions::KHR::device_group>() + .addEnabledExtensions({"VK_NV_mesh_shader"_s}) // can be plain strings too +}; +/* [Device-usage-extensions] */ +} + +{ +Vk::Instance instance; +Vk::DeviceProperties props{NoCreate}; +using namespace Containers::Literals; +/* [Device-usage-check-supported] */ +Vk::ExtensionProperties extensions = props.enumerateExtensionProperties(); + +Vk::DeviceCreateInfo info{props}; +if(extensions.isSupported()) + info.addEnabledExtensions(); +if(extensions.isSupported("VK_NV_mesh_shader"_s)) + info.addEnabledExtensions({"VK_NV_mesh_shader"_s}); +DOXYGEN_IGNORE() +/* [Device-usage-check-supported] */ +} + +{ +Vk::Instance instance; +VkQueryPool pool{}; +/* [Device-function-pointers] */ +Vk::Device device{DOXYGEN_IGNORE(instance, Vk::DeviceCreateInfo{Vk::pickDevice(instance)})}; + +// ... +device->ResetQueryPoolEXT(device, DOXYGEN_IGNORE(pool, 0, 0)); +/* [Device-function-pointers] */ +} + +{ +VkQueryPool pool{}; /* Header included again inside a function, but it's fine as the guards will make it empty */ /* [Device-global-function-pointers] */ #include -// … +DOXYGEN_IGNORE() Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; device.populateGlobalFunctionPointers(); -VkCommandPool commandPool; -VkCommandPoolCreateInfo info{}; -// … -vkCreateCommandPool(device, &info, nullptr, &commandPool); +DOXYGEN_IGNORE() +vkResetQueryPoolEXT(device, DOXYGEN_IGNORE(pool, 0, 0)); /* [Device-global-function-pointers] */ } { -Vk::Instance instance; -/* [Instance-isExtensionEnabled] */ -if(instance.isExtensionEnabled()) { - // use the fancy debugging APIs -} else if(instance.isExtensionEnabled()) { - // use the non-fancy and deprecated debugging APIs +Vk::Device device{NoCreate}; +/* [Device-isExtensionEnabled] */ +if(device.isExtensionEnabled()) { + // keep mesh indices 8bit } else { - // well, tough luck + // convert them to 16bit } -/* [Instance-isExtensionEnabled] */ +/* [Device-isExtensionEnabled] */ } { +int argc{}; +const char** argv{}; +/* [Instance-usage] */ +using namespace Containers::Literals; + +Vk::Instance instance{Vk::InstanceCreateInfo{argc, argv} + .setApplicationInfo("My Vulkan Application"_s, Vk::version(1, 2, 3)) +}; +/* [Instance-usage] */ +} + +{ +int argc{}; +const char** argv{}; +using namespace Containers::Literals; +/* [Instance-usage-layers-extensions] */ +Vk::Instance instance{Vk::InstanceCreateInfo{argc, argv} + DOXYGEN_IGNORE() + .addEnabledLayers({"VK_LAYER_KHRONOS_validation"_s}) + .addEnabledExtensions< // predefined extensions + Vk::Extensions::EXT::debug_report, + Vk::Extensions::KHR::external_fence_capabilities>() + .addEnabledExtensions({"VK_KHR_xcb_surface"_s}) // can be plain strings too +}; +/* [Instance-usage-layers-extensions] */ +} + +{ +int argc{}; +const char** argv{}; +using namespace Containers::Literals; +/* [Instance-usage-check-supported] */ +/* Query layer and extension support */ +Vk::LayerProperties layers = Vk::enumerateLayerProperties(); +Vk::InstanceExtensionProperties extensions = + /* ... including extensions exposed only by the extra layers */ + Vk::enumerateInstanceExtensionProperties(layers.names()); + +/* Enable only those that are supported */ +Vk::InstanceCreateInfo info{argc, argv, &layers, &extensions}; +if(layers.isSupported("VK_LAYER_KHRONOS_validation"_s)) + info.addEnabledLayers({"VK_LAYER_KHRONOS_validation"_s}); +if(extensions.isSupported()) + info.addEnabledExtensions(); +DOXYGEN_IGNORE() + +Vk::Instance instance{info}; +/* [Instance-usage-check-supported] */ +} + +{ +/* [Instance-function-pointers] */ +Vk::Instance instance{DOXYGEN_IGNORE()}; + +VkPhysicalDeviceGroupPropertiesKHR properties[10]; +UnsignedInt count = Containers::arraySize(properties); +instance->EnumeratePhysicalDeviceGroupsKHR(instance, &count, properties); +/* [Instance-function-pointers] */ +} + +{ +Vk::Instance instance; /* Header included again inside a function, but it's fine as the guards will make it empty */ /* [Instance-global-function-pointers] */ #include -// … +DOXYGEN_IGNORE() -Vk::Instance instance; instance.populateGlobalFunctionPointers(); -VkPhysicalDeviceGroupProperties properties[10]; +VkPhysicalDeviceGroupPropertiesKHR properties[10]; UnsignedInt count = Containers::arraySize(properties); vkEnumeratePhysicalDeviceGroupsKHR(instance, &count, properties); /* [Instance-global-function-pointers] */ } +{ +Vk::Instance instance; +/* [Instance-isExtensionEnabled] */ +if(instance.isExtensionEnabled()) { + // use the fancy debugging APIs +} else if(instance.isExtensionEnabled()) { + // use the non-fancy and deprecated debugging APIs +} else { + // well, tough luck +} +/* [Instance-isExtensionEnabled] */ +} + { /* [Integration] */ VkOffset2D a{64, 32}; @@ -108,4 +264,5 @@ VkClearColorValue c = VkClearColorValue(0xff9391_srgbf); static_cast(b); static_cast(c); } + } diff --git a/doc/vulkan-wrapping.dox b/doc/vulkan-wrapping.dox new file mode 100644 index 000000000..6649bcefa --- /dev/null +++ b/doc/vulkan-wrapping.dox @@ -0,0 +1,102 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +namespace Magnum { +/** @page vulkan-wrapping Vulkan wrapping layer +@brief Overview of the base Vulkan wrapper API + +@tableofcontents +@m_footernavigation + +The @ref Magnum::Vk library is a thin but high-level abstraction of the +[Vulkan](https://www.khronos.org/vulkan/) GPU API, providing sane defaults with +ability to opt-in for greater control and performance. + +@section vulkan-wrapping-instance-device Instance and device wrappers + +Compared to OpenGL, which has a concept of "current context", Vulkan doesn't +have any implicit globals. The @ref Vk library follows that, with each object +carrying a reference to a corresponding instance or device along. This was +chosen as a reasonable tradeoff between requiring an explicit instance/device +parameter in each API (which would be too error-prone and annoying to use) and +having an implicit thread-local instance/device (which would repeat the +well-known pain points of OpenGL). + +Vulkan API entrypoints aren't global either because each instance and device +can have a different set of enabled layers and extensions, and thus different +instance- and device-local function pointers. While the Vulkan specification +allows device-level functions to be queried on an instance and thus use the +same function pointers on a variety of devices, such workflow implies +additional dispatch overhead, and thus isn't recommended. Magnum instead +stores instance- and device-level function pointers locally in each +@ref Vk::Instance and @ref Vk::Device to avoid this overhead --- these are then +accessible through @ref Vk::Instance::operator->() "operator->()" on both: + +@snippet MagnumVk.cpp Instance-function-pointers + +For convenience and for easier interaction with 3rd party code, such pointers +can be made global by calling @ref Vk::Instance::populateGlobalFunctionPointers() +and @ref Vk::Device::populateGlobalFunctionPointers(), after which you can use +the `vk*` functions as usual. However, all implications coming from these being +tied to a particular instance/device still apply: + +@snippet MagnumVk.cpp Instance-global-function-pointers + +@section vulkan-wrapping-create-info CreateInfo structure wrappers + +In most cases, a`Vk::*CreateInfo` instance has all required fields set to valid +values upon construction, with everything else optional. One exception to this +rule is for example @ref Vk::DeviceCreateInfo, where the user is expected to +call @ref Vk::DeviceCreateInfo::addQueues() "addQueues()". + +@section vulkan-wrapping-raw Common interfaces for interaction with raw Vulkan code + +Each wrapped Vulkan object has a @ref Vk::Instance::handle() "handle()" getter, +giving back the underlying Vulkan handle such as @type_vk{Instance}. In +addition it's also implicitly convertible to that handle type, which means you +can pass it as-is to raw Vulkan APIs. You can also use +@ref Vk::Instance::release() "release()" to release its ownership and continue +to use it as a regular handle. Conversely, any Vulkan handle can be wrapped +into a first-class Magnum object using a corresponding +@ref Vk::Instance::wrap() "wrap()" function. + +Similarly, all @ref Vk::InstanceCreateInfo "Vk::*CreateInfo" wrapper classes +are convertible to a `Vk*CreateInfo` pointer in order to be easily passable +directly to Vulkan APIs. You can create them from an existing +`Vk*CreateInfo` instances as well, and use +@ref Vk::InstanceCreateInfo::operator->() "operator->()" to access the wrapped +structure to supply additional parameters not exposed by Magnum. However take +care to not clash with values and pointers already set: + +@snippet MagnumVk.cpp wrapping-extending-create-info + +To completely mitigate the overhead from instantiating wrapper `*CreateInfo` +classes, each of them can also be constructed using the @ref NoInit tag, which +will skip all initialization and leave the contents unspecified to be filled +later. Note that at that point you have the full responsibility to correctly +set up all members. + +*/ +} diff --git a/src/Magnum/Vk/CommandBuffer.h b/src/Magnum/Vk/CommandBuffer.h index f673dccb1..3997307fd 100644 --- a/src/Magnum/Vk/CommandBuffer.h +++ b/src/Magnum/Vk/CommandBuffer.h @@ -71,7 +71,8 @@ CORRADE_ENUMSET_OPERATORS(CommandBufferResetFlags) @brief Command buffer @m_since_latest -Wraps a @type_vk_keyword{CommandBuffer}. +Wraps a @type_vk_keyword{CommandBuffer}. A command buffer instance is usually +allocated from a @ref CommandPool, see its documentation for usage information. */ class MAGNUM_VK_EXPORT CommandBuffer { public: diff --git a/src/Magnum/Vk/CommandPool.h b/src/Magnum/Vk/CommandPool.h index 329fdbca1..2d2b5c38f 100644 --- a/src/Magnum/Vk/CommandPool.h +++ b/src/Magnum/Vk/CommandPool.h @@ -177,7 +177,23 @@ CORRADE_ENUMSET_OPERATORS(CommandPoolResetFlags) @brief Command pool @m_since_latest -Wraps a @type_vk_keyword{CommandPool}. +Wraps a @type_vk_keyword{CommandPool} and handles allocation of +@ref CommandBuffer "CommandBuffer"s. + +@section Vk-CommandPool-usage Usage + +A @ref CommandPoolCreateInfo doesn't need many inputs --- the only required is +queue family index coming from @ref DeviceProperties of the device it's created +on: + +@snippet MagnumVk.cpp CommandPool-usage + +After that, you can allocate command buffers as follows. The command buffers +are freed at the end of their instance lifetime, you can also put all allocated +buffers back to initial state by calling @ref reset(), or alternatively reset +each buffer separately using @ref CommandBuffer::reset(). + +@snippet MagnumVk.cpp CommandPool-usage-allocate */ class MAGNUM_VK_EXPORT CommandPool { public: diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h index c3d086075..73fe11b29 100644 --- a/src/Magnum/Vk/Device.h +++ b/src/Magnum/Vk/Device.h @@ -53,8 +53,8 @@ namespace Implementation { @brief Device creation info @m_since_latest -Wraps a @type_vk_keyword{DeviceCreateInfo}. -@see @ref Device +Wraps a @type_vk_keyword{DeviceCreateInfo}. See @ref Device for usage +information. */ class MAGNUM_VK_EXPORT DeviceCreateInfo { public: @@ -213,8 +213,99 @@ CORRADE_ENUMSET_OPERATORS(DeviceCreateInfo::Flags) @brief Device @m_since_latest -Wraps a @type_vk_keyword{Device} and stores all device-specific function +Wraps a @type_vk_keyword{Device} and stores device-specific Vulkan function pointers. + +@section Vk-Device-usage Usage + +With an @ref Instance ready, a device has to be picked first. Commonly it's +done by calling @ref pickDevice() and letting the library choose. This +selection is affected by the `--magnum-device` +@ref Vk-Device-command-line "command-line option", giving the end users an +ability to pick a particular device, choose a discrete or integrated GPU or +even a software implementation. If the application needs something specific, +you can use @ref enumerateDevices() instead, pick a device from the list +manually, provide the users with a list to choose from etc. + +@snippet MagnumVk.cpp Device-usage-pick + +After a device is picked, a @ref Device can be created using +@ref DeviceCreateInfo. At the very least you'll need to set up queues, as every +Vulkan device needs at least one. That's done by creating an empty @ref Queue +instance and then referencing it from @ref DeviceCreateInfo::addQueues(). After +the device is constructed, the queue gets populated and is ready to be used. + +@snippet MagnumVk.cpp Device-usage-construct-queue + +In the above snippet, we requested a graphics queue --- the +@ref DeviceProperties instance we made earlier acts as knowledge base for a +particular device, providing info about available queues, extensions, features, +memory heaps and implementation limits --- and we used a convenience API to +pick the first available graphics queue. As with device picking, you can +also iterate through all @ref DeviceProperties::queueFamilyCount() and choose +one manually. + +Same as with @ref Instance, the above won't enable any additional extensions +except for what the engine itself needs or what's supplied on the command line. Use @ref DeviceCreateInfo::addEnabledExtensions() to enable them, you can use +both string names as well as predefined *device* extensions from the +@ref Extensions namespace. Later on, presence of predefined extensions can be +checked with @ref isExtensionEnabled(). + +@snippet MagnumVk.cpp Device-usage-extensions + +Usually you'll be first checking for extension availability instead, which is +again accessible through the @ref DeviceProperties instance. Similarly to +@ref Instance, as the extension list is used internally as well, pass it in the +constructor to avoid querying them again internally: + +@snippet MagnumVk.cpp Device-usage-check-supported + +With both @ref Instance and @ref Device created, you can proceed to setting up +a @ref CommandPool. + +@section Vk-Device-command-line Command-line options + +The @ref Device inherits a subset of the +@ref Vk-Instance-command-line "Instance command-line options", in particular +the following. If the @ref Instance didn't get `argc` / `argv` passed, only the +environment variables are used. + +- `--magnum-disable-extensions LIST` --- Vulkan instance or device extensions + to disable, meaning @ref DeviceCreateInfo::addEnabledExtensions() will skip + them (environment: `MAGNUM_DISABLE_EXTENSIONS`) +- `--magnum-enable-extensions LIST` --- Vulkan device extensions to enable in + addition to @ref DeviceCreateInfo defaults and what the application + requests (environment: `MAGNUM_ENABLE_EXTENSIONS`) +- `--magnum-vulkan-version X.Y` --- force @ref Device Vulkan version instead + of using what the device reports as supported, affecting what entrypoints + and extensions get used (environment: `MAGNUM_VULKAN_VERSION`) +- `--magnum-log default|quiet|verbose` --- console logging (environment: + `MAGNUM_LOG`) (default: `default`) +- `--magnum-device ID|integrated|discrete|virtual|cpu` --- device ID or kind + to pick in @ref pickDevice(); if a device is selected through + @ref enumerateDevices() or any other way, this option has no effect + (environment: `MAGNUM_DEVICE`) + +@section Vk-Device-raw Interaction with raw Vulkan code + +In addition to the common properties explained in @ref vulkan-wrapping-raw, +the @ref Device contains device-level Vulkan function pointers, accessible +through @ref operator->(): + +@snippet MagnumVk.cpp Device-function-pointers + +These functions are by default not accessible globally (and neither there is a +global "current instance"), which is done in order to avoid multiple +independent instances affecting each other. Sometimes it is however desirable +to have global function pointers --- for example when a 3rd party code needs to +operate on the same instance, or when writing quick prototype code --- and then +it's possible to populate those using @ref populateGlobalFunctionPointers(). +Compared to the above, the same custom code would then look like this: + +@snippet MagnumVk.cpp Device-global-function-pointers + +Similarly you can use @ref Instance::populateGlobalFunctionPointers() to +populate instance-level global function pointers. */ class MAGNUM_VK_EXPORT Device { public: @@ -235,6 +326,11 @@ class MAGNUM_VK_EXPORT Device { * things. If @p enabledExtensions is empty, the device will behave as * if no extensions were enabled. * + * Note that this function retrieves all device-specific Vulkan + * function pointers, which is a relatively costly operation. It's thus + * not recommended to call this function repeatedly for creating + * short-lived device instances, even though it's technically correct. + * * Unlike a device created using a constructor, the Vulkan device is by * default not deleted on destruction, use @p flags for different * behavior. @@ -352,12 +448,9 @@ class MAGNUM_VK_EXPORT Device { * @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 + * code is able to call global device-level `vk*` functions. See + * @ref Vk-Device-raw for more information. * - * 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 diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 9a2efd0b2..ff0a7860a 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -135,7 +135,11 @@ MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, QueueFlags value); Wraps a @type_vk_keyword{PhysicalDevice} along with its (lazy-populated) properties such as @type_vk_keyword{PhysicalDeviceProperties2} and @type_vk_keyword{GetPhysicalDeviceQueueFamilyProperties2}. -@see @ref enumerateDevices() + +See the @ref Vk-Device-usage "Device class docs" for an example of using this +class for enumerating available devices and picking one of them. + +@see @ref pickDevice(), @ref enumerateDevices() */ class MAGNUM_VK_EXPORT DeviceProperties { public: @@ -339,6 +343,9 @@ class MAGNUM_VK_EXPORT DeviceProperties { @brief Enumerate physical devices @m_since_latest +Returns a list of all devices present on the system. See @ref pickDevice() for +an alternative that pick just a single appropriate device. See @ref Device for +general usage information. @see @fn_vk_keyword{EnumeratePhysicalDevices} */ MAGNUM_VK_EXPORT Containers::Array enumerateDevices(Instance& instance); @@ -348,9 +355,9 @@ MAGNUM_VK_EXPORT Containers::Array enumerateDevices(Instance& @m_since_latest Calls @ref enumerateDevices() and selects a device based on preferences -specified through command-line parameters or the environment. If a device is -not found, exits. See @ref tryPickDevice() for an alternative that doesn't exit -on failure. +specified through the `--magnum-device` @ref Vk-Instance-command-line "command-line option". +If a device is not found, exits. See @ref tryPickDevice() for an alternative +that doesn't exit on failure. See @ref Device for general usage information. */ MAGNUM_VK_EXPORT DeviceProperties pickDevice(Instance& instance); diff --git a/src/Magnum/Vk/ExtensionProperties.h b/src/Magnum/Vk/ExtensionProperties.h index b2589ef9a..4935f6e30 100644 --- a/src/Magnum/Vk/ExtensionProperties.h +++ b/src/Magnum/Vk/ExtensionProperties.h @@ -47,7 +47,13 @@ namespace Magnum { namespace Vk { Provides a searchable container of Vulkan device extensions enumerated with @ref DeviceProperties::enumerateExtensionProperties(). -@see @ref InstanceExtensionProperties, @type_vk_keyword{ExtensionProperties} + +See the @ref Vk-Device-usage "Device class docs" for an example of using this +class for checking available extensions before enabling them on a device. See +@ref Vk-Instance-usage "Instance class docs" for the same but using +@ref InstanceExtensionProperties. + +@see @type_vk_keyword{ExtensionProperties} */ class MAGNUM_VK_EXPORT ExtensionProperties { public: diff --git a/src/Magnum/Vk/Instance.h b/src/Magnum/Vk/Instance.h index 5c4e520d8..7abf547f1 100644 --- a/src/Magnum/Vk/Instance.h +++ b/src/Magnum/Vk/Instance.h @@ -52,8 +52,7 @@ namespace Implementation { @m_since_latest Wraps a @type_vk_keyword{InstanceCreateInfo} and -@type_vk_keyword{ApplicationInfo}. -@see @ref Instance +@type_vk_keyword{ApplicationInfo}. See @ref Instance for usage information. */ class MAGNUM_VK_EXPORT InstanceCreateInfo { public: @@ -156,6 +155,10 @@ class MAGNUM_VK_EXPORT InstanceCreateInfo { * * Use the @ref version() helper to create the @p version value. The * name is @cpp nullptr @ce by default. + * + * 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& setApplicationInfo(Containers::StringView name, Version version); @@ -164,7 +167,10 @@ class MAGNUM_VK_EXPORT InstanceCreateInfo { * @return Reference to self (for method chaining) * * All listed layers are expected be supported, use - * @ref LayerProperties::isSupported() to check for their presence. + * @ref LayerProperties::isSupported() to check for their presence. If + * a particular layer is listed among `--magnum-disable-layers` in + * @ref Vk-Instance-command-line "command-line options", it's not + * added. * * The function makes copies of string views that are not owning or * null-terminated, use the @link Containers::Literals::operator""_s() @endlink @@ -181,7 +187,10 @@ class MAGNUM_VK_EXPORT InstanceCreateInfo { * 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. + * presence. If a particular extension is listed among + * `--magnum-disable-extensions` in + * @ref Vk-Instance-command-line "command-line options", it's not + * added. * * The function makes copies of string views that are not owning or * null-terminated, use the @link Containers::Literals::operator""_s() @endlink @@ -228,7 +237,121 @@ CORRADE_ENUMSET_OPERATORS(InstanceCreateInfo::Flags) Wraps a @type_vk_keyword{Instance} and stores instance-specific Vulkan function pointers. -@see @ref vulkan-wrapping + +@section Vk-Instance-usage Usage + +While an @ref Instance can be default-constructed without much fuss, it's +recommended to pass a @ref InstanceCreateInfo with at least the `argc` / `argv` +pair, which allows you to use various `--magnum-*` +@ref Vk-Instance-command-line "command-line options" listed below. Setting +application info may be beneficial for the driver, but it's not required +either. + +@snippet MagnumVk.cpp Instance-usage + + + +@m_class{m-note m-success} + +@par + The above code uses the @link Containers::Literals::operator""_s() @endlink + literal, which lets the library know that given string is global and + null-terminated. Such strings then don't need to be copied internally to + keep them in scope until they're consumed by Vulkan APIs. The same is + recommended to do for extension and layer names where possible. + +The above won't enable any additional layers or extensions except for what the +engine itself needs or what's supplied on the command line. Use +@ref InstanceCreateInfo::addEnabledLayers() and +@ref InstanceCreateInfo::addEnabledExtensions() to enable them, you can use +both string names as well as predefined *instance* extensions from the +@ref Extensions namespace. Later on, presence of predefined extensions can be +checked with @ref isExtensionEnabled(). + +@snippet MagnumVk.cpp Instance-usage-layers-extensions + +However, with the above approach, if any layer or extension isn't available, +the instance creation will abort. The recommended workflow is thus first +checking layer and extension availability using @ref enumerateLayerProperties() +and @ref enumerateInstanceExtensionProperties(). The @ref InstanceCreateInfo +class uses these properties internally as well, pass them in the constructor +to avoid querying those again internally: + +@snippet MagnumVk.cpp Instance-usage-check-supported + +Next step after creating a Vulkan instance is picking and creating a +@ref Device. + +@section Vk-Instance-command-line Command-line options + +The @ref Instance is configurable through command-line options that are passed +through the @ref InstanceCreateInfo `argc` / `argv` parameters. If those are +not passed, only the environment variables are used. A subset of these options +is reused by a subsequently created @ref Device as well. + +@code{.sh} + [--magnum-help] + [--magnum-disable-layers LIST] + [--magnum-disable-extensions LIST] + [--magnum-enable-layers LIST] + [--magnum-enable-instance-extensions LIST] + [--magnum-enable-extensions LIST] + [--magnum-vulkan-version X.Y] + [--magnum-log default|quiet|verbose] + [--magnum-device ID|integrated|discrete|virtual|cpu] ... +@endcode + +Arguments: + +- `...` --- main application arguments (see `-h` or `--help` for details) +- `--magnum-help` --- display this help message and exit +- `--magnum-disable-layers LIST` --- Vulkan layers to disable, meaning + @ref InstanceCreateInfo::addEnabledLayers() will skip them (environment: + `MAGNUM_DISABLE_LAYERS`) +- `--magnum-disable-extensions LIST` --- Vulkan instance or device extensions + to disable, meaning @ref InstanceCreateInfo::addEnabledExtensions() and + @ref DeviceCreateInfo::addEnabledExtensions() will skip them (environment: + `MAGNUM_DISABLE_EXTENSIONS`) +- `--magnum-enable-layers LIST` --- Vulkan layers to enable in addition to + @ref InstanceCreateInfo defaults and what the application requests + (environment: `MAGNUM_ENABLE_LAYERS`) +- `--magnum-enable-instance-extensions LIST` --- Vulkan instance extensions + to enable in addition to @ref InstanceCreateInfo defaults and what the + application requests (environment: `MAGNUM_ENABLE_INSTANCE_EXTENSIONS`) +- `--magnum-enable-extensions LIST` --- Vulkan device extensions to enable in + addition to @ref DeviceCreateInfo defaults and what the application + requests (environment: `MAGNUM_ENABLE_EXTENSIONS`) +- `--magnum-vulkan-version X.Y` --- force @ref Instance and @ref Device + Vulkan version instead of using what the instance / device reports as + supported, affecting what entrypoints and extensions get used (environment: + `MAGNUM_VULKAN_VERSION`) +- `--magnum-log default|quiet|verbose` --- console logging (environment: + `MAGNUM_LOG`) (default: `default`) +- `--magnum-device ID|integrated|discrete|virtual|cpu` --- device ID or kind + to pick in @ref pickDevice(); if a device is selected through + @ref enumerateDevices() or any other way, this option has no effect + (environment: `MAGNUM_DEVICE`) + +@section Vk-Instance-raw Interaction with raw Vulkan code + +In addition to the common properties explained in @ref vulkan-wrapping-raw, the +@ref Instance contains instance-level Vulkan function pointers, accessible +through @ref operator->(): + +@snippet MagnumVk.cpp Instance-function-pointers + +These functions are by default not accessible globally (and neither there is a +global "current instance"), which is done in order to avoid multiple +independent instances affecting each other. Sometimes it is however desirable +to have global function pointers --- for example when a 3rd party code needs to +operate on the same instance, or when writing quick prototype code --- and then +it's possible to populate those using @ref populateGlobalFunctionPointers(). +Compared to the above, the same custom code would then look like this: + +@snippet MagnumVk.cpp Instance-global-function-pointers + +Similarly you can use @ref Device::populateGlobalFunctionPointers() to populate +device-level global function pointers. */ class MAGNUM_VK_EXPORT Instance { public: @@ -248,6 +371,11 @@ class MAGNUM_VK_EXPORT Instance { * things. If @p enabledExtensions empty, the instance will behave as * if no extensions were enabled. * + * Note that this function retrieves all instance-specific Vulkan + * function pointers, which is a relatively costly operation. It's thus + * not recommended to call this function repeatedly for creating + * short-lived instances, even though it's technically correct. + * * Unlike an instance created using a constructor, the Vulkan instance * is by default not deleted on destruction, use @p flags for different * behavior. @@ -360,12 +488,9 @@ class MAGNUM_VK_EXPORT Instance { * @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 + * code is able to call global instance-level `vk*` functions. See + * @ref Vk-Instance-raw for more information. * - * 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 diff --git a/src/Magnum/Vk/LayerProperties.h b/src/Magnum/Vk/LayerProperties.h index 98a6cc0e2..a3b669f4e 100644 --- a/src/Magnum/Vk/LayerProperties.h +++ b/src/Magnum/Vk/LayerProperties.h @@ -50,6 +50,10 @@ layers are [deprecated since Vulkan 1.0.13](https://github.com/KhronosGroup/Vulk and the assumption is that no drivers currently use rely on these anymore. See [§ 37.3.1](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap38.html#extendingvulkan-layers-devicelayerdeprecation) for more information. + +See the @ref Vk-Instance-usage "Instance class docs" for an example of using +this class for checking available layers before enabling them on an instance. + @see @ref ExtensionProperties, @ref enumerateInstanceExtensionProperties(), @type_vk_keyword{LayerProperties} */