├── LICENSE ├── README.md ├── Reference.md ├── UserGuide.md └── VkExtensionsFeaturesHelp.hpp /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Adam Sawicki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Vulkan Extensions & Features Help**, or **VkExtensionsFeaturesHelp**, is a small, header-only, C++ library for developers who use Vulkan API. It helps to avoid boilerplate code while creating `VkInstance` and `VkDevice` object by providing a convenient way to query and then enable: 2 | 3 | - instance layers 4 | - instance extensions 5 | - instance feature structures 6 | - device features 7 | - device extensions 8 | - device feature structures 9 | 10 | The library provides a domain-specific language to describe the list of required or supported extensions, features, and layers. The language is fully defined in terms of preprocessor macros, so no custom build step is needed. 11 | 12 | Author: Adam Sawicki - https://asawicki.info
13 | Version: 1.1.0, 2021-04-08
14 | License: MIT (see file: [LICENSE](LICENSE)) 15 | 16 | ## Documentation 17 | 18 | - **[User Guide](UserGuide.md)** 19 | - **[Reference](Reference.md)** 20 | 21 | ## Quick example 22 | 23 | - Copy file "VkExtensionsFeaturesHelp.hpp" into your project and `#include` it. 24 | - Create file "VkExtensionsFeatures.inl" and put appropriate macros there to define device extensions and features your program supports, e.g.: 25 | 26 | ```cpp 27 | VKEFH_DEVICE_EXTENSION(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME) 28 | VKEFH_DEVICE_FEATURE_STRUCT(VkPhysicalDeviceMemoryPriorityFeaturesEXT, 29 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT) 30 | ``` 31 | 32 | - Use class `VKEFH::DeviceInitHelp` to conveniently create Vulkan device object: 33 | 34 | ```cpp 35 | VKEFH::DeviceInitHelp devInitHelp; 36 | devInitHelp.GetPhysicalDeviceFeatures(physicalDevice); 37 | devInitHelp.EnumerateExtensions(physicalDevice); 38 | 39 | bool memoryPrioritySupported = 40 | devInitHelp.IsExtensionSupported(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME) && 41 | devInitHelp.GetVkPhysicalDeviceMemoryPriorityFeaturesEXT().memoryPriority; 42 | 43 | devInitHelp.PrepareCreation(); 44 | 45 | VkDeviceCreateInfo devCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; 46 | devCreateInfo.pNext = devInitHelp.GetFeaturesChain(); 47 | devCreateInfo.enabledExtensionCount = devInitHelp.GetEnabledExtensionCount(); 48 | devCreateInfo.ppEnabledExtensionNames = devInitHelp.GetEnabledExtensionNames(); 49 | devCreateInfo.queueCreateInfoCount = /* ... */; 50 | devCreateInfo.pQueueCreateInfos = /* ... */; 51 | 52 | VkDevice device = nullptr; 53 | VkResult res = vkCreateDevice(physicalDevice, &devCreateInfo, nullptr, &device); 54 | ``` 55 | 56 | ## Example integration 57 | 58 | To see example integration of this library and check how much it simplifies the code, visit following forked repositories: 59 | 60 | - [Wicked Engine](https://github.com/sawickiap/WickedEngine/tree/experiment-VkExtensionsFeaturesHelp-integration) ([see code comparison](https://github.com/sawickiap/WickedEngine/compare/16fe939..a98e136)) 61 | - [Vulkan Memory Allocator](https://github.com/sawickiap/VulkanMemoryAllocator/tree/experiment-VkExtensionsFeaturesIntegration) library and its sample app from AMD ([see code comparison](https://github.com/sawickiap/VulkanMemoryAllocator/compare/d1851f0..f944c4d)) 62 | - [Cauldron](https://github.com/sawickiap/Cauldron/tree/experiment-VkExtensionsFeaturesHelp-integration) framework with CACAO effect sample by AMD ([see code comparison](https://github.com/sawickiap/Cauldron/compare/e850540..69f9cdf)) 63 | -------------------------------------------------------------------------------- /Reference.md: -------------------------------------------------------------------------------- 1 | # VkExtensionsFeaturesHelp Reference 2 | 3 | This document provides description of the entire API of the library. For step-by-step guide, see the other document: **[User Guide](UserGuide.md)**. See also main page: **[README](README.md)**. 4 | 5 | All names of structures, extensions, layers passed to the library as strings are case-sensitive. 6 | 7 | ## Definition file 8 | 9 | To use the library, you need to create *definition file* - file **"VkExtensionsFeatures.inl"** in the same directory as the library source code. It is `#include`-d by the library multiple times, for different definitions of the macros used in it. 10 | 11 | It is actually a C++ source file where you should only use specific macros, documented below. Their purpose is to define list of extensions, layers, and feature structures that are required or optionally used by your program. Their order doesn't matter. They shall not have trailing semicolon `;`. 12 | 13 | You can also use other preprocessor macros, like `#ifdef _WIN32` to enable some parts conditionally depending on platform or other project configuration. 14 | 15 | **VKEFH_INSTANCE_EXTENSION(const char\* extensionName)** 16 | 17 | Defines Vulkan instance extension used by your program. 18 | 19 | - *extensionName* - Name of the extension. It must be a string. It may be a literal string, e.g. `"VK_KHR_surface"`, or appropriate constant as defined in Vulkan headers, e.g. `VK_KHR_SURFACE_EXTENSION_NAME`. 20 | 21 | **VKEFH_INSTANCE_LAYER(const char\* layerName)** 22 | 23 | Defines Vulkan instance layer used by your program. 24 | 25 | - *layerName* - Name of the layer. It must be a string, e.g. `"VK_LAYER_KHRONOS_validation"`. 26 | 27 | **VKEFH_INSTANCE_FEATURE_STRUCT(SYMBOL structName, VkStructureType sType)** 28 | 29 | Defines Vulkan instance feature structure used by your program. 30 | 31 | - *structName* - Name of the structure, e.g. `VkValidationFeaturesEXT`. It must be a C++ symbol, not a string. 32 | - *sType* - Enum value that identfies type of this structure, to be passed in its `sType` member. It must match the structure as defined in Vulkan specification. 33 | 34 | **VKEFH_DEVICE_EXTENSION(const char\* extensionName)** 35 | 36 | Defines Vulkan device extension used by your program. 37 | 38 | - *extensionName* - Name of the extension. It must be a string. It may be a literal string, e.g. `"VK_KHR_swapchain"`, or appropriate constant as defined in Vulkan headers, e.g. `VK_KHR_SWAPCHAIN_EXTENSION_NAME`. 39 | 40 | **VKEFH_DEVICE_FEATURE_STRUCT(SYMBOL structName, VkStructureType sType)** 41 | 42 | Defines Vulkan device feature structure used by your program. 43 | 44 | - *structName* - Name of the structure, e.g. `VkPhysicalDeviceMemoryPriorityFeaturesEXT`. It must be a C++ symbol, not a string. 45 | - *sType* - Enum value that identfies type of this structure, to be passed in its `sType` member, e.g. `VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT`. It must match the structure as defined in Vulkan specification. 46 | 47 | ## Library API 48 | 49 | All members of the library are defined inside namespace `VKEFH`, assumed implicitly from now on. There are 2 classes provided for direct use. They use inheritance from a base class, but this is not important and so not described here. 50 | 51 | Members described below are sorted roughly by the order in which you should call them, not alphabetically. 52 | 53 | ### class InstanceInitHelp 54 | 55 | Provides help in creation of `VkInstance` object. You should create only one object of this type and use it for filling structure `VkInstanceCreateInfo`. 56 | 57 | **InstanceInitHelp()** 58 | 59 | Default constructor. Initializes the object to an empty state. 60 | 61 | **Reset()** 62 | 63 | Resets the object back to its empty state, like after creation. 64 | 65 | **VkResult EnumerateLayers()** 66 | 67 | Uses `vkEnumerateInstanceLayerProperties` to inspect the list of available instance layers. You must call it once at the beginning. 68 | 69 | **VkResult EnumerateExtensions()** 70 | 71 | Uses `vkEnumerateInstanceExtensionProperties` to inspect the list of available instance extensions. You must call it once at the beginning. 72 | 73 | **bool IsExtensionSupported(const char\* extensionName) const** 74 | 75 | Returns `true` if given extension was found available in the current system. You can call it only after `EnumerateExtensions`. You can call it only for instance extensions specified in the definition file. 76 | 77 | **bool IsExtensionEnabled(const char\* extensionName) const** 78 | 79 | Returns `true` if given extension is enabled. Extensions are enabled by default when they are supported. You can call it only after `EnumerateExtensions`. You can call it only for instance extensions specified in the definition file. 80 | 81 | **bool EnableExtension(const char\* extensionName, bool enabled)** 82 | 83 | Enables or disabled given extension. Returns `true` if `enabled == true` and the extension is supported so it can be enabled. For `enabled == false` always returns `false`. You can call it only after `EnumerateExtensions`. You can call it only for instance extensions specified in the definition file. 84 | 85 | **void EnableAllExtensions(bool enabled)** 86 | 87 | Enables or disables all supported extensions. You can call it only after `EnumerateExtensions`. 88 | 89 | **bool IsLayerSupported(const char\* layerName) const** 90 | 91 | Returns `true` if given layer was found available in the current system. You can call it only after `EnumerateLayers`. You can call it only for instance layers specified in the definition file. 92 | 93 | **bool IsLayerEnabled(const char\* layerName) const** 94 | 95 | Returns `true` if given layer is enabled. Layers are enabled by default when they are supported. You can call it only after `EnumerateLayers`. You can call it only for instance layers specified in the definition file. 96 | 97 | **bool EnableLayer(const char\* layerName, bool enabled)** 98 | 99 | Enables or disabled given layer. Returns `true` if `enabled == true` and the layer is supported so it can be enabled. For `enabled == false` always returns `false`. You can call it only after `EnumerateLayers`. You can call it only for instance layers specified in the definition file. 100 | 101 | **void EnableAllLayers(bool enabled)** 102 | 103 | Enables or disables all supported layers. You can call it only after `EnumerateLayers`. 104 | 105 | **bool IsFeatureStructEnabled(const char\* structName) const** 106 | 107 | Returns `true` if given feature structure, identified by its name passed as a string, is enabled. All feature structures are enabled by default. You can call it only for instance feature structures specified in the definition file. 108 | 109 | **void EnableFeatureStruct(const char\* structName, bool enabled)** 110 | 111 | Enables or disabled given feature structure, identified by its name passed as a string, to control whether it should be added to `VkInstanceCreateInfo::pNext` chain. You can call it only for instance feature structures specified in the definition file. 112 | 113 | **bool IsFeatureStructEnabled(VkStructureType sType) const** 114 | 115 | Returns `true` if given feature structure, identified by Vulkan structure type enum, is enabled. All feature structures are enabled by default. You can call it only for instance feature structures specified in the definition file. 116 | 117 | **void EnableFeatureStruct(VkStructureType sType, bool enabled)** 118 | 119 | Enables or disabled given feature structure, identified by Vulkan structure type enum, to control whether it should be added to `VkInstanceCreateInfo::pNext` chain. You can call it only for instance feature structures specified in the definition file. 120 | 121 | **void EnableAllFeatureStructs(bool enabled)** 122 | 123 | Enables or disables all instance feature structures specified in the definition file. 124 | 125 | **STRUCT_NAME& Get##STRUCT_NAME()**
126 | **const STRUCT_NAME& Get##STRUCT_NAME() const** 127 | 128 | Returns reference to a given feature structure, so it can be altered before it is passed as `VkInstanceCreateInfo::pNext`. You shall only disable features, not enable those that are `VK_FALSE` already and so not supported. 129 | 130 | **void PrepareCreation()** 131 | 132 | Prepares internal data needed for creation of `VkInstance` object. You can call it only after `EnumerateExtensions` and `EnumerateLayers`. You should enable/disable extensions, layers, and feature structures as required before this call. 133 | 134 | **const void\* GetFeaturesChain() const** 135 | 136 | Returns parameter to be passed as `VkInstanceCreateInfo::pNext`. It must be called after `PrepareCreation`. Returned pointer is valid only as long as this object remains alive and unchanged. 137 | 138 | **uint32_t GetEnabledExtensionCount() const** 139 | 140 | Returns parameter to be passed as `VkInstanceCreateInfo::enabledExtensionsCount`. It must be called after `PrepareCreation`. 141 | 142 | **const char\* const\* GetEnabledExtensionNames() const** 143 | 144 | Returns parameter to be passed as `VkInstanceCreateInfo::enabledExtensionNames`. It must be called after `PrepareCreation`. Returned pointer is valid only as long as this object remains alive and unchanged. 145 | 146 | **uint32_t GetEnabledLayerCount() const** 147 | 148 | Returns parameter to be passed as `VkInstanceCreateInfo::enabledLayerCount`. It must be called after `PrepareCreation`. 149 | 150 | **const char\* const\* GetEnabledLayerNames() const** 151 | 152 | Returns parameter to be passed as `VkInstanceCreateInfo::enabledLayerNames`. It must be called after `PrepareCreation`. Returned pointer is valid only as long as this object remains alive and unchanged. 153 | 154 | ### class DeviceInitHelp 155 | 156 | Provides help in creation of `VkDevice` object. You should use it for filling structure `VkDeviceCreateInfo`. 157 | 158 | **DeviceInitHelp()** 159 | 160 | Default constructor. Initializes the object to an empty state. 161 | 162 | **Reset()** 163 | 164 | Resets the object back to its empty state, like after creation. 165 | 166 | **void GetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice)** 167 | 168 | Uses `vkGetPhysicalDeviceFeatures2` to inspect the list of available device features. You must call it once at the beginning. 169 | 170 | **VkResult EnumerateExtensions(VkPhysicalDevice physicalDevice)** 171 | 172 | Uses `vkEnumerateDeviceExtensionProperties` to inspect the list of available device extensions. You must call it once at the beginning. 173 | 174 | **bool IsExtensionSupported(const char\* extensionName) const** 175 | 176 | Returns `true` if given extension was found available in the current system. You can call it only after `EnumerateExtensions`. You can call it only for device extensions specified in the definition file. 177 | 178 | **bool IsExtensionEnabled(const char\* extensionName) const** 179 | 180 | Returns `true` if given extension is enabled. Extensions are enabled by default when they are supported. You can call it only after `EnumerateExtensions`. You can call it only for device extensions specified in the definition file. 181 | 182 | **bool EnableExtension(const char\* extensionName, bool enabled)** 183 | 184 | Enables or disabled given extension. Returns `true` if `enabled == true` and the extension is supported so it can be enabled. For `enabled == false` always returns `false`. You can call it only after `EnumerateExtensions`. You can call it only for device extensions specified in the definition file. 185 | 186 | **void EnableAllExtensions(bool enabled)** 187 | 188 | Enables or disables all supported extensions. You can call it only after `EnumerateExtensions`. 189 | 190 | **bool IsFeatureStructEnabled(const char\* structName) const** 191 | 192 | Returns `true` if given feature structure, identified by its name passed as a string, is enabled. All feature structures are enabled by default. You can call it only for device feature structures specified in the definition file. 193 | 194 | **bool IsFeatureStructEnabled(VkStructureType sType) const** 195 | 196 | Returns `true` if given feature structure, identified by Vulkan structure type enum, is enabled. All feature structures are enabled by default. You can call it only for device feature structures specified in the definition file. 197 | 198 | **void EnableFeatureStruct(const char\* structName, bool enabled)** 199 | 200 | Enables or disabled given feature structure, identified by its name passed as a string, to control whether it should be added to `vkGetPhysicalDeviceFeatures2` (if called before `GetPhysicalDeviceFeatures`) or `VkDeviceCreateInfo::pNext` chain (if called before `PrepareCreation`). You can call it only for device feature structures specified in the definition file. 201 | 202 | **void EnableFeatureStruct(VkStructureType sType, bool enabled)** 203 | 204 | Enables or disabled given feature structure, identified by Vulkan structure type enum, to control whether it should be added to `vkGetPhysicalDeviceFeatures2` (if called before `GetPhysicalDeviceFeatures`) or `VkDeviceCreateInfo::pNext` chain (if called before `PrepareCreation`). You can call it only for device feature structures specified in the definition file. 205 | 206 | **void EnableAllFeatureStructs(bool enabled)** 207 | 208 | Enables or disables all device feature structures specified in the definition file. 209 | 210 | **STRUCT_NAME& Get##STRUCT_NAME()**
211 | **const STRUCT_NAME& Get##STRUCT_NAME() const** 212 | 213 | Returns reference to a given feature structure, so you can inspect it to see which specific features are supported, as well as alter it before it is passed as `VkDeviceCreateInfo::pNext`. You can call it only after `GetPhysicalDeviceFeatures`. You shall only disable features, not enable those that are `VK_FALSE` already and so not supported. 214 | 215 | **VkPhysicalDeviceFeatures& GetFeatures()**
216 | **const VkPhysicalDeviceFeatures& GetFeatures() const** 217 | 218 | Returns reference to the structure with switches for standard Vulkan features, so you can inspect it to see which features are supported, as well as alter it before it is passed to device creation. You can call it only after `GetPhysicalDeviceFeatures`. You shall only disable features, not enable those that are `VK_FALSE` already and so not supported. 219 | 220 | **void PrepareCreation()** 221 | 222 | Prepares internal data needed for creation of `VkDevice` object. You can call it only after `EnumerateExtensions` and `GetPhysicalDeviceFeatures`. You should enable/disable extensions, feature structures, and specific features as required before this call. 223 | 224 | **const void\* GetFeaturesChain() const** 225 | 226 | Returns parameter to be passed as `VkDeviceCreateInfo::pNext`. It must be called after `PrepareCreation`. Returned pointer is valid only as long as this object remains alive and unchanged. 227 | 228 | **uint32_t GetEnabledExtensionCount() const** 229 | 230 | Returns parameter to be passed as `VkDeviceCreateInfo::enabledExtensionsCount`. It must be called after `PrepareCreation`. 231 | 232 | **const char\* const\* GetEnabledExtensionNames() const** 233 | 234 | Returns parameter to be passed as `VkDeviceCreateInfo::enabledExtensionNames`. It must be called after `PrepareCreation`. Returned pointer is valid only as long as this object remains alive and unchanged. 235 | -------------------------------------------------------------------------------- /UserGuide.md: -------------------------------------------------------------------------------- 1 | # VkExtensionsFeaturesHelp User Guide 2 | 3 | ## Introduction 4 | 5 | [Vulkan](https://www.khronos.org/vulkan/) is a cross-platform, modern, graphics API used by many games and other interactive applications. Its API, defined in C, with the distinctive idea of supporting extensions and chaining structures using `pNext` pointer, is very smart and clean, but requires lots of boilerplate code. Every Vulkan application needs to create two main objects at the beginning: `VkInstance` and `VkDevice`. Nowadays, years after Vulkan first release, most applications have a list of additional extensions and features they need to enable. VkExtensionsFeaturesHelp library provides help with this task. 6 | 7 | This document provides step-by-step guide needed to setup and use the library. A more systematic description of its entire API can be found in the other document: **[Reference](Reference.md)**. See also main page: **[README](README.md)**. 8 | 9 | ## Prerequisites 10 | 11 | - Written in C++, using some basic features of C++11. 12 | - Depends only on standard C/C++ headers and Vulkan. 13 | - Standard headers like `` are included internally. 14 | - Vulkan header needs to be included by the user. 15 | - Should be compatible with various platforms, compilers, 32-bit as well as 64-bit (although it was developed and tested just on Windows using Visual Studio 2019, x64). 16 | - Errors are reported by returning `VkResult` from functions, like in Vulkan. 17 | - Correct use of the API is validated using `assert` (can be configured using a macro). 18 | - C++ exceptions and RTTI are not used. 19 | 20 | ## Setup 21 | 22 | The library is single-header - not in "STB-style", where implementation needs to be extracted in one CPP file using a special macro, but it is truly header-only, with all functions inline. To use the library: 23 | 24 | 1. Copy file **"VkExtensionsFeaturesHelp.hpp"** to your project. 25 | 2. In the same directory, create file **"VkExtensionsFeatures.inl"**. We will put macros with the list of extensions and features there. It is included by the library multiple times. From now on, we will call it *definition file*. 26 | 3. `#include "VkExtensionsFeaturesHelp.hpp"` in every CPP file where you need it, especially where you create your `VkInstance` and `VkDevice` objects. 27 | 28 | ## Instance creation 29 | 30 | Imagine we need to enable following instance extensions "VK_KHR_surface", "VK_KHR_win32_surface". We can additionally use "VK_EXT_debug_utils" if available. We also want to use instance layer "VK_LAYER_KHRONOS_validation" if available. 31 | 32 | Without help of this library, you would need to: call `vkEnumerateInstanceExtensionProperties` to get the list of supported extensions, call `vkEnumerateInstanceLayerProperties` to get the list of supported layers, then inspect these lists, decide what to enable, create your own list of extensions and layers to enable, and finally call `vkCreateInstance` with right parameters. 33 | 34 | ### Step 1 35 | 36 | Using this library, we start by defining required or supported extensions and layers in the definition file "VkExtensionsFeatures.inl" using appropriate macros: 37 | 38 | ``` 39 | VKEFH_INSTANCE_EXTENSION(VK_KHR_SURFACE_EXTENSION_NAME) 40 | VKEFH_INSTANCE_EXTENSION(VK_KHR_WIN32_SURFACE_EXTENSION_NAME) 41 | VKEFH_INSTANCE_EXTENSION(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) 42 | VKEFH_INSTANCE_LAYER("VK_LAYER_KHRONOS_validation") 43 | ``` 44 | 45 | You can also use other preprocessor macros there, like `#ifdef _WIN32` to enable some parts of the code depending on platform or project configuration. 46 | 47 | ### Step 2 48 | 49 | Next, inside C++ code, we need to create an object of type `InstanceInitHelp` and call appropriate methods to initialize it - to load data about supported extensions and layers from Vulkan: 50 | 51 | ```cpp 52 | VKEFH::InstanceInitHelp instInitHelp; 53 | VkResult res = instInitHelp.EnumerateExtensions(); 54 | // Handle error if res != VK_SUCCESS... 55 | res = instInitHelp.EnumerateLayers(); 56 | // Handle error if res != VK_SUCCESS... 57 | ``` 58 | 59 | ### Step 3 60 | 61 | By default, every extension and layer we listed will be enabled if available. If this is the expected behavior, you can skip this step. 62 | 63 | If some of them are required for your program to work, you can check their support like this: 64 | 65 | ```cpp 66 | bool requiredExtensionsSupported = 67 | instInitHelp.IsExtensionSupported(VK_KHR_SURFACE_EXTENSION_NAME) && 68 | instInitHelp.IsExtensionSupported(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); 69 | if(!requiredExtensionsSupported) 70 | // Handle error... 71 | ``` 72 | 73 | You can disable some of them so they won't be enabled while creating Vulkan instance despite they are supported. This may be useful e.g. if you have a set of extensions or layers that you want to enable as "all or nothing". Example: 74 | 75 | ```cpp 76 | bool debuggingSupported = 77 | instInitHelp.IsExtensionSupported(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) && 78 | instInitHelp.IsLayerSupported("VK_LAYER_KHRONOS_validation"); 79 | if(!debuggingSupported) 80 | { 81 | instInitHelp.EnableExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, false); 82 | instInitHelp.EnableLayer("VK_LAYER_KHRONOS_validation", false); 83 | } 84 | ``` 85 | 86 | You can also use opt-in approach when you don't want to enable anything unless explicitly requested. To do this: 87 | 88 | ```cpp 89 | instInitHelp.EnableAllExtensions(false); 90 | instInitHelp.EnableAllLayers(false); 91 | bool debugUtilsEnabled = instInitHelp.EnableExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, true); 92 | ``` 93 | 94 | Function `EnableExtension` enables the extension and returns `true` only if the extenion is supported. Function `EnableLayer` behaves the same way. Please note that you can use `EnableExtension`, `EnableLayer` only with the extensions/layers specified previously in the definition file. 95 | 96 | ### Step 4 97 | 98 | You need to call `PrepareCreation` method to proceed with next step. Then you can fetch data from the library object to help fill in the structure used to create Vulkan instance. 99 | 100 | ```cpp 101 | instInitHelp.PrepareCreation(); 102 | 103 | VkApplicationInfo appInfo = /* ... */; 104 | VkInstanceCreateInfo instCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; 105 | instCreateInfo.pNext = instInitHelp.GetFeaturesChain(); 106 | instCreateInfo.pApplicationInfo = &appInfo; 107 | instCreateInfo.enabledExtensionCount = instInitHelp.GetEnabledExtensionCount(); 108 | instCreateInfo.ppEnabledExtensionNames = instInitHelp.GetEnabledExtensionNames(); 109 | instCreateInfo.enabledLayerCount = instInitHelp.GetEnabledLayerCount(); 110 | instCreateInfo.ppEnabledLayerNames = instInitHelp.GetEnabledLayerNames(); 111 | 112 | VkInstance instance = nullptr; 113 | res = vkCreateInstance(&instCreateInfo, nullptr, &instance); 114 | ``` 115 | 116 | ## Device creation 117 | 118 | Second task this library can help with is the creation of `VkDevice` object. Usage of this feature is similar to what was described above. Imagine we need to enable device extension "VK_KHR_swapchain". We can also make use of extensions "VK_EXT_memory_budget" and "VK_EXT_memory_priority" if available. 119 | 120 | Without this library, we would need to call `vkEnumerateDeviceExtensionProperties` to get the list of supported extensions and `vkGetPhysicalDeviceFeatures2` to query for supported device features. Some extensions also come with a dedicated structure that describes parameters of the support for this particular feature, like `VkPhysicalDeviceMemoryPriorityFeaturesEXT`. Such structure needs to be chained to `VkPhysicalDeviceFeatures2::pNext`. We would then inspect the list of supported extensions and the values returned in the structures, decide which extensions and features to enable, assemble a new list of extensions, and pass them, together with the chain of custom feature structures, to `VkDeviceCreateInfo` to create the device object. By the way, there is also `VkPhysicalDeviceFeatures` structure with a long list of toggles to enable/disable standard Vulkan features. Handling this all would require a lot of boilerplate code. 121 | 122 | ### Step 1 123 | 124 | Using this library, we start by defining required or supported extensions and related structures in the definition file "VkExtensionsFeatures.inl" using appropriate macros: 125 | 126 | ``` 127 | VKEFH_DEVICE_EXTENSION(VK_KHR_SWAPCHAIN_EXTENSION_NAME) 128 | VKEFH_DEVICE_EXTENSION(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) 129 | VKEFH_DEVICE_EXTENSION(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME) 130 | VKEFH_DEVICE_FEATURE_STRUCT(VkPhysicalDeviceMemoryPriorityFeaturesEXT, 131 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT) 132 | ``` 133 | 134 | Structures describing standard features of new Vulkan versions: `VkPhysicalDeviceVulkan11Features` and `VkPhysicalDeviceVulkan12Features` can also be added this way, not only ones provided by extensions. 135 | 136 | ### Step 2 137 | 138 | Next, inside C++ code, we need to create an object of type `DeviceInitHelp` and call appropriate methods to initialize it - to load data about supported extensions and features from given `VkPhysicalDevice`: 139 | 140 | ```cpp 141 | VKEFH::DeviceInitHelp devInitHelp; 142 | devInitHelp.GetPhysicalDeviceFeatures(physicalDevice); 143 | VkResult res = instInitHelp.EnumerateExtensions(physicalDevice); 144 | // Handle error if res != VK_SUCCESS... 145 | ``` 146 | 147 | ### Step 3 148 | 149 | By default, every extension and feature we listed in the definition file will be enabled if supported. If this is the expected behavior, you can skip this step. 150 | 151 | If some of them are required for your program to work, you can check their support like this: 152 | 153 | ```cpp 154 | bool requiredExtensionsSupported = 155 | devInitHelp.IsExtensionSupported(VK_KHR_SWAPCHAIN_EXTENSION_NAME); 156 | if(!requiredExtensionsSupported) 157 | // Handle error... 158 | ``` 159 | 160 | You can inspect the content of `VkPhysicalDeviceFeatures` structure and alter it, e.g. disable some standard Vulkan features even if supported. This is especially important with `robustBufferAccess` - a feature that is usually not required while it may slow down your program. 161 | 162 | ```cpp 163 | VkPhysicalDeviceFeatures& devFeatures = devInitHelp.GetFeatures(); 164 | devFeatures.robustBufferAccess = VK_FALSE; 165 | ``` 166 | 167 | You can inspect the content of additional feature structures specified in the definition file in a similar way - get references to them by calling auto-generated methods `Get` + structure name, e.g.: 168 | 169 | ```cpp 170 | bool memoryPrioritySupported = 171 | devInitHelp.IsExtensionSupported(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME) && 172 | devInitHelp.GetVkPhysicalDeviceMemoryPriorityFeaturesEXT().memoryPriority; 173 | ``` 174 | 175 | You can then enable and disable extensions, as well as entire structures. Disabled structures won't be chanined in `pNext` while creating device object. 176 | 177 | ```cpp 178 | devInitHelp.EnableExtension(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME, memoryPrioritySupported); 179 | devInitHelp.EnableFeatureStruct("VkPhysicalDeviceMemoryPriorityFeaturesEXT", memoryPrioritySupported); 180 | ``` 181 | 182 | You can also alter the structures. For example, if you specify `VkPhysicalDeviceBufferDeviceAddressFeatures` structure in the definition file, you can use only 1 out of 3 features offered there and disable the other 2 even if supported: 183 | 184 | ```cpp 185 | VkPhysicalDeviceBufferDeviceAddressFeatures& bufDevAddressFeatures = 186 | devInitHelp.GetVkPhysicalDeviceBufferDeviceAddressFeatures(); 187 | bool bufDevAddressSupported = bufDevAddressFeatures.bufferDeviceAddress != VK_FALSE; 188 | bufDevAddressFeatures.bufferDeviceAddressCaptureReplay = VK_FALSE; 189 | bufDevAddressFeatures.bufferDeviceAddressMultiDevice = VK_FALSE; 190 | ``` 191 | 192 | Just like with instance creation, there is a possibility to opt-in to only the extensions and structures you explicitly enable: 193 | 194 | ```cpp 195 | devInitHelp.EnableAllExtensions(false); 196 | devInitHelp.EnableAllFeatureStructs(false); 197 | if(memoryPrioritySupported) 198 | { 199 | devInitHelp.EnableExtension(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME, true); 200 | devInitHelp.EnableFeatureStruct("VkPhysicalDeviceMemoryPriorityFeaturesEXT", true); 201 | } 202 | ``` 203 | 204 | ### Step 4 205 | 206 | You need to call `PrepareCreation` method to proceed with next step. Then you can fetch data from the library object to help fill in the structure used to create Vulkan device. 207 | 208 | ```cpp 209 | devInitHelp.PrepareCreation(); 210 | 211 | VkDeviceCreateInfo devCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; 212 | devCreateInfo.pNext = devInitHelp.GetFeaturesChain(); 213 | devCreateInfo.enabledExtensionCount = devInitHelp.GetEnabledExtensionCount(); 214 | devCreateInfo.ppEnabledExtensionNames = devInitHelp.GetEnabledExtensionNames(); 215 | devCreateInfo.queueCreateInfoCount = /* ... */; 216 | devCreateInfo.pQueueCreateInfos = /* ... */; 217 | 218 | VkDevice device = nullptr; 219 | VkResult res = vkCreateDevice(physicalDevice, &devCreateInfo, nullptr, &device); 220 | ``` 221 | 222 | Note we don't assign `devCreateInfo.pEnabledFeatures`. Instead, `VkPhysicalDeviceFeatures` structure is automatically passed in form of `VkPhysicalDeviceFeatures2` as part of the `devCreateInfo.pNext` chain - a method recommended in modern Vulkan versions. 223 | 224 | Instance creation supports feature structures (e.g. `VkValidationFeaturesEXT`) just like device creation, despite not shown in this document. To use them, put macro `VKEFH_INSTANCE_FEATURE_STRUCT` in the definition file and call related methods on class `InstanceInitHelp` same way as on class `DeviceInitHelp`, e.g. `IsFeatureStructEnabled`, `EnableFeatureStruct`, `EnableAllFeatureStructs`, and `Get` + structure name to get reference to its content. 225 | 226 | ## Features not supported 227 | 228 | Following features are left out of scope of this library, at least for now: 229 | 230 | - Calling `vkEnumerateInstanceExtensionProperties` and `vkEnumerateDeviceExtensionProperties` with `layerName != null`. Is is useful? 231 | - Inspecting `VkLayerProperties` or `VkExtensionProperties` to check supported version of a layer/extension. Is it useful? 232 | - Device layers. These are deprecated and ignored in modern Vulkan versions. Only instance object supports layers, not device object. 233 | -------------------------------------------------------------------------------- /VkExtensionsFeaturesHelp.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | VkExtensionsFeaturesHelp - Small header-only C++ library that helps to initialize Vulkan instance and device object 3 | 4 | Author: Adam Sawicki - https://asawicki.info - adam__DELETE__@asawicki.info 5 | Version: 1.1.0, 2021-04-08 6 | License: MIT 7 | 8 | Documentation: see README.md and other .md files in the repository or online on GitHub: 9 | https://github.com/sawickiap/VkExtensionsFeaturesHelp 10 | 11 | # Version history 12 | 13 | Version 1.1.0, 2021-04-08 14 | 15 | - Added macro VKEFH_ASSERT that can be defined before including this file to use 16 | custom assert. (Thanks Kamil Nowakowski for the suggestion!) 17 | 18 | Version 1.0.0, 2021-03-28 19 | 20 | - First version. 21 | 22 | # License 23 | 24 | Copyright 2021 Adam Sawicki 25 | 26 | Permission is hereby granted, free of charge, to any person obtaining a copy of 27 | this software and associated documentation files (the "Software"), to deal in 28 | the Software without restriction, including without limitation the rights to 29 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 30 | of the Software, and to permit persons to whom the Software is furnished to do 31 | so, subject to the following conditions: 32 | 33 | The above copyright notice and this permission notice shall be included in all 34 | copies or substantial portions of the Software. 35 | 36 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 37 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 38 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 39 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 40 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 41 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 42 | SOFTWARE. 43 | */ 44 | #pragma once 45 | 46 | #include 47 | #include 48 | #include 49 | 50 | // Define this macro before including this file to use your own assert. 51 | #ifndef VKEFH_ASSERT 52 | #include 53 | #define VKEFH_ASSERT(expr) assert(expr) 54 | #endif 55 | 56 | namespace VKEFH 57 | { 58 | 59 | class EnabledItemVector 60 | { 61 | public: 62 | struct EnabledItem 63 | { 64 | const char* m_Name; 65 | bool m_Supported, m_Enabled; 66 | }; 67 | std::vector m_Items; 68 | std::vector m_EnabledItemNames; 69 | 70 | void Reset() 71 | { 72 | for(size_t i = 0, count = m_Items.size(); i < count; ++i) 73 | { 74 | m_Items[i].m_Supported = false; 75 | m_Items[i].m_Enabled = false; 76 | } 77 | m_EnabledItemNames.clear(); 78 | } 79 | 80 | bool IsSupported(const char* name) const 81 | { 82 | size_t index = Find(name); 83 | if(index != SIZE_MAX) 84 | return m_Items[index].m_Supported; 85 | VKEFH_ASSERT(0 && "You can query only for items specified in VkExtensionsFeatures.inl."); 86 | return false; 87 | } 88 | bool IsEnabled(const char* name) const 89 | { 90 | size_t index = Find(name); 91 | if(index != SIZE_MAX) 92 | return m_Items[index].m_Enabled; 93 | VKEFH_ASSERT(0 && "You can query only for items specified in VkExtensionsFeatures.inl."); 94 | return false; 95 | } 96 | bool Enable(const char* name, bool enabled) 97 | { 98 | size_t index = Find(name); 99 | if(index != SIZE_MAX) 100 | { 101 | if(enabled && m_Items[index].m_Supported) 102 | { 103 | m_Items[index].m_Enabled = true; 104 | return true; 105 | } 106 | m_Items[index].m_Enabled = false; 107 | return false; 108 | } 109 | VKEFH_ASSERT(0 && "You can enable only for items specified in VkExtensionsFeatures.inl."); 110 | return false; 111 | } 112 | void EnableAll(bool enabled) 113 | { 114 | for(size_t i = 0, count = m_Items.size(); i < count; ++i) 115 | { 116 | m_Items[i].m_Enabled = enabled && m_Items[i].m_Supported; 117 | } 118 | } 119 | void PrepareEnabled() 120 | { 121 | m_EnabledItemNames.clear(); 122 | for(size_t i = 0, count = m_Items.size(); i < count; ++i) 123 | { 124 | if(m_Items[i].m_Enabled) 125 | { 126 | VKEFH_ASSERT(m_Items[i].m_Supported); 127 | m_EnabledItemNames.push_back(m_Items[i].m_Name); 128 | } 129 | } 130 | } 131 | 132 | private: 133 | size_t Find(const char* name) const 134 | { 135 | for(size_t i = 0, count = m_Items.size(); i < count; ++i) 136 | { 137 | if(strcmp(name, m_Items[i].m_Name) == 0) 138 | { 139 | return i; 140 | } 141 | } 142 | return SIZE_MAX; 143 | } 144 | }; 145 | 146 | class InitHelpBase 147 | { 148 | public: 149 | InitHelpBase(const InitHelpBase&) = delete; 150 | InitHelpBase(InitHelpBase&&) = delete; 151 | InitHelpBase& operator=(const InitHelpBase&) = delete; 152 | InitHelpBase& operator=(InitHelpBase&&) = delete; 153 | 154 | bool IsExtensionSupported(const char* extensionName) const 155 | { 156 | VKEFH_ASSERT(m_ExtensionsEnumerated && "You should call EnumerateExtensions first."); 157 | return m_Extensions.IsSupported(extensionName); 158 | } 159 | bool IsExtensionEnabled(const char* extensionName) const 160 | { 161 | VKEFH_ASSERT(m_ExtensionsEnumerated && "You should call EnumerateExtensions first."); 162 | return m_Extensions.IsEnabled(extensionName); 163 | } 164 | bool EnableExtension(const char* extensionName, bool enabled) 165 | { 166 | VKEFH_ASSERT(m_ExtensionsEnumerated && "You should call EnumerateExtensions first."); 167 | return m_Extensions.Enable(extensionName, enabled); 168 | } 169 | void EnableAllExtensions(bool enabled) 170 | { 171 | VKEFH_ASSERT(m_ExtensionsEnumerated && "You should call EnumerateExtensions first."); 172 | m_Extensions.EnableAll(enabled); 173 | } 174 | 175 | uint32_t GetEnabledExtensionCount() const 176 | { 177 | VKEFH_ASSERT(m_CreationPrepared && "You need to call PrepareCreation first."); 178 | return (uint32_t)m_Extensions.m_EnabledItemNames.size(); 179 | } 180 | const char* const* GetEnabledExtensionNames() const 181 | { 182 | VKEFH_ASSERT(m_CreationPrepared && "You need to call PrepareCreation first."); 183 | return !m_Extensions.m_EnabledItemNames.empty() ? m_Extensions.m_EnabledItemNames.data() : nullptr; 184 | } 185 | 186 | bool IsFeatureStructEnabled(const char* structName) const 187 | { 188 | const size_t index = FindFeatureStruct(structName); 189 | if(index != SIZE_MAX) 190 | { 191 | return m_FeatureStructs[index].m_Enabled; 192 | } 193 | VKEFH_ASSERT(0 && "You can query only for feature structs specified in VkExtensionsFeatures.inl."); 194 | return false; 195 | } 196 | void EnableFeatureStruct(const char* structName, bool enabled) 197 | { 198 | const size_t index = FindFeatureStruct(structName); 199 | if(index != SIZE_MAX) 200 | { 201 | m_FeatureStructs[index].m_Enabled = enabled; 202 | return; 203 | } 204 | VKEFH_ASSERT(0 && "You can enable only feature structs specified in VkExtensionsFeatures.inl."); 205 | } 206 | 207 | bool IsFeatureStructEnabled(VkStructureType sType) const 208 | { 209 | const size_t index = FindFeatureStruct(sType); 210 | if(index != SIZE_MAX) 211 | { 212 | return m_FeatureStructs[index].m_Enabled; 213 | } 214 | VKEFH_ASSERT(0 && "You can query only for feature structs specified in VkExtensionsFeatures.inl."); 215 | return false; 216 | } 217 | void EnableFeatureStruct(VkStructureType sType, bool enabled) 218 | { 219 | const size_t index = FindFeatureStruct(sType); 220 | if(index != SIZE_MAX) 221 | { 222 | m_FeatureStructs[index].m_Enabled = enabled; 223 | return; 224 | } 225 | VKEFH_ASSERT(0 && "You can enable only feature structs specified in VkExtensionsFeatures.inl."); 226 | } 227 | 228 | void EnableAllFeatureStructs(bool enabled) 229 | { 230 | for(size_t i = 0, count = m_FeatureStructs.size(); i < count; ++i) 231 | { 232 | m_FeatureStructs[i].m_Enabled = enabled; 233 | } 234 | } 235 | 236 | protected: 237 | bool m_ExtensionsEnumerated = false; 238 | bool m_CreationPrepared = false; 239 | EnabledItemVector m_Extensions; 240 | 241 | struct FeatureStruct 242 | { 243 | const char* m_Name; 244 | VkStructureType m_sType; 245 | VkBaseInStructure* m_StructPtr; 246 | bool m_Enabled; 247 | }; 248 | std::vector m_FeatureStructs; 249 | 250 | InitHelpBase() { } 251 | 252 | void Reset() 253 | { 254 | m_ExtensionsEnumerated = false; 255 | m_CreationPrepared = false; 256 | m_Extensions.Reset(); 257 | for(size_t i = 0, count = m_FeatureStructs.size(); i < count; ++i) 258 | m_FeatureStructs[i].m_Enabled = true; 259 | } 260 | 261 | void LoadExtensions(const VkExtensionProperties* extProps, size_t extPropCount) 262 | { 263 | VKEFH_ASSERT(!m_ExtensionsEnumerated && "You should call EnumerateExtensions only once."); 264 | for(size_t extPropIndex = 0; extPropIndex < extPropCount; ++extPropIndex) 265 | { 266 | for(size_t extIndex = 0, extCount = m_Extensions.m_Items.size(); extIndex < extCount; ++extIndex) 267 | { 268 | if(strcmp(extProps[extPropIndex].extensionName, m_Extensions.m_Items[extIndex].m_Name) == 0) 269 | { 270 | m_Extensions.m_Items[extIndex].m_Supported = true; 271 | m_Extensions.m_Items[extIndex].m_Enabled = true; 272 | } 273 | } 274 | } 275 | m_ExtensionsEnumerated = true; 276 | } 277 | 278 | void PrepareEnabledExtensionNames() 279 | { 280 | VKEFH_ASSERT(m_ExtensionsEnumerated && "You should call EnumerateExtensions first."); 281 | m_Extensions.PrepareEnabled(); 282 | } 283 | 284 | private: 285 | size_t FindFeatureStruct(const char* name) const 286 | { 287 | for(size_t i = 0, count = m_FeatureStructs.size(); i < count; ++i) 288 | { 289 | if(strcmp(m_FeatureStructs[i].m_Name, name) == 0) 290 | { 291 | return i; 292 | } 293 | } 294 | return SIZE_MAX; 295 | } 296 | size_t FindFeatureStruct(VkStructureType sType) const 297 | { 298 | for(size_t i = 0, count = m_FeatureStructs.size(); i < count; ++i) 299 | { 300 | if(m_FeatureStructs[i].m_sType == sType) 301 | { 302 | return i; 303 | } 304 | } 305 | return SIZE_MAX; 306 | } 307 | }; 308 | 309 | class InstanceInitHelp : public InitHelpBase 310 | { 311 | InstanceInitHelp(const InstanceInitHelp&) = delete; 312 | InstanceInitHelp(InstanceInitHelp&&) = delete; 313 | InstanceInitHelp& operator=(const InstanceInitHelp&) = delete; 314 | InstanceInitHelp& operator=(InstanceInitHelp&&) = delete; 315 | 316 | #define VKEFH_INSTANCE_EXTENSION(extensionName) 317 | #define VKEFH_INSTANCE_LAYER(layerName) 318 | #define VKEFH_INSTANCE_FEATURE_STRUCT(structName, sType) \ 319 | private: structName m_##structName = { (sType) }; \ 320 | public: structName& Get##structName() { return m_##structName; } \ 321 | public: const structName& Get##structName() const { return m_##structName; } 322 | #define VKEFH_DEVICE_EXTENSION(extensionName) 323 | #define VKEFH_DEVICE_FEATURE_STRUCT(structName, sType) 324 | 325 | #include "VkExtensionsFeatures.inl" 326 | 327 | #undef VKEFH_INSTANCE_EXTENSION 328 | #undef VKEFH_INSTANCE_LAYER 329 | #undef VKEFH_INSTANCE_FEATURE_STRUCT 330 | #undef VKEFH_DEVICE_EXTENSION 331 | #undef VKEFH_DEVICE_FEATURE_STRUCT 332 | 333 | public: 334 | InstanceInitHelp() 335 | { 336 | #define VKEFH_INSTANCE_EXTENSION(extensionName) m_Extensions.m_Items.push_back({(extensionName), false, false}); 337 | #define VKEFH_INSTANCE_LAYER(layerName) m_Layers.m_Items.push_back({(layerName), false, false}); 338 | #define VKEFH_INSTANCE_FEATURE_STRUCT(structName, sType) m_FeatureStructs.push_back({(#structName), (sType), (VkBaseInStructure*)(&m_##structName), true}); 339 | #define VKEFH_DEVICE_EXTENSION(extensionName) 340 | #define VKEFH_DEVICE_FEATURE_STRUCT(structName, sType) 341 | 342 | #include "VkExtensionsFeatures.inl" 343 | 344 | #undef VKEFH_INSTANCE_EXTENSION 345 | #undef VKEFH_INSTANCE_LAYER 346 | #undef VKEFH_INSTANCE_FEATURE_STRUCT 347 | #undef VKEFH_DEVICE_EXTENSION 348 | #undef VKEFH_DEVICE_FEATURE_STRUCT 349 | } 350 | 351 | void Reset() 352 | { 353 | InitHelpBase::Reset(); 354 | m_LayersEnumerated = false; 355 | m_Layers.Reset(); 356 | m_FeaturesChain = nullptr; 357 | } 358 | 359 | VkResult EnumerateExtensions() 360 | { 361 | uint32_t extPropCount = 0; 362 | VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extPropCount, nullptr); 363 | if(res != VK_SUCCESS) 364 | return res; 365 | if(extPropCount) 366 | { 367 | std::vector extProps(extPropCount); 368 | res = vkEnumerateInstanceExtensionProperties(nullptr, &extPropCount, extProps.data()); 369 | if(res != VK_SUCCESS) 370 | return res; 371 | LoadExtensions(extProps.data(), extPropCount); 372 | } 373 | return VK_SUCCESS; 374 | } 375 | 376 | VkResult EnumerateLayers() 377 | { 378 | VKEFH_ASSERT(!m_LayersEnumerated && "You should call EnumerateLayers only once."); 379 | uint32_t layerPropCount = 0; 380 | VkResult res = vkEnumerateInstanceLayerProperties(&layerPropCount, nullptr); 381 | if(res != VK_SUCCESS) 382 | return res; 383 | if(layerPropCount) 384 | { 385 | std::vector layerProps(layerPropCount); 386 | res = vkEnumerateInstanceLayerProperties(&layerPropCount, layerProps.data()); 387 | if(res != VK_SUCCESS) 388 | return res; 389 | LoadLayers(layerProps.data(), layerPropCount); 390 | } 391 | m_LayersEnumerated = true; 392 | return VK_SUCCESS; 393 | } 394 | 395 | bool IsLayerSupported(const char* layerName) const 396 | { 397 | VKEFH_ASSERT(m_LayersEnumerated && "You should call EnumerateLayers first."); 398 | return m_Layers.IsSupported(layerName); 399 | } 400 | bool IsLayerEnabled(const char* layerName) const 401 | { 402 | VKEFH_ASSERT(m_LayersEnumerated && "You should call EnumerateLayers first."); 403 | return m_Layers.IsEnabled(layerName); 404 | } 405 | bool EnableLayer(const char* layerName, bool enabled) 406 | { 407 | VKEFH_ASSERT(m_LayersEnumerated && "You should call EnumerateLayers first."); 408 | return m_Layers.Enable(layerName, enabled); 409 | } 410 | void EnableAllLayers(bool enabled) 411 | { 412 | VKEFH_ASSERT(m_LayersEnumerated && "You should call EnumerateLayers first."); 413 | m_Layers.EnableAll(enabled); 414 | } 415 | 416 | void PrepareCreation() 417 | { 418 | VKEFH_ASSERT(m_LayersEnumerated && "You should call EnumerateLayers first."); 419 | 420 | PrepareEnabledExtensionNames(); 421 | 422 | m_Layers.PrepareEnabled(); 423 | 424 | m_FeaturesChain = nullptr; 425 | for(size_t structIndex = 0, structCount = m_FeatureStructs.size(); structIndex < structCount; ++structIndex) 426 | { 427 | if(m_FeatureStructs[structIndex].m_Enabled) 428 | { 429 | VKEFH_ASSERT(m_FeatureStructs[structIndex].m_StructPtr->sType == m_FeatureStructs[structIndex].m_sType); 430 | m_FeatureStructs[structIndex].m_StructPtr->pNext = m_FeaturesChain; 431 | m_FeaturesChain = m_FeatureStructs[structIndex].m_StructPtr; 432 | } 433 | } 434 | 435 | m_CreationPrepared = true; 436 | } 437 | 438 | uint32_t GetEnabledLayerCount() const 439 | { 440 | VKEFH_ASSERT(m_CreationPrepared && "You need to call PrepareCreation first."); 441 | return (uint32_t)m_Layers.m_EnabledItemNames.size(); 442 | } 443 | const char* const* GetEnabledLayerNames() const 444 | { 445 | VKEFH_ASSERT(m_CreationPrepared && "You need to call PrepareCreation first."); 446 | return !m_Layers.m_EnabledItemNames.empty() ? m_Layers.m_EnabledItemNames.data() : nullptr; 447 | } 448 | const void* GetFeaturesChain() const 449 | { 450 | VKEFH_ASSERT(m_CreationPrepared && "You need to call PrepareCreation first."); 451 | return m_FeaturesChain; 452 | } 453 | 454 | private: 455 | bool m_LayersEnumerated = false; 456 | EnabledItemVector m_Layers; 457 | VkBaseInStructure* m_FeaturesChain = nullptr; 458 | 459 | void LoadLayers(const VkLayerProperties* layerProps, size_t layerPropCount) 460 | { 461 | for(size_t layerPropIndex = 0; layerPropIndex < layerPropCount; ++layerPropIndex) 462 | { 463 | for(size_t layerIndex = 0, layerCount = m_Layers.m_Items.size(); layerIndex < layerCount; ++layerIndex) 464 | { 465 | if(strcmp(layerProps[layerPropIndex].layerName, m_Layers.m_Items[layerIndex].m_Name) == 0) 466 | { 467 | m_Layers.m_Items[layerIndex].m_Supported = true; 468 | m_Layers.m_Items[layerIndex].m_Enabled = true; 469 | } 470 | } 471 | } 472 | } 473 | }; 474 | 475 | class DeviceInitHelp : public InitHelpBase 476 | { 477 | DeviceInitHelp(const DeviceInitHelp&) = delete; 478 | DeviceInitHelp(DeviceInitHelp&&) = delete; 479 | DeviceInitHelp& operator=(const DeviceInitHelp&) = delete; 480 | DeviceInitHelp& operator=(DeviceInitHelp&&) = delete; 481 | 482 | #define VKEFH_INSTANCE_EXTENSION(extensionName) 483 | #define VKEFH_INSTANCE_LAYER(layerName) 484 | #define VKEFH_INSTANCE_FEATURE_STRUCT(structName, sType) 485 | #define VKEFH_DEVICE_EXTENSION(extensionName) 486 | #define VKEFH_DEVICE_FEATURE_STRUCT(structName, sType) \ 487 | private: structName m_##structName = { (sType) }; \ 488 | public: structName& Get##structName() \ 489 | { \ 490 | VKEFH_ASSERT(m_PhysicalDeviceFeaturesQueried && "You need to call GetPhysicalDeviceFeatures first."); \ 491 | return m_##structName; \ 492 | } \ 493 | public: const structName& Get##structName() const \ 494 | { \ 495 | VKEFH_ASSERT(m_PhysicalDeviceFeaturesQueried && "You need to call GetPhysicalDeviceFeatures first."); \ 496 | return m_##structName; \ 497 | } 498 | 499 | #include "VkExtensionsFeatures.inl" 500 | 501 | #undef VKEFH_INSTANCE_EXTENSION 502 | #undef VKEFH_INSTANCE_LAYER 503 | #undef VKEFH_INSTANCE_FEATURE_STRUCT 504 | #undef VKEFH_DEVICE_EXTENSION 505 | #undef VKEFH_DEVICE_FEATURE_STRUCT 506 | 507 | public: 508 | DeviceInitHelp() 509 | { 510 | #define VKEFH_INSTANCE_EXTENSION(extensionName) 511 | #define VKEFH_INSTANCE_LAYER(layerName) 512 | #define VKEFH_INSTANCE_FEATURE_STRUCT(structName, sType) 513 | #define VKEFH_DEVICE_EXTENSION(extensionName) m_Extensions.m_Items.push_back({(extensionName), false, false}); 514 | #define VKEFH_DEVICE_FEATURE_STRUCT(structName, sType) m_FeatureStructs.push_back({(#structName), (sType), (VkBaseInStructure*)(&m_##structName), true}); 515 | 516 | #include "VkExtensionsFeatures.inl" 517 | 518 | #undef VKEFH_INSTANCE_EXTENSION 519 | #undef VKEFH_INSTANCE_LAYER 520 | #undef VKEFH_INSTANCE_FEATURE_STRUCT 521 | #undef VKEFH_DEVICE_EXTENSION 522 | #undef VKEFH_DEVICE_FEATURE_STRUCT 523 | } 524 | 525 | void Reset() 526 | { 527 | InitHelpBase::Reset(); 528 | m_PhysicalDeviceFeaturesQueried = false; 529 | m_Features2 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 }; 530 | } 531 | 532 | VkResult EnumerateExtensions(VkPhysicalDevice physicalDevice) 533 | { 534 | VKEFH_ASSERT(physicalDevice); 535 | uint32_t extPropCount = 0; 536 | VkResult res = vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extPropCount, nullptr); 537 | if(res != VK_SUCCESS) 538 | { 539 | return res; 540 | } 541 | std::vector extProps{extPropCount}; 542 | if(extPropCount) 543 | { 544 | res = vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extPropCount, extProps.data()); 545 | if(res != VK_SUCCESS) 546 | { 547 | return res; 548 | } 549 | LoadExtensions(extProps.data(), extPropCount); 550 | } 551 | return VK_SUCCESS; 552 | } 553 | 554 | void GetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice) 555 | { 556 | VKEFH_ASSERT(physicalDevice); 557 | VKEFH_ASSERT(!m_PhysicalDeviceFeaturesQueried && "You should call GetPhysicalDeviceFeatures only once."); 558 | 559 | VKEFH_ASSERT(m_Features2.sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2); 560 | m_Features2.pNext = nullptr; 561 | 562 | for(size_t structIndex = 0, structCount = m_FeatureStructs.size(); structIndex < structCount; ++structIndex) 563 | { 564 | if(m_FeatureStructs[structIndex].m_Enabled) 565 | { 566 | VKEFH_ASSERT(m_FeatureStructs[structIndex].m_StructPtr->sType == m_FeatureStructs[structIndex].m_sType); 567 | m_FeatureStructs[structIndex].m_StructPtr->pNext = (VkBaseInStructure*)m_Features2.pNext; 568 | m_Features2.pNext = m_FeatureStructs[structIndex].m_StructPtr; 569 | } 570 | } 571 | 572 | vkGetPhysicalDeviceFeatures2(physicalDevice, &m_Features2); 573 | 574 | m_PhysicalDeviceFeaturesQueried = true; 575 | } 576 | 577 | VkPhysicalDeviceFeatures& GetFeatures() 578 | { 579 | VKEFH_ASSERT(m_PhysicalDeviceFeaturesQueried && "You need to call GetPhysicalDeviceFeatures first."); 580 | return m_Features2.features; 581 | } 582 | const VkPhysicalDeviceFeatures& GetFeatures() const 583 | { 584 | VKEFH_ASSERT(m_PhysicalDeviceFeaturesQueried && "You need to call GetPhysicalDeviceFeatures first."); 585 | return m_Features2.features; 586 | } 587 | 588 | void PrepareCreation() 589 | { 590 | VKEFH_ASSERT(m_ExtensionsEnumerated && "You need to call EnumerateExtensions first."); 591 | VKEFH_ASSERT(m_PhysicalDeviceFeaturesQueried && "You need to call GetPhysicalDeviceFeatures first."); 592 | 593 | PrepareEnabledExtensionNames(); 594 | 595 | VKEFH_ASSERT(m_Features2.sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2); 596 | m_Features2.pNext = nullptr; 597 | for(size_t structIndex = 0, structCount = m_FeatureStructs.size(); structIndex < structCount; ++structIndex) 598 | { 599 | if(m_FeatureStructs[structIndex].m_Enabled) 600 | { 601 | VKEFH_ASSERT(m_FeatureStructs[structIndex].m_StructPtr->sType == m_FeatureStructs[structIndex].m_sType); 602 | m_FeatureStructs[structIndex].m_StructPtr->pNext = (VkBaseInStructure*)m_Features2.pNext; 603 | m_Features2.pNext = m_FeatureStructs[structIndex].m_StructPtr; 604 | } 605 | } 606 | 607 | m_CreationPrepared = true; 608 | } 609 | 610 | const void* GetFeaturesChain() const 611 | { 612 | VKEFH_ASSERT(m_CreationPrepared && "You need to call PrepareCreation first."); 613 | return &m_Features2; 614 | } 615 | 616 | private: 617 | bool m_PhysicalDeviceFeaturesQueried = false; 618 | VkPhysicalDeviceFeatures2 m_Features2 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 }; 619 | }; 620 | 621 | } // namespace VKEFH 622 | --------------------------------------------------------------------------------