| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated | ||
| 3 | * documentation files (the “Software”), to deal in the Software without restriction, including without | ||
| 4 | * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
| 5 | * of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
| 6 | * | ||
| 7 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
| 8 | * | ||
| 9 | * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT | ||
| 10 | * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| 11 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
| 12 | * 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. | ||
| 13 | * | ||
| 14 | * Copyright © 2020 Charles Giessen (charles@lunarg.com) | ||
| 15 | */ | ||
| 16 | |||
| 17 | #pragma once | ||
| 18 | |||
| 19 | #include <cassert> | ||
| 20 | |||
| 21 | #include <vector> | ||
| 22 | #include <system_error> | ||
| 23 | |||
| 24 | #include <vulkan/vulkan.h> | ||
| 25 | |||
| 26 | |||
| 27 | namespace vkb { | ||
| 28 | |||
| 29 | namespace detail { | ||
| 30 | |||
| 31 | struct Error { | ||
| 32 | std::error_code type; | ||
| 33 | VkResult vk_result = VK_SUCCESS; // optional error value if a vulkan call failed | ||
| 34 | }; | ||
| 35 | |||
| 36 | template <typename T> class Result { | ||
| 37 | public: | ||
| 38 | Result (const T& value) : m_value{ value }, m_init{ true } {} | ||
| 39 | Result (T&& value) : m_value{ std::move (value) }, m_init{ true } {} | ||
| 40 | |||
| 41 | Result (Error error) : m_error{ error }, m_init{ false } {} | ||
| 42 | |||
| 43 | Result (std::error_code error_code, VkResult result = VK_SUCCESS) | ||
| 44 | ✗ | : m_error{ error_code, result }, m_init{ false } {} | |
| 45 | |||
| 46 | ~Result () { destroy (); } | ||
| 47 | Result (Result const& expected) : m_init (expected.m_init) { | ||
| 48 | if (m_init) | ||
| 49 | new (&m_value) T{ expected.m_value }; | ||
| 50 | else | ||
| 51 | m_error = expected.m_error; | ||
| 52 | } | ||
| 53 | Result (Result&& expected) : m_init (expected.m_init) { | ||
| 54 | if (m_init) | ||
| 55 | new (&m_value) T{ std::move (expected.m_value) }; | ||
| 56 | else | ||
| 57 | m_error = std::move (expected.m_error); | ||
| 58 | expected.destroy (); | ||
| 59 | } | ||
| 60 | |||
| 61 | Result& operator= (const T& expect) { | ||
| 62 | destroy (); | ||
| 63 | m_init = true; | ||
| 64 | new (&m_value) T{ expect }; | ||
| 65 | return *this; | ||
| 66 | } | ||
| 67 | Result& operator= (T&& expect) { | ||
| 68 | destroy (); | ||
| 69 | m_init = true; | ||
| 70 | new (&m_value) T{ std::move (expect) }; | ||
| 71 | return *this; | ||
| 72 | } | ||
| 73 | Result& operator= (const Error& error) { | ||
| 74 | destroy (); | ||
| 75 | m_init = false; | ||
| 76 | m_error = error; | ||
| 77 | return *this; | ||
| 78 | } | ||
| 79 | Result& operator= (Error&& error) { | ||
| 80 | destroy (); | ||
| 81 | m_init = false; | ||
| 82 | m_error = error; | ||
| 83 | return *this; | ||
| 84 | } | ||
| 85 | // clang-format off | ||
| 86 | const T* operator-> () const { assert (m_init); return &m_value; } | ||
| 87 | T* operator-> () { assert (m_init); return &m_value; } | ||
| 88 | const T& operator* () const& { assert (m_init); return m_value; } | ||
| 89 | T& operator* () & { assert (m_init); return m_value; } | ||
| 90 | T&& operator* () && { assert (m_init); return std::move (m_value); } | ||
| 91 | const T& value () const& { assert (m_init); return m_value; } | ||
| 92 | T& value () & { assert (m_init); return m_value; } | ||
| 93 | const T&& value () const&& { assert (m_init); return std::move (m_value); } | ||
| 94 | T&& value () && { assert (m_init); return std::move (m_value); } | ||
| 95 | |||
| 96 | std::error_code error() const { assert (!m_init); return m_error.type; } | ||
| 97 | VkResult vk_result() const { assert (!m_init); return m_error.vk_result; } | ||
| 98 | // clang-format on | ||
| 99 | |||
| 100 | |||
| 101 | bool has_value () const { return m_init; } | ||
| 102 | explicit operator bool () const { return m_init; } | ||
| 103 | |||
| 104 | private: | ||
| 105 | void destroy () { | ||
| 106 | ✗ | if (m_init) m_value.~T (); | |
| 107 | ✗ | } | |
| 108 | union { | ||
| 109 | T m_value; | ||
| 110 | Error m_error; | ||
| 111 | }; | ||
| 112 | bool m_init; | ||
| 113 | }; | ||
| 114 | |||
| 115 | } // namespace detail | ||
| 116 | |||
| 117 | enum class InstanceError { | ||
| 118 | vulkan_unavailable, | ||
| 119 | vulkan_version_unavailable, | ||
| 120 | vulkan_version_1_1_unavailable, | ||
| 121 | vulkan_version_1_2_unavailable, | ||
| 122 | failed_create_instance, | ||
| 123 | failed_create_debug_messenger, | ||
| 124 | requested_layers_not_present, | ||
| 125 | requested_extensions_not_present, | ||
| 126 | windowing_extensions_not_present, | ||
| 127 | }; | ||
| 128 | enum class PhysicalDeviceError { | ||
| 129 | no_surface_provided, | ||
| 130 | failed_enumerate_physical_devices, | ||
| 131 | no_physical_devices_found, | ||
| 132 | no_suitable_device, | ||
| 133 | }; | ||
| 134 | enum class QueueError { | ||
| 135 | present_unavailable, | ||
| 136 | graphics_unavailable, | ||
| 137 | compute_unavailable, | ||
| 138 | transfer_unavailable, | ||
| 139 | queue_index_out_of_range, | ||
| 140 | invalid_queue_family_index | ||
| 141 | }; | ||
| 142 | enum class DeviceError { | ||
| 143 | failed_create_device, | ||
| 144 | }; | ||
| 145 | enum class SwapchainError { | ||
| 146 | surface_handle_not_provided, | ||
| 147 | failed_query_surface_support_details, | ||
| 148 | failed_create_swapchain, | ||
| 149 | failed_get_swapchain_images, | ||
| 150 | failed_create_swapchain_image_views, | ||
| 151 | }; | ||
| 152 | |||
| 153 | std::error_code make_error_code (InstanceError instance_error); | ||
| 154 | std::error_code make_error_code (PhysicalDeviceError physical_device_error); | ||
| 155 | std::error_code make_error_code (QueueError queue_error); | ||
| 156 | std::error_code make_error_code (DeviceError device_error); | ||
| 157 | std::error_code make_error_code (SwapchainError swapchain_error); | ||
| 158 | |||
| 159 | const char* to_string_message_severity (VkDebugUtilsMessageSeverityFlagBitsEXT s); | ||
| 160 | const char* to_string_message_type (VkDebugUtilsMessageTypeFlagsEXT s); | ||
| 161 | |||
| 162 | const char* to_string (InstanceError err); | ||
| 163 | const char* to_string (PhysicalDeviceError err); | ||
| 164 | const char* to_string (QueueError err); | ||
| 165 | const char* to_string (DeviceError err); | ||
| 166 | const char* to_string (SwapchainError err); | ||
| 167 | |||
| 168 | // Gathers useful information about the available vulkan capabilities, like layers and instance extensions. | ||
| 169 | // Use this for enabling features conditionally, ie if you would like an extension but can use a fallback if | ||
| 170 | // it isn't supported but need to know if support is available first. | ||
| 171 | struct SystemInfo { | ||
| 172 | private: | ||
| 173 | SystemInfo (); | ||
| 174 | |||
| 175 | public: | ||
| 176 | // Use get_system_info to create a SystemInfo struct. This is because loading vulkan could fail. | ||
| 177 | static detail::Result<SystemInfo> get_system_info (); | ||
| 178 | static detail::Result<SystemInfo> get_system_info (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr); | ||
| 179 | |||
| 180 | // Returns true if a layer is available | ||
| 181 | bool is_layer_available (const char* layer_name) const; | ||
| 182 | // Returns true if an extension is available | ||
| 183 | bool is_extension_available (const char* extension_name) const; | ||
| 184 | |||
| 185 | std::vector<VkLayerProperties> available_layers; | ||
| 186 | std::vector<VkExtensionProperties> available_extensions; | ||
| 187 | bool validation_layers_available = false; | ||
| 188 | bool debug_utils_available = false; | ||
| 189 | }; | ||
| 190 | |||
| 191 | |||
| 192 | class InstanceBuilder; | ||
| 193 | class PhysicalDeviceSelector; | ||
| 194 | |||
| 195 | struct Instance { | ||
| 196 | VkInstance instance = VK_NULL_HANDLE; | ||
| 197 | VkDebugUtilsMessengerEXT debug_messenger = VK_NULL_HANDLE; | ||
| 198 | VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; | ||
| 199 | |||
| 200 | PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = nullptr; | ||
| 201 | |||
| 202 | private: | ||
| 203 | bool headless = false; | ||
| 204 | uint32_t instance_version = VK_MAKE_VERSION (1, 0, 0); | ||
| 205 | |||
| 206 | friend class InstanceBuilder; | ||
| 207 | friend class PhysicalDeviceSelector; | ||
| 208 | }; | ||
| 209 | |||
| 210 | void destroy_instance (Instance instance); // release instance resources | ||
| 211 | |||
| 212 | class InstanceBuilder { | ||
| 213 | public: | ||
| 214 | // Default constructor, will load vulkan. | ||
| 215 | explicit InstanceBuilder (); | ||
| 216 | // Optional: Can use your own PFN_vkGetInstanceProcAddr | ||
| 217 | explicit InstanceBuilder (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr); | ||
| 218 | |||
| 219 | // Create a VkInstance. Return an error if it failed. | ||
| 220 | detail::Result<Instance> build () const; | ||
| 221 | |||
| 222 | // Sets the name of the application. Defaults to "" if none is provided. | ||
| 223 | InstanceBuilder& set_app_name (const char* app_name); | ||
| 224 | // Sets the name of the engine. Defaults to "" if none is provided. | ||
| 225 | InstanceBuilder& set_engine_name (const char* engine_name); | ||
| 226 | // Sets the (major, minor, patch) version of the application. | ||
| 227 | InstanceBuilder& set_app_version (uint32_t major, uint32_t minor, uint32_t patch = 0); | ||
| 228 | // Sets the (major, minor, patch) version of the engine. | ||
| 229 | InstanceBuilder& set_engine_version (uint32_t major, uint32_t minor, uint32_t patch = 0); | ||
| 230 | // Require a vulkan instance API version. Will fail to create if this version isn't available. | ||
| 231 | InstanceBuilder& require_api_version (uint32_t major, uint32_t minor, uint32_t patch = 0); | ||
| 232 | // Prefer a vulkan instance API version. If the desired version isn't available, it will use the highest version available. | ||
| 233 | InstanceBuilder& desire_api_version (uint32_t major, uint32_t minor, uint32_t patch = 0); | ||
| 234 | |||
| 235 | // Adds a layer to be enabled. Will fail to create an instance if the layer isn't available. | ||
| 236 | InstanceBuilder& enable_layer (const char* layer_name); | ||
| 237 | // Adds an extension to be enabled. Will fail to create an instance if the extension isn't available. | ||
| 238 | InstanceBuilder& enable_extension (const char* extension_name); | ||
| 239 | |||
| 240 | // Headless Mode does not load the required extensions for presentation. Defaults to true. | ||
| 241 | InstanceBuilder& set_headless (bool headless = true); | ||
| 242 | |||
| 243 | // Enables the validation layers. Will fail to create an instance if the validation layers aren't available. | ||
| 244 | InstanceBuilder& enable_validation_layers (bool require_validation = true); | ||
| 245 | // Checks if the validation layers are available and loads them if they are. | ||
| 246 | InstanceBuilder& request_validation_layers (bool enable_validation = true); | ||
| 247 | |||
| 248 | // Use a default debug callback that prints to standard out. | ||
| 249 | InstanceBuilder& use_default_debug_messenger (); | ||
| 250 | // Provide a user defined debug callback. | ||
| 251 | InstanceBuilder& set_debug_callback (PFN_vkDebugUtilsMessengerCallbackEXT callback); | ||
| 252 | // Set what message severity is needed to trigger the callback. | ||
| 253 | InstanceBuilder& set_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity); | ||
| 254 | // Add a message severity to the list that triggers the callback. | ||
| 255 | InstanceBuilder& add_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity); | ||
| 256 | // Set what message type triggers the callback. | ||
| 257 | InstanceBuilder& set_debug_messenger_type (VkDebugUtilsMessageTypeFlagsEXT type); | ||
| 258 | // Add a message type to the list of that triggers the callback. | ||
| 259 | InstanceBuilder& add_debug_messenger_type (VkDebugUtilsMessageTypeFlagsEXT type); | ||
| 260 | |||
| 261 | // Disable some validation checks. | ||
| 262 | // Checks: All, and Shaders | ||
| 263 | InstanceBuilder& add_validation_disable (VkValidationCheckEXT check); | ||
| 264 | |||
| 265 | // Enables optional parts of the validation layers. | ||
| 266 | // Parts: best practices, gpu assisted, and gpu assisted reserve binding slot. | ||
| 267 | InstanceBuilder& add_validation_feature_enable (VkValidationFeatureEnableEXT enable); | ||
| 268 | |||
| 269 | // Disables sections of the validation layers. | ||
| 270 | // Options: All, shaders, thread safety, api parameters, object lifetimes, core checks, and unique handles. | ||
| 271 | InstanceBuilder& add_validation_feature_disable (VkValidationFeatureDisableEXT disable); | ||
| 272 | |||
| 273 | // Provide custom allocation callbacks. | ||
| 274 | InstanceBuilder& set_allocation_callbacks (VkAllocationCallbacks* callbacks); | ||
| 275 | |||
| 276 | private: | ||
| 277 | struct InstanceInfo { | ||
| 278 | // VkApplicationInfo | ||
| 279 | const char* app_name = nullptr; | ||
| 280 | const char* engine_name = nullptr; | ||
| 281 | uint32_t application_version = 0; | ||
| 282 | uint32_t engine_version = 0; | ||
| 283 | uint32_t required_api_version = VK_MAKE_VERSION (1, 0, 0); | ||
| 284 | uint32_t desired_api_version = VK_MAKE_VERSION (1, 0, 0); | ||
| 285 | |||
| 286 | // VkInstanceCreateInfo | ||
| 287 | std::vector<const char*> layers; | ||
| 288 | std::vector<const char*> extensions; | ||
| 289 | VkInstanceCreateFlags flags = 0; | ||
| 290 | std::vector<VkBaseOutStructure*> pNext_elements; | ||
| 291 | |||
| 292 | // debug callback | ||
| 293 | PFN_vkDebugUtilsMessengerCallbackEXT debug_callback = nullptr; | ||
| 294 | VkDebugUtilsMessageSeverityFlagsEXT debug_message_severity = | ||
| 295 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; | ||
| 296 | VkDebugUtilsMessageTypeFlagsEXT debug_message_type = | ||
| 297 | VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | | ||
| 298 | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; | ||
| 299 | |||
| 300 | // validation features | ||
| 301 | std::vector<VkValidationCheckEXT> disabled_validation_checks; | ||
| 302 | std::vector<VkValidationFeatureEnableEXT> enabled_validation_features; | ||
| 303 | std::vector<VkValidationFeatureDisableEXT> disabled_validation_features; | ||
| 304 | |||
| 305 | // Custom allocator | ||
| 306 | VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; | ||
| 307 | |||
| 308 | bool request_validation_layers = false; | ||
| 309 | bool enable_validation_layers = false; | ||
| 310 | bool use_debug_messenger = false; | ||
| 311 | bool headless_context = false; | ||
| 312 | |||
| 313 | PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = nullptr; | ||
| 314 | } info; | ||
| 315 | }; | ||
| 316 | |||
| 317 | VKAPI_ATTR VkBool32 VKAPI_CALL default_debug_callback (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, | ||
| 318 | VkDebugUtilsMessageTypeFlagsEXT messageType, | ||
| 319 | const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, | ||
| 320 | void* pUserData); | ||
| 321 | |||
| 322 | void destroy_debug_utils_messenger(VkInstance const instance, VkDebugUtilsMessengerEXT const messenger, VkAllocationCallbacks* allocation_callbacks = nullptr); | ||
| 323 | |||
| 324 | // ---- Physical Device ---- // | ||
| 325 | class PhysicalDeviceSelector; | ||
| 326 | class DeviceBuilder; | ||
| 327 | |||
| 328 | struct PhysicalDevice { | ||
| 329 | VkPhysicalDevice physical_device = VK_NULL_HANDLE; | ||
| 330 | VkSurfaceKHR surface = VK_NULL_HANDLE; | ||
| 331 | |||
| 332 | VkPhysicalDeviceFeatures features{}; | ||
| 333 | VkPhysicalDeviceProperties properties{}; | ||
| 334 | VkPhysicalDeviceMemoryProperties memory_properties{}; | ||
| 335 | |||
| 336 | // Has a queue family that supports compute operations but not graphics nor transfer. | ||
| 337 | bool has_dedicated_compute_queue () const; | ||
| 338 | // Has a queue family that supports transfer operations but not graphics nor compute. | ||
| 339 | bool has_dedicated_transfer_queue () const; | ||
| 340 | |||
| 341 | // Has a queue family that supports transfer operations but not graphics. | ||
| 342 | bool has_separate_compute_queue () const; | ||
| 343 | // Has a queue family that supports transfer operations but not graphics. | ||
| 344 | bool has_separate_transfer_queue () const; | ||
| 345 | |||
| 346 | // Advanced: Get the VkQueueFamilyProperties of the device if special queue setup is needed | ||
| 347 | std::vector<VkQueueFamilyProperties> get_queue_families () const; | ||
| 348 | |||
| 349 | private: | ||
| 350 | std::vector<const char*> extensions_to_enable; | ||
| 351 | std::vector<VkQueueFamilyProperties> queue_families; | ||
| 352 | bool defer_surface_initialization = false; | ||
| 353 | friend class PhysicalDeviceSelector; | ||
| 354 | friend class DeviceBuilder; | ||
| 355 | }; | ||
| 356 | |||
| 357 | enum class PreferredDeviceType { | ||
| 358 | other = 0, | ||
| 359 | integrated = 1, | ||
| 360 | discrete = 2, | ||
| 361 | virtual_gpu = 3, | ||
| 362 | cpu = 4 | ||
| 363 | }; | ||
| 364 | |||
| 365 | class PhysicalDeviceSelector { | ||
| 366 | public: | ||
| 367 | // Requires a vkb::Instance to construct, needed to pass instance creation info. | ||
| 368 | explicit PhysicalDeviceSelector (Instance const& instance); | ||
| 369 | |||
| 370 | detail::Result<PhysicalDevice> select () const; | ||
| 371 | |||
| 372 | // Set the surface in which the physical device should render to. | ||
| 373 | PhysicalDeviceSelector& set_surface (VkSurfaceKHR surface); | ||
| 374 | // Set the desired physical device type to select. Defaults to PreferredDeviceType::discrete. | ||
| 375 | PhysicalDeviceSelector& prefer_gpu_device_type (PreferredDeviceType type = PreferredDeviceType::discrete); | ||
| 376 | // Allow selection of a gpu device type that isn't the preferred physical device type. Defaults to true. | ||
| 377 | PhysicalDeviceSelector& allow_any_gpu_device_type (bool allow_any_type = true); | ||
| 378 | |||
| 379 | // Require that a physical device supports presentation. Defaults to true. | ||
| 380 | PhysicalDeviceSelector& require_present (bool require = true); | ||
| 381 | |||
| 382 | // Require a queue family that supports compute operations but not graphics nor transfer. | ||
| 383 | PhysicalDeviceSelector& require_dedicated_compute_queue (); | ||
| 384 | // Require a queue family that supports transfer operations but not graphics nor compute. | ||
| 385 | PhysicalDeviceSelector& require_dedicated_transfer_queue (); | ||
| 386 | |||
| 387 | // Require a queue family that supports compute operations but not graphics. | ||
| 388 | PhysicalDeviceSelector& require_separate_compute_queue (); | ||
| 389 | // Require a queue family that supports transfer operations but not graphics. | ||
| 390 | PhysicalDeviceSelector& require_separate_transfer_queue (); | ||
| 391 | |||
| 392 | // Require a memory heap from VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT with `size` memory available. | ||
| 393 | PhysicalDeviceSelector& required_device_memory_size (VkDeviceSize size); | ||
| 394 | // Prefer a memory heap from VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT with `size` memory available. | ||
| 395 | PhysicalDeviceSelector& desired_device_memory_size (VkDeviceSize size); | ||
| 396 | |||
| 397 | // Require a physical device which supports a specific extension. | ||
| 398 | PhysicalDeviceSelector& add_required_extension (const char* extension); | ||
| 399 | // Require a physical device which supports a set of extensions. | ||
| 400 | PhysicalDeviceSelector& add_required_extensions (std::vector<const char*> extensions); | ||
| 401 | |||
| 402 | // Prefer a physical device which supports a specific extension. | ||
| 403 | PhysicalDeviceSelector& add_desired_extension (const char* extension); | ||
| 404 | // Prefer a physical device which supports a set of extensions. | ||
| 405 | PhysicalDeviceSelector& add_desired_extensions (std::vector<const char*> extensions); | ||
| 406 | |||
| 407 | // Prefer a physical device that supports a (major, minor) version of vulkan. | ||
| 408 | PhysicalDeviceSelector& set_desired_version (uint32_t major, uint32_t minor); | ||
| 409 | // Require a physical device that supports a (major, minor) version of vulkan. | ||
| 410 | PhysicalDeviceSelector& set_minimum_version (uint32_t major, uint32_t minor); | ||
| 411 | |||
| 412 | // Require a physical device which supports the features in VkPhysicalDeviceFeatures. | ||
| 413 | PhysicalDeviceSelector& set_required_features (VkPhysicalDeviceFeatures features); | ||
| 414 | |||
| 415 | // Used when surface creation happens after physical device selection. | ||
| 416 | // Warning: This disables checking if the physical device supports a given surface. | ||
| 417 | PhysicalDeviceSelector& defer_surface_initialization (); | ||
| 418 | |||
| 419 | // Ignore all criteria and choose the first physical device that is available. | ||
| 420 | // Only use when: The first gpu in the list may be set by global user preferences and an application may wish to respect it. | ||
| 421 | PhysicalDeviceSelector& select_first_device_unconditionally (bool unconditionally = true); | ||
| 422 | |||
| 423 | private: | ||
| 424 | struct SystemInfo { | ||
| 425 | VkInstance instance = VK_NULL_HANDLE; | ||
| 426 | VkSurfaceKHR surface = VK_NULL_HANDLE; | ||
| 427 | bool headless = false; | ||
| 428 | } system_info; | ||
| 429 | |||
| 430 | struct PhysicalDeviceDesc { | ||
| 431 | VkPhysicalDevice phys_device = VK_NULL_HANDLE; | ||
| 432 | std::vector<VkQueueFamilyProperties> queue_families; | ||
| 433 | |||
| 434 | VkPhysicalDeviceFeatures device_features{}; | ||
| 435 | VkPhysicalDeviceProperties device_properties{}; | ||
| 436 | VkPhysicalDeviceMemoryProperties mem_properties{}; | ||
| 437 | }; | ||
| 438 | PhysicalDeviceDesc populate_device_details (VkPhysicalDevice phys_device) const; | ||
| 439 | |||
| 440 | struct SelectionCriteria { | ||
| 441 | PreferredDeviceType preferred_type = PreferredDeviceType::discrete; | ||
| 442 | bool allow_any_type = true; | ||
| 443 | bool require_present = true; | ||
| 444 | bool require_dedicated_transfer_queue = false; | ||
| 445 | bool require_dedicated_compute_queue = false; | ||
| 446 | bool require_separate_transfer_queue = false; | ||
| 447 | bool require_separate_compute_queue = false; | ||
| 448 | VkDeviceSize required_mem_size = 0; | ||
| 449 | VkDeviceSize desired_mem_size = 0; | ||
| 450 | |||
| 451 | std::vector<const char*> required_extensions; | ||
| 452 | std::vector<const char*> desired_extensions; | ||
| 453 | |||
| 454 | uint32_t required_version = VK_MAKE_VERSION (1, 0, 0); | ||
| 455 | uint32_t desired_version = VK_MAKE_VERSION (1, 0, 0); | ||
| 456 | |||
| 457 | VkPhysicalDeviceFeatures required_features{}; | ||
| 458 | |||
| 459 | bool defer_surface_initialization = false; | ||
| 460 | bool use_first_gpu_unconditionally = false; | ||
| 461 | } criteria; | ||
| 462 | |||
| 463 | enum class Suitable { yes, partial, no }; | ||
| 464 | |||
| 465 | Suitable is_device_suitable (PhysicalDeviceDesc phys_device) const; | ||
| 466 | }; | ||
| 467 | |||
| 468 | // ---- Queue ---- // | ||
| 469 | enum class QueueType { present, graphics, compute, transfer }; | ||
| 470 | |||
| 471 | // ---- Device ---- // | ||
| 472 | |||
| 473 | struct Device { | ||
| 474 | VkDevice device = VK_NULL_HANDLE; | ||
| 475 | PhysicalDevice physical_device; | ||
| 476 | VkSurfaceKHR surface = VK_NULL_HANDLE; | ||
| 477 | std::vector<VkQueueFamilyProperties> queue_families; | ||
| 478 | VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; | ||
| 479 | |||
| 480 | detail::Result<uint32_t> get_queue_index (QueueType type) const; | ||
| 481 | // Only a compute or transfer queue type is valid. All other queue types do not support a 'dedicated' queue index | ||
| 482 | detail::Result<uint32_t> get_dedicated_queue_index (QueueType type) const; | ||
| 483 | |||
| 484 | detail::Result<VkQueue> get_queue (QueueType type) const; | ||
| 485 | // Only a compute or transfer queue type is valid. All other queue types do not support a 'dedicated' queue | ||
| 486 | detail::Result<VkQueue> get_dedicated_queue (QueueType type) const; | ||
| 487 | }; | ||
| 488 | |||
| 489 | // For advanced device queue setup | ||
| 490 | struct CustomQueueDescription { | ||
| 491 | explicit CustomQueueDescription (uint32_t index, uint32_t count, std::vector<float> priorities); | ||
| 492 | uint32_t index = 0; | ||
| 493 | uint32_t count = 0; | ||
| 494 | std::vector<float> priorities; | ||
| 495 | }; | ||
| 496 | |||
| 497 | void destroy_device (Device device); | ||
| 498 | |||
| 499 | class DeviceBuilder { | ||
| 500 | public: | ||
| 501 | // Any features and extensions that are requested/required in PhysicalDeviceSelector are automatically enabled. | ||
| 502 | explicit DeviceBuilder (PhysicalDevice physical_device); | ||
| 503 | |||
| 504 | detail::Result<Device> build () const; | ||
| 505 | |||
| 506 | // For Advanced Users: specify the exact list of VkDeviceQueueCreateInfo's needed for the application. | ||
| 507 | // If a custom queue setup is provided, getting the queues and queue indexes is up to the application. | ||
| 508 | DeviceBuilder& custom_queue_setup (std::vector<CustomQueueDescription> queue_descriptions); | ||
| 509 | |||
| 510 | // Add a structure to the pNext chain of VkDeviceCreateInfo. | ||
| 511 | // The structure must be valid when DeviceBuilder::build() is called. | ||
| 512 | template <typename T> DeviceBuilder& add_pNext (T* structure) { | ||
| 513 | info.pNext_chain.push_back (reinterpret_cast<VkBaseOutStructure*> (structure)); | ||
| 514 | return *this; | ||
| 515 | } | ||
| 516 | |||
| 517 | // Provide custom allocation callbacks. | ||
| 518 | DeviceBuilder& set_allocation_callbacks (VkAllocationCallbacks* callbacks); | ||
| 519 | |||
| 520 | private: | ||
| 521 | struct DeviceInfo { | ||
| 522 | VkDeviceCreateFlags flags = 0; | ||
| 523 | std::vector<VkBaseOutStructure*> pNext_chain; | ||
| 524 | PhysicalDevice physical_device; | ||
| 525 | VkSurfaceKHR surface = VK_NULL_HANDLE; | ||
| 526 | bool defer_surface_initialization = false; | ||
| 527 | std::vector<VkQueueFamilyProperties> queue_families; | ||
| 528 | VkPhysicalDeviceFeatures features{}; | ||
| 529 | std::vector<const char*> extensions_to_enable; | ||
| 530 | std::vector<CustomQueueDescription> queue_descriptions; | ||
| 531 | VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; | ||
| 532 | } info; | ||
| 533 | }; | ||
| 534 | |||
| 535 | // ---- Swapchain ---- // | ||
| 536 | struct Swapchain { | ||
| 537 | VkDevice device = VK_NULL_HANDLE; | ||
| 538 | VkSwapchainKHR swapchain = VK_NULL_HANDLE; | ||
| 539 | uint32_t image_count = 0; | ||
| 540 | VkFormat image_format = VK_FORMAT_UNDEFINED; | ||
| 541 | VkExtent2D extent = { 0, 0 }; | ||
| 542 | VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; | ||
| 543 | |||
| 544 | // Returns a vector of VkImage handles to the swapchain. | ||
| 545 | detail::Result<std::vector<VkImage>> get_images (); | ||
| 546 | |||
| 547 | // Returns a vector of VkImageView's to the VkImage's of the swapchain. | ||
| 548 | // VkImageViews must be destroyed. | ||
| 549 | detail::Result<std::vector<VkImageView>> get_image_views (); | ||
| 550 | void destroy_image_views (std::vector<VkImageView> const& image_views); | ||
| 551 | }; | ||
| 552 | |||
| 553 | void destroy_swapchain (Swapchain const& swapchain); | ||
| 554 | |||
| 555 | class SwapchainBuilder { | ||
| 556 | public: | ||
| 557 | explicit SwapchainBuilder (Device const& device); | ||
| 558 | explicit SwapchainBuilder (Device const& device, VkSurfaceKHR const surface); | ||
| 559 | explicit SwapchainBuilder (VkPhysicalDevice const physical_device, VkDevice const device, VkSurfaceKHR const surface, int32_t graphics_queue_index = -1, int32_t present_queue_index = -1); | ||
| 560 | |||
| 561 | detail::Result<Swapchain> build () const; | ||
| 562 | |||
| 563 | // Set the oldSwapchain member of VkSwapchainCreateInfoKHR. | ||
| 564 | // For use in rebuilding a swapchain. | ||
| 565 | SwapchainBuilder& set_old_swapchain (VkSwapchainKHR old_swapchain); | ||
| 566 | SwapchainBuilder& set_old_swapchain (Swapchain const& swapchain); | ||
| 567 | |||
| 568 | |||
| 569 | // Desired size of the swapchain. By default, the swapchain will use the size | ||
| 570 | // of the window being drawn to. | ||
| 571 | SwapchainBuilder& set_desired_extent (uint32_t width, uint32_t height); | ||
| 572 | |||
| 573 | // When determining the surface format, make this the first to be used if supported. | ||
| 574 | SwapchainBuilder& set_desired_format (VkSurfaceFormatKHR format); | ||
| 575 | // Add this swapchain format to the end of the list of formats selected from. | ||
| 576 | SwapchainBuilder& add_fallback_format (VkSurfaceFormatKHR format); | ||
| 577 | // Use the default swapchain formats. This is done if no formats are provided. | ||
| 578 | SwapchainBuilder& use_default_format_selection (); | ||
| 579 | |||
| 580 | // When determining the present mode, make this the first to be used if supported. | ||
| 581 | SwapchainBuilder& set_desired_present_mode (VkPresentModeKHR present_mode); | ||
| 582 | // Add this present mode to the end of the list of present modes selected from. | ||
| 583 | SwapchainBuilder& add_fallback_present_mode (VkPresentModeKHR present_mode); | ||
| 584 | // Use the default presentation mode. This is done if no present modes are provided. | ||
| 585 | SwapchainBuilder& use_default_present_mode_selection (); | ||
| 586 | |||
| 587 | // Set the bitmask of the image usage for acquired swapchain images. | ||
| 588 | SwapchainBuilder& set_image_usage_flags (VkImageUsageFlags usage_flags); | ||
| 589 | // Add a image usage to the bitmask for acquired swapchain images. | ||
| 590 | SwapchainBuilder& add_image_usage_flags (VkImageUsageFlags usage_flags); | ||
| 591 | // Use the default image usage bitmask values. This is the default if no image usages | ||
| 592 | // are provided. The default is VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | ||
| 593 | SwapchainBuilder& use_default_image_usage_flags (); | ||
| 594 | |||
| 595 | // Set the number of views in for multiview/stereo surface | ||
| 596 | SwapchainBuilder& set_image_array_layer_count (uint32_t array_layer_count); | ||
| 597 | |||
| 598 | // Set whether the Vulkan implementation is allowed to discard rendering operations that | ||
| 599 | // affect regions of the surface that are not visible. Default is true. | ||
| 600 | // Note: Applications should use the default of true if they do not expect to read back the content | ||
| 601 | // of presentable images before presenting them or after reacquiring them, and if their fragment | ||
| 602 | // shaders do not have any side effects that require them to run for all pixels in the presentable image. | ||
| 603 | SwapchainBuilder& set_clipped (bool clipped = true); | ||
| 604 | |||
| 605 | // Set the VkSwapchainCreateFlagBitsKHR. | ||
| 606 | SwapchainBuilder& set_create_flags (VkSwapchainCreateFlagBitsKHR create_flags); | ||
| 607 | // Set the transform to be applied, like a 90 degree rotation. Default is the current transform. | ||
| 608 | SwapchainBuilder& set_pre_transform_flags (VkSurfaceTransformFlagBitsKHR pre_transform_flags); | ||
| 609 | // Set the alpha channel to be used with other windows in on the system. Default is VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR. | ||
| 610 | SwapchainBuilder& set_composite_alpha_flags (VkCompositeAlphaFlagBitsKHR composite_alpha_flags); | ||
| 611 | |||
| 612 | // Add a structure to the pNext chain of VkSwapchainCreateInfoKHR. | ||
| 613 | // The structure must be valid when SwapchainBuilder::build() is called. | ||
| 614 | template <typename T> SwapchainBuilder& add_pNext (T* structure) { | ||
| 615 | info.pNext_chain.push_back (reinterpret_cast<VkBaseOutStructure*> (structure)); | ||
| 616 | return *this; | ||
| 617 | } | ||
| 618 | |||
| 619 | // Provide custom allocation callbacks. | ||
| 620 | SwapchainBuilder& set_allocation_callbacks (VkAllocationCallbacks* callbacks); | ||
| 621 | |||
| 622 | private: | ||
| 623 | void add_desired_formats (std::vector<VkSurfaceFormatKHR>& formats) const; | ||
| 624 | void add_desired_present_modes (std::vector<VkPresentModeKHR>& modes) const; | ||
| 625 | |||
| 626 | struct SwapchainInfo { | ||
| 627 | VkPhysicalDevice physical_device = VK_NULL_HANDLE; | ||
| 628 | VkDevice device = VK_NULL_HANDLE; | ||
| 629 | std::vector<VkBaseOutStructure*> pNext_chain; | ||
| 630 | VkSwapchainCreateFlagBitsKHR create_flags = static_cast<VkSwapchainCreateFlagBitsKHR> (0); | ||
| 631 | VkSurfaceKHR surface = VK_NULL_HANDLE; | ||
| 632 | std::vector<VkSurfaceFormatKHR> desired_formats; | ||
| 633 | uint32_t desired_width = 256; | ||
| 634 | uint32_t desired_height = 256; | ||
| 635 | uint32_t array_layer_count = 1; | ||
| 636 | VkImageUsageFlags image_usage_flags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; | ||
| 637 | uint32_t graphics_queue_index = 0; | ||
| 638 | uint32_t present_queue_index = 0; | ||
| 639 | VkSurfaceTransformFlagBitsKHR pre_transform = static_cast<VkSurfaceTransformFlagBitsKHR> (0); | ||
| 640 | VkCompositeAlphaFlagBitsKHR composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; | ||
| 641 | std::vector<VkPresentModeKHR> desired_present_modes; | ||
| 642 | bool clipped = true; | ||
| 643 | VkSwapchainKHR old_swapchain = VK_NULL_HANDLE; | ||
| 644 | VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; | ||
| 645 | } info; | ||
| 646 | }; | ||
| 647 | |||
| 648 | } // namespace vkb | ||
| 649 | |||
| 650 | |||
| 651 | namespace std { | ||
| 652 | template <> struct is_error_code_enum<vkb::InstanceError> : true_type {}; | ||
| 653 | template <> struct is_error_code_enum<vkb::PhysicalDeviceError> : true_type {}; | ||
| 654 | template <> struct is_error_code_enum<vkb::QueueError> : true_type {}; | ||
| 655 | template <> struct is_error_code_enum<vkb::DeviceError> : true_type {}; | ||
| 656 | template <> struct is_error_code_enum<vkb::SwapchainError> : true_type {}; | ||
| 657 | } // namespace std | ||
| 658 |