├── CMakeLists.txt ├── VkBootstrap.cpp ├── VkBootstrap.h ├── compute.cxx ├── context.cxx ├── context.hxx ├── launch.hxx ├── transform.hxx └── vk_mem_alloc.h /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | project(compute) 3 | 4 | # include_directories(../include) 5 | 6 | set(SOURCE_FILES 7 | compute.cxx 8 | context.cxx 9 | VkBootstrap.cpp 10 | ) 11 | 12 | set_source_files_properties(compute.cxx PROPERTIES COMPILE_FLAGS -shader) 13 | 14 | add_executable(compute ${SOURCE_FILES}) 15 | 16 | target_link_libraries(compute 17 | glfw 18 | vulkan 19 | dl 20 | pthread 21 | ) 22 | -------------------------------------------------------------------------------- /VkBootstrap.cpp: -------------------------------------------------------------------------------- 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 | #include "VkBootstrap.h" 18 | 19 | #include 20 | #include 21 | 22 | #if defined(_WIN32) 23 | #include 24 | #define NOMINMAX 25 | #include 26 | #endif // _WIN32 27 | 28 | #if defined(__linux__) || defined(__APPLE__) 29 | #include 30 | #endif 31 | 32 | #include 33 | 34 | namespace vkb { 35 | 36 | namespace detail { 37 | 38 | class VulkanFunctions { 39 | private: 40 | std::mutex init_mutex; 41 | struct VulkanLibrary { 42 | #if defined(__linux__) || defined(__APPLE__) 43 | void* library; 44 | #elif defined(_WIN32) 45 | HMODULE library; 46 | #endif 47 | PFN_vkGetInstanceProcAddr ptr_vkGetInstanceProcAddr = VK_NULL_HANDLE; 48 | 49 | VulkanLibrary () { 50 | #if defined(__linux__) 51 | library = dlopen ("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL); 52 | if (!library) library = dlopen ("libvulkan.so", RTLD_NOW | RTLD_LOCAL); 53 | #elif defined(__APPLE__) 54 | library = dlopen ("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL); 55 | if (!library) library = dlopen ("libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL); 56 | #elif defined(_WIN32) 57 | library = LoadLibrary (TEXT ("vulkan-1.dll")); 58 | #else 59 | assert (false && "Unsupported platform"); 60 | #endif 61 | if (!library) return; 62 | load_func (ptr_vkGetInstanceProcAddr, "vkGetInstanceProcAddr"); 63 | } 64 | 65 | template void load_func (T& func_dest, const char* func_name) { 66 | #if defined(__linux__) || defined(__APPLE__) 67 | func_dest = reinterpret_cast (dlsym (library, func_name)); 68 | #elif defined(_WIN32) 69 | func_dest = reinterpret_cast (GetProcAddress (library, func_name)); 70 | #endif 71 | } 72 | void close () { 73 | #if defined(__linux__) || defined(__APPLE__) 74 | dlclose (library); 75 | #elif defined(_WIN32) 76 | FreeLibrary (library); 77 | #endif 78 | library = 0; 79 | } 80 | }; 81 | VulkanLibrary& get_vulkan_library () { 82 | static VulkanLibrary lib; 83 | return lib; 84 | } 85 | 86 | bool load_vulkan (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = nullptr) { 87 | if (fp_vkGetInstanceProcAddr != nullptr) { 88 | ptr_vkGetInstanceProcAddr = fp_vkGetInstanceProcAddr; 89 | return true; 90 | } else { 91 | auto& lib = get_vulkan_library (); 92 | ptr_vkGetInstanceProcAddr = lib.ptr_vkGetInstanceProcAddr; 93 | return lib.library != nullptr && lib.ptr_vkGetInstanceProcAddr != VK_NULL_HANDLE; 94 | } 95 | } 96 | 97 | template void get_proc_addr (T& out_ptr, const char* func_name) { 98 | out_ptr = reinterpret_cast (ptr_vkGetInstanceProcAddr (instance, func_name)); 99 | } 100 | 101 | void init_pre_instance_funcs () { 102 | get_proc_addr (fp_vkEnumerateInstanceExtensionProperties, "vkEnumerateInstanceExtensionProperties"); 103 | get_proc_addr (fp_vkEnumerateInstanceLayerProperties, "vkEnumerateInstanceLayerProperties"); 104 | get_proc_addr (fp_vkEnumerateInstanceVersion, "vkEnumerateInstanceVersion"); 105 | get_proc_addr (fp_vkCreateInstance, "vkCreateInstance"); 106 | } 107 | 108 | public: 109 | PFN_vkGetInstanceProcAddr ptr_vkGetInstanceProcAddr = nullptr; 110 | VkInstance instance = nullptr; 111 | 112 | PFN_vkEnumerateInstanceExtensionProperties fp_vkEnumerateInstanceExtensionProperties = nullptr; 113 | PFN_vkEnumerateInstanceLayerProperties fp_vkEnumerateInstanceLayerProperties = nullptr; 114 | PFN_vkEnumerateInstanceVersion fp_vkEnumerateInstanceVersion = nullptr; 115 | PFN_vkCreateInstance fp_vkCreateInstance = nullptr; 116 | PFN_vkDestroyInstance fp_vkDestroyInstance = nullptr; 117 | 118 | PFN_vkEnumeratePhysicalDevices fp_vkEnumeratePhysicalDevices = nullptr; 119 | PFN_vkGetPhysicalDeviceFeatures fp_vkGetPhysicalDeviceFeatures = nullptr; 120 | PFN_vkGetPhysicalDeviceFeatures2 fp_vkGetPhysicalDeviceFeatures2 = nullptr; 121 | PFN_vkGetPhysicalDeviceFormatProperties fp_vkGetPhysicalDeviceFormatProperties = nullptr; 122 | PFN_vkGetPhysicalDeviceImageFormatProperties fp_vkGetPhysicalDeviceImageFormatProperties = nullptr; 123 | PFN_vkGetPhysicalDeviceProperties fp_vkGetPhysicalDeviceProperties = nullptr; 124 | PFN_vkGetPhysicalDeviceProperties2 fp_vkGetPhysicalDeviceProperties2 = nullptr; 125 | PFN_vkGetPhysicalDeviceQueueFamilyProperties fp_vkGetPhysicalDeviceQueueFamilyProperties = nullptr; 126 | PFN_vkGetPhysicalDeviceQueueFamilyProperties2 fp_vkGetPhysicalDeviceQueueFamilyProperties2 = nullptr; 127 | PFN_vkGetPhysicalDeviceMemoryProperties fp_vkGetPhysicalDeviceMemoryProperties = nullptr; 128 | PFN_vkGetPhysicalDeviceFormatProperties2 fp_vkGetPhysicalDeviceFormatProperties2 = nullptr; 129 | PFN_vkGetPhysicalDeviceMemoryProperties2 fp_vkGetPhysicalDeviceMemoryProperties2 = nullptr; 130 | 131 | PFN_vkCreateDevice fp_vkCreateDevice = nullptr; 132 | PFN_vkDestroyDevice fp_vkDestroyDevice = nullptr; 133 | PFN_vkEnumerateDeviceExtensionProperties fp_vkEnumerateDeviceExtensionProperties = nullptr; 134 | PFN_vkGetDeviceQueue fp_vkGetDeviceQueue = nullptr; 135 | 136 | PFN_vkCreateImageView fp_vkCreateImageView = nullptr; 137 | PFN_vkDestroyImageView fp_vkDestroyImageView = nullptr; 138 | 139 | PFN_vkDestroySurfaceKHR fp_vkDestroySurfaceKHR = nullptr; 140 | PFN_vkGetPhysicalDeviceSurfaceSupportKHR fp_vkGetPhysicalDeviceSurfaceSupportKHR = nullptr; 141 | PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fp_vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr; 142 | PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fp_vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr; 143 | PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; 144 | PFN_vkCreateSwapchainKHR fp_vkCreateSwapchainKHR = nullptr; 145 | PFN_vkDestroySwapchainKHR fp_vkDestroySwapchainKHR = nullptr; 146 | PFN_vkGetSwapchainImagesKHR fp_vkGetSwapchainImagesKHR = nullptr; 147 | 148 | bool init_vulkan_funcs (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr) { 149 | std::lock_guard lg (init_mutex); 150 | if (!load_vulkan (fp_vkGetInstanceProcAddr)) return false; 151 | init_pre_instance_funcs (); 152 | return true; 153 | } 154 | 155 | template void get_inst_proc_addr (T& out_ptr, const char* func_name) { 156 | std::lock_guard lg (init_mutex); 157 | get_proc_addr (out_ptr, func_name); 158 | } 159 | 160 | void init_instance_funcs (VkInstance inst) { 161 | std::lock_guard lg (init_mutex); 162 | 163 | instance = inst; 164 | get_proc_addr (fp_vkDestroyInstance, "vkDestroyInstance"); 165 | get_proc_addr (fp_vkEnumeratePhysicalDevices, "vkEnumeratePhysicalDevices"); 166 | get_proc_addr (fp_vkGetPhysicalDeviceFeatures, "vkGetPhysicalDeviceFeatures"); 167 | get_proc_addr (fp_vkGetPhysicalDeviceFeatures2, "vkGetPhysicalDeviceFeatures2"); 168 | get_proc_addr (fp_vkGetPhysicalDeviceFormatProperties, "vkGetPhysicalDeviceFormatProperties"); 169 | get_proc_addr (fp_vkGetPhysicalDeviceImageFormatProperties, "vkGetPhysicalDeviceImageFormatProperties"); 170 | get_proc_addr (fp_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties"); 171 | get_proc_addr (fp_vkGetPhysicalDeviceProperties2, "vkGetPhysicalDeviceProperties2"); 172 | get_proc_addr (fp_vkGetPhysicalDeviceQueueFamilyProperties, "vkGetPhysicalDeviceQueueFamilyProperties"); 173 | get_proc_addr (fp_vkGetPhysicalDeviceQueueFamilyProperties2, "vkGetPhysicalDeviceQueueFamilyProperties2"); 174 | get_proc_addr (fp_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties"); 175 | get_proc_addr (fp_vkGetPhysicalDeviceFormatProperties2, "vkGetPhysicalDeviceFormatProperties2"); 176 | get_proc_addr (fp_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2"); 177 | 178 | get_proc_addr (fp_vkCreateDevice, "vkCreateDevice"); 179 | get_proc_addr (fp_vkDestroyDevice, "vkDestroyDevice"); 180 | get_proc_addr (fp_vkEnumerateDeviceExtensionProperties, "vkEnumerateDeviceExtensionProperties"); 181 | get_proc_addr (fp_vkGetDeviceQueue, "vkGetDeviceQueue"); 182 | 183 | get_proc_addr (fp_vkCreateImageView, "vkCreateImageView"); 184 | get_proc_addr (fp_vkDestroyImageView, "vkDestroyImageView"); 185 | 186 | get_proc_addr (fp_vkDestroySurfaceKHR, "vkDestroySurfaceKHR"); 187 | get_proc_addr (fp_vkGetPhysicalDeviceSurfaceSupportKHR, "vkGetPhysicalDeviceSurfaceSupportKHR"); 188 | get_proc_addr (fp_vkGetPhysicalDeviceSurfaceFormatsKHR, "vkGetPhysicalDeviceSurfaceFormatsKHR"); 189 | get_proc_addr (fp_vkGetPhysicalDeviceSurfacePresentModesKHR, "vkGetPhysicalDeviceSurfacePresentModesKHR"); 190 | get_proc_addr (fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); 191 | get_proc_addr (fp_vkCreateSwapchainKHR, "vkCreateSwapchainKHR"); 192 | get_proc_addr (fp_vkDestroySwapchainKHR, "vkDestroySwapchainKHR"); 193 | get_proc_addr (fp_vkGetSwapchainImagesKHR, "vkGetSwapchainImagesKHR"); 194 | } 195 | }; 196 | 197 | VulkanFunctions& vulkan_functions () { 198 | static VulkanFunctions v; 199 | return v; 200 | } 201 | 202 | // Helper for robustly executing the two-call pattern 203 | template 204 | auto get_vector (std::vector& out, F&& f, Ts&&... ts) -> VkResult { 205 | uint32_t count = 0; 206 | VkResult err; 207 | do { 208 | err = f (ts..., &count, nullptr); 209 | if (err) { 210 | return err; 211 | }; 212 | out.resize (count); 213 | err = f (ts..., &count, out.data ()); 214 | out.resize (count); 215 | } while (err == VK_INCOMPLETE); 216 | return err; 217 | } 218 | 219 | template 220 | auto get_vector_noerror (F&& f, Ts&&... ts) -> std::vector { 221 | uint32_t count = 0; 222 | std::vector results; 223 | f (ts..., &count, nullptr); 224 | results.resize (count); 225 | f (ts..., &count, results.data ()); 226 | results.resize (count); 227 | return results; 228 | } 229 | } // namespace detail 230 | 231 | const char* to_string_message_severity (VkDebugUtilsMessageSeverityFlagBitsEXT s) { 232 | switch (s) { 233 | case VkDebugUtilsMessageSeverityFlagBitsEXT::VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: 234 | return "VERBOSE"; 235 | case VkDebugUtilsMessageSeverityFlagBitsEXT::VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: 236 | return "ERROR"; 237 | case VkDebugUtilsMessageSeverityFlagBitsEXT::VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: 238 | return "WARNING"; 239 | case VkDebugUtilsMessageSeverityFlagBitsEXT::VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: 240 | return "INFO"; 241 | default: 242 | return "UNKNOWN"; 243 | } 244 | } 245 | const char* to_string_message_type (VkDebugUtilsMessageTypeFlagsEXT s) { 246 | if (s == 7) return "General | Validation | Performance"; 247 | if (s == 6) return "Validation | Performance"; 248 | if (s == 5) return "General | Performance"; 249 | if (s == 4 /*VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT*/) return "Performance"; 250 | if (s == 3) return "General | Validation"; 251 | if (s == 2 /*VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT*/) return "Validation"; 252 | if (s == 1 /*VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT*/) return "General"; 253 | return "Unknown"; 254 | } 255 | 256 | VkResult create_debug_utils_messenger (VkInstance instance, 257 | PFN_vkDebugUtilsMessengerCallbackEXT debug_callback, 258 | VkDebugUtilsMessageSeverityFlagsEXT severity, 259 | VkDebugUtilsMessageTypeFlagsEXT type, 260 | VkDebugUtilsMessengerEXT* pDebugMessenger, 261 | VkAllocationCallbacks* allocation_callbacks) { 262 | 263 | if (debug_callback == nullptr) debug_callback = default_debug_callback; 264 | VkDebugUtilsMessengerCreateInfoEXT messengerCreateInfo = {}; 265 | messengerCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; 266 | messengerCreateInfo.pNext = nullptr; 267 | messengerCreateInfo.messageSeverity = severity; 268 | messengerCreateInfo.messageType = type; 269 | messengerCreateInfo.pfnUserCallback = debug_callback; 270 | 271 | PFN_vkCreateDebugUtilsMessengerEXT createMessengerFunc; 272 | detail::vulkan_functions ().get_inst_proc_addr (createMessengerFunc, "vkCreateDebugUtilsMessengerEXT"); 273 | 274 | if (createMessengerFunc != nullptr) { 275 | return createMessengerFunc (instance, &messengerCreateInfo, allocation_callbacks, pDebugMessenger); 276 | } else { 277 | return VK_ERROR_EXTENSION_NOT_PRESENT; 278 | } 279 | } 280 | 281 | void destroy_debug_utils_messenger ( 282 | VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, VkAllocationCallbacks* allocation_callbacks) { 283 | 284 | PFN_vkDestroyDebugUtilsMessengerEXT deleteMessengerFunc; 285 | detail::vulkan_functions ().get_inst_proc_addr (deleteMessengerFunc, "vkDestroyDebugUtilsMessengerEXT"); 286 | 287 | if (deleteMessengerFunc != nullptr) { 288 | deleteMessengerFunc (instance, debugMessenger, allocation_callbacks); 289 | } 290 | } 291 | 292 | VKAPI_ATTR VkBool32 VKAPI_CALL default_debug_callback (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, 293 | VkDebugUtilsMessageTypeFlagsEXT messageType, 294 | const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, 295 | void*) { 296 | auto ms = to_string_message_severity (messageSeverity); 297 | auto mt = to_string_message_type (messageType); 298 | printf ("[%s: %s]\n%s\n", ms, mt, pCallbackData->pMessage); 299 | 300 | return VK_FALSE; 301 | } 302 | 303 | namespace detail { 304 | bool check_layer_supported (std::vector const& available_layers, const char* layer_name) { 305 | if (!layer_name) return false; 306 | for (const auto& layer_properties : available_layers) { 307 | if (strcmp (layer_name, layer_properties.layerName) == 0) { 308 | return true; 309 | } 310 | } 311 | return false; 312 | } 313 | 314 | bool check_layers_supported (std::vector const& available_layers, 315 | std::vector const& layer_names) { 316 | bool all_found = true; 317 | for (const auto& layer_name : layer_names) { 318 | bool found = check_layer_supported (available_layers, layer_name); 319 | if (!found) all_found = false; 320 | } 321 | return all_found; 322 | } 323 | 324 | bool check_extension_supported ( 325 | std::vector const& available_extensions, const char* extension_name) { 326 | if (!extension_name) return false; 327 | for (const auto& extension_properties : available_extensions) { 328 | if (strcmp (extension_name, extension_properties.extensionName) == 0) { 329 | return true; 330 | } 331 | } 332 | return false; 333 | } 334 | 335 | bool check_extensions_supported (std::vector const& available_extensions, 336 | std::vector const& extension_names) { 337 | bool all_found = true; 338 | for (const auto& extension_name : extension_names) { 339 | bool found = check_extension_supported (available_extensions, extension_name); 340 | if (!found) all_found = false; 341 | } 342 | return all_found; 343 | } 344 | 345 | template 346 | void setup_pNext_chain (T& structure, std::vector const& structs) { 347 | structure.pNext = nullptr; 348 | if (structs.size () <= 0) return; 349 | for (size_t i = 0; i < structs.size () - 1; i++) { 350 | structs.at (i)->pNext = structs.at (i + 1); 351 | } 352 | structure.pNext = structs.at (0); 353 | } 354 | const char* validation_layer_name = "VK_LAYER_KHRONOS_validation"; 355 | 356 | struct InstanceErrorCategory : std::error_category { 357 | const char* name () const noexcept override { return "vkb_instance"; } 358 | std::string message (int err) const override { 359 | return to_string (static_cast (err)); 360 | } 361 | }; 362 | const InstanceErrorCategory instance_error_category; 363 | 364 | struct PhysicalDeviceErrorCategory : std::error_category { 365 | const char* name () const noexcept override { return "vkb_physical_device"; } 366 | std::string message (int err) const override { 367 | return to_string (static_cast (err)); 368 | } 369 | }; 370 | const PhysicalDeviceErrorCategory physical_device_error_category; 371 | 372 | struct QueueErrorCategory : std::error_category { 373 | const char* name () const noexcept override { return "vkb_queue"; } 374 | std::string message (int err) const override { 375 | return to_string (static_cast (err)); 376 | } 377 | }; 378 | const QueueErrorCategory queue_error_category; 379 | 380 | struct DeviceErrorCategory : std::error_category { 381 | const char* name () const noexcept override { return "vkb_device"; } 382 | std::string message (int err) const override { 383 | return to_string (static_cast (err)); 384 | } 385 | }; 386 | const DeviceErrorCategory device_error_category; 387 | 388 | struct SwapchainErrorCategory : std::error_category { 389 | const char* name () const noexcept override { return "vbk_swapchain"; } 390 | std::string message (int err) const override { 391 | return to_string (static_cast (err)); 392 | } 393 | }; 394 | const SwapchainErrorCategory swapchain_error_category; 395 | 396 | } // namespace detail 397 | 398 | std::error_code make_error_code (InstanceError instance_error) { 399 | return { static_cast (instance_error), detail::instance_error_category }; 400 | } 401 | std::error_code make_error_code (PhysicalDeviceError physical_device_error) { 402 | return { static_cast (physical_device_error), detail::physical_device_error_category }; 403 | } 404 | std::error_code make_error_code (QueueError queue_error) { 405 | return { static_cast (queue_error), detail::queue_error_category }; 406 | } 407 | std::error_code make_error_code (DeviceError device_error) { 408 | return { static_cast (device_error), detail::device_error_category }; 409 | } 410 | std::error_code make_error_code (SwapchainError swapchain_error) { 411 | return { static_cast (swapchain_error), detail::swapchain_error_category }; 412 | } 413 | 414 | const char* to_string (InstanceError err) { 415 | switch (err) { 416 | case InstanceError::vulkan_unavailable: 417 | return "vulkan_unavailable"; 418 | case InstanceError::vulkan_version_unavailable: 419 | return "vulkan_version_unavailable"; 420 | case InstanceError::vulkan_version_1_1_unavailable: 421 | return "vulkan_version_1_1_unavailable"; 422 | case InstanceError::vulkan_version_1_2_unavailable: 423 | return "vulkan_version_1_2_unavailable"; 424 | case InstanceError::failed_create_debug_messenger: 425 | return "failed_create_debug_messenger"; 426 | case InstanceError::failed_create_instance: 427 | return "failed_create_instance"; 428 | case InstanceError::requested_layers_not_present: 429 | return "requested_layers_not_present"; 430 | case InstanceError::requested_extensions_not_present: 431 | return "requested_extensions_not_present"; 432 | case InstanceError::windowing_extensions_not_present: 433 | return "windowing_extensions_not_present"; 434 | default: 435 | return ""; 436 | } 437 | } 438 | const char* to_string (PhysicalDeviceError err) { 439 | switch (err) { 440 | case PhysicalDeviceError::no_surface_provided: 441 | return "no_surface_provided"; 442 | case PhysicalDeviceError::failed_enumerate_physical_devices: 443 | return "failed_enumerate_physical_devices"; 444 | case PhysicalDeviceError::no_physical_devices_found: 445 | return "no_physical_devices_found"; 446 | case PhysicalDeviceError::no_suitable_device: 447 | return "no_suitable_device"; 448 | default: 449 | return ""; 450 | } 451 | } 452 | const char* to_string (QueueError err) { 453 | switch (err) { 454 | case QueueError::present_unavailable: 455 | return "present_unavailable"; 456 | case QueueError::graphics_unavailable: 457 | return "graphics_unavailable"; 458 | case QueueError::compute_unavailable: 459 | return "compute_unavailable"; 460 | case QueueError::transfer_unavailable: 461 | return "transfer_unavailable"; 462 | case QueueError::queue_index_out_of_range: 463 | return "queue_index_out_of_range"; 464 | case QueueError::invalid_queue_family_index: 465 | return "invalid_queue_family_index"; 466 | default: 467 | return ""; 468 | } 469 | } 470 | const char* to_string (DeviceError err) { 471 | switch (err) { 472 | case DeviceError::failed_create_device: 473 | return "failed_create_device"; 474 | default: 475 | return ""; 476 | } 477 | } 478 | const char* to_string (SwapchainError err) { 479 | switch (err) { 480 | case SwapchainError::surface_handle_not_provided: 481 | return "surface_handle_not_provided"; 482 | case SwapchainError::failed_query_surface_support_details: 483 | return "failed_query_surface_support_details"; 484 | case SwapchainError::failed_create_swapchain: 485 | return "failed_create_swapchain"; 486 | case SwapchainError::failed_get_swapchain_images: 487 | return "failed_get_swapchain_images"; 488 | case SwapchainError::failed_create_swapchain_image_views: 489 | return "failed_create_swapchain_image_views"; 490 | default: 491 | return ""; 492 | } 493 | } 494 | 495 | detail::Result SystemInfo::get_system_info () { 496 | if (!detail::vulkan_functions ().init_vulkan_funcs (nullptr)) { 497 | return make_error_code (InstanceError::vulkan_unavailable); 498 | } 499 | return SystemInfo (); 500 | } 501 | 502 | detail::Result SystemInfo::get_system_info (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr) { 503 | // Using externally provided function pointers, assume the loader is available 504 | detail::vulkan_functions ().init_vulkan_funcs (fp_vkGetInstanceProcAddr); 505 | return SystemInfo (); 506 | } 507 | 508 | SystemInfo::SystemInfo () { 509 | auto available_layers_ret = detail::get_vector ( 510 | this->available_layers, detail::vulkan_functions ().fp_vkEnumerateInstanceLayerProperties); 511 | if (available_layers_ret != VK_SUCCESS) { 512 | this->available_layers.clear (); 513 | } 514 | 515 | for (auto& layer : this->available_layers) 516 | if (strcmp (layer.layerName, detail::validation_layer_name) == 0) 517 | validation_layers_available = true; 518 | 519 | auto available_extensions_ret = detail::get_vector (this->available_extensions, 520 | detail::vulkan_functions ().fp_vkEnumerateInstanceExtensionProperties, 521 | nullptr); 522 | if (available_extensions_ret != VK_SUCCESS) { 523 | this->available_extensions.clear (); 524 | } 525 | 526 | for (auto& ext : this->available_extensions) 527 | if (strcmp (ext.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) 528 | debug_utils_available = true; 529 | 530 | for (auto& layer : this->available_layers) { 531 | std::vector layer_extensions; 532 | auto layer_extensions_ret = detail::get_vector (layer_extensions, 533 | detail::vulkan_functions ().fp_vkEnumerateInstanceExtensionProperties, 534 | layer.layerName); 535 | if (layer_extensions_ret != VK_SUCCESS) { 536 | for (auto& ext : layer_extensions) 537 | if (strcmp (ext.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) 538 | debug_utils_available = true; 539 | } 540 | } 541 | } 542 | bool SystemInfo::is_extension_available (const char* extension_name) const { 543 | if (!extension_name) return false; 544 | return detail::check_extension_supported (available_extensions, extension_name); 545 | } 546 | bool SystemInfo::is_layer_available (const char* layer_name) const { 547 | if (!layer_name) return false; 548 | return detail::check_layer_supported (available_layers, layer_name); 549 | } 550 | 551 | void destroy_instance (Instance instance) { 552 | if (instance.instance != VK_NULL_HANDLE) { 553 | if (instance.debug_messenger != VK_NULL_HANDLE) 554 | destroy_debug_utils_messenger (instance.instance, instance.debug_messenger, instance.allocation_callbacks); 555 | detail::vulkan_functions ().fp_vkDestroyInstance (instance.instance, instance.allocation_callbacks); 556 | } 557 | } 558 | 559 | InstanceBuilder::InstanceBuilder (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr) { 560 | info.fp_vkGetInstanceProcAddr = fp_vkGetInstanceProcAddr; 561 | } 562 | InstanceBuilder::InstanceBuilder () {} 563 | 564 | detail::Result InstanceBuilder::build () const { 565 | 566 | auto sys_info_ret = SystemInfo::get_system_info (); 567 | if (!sys_info_ret) return sys_info_ret.error (); 568 | auto system = sys_info_ret.value (); 569 | 570 | uint32_t api_version = VK_MAKE_VERSION (1, 0, 0); 571 | 572 | if (info.required_api_version > VK_MAKE_VERSION (1, 0, 0) || 573 | info.desired_api_version > VK_MAKE_VERSION (1, 0, 0)) { 574 | PFN_vkEnumerateInstanceVersion pfn_vkEnumerateInstanceVersion = 575 | detail::vulkan_functions ().fp_vkEnumerateInstanceVersion; 576 | 577 | uint32_t queried_api_version = VK_MAKE_VERSION (1, 0, 0); 578 | if (pfn_vkEnumerateInstanceVersion != nullptr) { 579 | VkResult res = pfn_vkEnumerateInstanceVersion (&queried_api_version); 580 | // Should always return VK_SUCCESS 581 | if (res != VK_SUCCESS && info.required_api_version > 0) 582 | return make_error_code (InstanceError::vulkan_version_unavailable); 583 | } 584 | if (pfn_vkEnumerateInstanceVersion == nullptr || queried_api_version < info.required_api_version) { 585 | if (VK_VERSION_MINOR (info.required_api_version) == 2) 586 | return make_error_code (InstanceError::vulkan_version_1_2_unavailable); 587 | else if (VK_VERSION_MINOR (info.required_api_version)) 588 | return make_error_code (InstanceError::vulkan_version_1_1_unavailable); 589 | else 590 | return make_error_code (InstanceError::vulkan_version_unavailable); 591 | } 592 | if (info.required_api_version > VK_MAKE_VERSION (1, 0, 0)) { 593 | api_version = info.required_api_version; 594 | } else if (info.desired_api_version > VK_MAKE_VERSION (1, 0, 0)) { 595 | if (queried_api_version >= info.desired_api_version) 596 | api_version = info.desired_api_version; 597 | else 598 | api_version = queried_api_version; 599 | } 600 | } 601 | 602 | VkApplicationInfo app_info = {}; 603 | app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 604 | app_info.pNext = nullptr; 605 | app_info.pApplicationName = info.app_name != nullptr ? info.app_name : ""; 606 | app_info.applicationVersion = info.application_version; 607 | app_info.pEngineName = info.engine_name != nullptr ? info.engine_name : ""; 608 | app_info.engineVersion = info.engine_version; 609 | app_info.apiVersion = api_version; 610 | 611 | std::vector extensions; 612 | for (auto& ext : info.extensions) 613 | extensions.push_back (ext); 614 | if (info.debug_callback != nullptr && system.debug_utils_available) { 615 | extensions.push_back (VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 616 | } 617 | 618 | if (!info.headless_context) { 619 | auto check_add_window_ext = [&] (const char* name) -> bool { 620 | if (!detail::check_extension_supported (system.available_extensions, name)) 621 | return false; 622 | extensions.push_back (name); 623 | return true; 624 | }; 625 | bool khr_surface_added = check_add_window_ext ("VK_KHR_surface"); 626 | #if defined(_WIN32) 627 | bool added_window_exts = check_add_window_ext ("VK_KHR_win32_surface"); 628 | #elif defined(__ANDROID__) 629 | bool added_window_exts = check_add_window_ext ("VK_KHR_android_surface"); 630 | #elif defined(_DIRECT2DISPLAY) 631 | bool added_window_exts = check_add_window_ext ("VK_KHR_display"); 632 | #elif defined(__linux__) 633 | // make sure all three calls to check_add_window_ext, don't allow short circuiting 634 | bool added_window_exts = check_add_window_ext ("VK_KHR_xcb_surface"); 635 | added_window_exts = check_add_window_ext ("VK_KHR_xlib_surface") || added_window_exts; 636 | added_window_exts = check_add_window_ext ("VK_KHR_wayland_surface") || added_window_exts; 637 | #elif defined(__APPLE__) 638 | bool added_window_exts = check_add_window_ext ("VK_EXT_metal_surface"); 639 | #endif 640 | if (!khr_surface_added || !added_window_exts) 641 | return make_error_code (InstanceError::windowing_extensions_not_present); 642 | } 643 | bool all_extensions_supported = detail::check_extensions_supported (system.available_extensions, extensions); 644 | if (!all_extensions_supported) { 645 | return make_error_code (InstanceError::requested_extensions_not_present); 646 | } 647 | 648 | std::vector layers; 649 | for (auto& layer : info.layers) 650 | layers.push_back (layer); 651 | 652 | if (info.enable_validation_layers || (info.request_validation_layers && system.validation_layers_available)) { 653 | layers.push_back (detail::validation_layer_name); 654 | } 655 | bool all_layers_supported = detail::check_layers_supported (system.available_layers, layers); 656 | if (!all_layers_supported) { 657 | return make_error_code (InstanceError::requested_layers_not_present); 658 | } 659 | 660 | std::vector pNext_chain; 661 | 662 | VkDebugUtilsMessengerCreateInfoEXT messengerCreateInfo = {}; 663 | if (info.use_debug_messenger) { 664 | messengerCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; 665 | messengerCreateInfo.pNext = nullptr; 666 | messengerCreateInfo.messageSeverity = info.debug_message_severity; 667 | messengerCreateInfo.messageType = info.debug_message_type; 668 | messengerCreateInfo.pfnUserCallback = info.debug_callback; 669 | pNext_chain.push_back (reinterpret_cast (&messengerCreateInfo)); 670 | } 671 | 672 | VkValidationFeaturesEXT features{}; 673 | if (info.enabled_validation_features.size () != 0 || info.disabled_validation_features.size ()) { 674 | features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; 675 | features.pNext = nullptr; 676 | features.enabledValidationFeatureCount = 677 | static_cast (info.enabled_validation_features.size ()); 678 | features.pEnabledValidationFeatures = info.enabled_validation_features.data (); 679 | features.disabledValidationFeatureCount = 680 | static_cast (info.disabled_validation_features.size ()); 681 | features.pDisabledValidationFeatures = info.disabled_validation_features.data (); 682 | pNext_chain.push_back (reinterpret_cast (&features)); 683 | } 684 | 685 | VkValidationFlagsEXT checks{}; 686 | if (info.disabled_validation_checks.size () != 0) { 687 | checks.sType = VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT; 688 | checks.pNext = nullptr; 689 | checks.disabledValidationCheckCount = 690 | static_cast (info.disabled_validation_checks.size ()); 691 | checks.pDisabledValidationChecks = info.disabled_validation_checks.data (); 692 | pNext_chain.push_back (reinterpret_cast (&checks)); 693 | } 694 | 695 | VkInstanceCreateInfo instance_create_info = {}; 696 | instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 697 | detail::setup_pNext_chain (instance_create_info, pNext_chain); 698 | instance_create_info.flags = info.flags; 699 | instance_create_info.pApplicationInfo = &app_info; 700 | instance_create_info.enabledExtensionCount = static_cast (extensions.size ()); 701 | instance_create_info.ppEnabledExtensionNames = extensions.data (); 702 | instance_create_info.enabledLayerCount = static_cast (layers.size ()); 703 | instance_create_info.ppEnabledLayerNames = layers.data (); 704 | 705 | Instance instance; 706 | VkResult res = detail::vulkan_functions ().fp_vkCreateInstance ( 707 | &instance_create_info, info.allocation_callbacks, &instance.instance); 708 | if (res != VK_SUCCESS) 709 | return detail::Result (InstanceError::failed_create_instance, res); 710 | 711 | detail::vulkan_functions ().init_instance_funcs (instance.instance); 712 | 713 | if (info.use_debug_messenger) { 714 | res = create_debug_utils_messenger (instance.instance, 715 | info.debug_callback, 716 | info.debug_message_severity, 717 | info.debug_message_type, 718 | &instance.debug_messenger, 719 | info.allocation_callbacks); 720 | if (res != VK_SUCCESS) { 721 | return detail::Result (InstanceError::failed_create_debug_messenger, res); 722 | } 723 | } 724 | 725 | if (info.headless_context) { 726 | instance.headless = true; 727 | } 728 | instance.allocation_callbacks = info.allocation_callbacks; 729 | instance.instance_version = api_version; 730 | instance.fp_vkGetInstanceProcAddr = detail::vulkan_functions ().ptr_vkGetInstanceProcAddr; 731 | return instance; 732 | } 733 | 734 | InstanceBuilder& InstanceBuilder::set_app_name (const char* app_name) { 735 | if (!app_name) return *this; 736 | info.app_name = app_name; 737 | return *this; 738 | } 739 | InstanceBuilder& InstanceBuilder::set_engine_name (const char* engine_name) { 740 | if (!engine_name) return *this; 741 | info.engine_name = engine_name; 742 | return *this; 743 | } 744 | InstanceBuilder& InstanceBuilder::set_app_version (uint32_t major, uint32_t minor, uint32_t patch) { 745 | info.application_version = VK_MAKE_VERSION (major, minor, patch); 746 | return *this; 747 | } 748 | InstanceBuilder& InstanceBuilder::set_engine_version (uint32_t major, uint32_t minor, uint32_t patch) { 749 | info.engine_version = VK_MAKE_VERSION (major, minor, patch); 750 | return *this; 751 | } 752 | InstanceBuilder& InstanceBuilder::require_api_version (uint32_t major, uint32_t minor, uint32_t patch) { 753 | info.required_api_version = VK_MAKE_VERSION (major, minor, patch); 754 | return *this; 755 | } 756 | InstanceBuilder& InstanceBuilder::desire_api_version (uint32_t major, uint32_t minor, uint32_t patch) { 757 | info.desired_api_version = VK_MAKE_VERSION (major, minor, patch); 758 | return *this; 759 | } 760 | InstanceBuilder& InstanceBuilder::enable_layer (const char* layer_name) { 761 | if (!layer_name) return *this; 762 | info.layers.push_back (layer_name); 763 | return *this; 764 | } 765 | InstanceBuilder& InstanceBuilder::enable_extension (const char* extension_name) { 766 | if (!extension_name) return *this; 767 | info.extensions.push_back (extension_name); 768 | return *this; 769 | } 770 | InstanceBuilder& InstanceBuilder::enable_validation_layers (bool enable_validation) { 771 | info.enable_validation_layers = enable_validation; 772 | return *this; 773 | } 774 | InstanceBuilder& InstanceBuilder::request_validation_layers (bool enable_validation) { 775 | info.request_validation_layers = enable_validation; 776 | return *this; 777 | } 778 | InstanceBuilder& InstanceBuilder::use_default_debug_messenger () { 779 | info.use_debug_messenger = true; 780 | info.debug_callback = default_debug_callback; 781 | return *this; 782 | } 783 | InstanceBuilder& InstanceBuilder::set_debug_callback (PFN_vkDebugUtilsMessengerCallbackEXT callback) { 784 | info.use_debug_messenger = true; 785 | info.debug_callback = callback; 786 | return *this; 787 | } 788 | InstanceBuilder& InstanceBuilder::set_headless (bool headless) { 789 | info.headless_context = headless; 790 | return *this; 791 | } 792 | InstanceBuilder& InstanceBuilder::set_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity) { 793 | info.debug_message_severity = severity; 794 | return *this; 795 | } 796 | InstanceBuilder& InstanceBuilder::add_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity) { 797 | info.debug_message_severity = info.debug_message_severity | severity; 798 | return *this; 799 | } 800 | InstanceBuilder& InstanceBuilder::set_debug_messenger_type (VkDebugUtilsMessageTypeFlagsEXT type) { 801 | info.debug_message_type = type; 802 | return *this; 803 | } 804 | InstanceBuilder& InstanceBuilder::add_debug_messenger_type (VkDebugUtilsMessageTypeFlagsEXT type) { 805 | info.debug_message_type = info.debug_message_type | type; 806 | return *this; 807 | } 808 | InstanceBuilder& InstanceBuilder::add_validation_disable (VkValidationCheckEXT check) { 809 | info.disabled_validation_checks.push_back (check); 810 | return *this; 811 | } 812 | InstanceBuilder& InstanceBuilder::add_validation_feature_enable (VkValidationFeatureEnableEXT enable) { 813 | info.enabled_validation_features.push_back (enable); 814 | return *this; 815 | } 816 | InstanceBuilder& InstanceBuilder::add_validation_feature_disable (VkValidationFeatureDisableEXT disable) { 817 | info.disabled_validation_features.push_back (disable); 818 | return *this; 819 | } 820 | InstanceBuilder& InstanceBuilder::set_allocation_callbacks (VkAllocationCallbacks* callbacks) { 821 | info.allocation_callbacks = callbacks; 822 | return *this; 823 | } 824 | 825 | void destroy_debug_messenger (VkInstance const instance, VkDebugUtilsMessengerEXT const messenger); 826 | 827 | 828 | // ---- Physical Device ---- // 829 | 830 | namespace detail { 831 | 832 | std::vector check_device_extension_support ( 833 | VkPhysicalDevice device, std::vector desired_extensions) { 834 | std::vector available_extensions; 835 | auto available_extensions_ret = detail::get_vector ( 836 | available_extensions, detail::vulkan_functions ().fp_vkEnumerateDeviceExtensionProperties, device, nullptr); 837 | if (available_extensions_ret != VK_SUCCESS) return {}; 838 | 839 | std::vector extensions_to_enable; 840 | for (const auto& extension : available_extensions) { 841 | for (auto& req_ext : desired_extensions) { 842 | if (strcmp (req_ext, extension.extensionName) == 0) { 843 | extensions_to_enable.push_back (req_ext); 844 | break; 845 | } 846 | } 847 | } 848 | return extensions_to_enable; 849 | } 850 | 851 | bool supports_features (VkPhysicalDeviceFeatures supported, VkPhysicalDeviceFeatures requested) { 852 | // clang-format off 853 | if (requested.robustBufferAccess && !supported.robustBufferAccess) return false; 854 | if (requested.fullDrawIndexUint32 && !supported.fullDrawIndexUint32) return false; 855 | if (requested.imageCubeArray && !supported.imageCubeArray) return false; 856 | if (requested.independentBlend && !supported.independentBlend) return false; 857 | if (requested.geometryShader && !supported.geometryShader) return false; 858 | if (requested.tessellationShader && !supported.tessellationShader) return false; 859 | if (requested.sampleRateShading && !supported.sampleRateShading) return false; 860 | if (requested.dualSrcBlend && !supported.dualSrcBlend) return false; 861 | if (requested.logicOp && !supported.logicOp) return false; 862 | if (requested.multiDrawIndirect && !supported.multiDrawIndirect) return false; 863 | if (requested.drawIndirectFirstInstance && !supported.drawIndirectFirstInstance) return false; 864 | if (requested.depthClamp && !supported.depthClamp) return false; 865 | if (requested.depthBiasClamp && !supported.depthBiasClamp) return false; 866 | if (requested.fillModeNonSolid && !supported.fillModeNonSolid) return false; 867 | if (requested.depthBounds && !supported.depthBounds) return false; 868 | if (requested.wideLines && !supported.wideLines) return false; 869 | if (requested.largePoints && !supported.largePoints) return false; 870 | if (requested.alphaToOne && !supported.alphaToOne) return false; 871 | if (requested.multiViewport && !supported.multiViewport) return false; 872 | if (requested.samplerAnisotropy && !supported.samplerAnisotropy) return false; 873 | if (requested.textureCompressionETC2 && !supported.textureCompressionETC2) return false; 874 | if (requested.textureCompressionASTC_LDR && !supported.textureCompressionASTC_LDR) return false; 875 | if (requested.textureCompressionBC && !supported.textureCompressionBC) return false; 876 | if (requested.occlusionQueryPrecise && !supported.occlusionQueryPrecise) return false; 877 | if (requested.pipelineStatisticsQuery && !supported.pipelineStatisticsQuery) return false; 878 | if (requested.vertexPipelineStoresAndAtomics && !supported.vertexPipelineStoresAndAtomics) return false; 879 | if (requested.fragmentStoresAndAtomics && !supported.fragmentStoresAndAtomics) return false; 880 | if (requested.shaderTessellationAndGeometryPointSize && !supported.shaderTessellationAndGeometryPointSize) return false; 881 | if (requested.shaderImageGatherExtended && !supported.shaderImageGatherExtended) return false; 882 | if (requested.shaderStorageImageExtendedFormats && !supported.shaderStorageImageExtendedFormats) return false; 883 | if (requested.shaderStorageImageMultisample && !supported.shaderStorageImageMultisample) return false; 884 | if (requested.shaderStorageImageReadWithoutFormat && !supported.shaderStorageImageReadWithoutFormat) return false; 885 | if (requested.shaderStorageImageWriteWithoutFormat && !supported.shaderStorageImageWriteWithoutFormat) return false; 886 | if (requested.shaderUniformBufferArrayDynamicIndexing && !supported.shaderUniformBufferArrayDynamicIndexing) return false; 887 | if (requested.shaderSampledImageArrayDynamicIndexing && !supported.shaderSampledImageArrayDynamicIndexing) return false; 888 | if (requested.shaderStorageBufferArrayDynamicIndexing && !supported.shaderStorageBufferArrayDynamicIndexing) return false; 889 | if (requested.shaderStorageImageArrayDynamicIndexing && !supported.shaderStorageImageArrayDynamicIndexing) return false; 890 | if (requested.shaderClipDistance && !supported.shaderClipDistance) return false; 891 | if (requested.shaderCullDistance && !supported.shaderCullDistance) return false; 892 | if (requested.shaderFloat64 && !supported.shaderFloat64) return false; 893 | if (requested.shaderInt64 && !supported.shaderInt64) return false; 894 | if (requested.shaderInt16 && !supported.shaderInt16) return false; 895 | if (requested.shaderResourceResidency && !supported.shaderResourceResidency) return false; 896 | if (requested.shaderResourceMinLod && !supported.shaderResourceMinLod) return false; 897 | if (requested.sparseBinding && !supported.sparseBinding) return false; 898 | if (requested.sparseResidencyBuffer && !supported.sparseResidencyBuffer) return false; 899 | if (requested.sparseResidencyImage2D && !supported.sparseResidencyImage2D) return false; 900 | if (requested.sparseResidencyImage3D && !supported.sparseResidencyImage3D) return false; 901 | if (requested.sparseResidency2Samples && !supported.sparseResidency2Samples) return false; 902 | if (requested.sparseResidency4Samples && !supported.sparseResidency4Samples) return false; 903 | if (requested.sparseResidency8Samples && !supported.sparseResidency8Samples) return false; 904 | if (requested.sparseResidency16Samples && !supported.sparseResidency16Samples) return false; 905 | if (requested.sparseResidencyAliased && !supported.sparseResidencyAliased) return false; 906 | if (requested.variableMultisampleRate && !supported.variableMultisampleRate) return false; 907 | if (requested.inheritedQueries && !supported.inheritedQueries) return false; 908 | // clang-format on 909 | return true; 910 | } 911 | 912 | // finds the first queue which supports graphics operations. returns -1 if none is found 913 | int get_graphics_queue_index (std::vector const& families) { 914 | for (size_t i = 0; i < families.size (); i++) { 915 | if (families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) return static_cast (i); 916 | } 917 | return -1; 918 | } 919 | // finds a compute queue which is separate from the graphics queue and tries to find one without 920 | // transfer support returns -1 if none is found 921 | int get_separate_compute_queue_index (std::vector const& families) { 922 | int compute = -1; 923 | for (size_t i = 0; i < families.size (); i++) { 924 | if ((families[i].queueFlags & VK_QUEUE_COMPUTE_BIT) && 925 | ((families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) { 926 | if ((families[i].queueFlags & VK_QUEUE_TRANSFER_BIT) == 0) { 927 | return static_cast (i); 928 | } else { 929 | compute = static_cast (i); 930 | } 931 | } 932 | } 933 | return compute; 934 | } 935 | // finds a transfer queue which is separate from the graphics queue and tries to find one without 936 | // compute support returns -1 if none is found 937 | int get_separate_transfer_queue_index (std::vector const& families) { 938 | int transfer = -1; 939 | for (size_t i = 0; i < families.size (); i++) { 940 | if ((families[i].queueFlags & VK_QUEUE_TRANSFER_BIT) && 941 | ((families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) { 942 | if ((families[i].queueFlags & VK_QUEUE_COMPUTE_BIT) == 0) { 943 | return static_cast (i); 944 | } else { 945 | transfer = static_cast (i); 946 | } 947 | } 948 | } 949 | return transfer; 950 | } 951 | // finds the first queue which supports only compute (not graphics or transfer). returns -1 if none is found 952 | int get_dedicated_compute_queue_index (std::vector const& families) { 953 | for (size_t i = 0; i < families.size (); i++) { 954 | if ((families[i].queueFlags & VK_QUEUE_COMPUTE_BIT) && 955 | (families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0 && 956 | (families[i].queueFlags & VK_QUEUE_TRANSFER_BIT) == 0) 957 | return static_cast (i); 958 | } 959 | return -1; 960 | } 961 | // finds the first queue which supports only transfer (not graphics or compute). returns -1 if none is found 962 | int get_dedicated_transfer_queue_index (std::vector const& families) { 963 | for (size_t i = 0; i < families.size (); i++) { 964 | if ((families[i].queueFlags & VK_QUEUE_TRANSFER_BIT) && 965 | (families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0 && 966 | (families[i].queueFlags & VK_QUEUE_COMPUTE_BIT) == 0) 967 | return static_cast (i); 968 | } 969 | return -1; 970 | } 971 | // finds the first queue which supports presenting. returns -1 if none is found 972 | int get_present_queue_index (VkPhysicalDevice const phys_device, 973 | VkSurfaceKHR const surface, 974 | std::vector const& families) { 975 | for (size_t i = 0; i < families.size (); i++) { 976 | VkBool32 presentSupport = false; 977 | if (surface != VK_NULL_HANDLE) { 978 | VkResult res = detail::vulkan_functions ().fp_vkGetPhysicalDeviceSurfaceSupportKHR ( 979 | phys_device, static_cast (i), surface, &presentSupport); 980 | if (res != VK_SUCCESS) return -1; // TODO: determine if this should fail another way 981 | } 982 | if (presentSupport == VK_TRUE) return static_cast (i); 983 | } 984 | return -1; 985 | } 986 | } // namespace detail 987 | 988 | 989 | PhysicalDeviceSelector::PhysicalDeviceDesc PhysicalDeviceSelector::populate_device_details ( 990 | VkPhysicalDevice phys_device) const { 991 | PhysicalDeviceSelector::PhysicalDeviceDesc desc{}; 992 | desc.phys_device = phys_device; 993 | auto queue_families = detail::get_vector_noerror ( 994 | detail::vulkan_functions ().fp_vkGetPhysicalDeviceQueueFamilyProperties, phys_device); 995 | desc.queue_families = queue_families; 996 | 997 | detail::vulkan_functions ().fp_vkGetPhysicalDeviceProperties (phys_device, &desc.device_properties); 998 | detail::vulkan_functions ().fp_vkGetPhysicalDeviceFeatures (phys_device, &desc.device_features); 999 | detail::vulkan_functions ().fp_vkGetPhysicalDeviceMemoryProperties (phys_device, &desc.mem_properties); 1000 | return desc; 1001 | } 1002 | 1003 | PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (PhysicalDeviceDesc pd) const { 1004 | Suitable suitable = Suitable::yes; 1005 | 1006 | if (criteria.required_version > pd.device_properties.apiVersion) return Suitable::no; 1007 | if (criteria.desired_version > pd.device_properties.apiVersion) suitable = Suitable::partial; 1008 | 1009 | bool dedicated_compute = detail::get_dedicated_compute_queue_index (pd.queue_families) >= 0; 1010 | bool dedicated_transfer = detail::get_dedicated_transfer_queue_index (pd.queue_families) >= 0; 1011 | bool separate_compute = detail::get_separate_compute_queue_index (pd.queue_families) >= 0; 1012 | bool separate_transfer = detail::get_separate_transfer_queue_index (pd.queue_families) >= 0; 1013 | 1014 | bool present_queue = 1015 | detail::get_present_queue_index (pd.phys_device, system_info.surface, pd.queue_families) >= 0; 1016 | 1017 | if (criteria.require_dedicated_compute_queue && !dedicated_compute) return Suitable::no; 1018 | if (criteria.require_dedicated_transfer_queue && !dedicated_transfer) return Suitable::no; 1019 | if (criteria.require_separate_compute_queue && !separate_compute) return Suitable::no; 1020 | if (criteria.require_separate_transfer_queue && !separate_transfer) return Suitable::no; 1021 | if (criteria.require_present && !present_queue && !criteria.defer_surface_initialization) 1022 | return Suitable::no; 1023 | 1024 | auto required_extensions_supported = 1025 | detail::check_device_extension_support (pd.phys_device, criteria.required_extensions); 1026 | if (required_extensions_supported.size () != criteria.required_extensions.size ()) 1027 | return Suitable::no; 1028 | 1029 | auto desired_extensions_supported = 1030 | detail::check_device_extension_support (pd.phys_device, criteria.desired_extensions); 1031 | if (desired_extensions_supported.size () != criteria.desired_extensions.size ()) 1032 | suitable = Suitable::partial; 1033 | 1034 | 1035 | bool swapChainAdequate = false; 1036 | if (criteria.defer_surface_initialization) { 1037 | swapChainAdequate = true; 1038 | } else if (!system_info.headless) { 1039 | std::vector formats; 1040 | std::vector present_modes; 1041 | 1042 | auto formats_ret = detail::get_vector (formats, 1043 | detail::vulkan_functions ().fp_vkGetPhysicalDeviceSurfaceFormatsKHR, 1044 | pd.phys_device, 1045 | system_info.surface); 1046 | auto present_modes_ret = detail::get_vector (present_modes, 1047 | detail::vulkan_functions ().fp_vkGetPhysicalDeviceSurfacePresentModesKHR, 1048 | pd.phys_device, 1049 | system_info.surface); 1050 | 1051 | if (formats_ret == VK_SUCCESS && present_modes_ret == VK_SUCCESS) { 1052 | swapChainAdequate = !formats.empty () && !present_modes.empty (); 1053 | } 1054 | } 1055 | if (criteria.require_present && !swapChainAdequate) return Suitable::no; 1056 | 1057 | if (pd.device_properties.deviceType != static_cast (criteria.preferred_type)) { 1058 | if (criteria.allow_any_type) 1059 | suitable = Suitable::partial; 1060 | else 1061 | return Suitable::no; 1062 | } 1063 | 1064 | bool required_features_supported = 1065 | detail::supports_features (pd.device_features, criteria.required_features); 1066 | if (!required_features_supported) return Suitable::no; 1067 | 1068 | bool has_required_memory = false; 1069 | bool has_preferred_memory = false; 1070 | for (uint32_t i = 0; i < pd.mem_properties.memoryHeapCount; i++) { 1071 | if (pd.mem_properties.memoryHeaps[i].flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { 1072 | if (pd.mem_properties.memoryHeaps[i].size > criteria.required_mem_size) { 1073 | has_required_memory = true; 1074 | } 1075 | if (pd.mem_properties.memoryHeaps[i].size > criteria.desired_mem_size) { 1076 | has_preferred_memory = true; 1077 | } 1078 | } 1079 | } 1080 | if (!has_required_memory) return Suitable::no; 1081 | if (!has_preferred_memory) suitable = Suitable::partial; 1082 | 1083 | return suitable; 1084 | } 1085 | 1086 | PhysicalDeviceSelector::PhysicalDeviceSelector (Instance const& instance) { 1087 | system_info.instance = instance.instance; 1088 | system_info.headless = instance.headless; 1089 | criteria.require_present = !instance.headless; 1090 | criteria.required_version = instance.instance_version; 1091 | criteria.desired_version = instance.instance_version; 1092 | } 1093 | 1094 | detail::Result PhysicalDeviceSelector::select () const { 1095 | if (!system_info.headless && !criteria.defer_surface_initialization) { 1096 | if (system_info.surface == VK_NULL_HANDLE) 1097 | return detail::Result{ PhysicalDeviceError::no_surface_provided }; 1098 | } 1099 | 1100 | 1101 | std::vector physical_devices; 1102 | 1103 | auto physical_devices_ret = detail::get_vector ( 1104 | physical_devices, detail::vulkan_functions ().fp_vkEnumeratePhysicalDevices, system_info.instance); 1105 | if (physical_devices_ret != VK_SUCCESS) { 1106 | return detail::Result{ PhysicalDeviceError::failed_enumerate_physical_devices, 1107 | physical_devices_ret }; 1108 | } 1109 | if (physical_devices.size () == 0) { 1110 | return detail::Result{ PhysicalDeviceError::no_physical_devices_found }; 1111 | } 1112 | 1113 | std::vector phys_device_descriptions; 1114 | for (auto& phys_device : physical_devices) { 1115 | phys_device_descriptions.push_back (populate_device_details (phys_device)); 1116 | } 1117 | 1118 | PhysicalDeviceDesc selected_device{}; 1119 | 1120 | if (criteria.use_first_gpu_unconditionally) { 1121 | selected_device = phys_device_descriptions.at (0); 1122 | } else { 1123 | for (const auto& device : phys_device_descriptions) { 1124 | auto suitable = is_device_suitable (device); 1125 | if (suitable == Suitable::yes) { 1126 | selected_device = device; 1127 | break; 1128 | } else if (suitable == Suitable::partial) { 1129 | selected_device = device; 1130 | } 1131 | } 1132 | } 1133 | 1134 | if (selected_device.phys_device == VK_NULL_HANDLE) { 1135 | return detail::Result{ PhysicalDeviceError::no_suitable_device }; 1136 | } 1137 | PhysicalDevice out_device{}; 1138 | out_device.physical_device = selected_device.phys_device; 1139 | out_device.surface = system_info.surface; 1140 | out_device.features = criteria.required_features; 1141 | out_device.properties = selected_device.device_properties; 1142 | out_device.memory_properties = selected_device.mem_properties; 1143 | out_device.queue_families = selected_device.queue_families; 1144 | out_device.defer_surface_initialization = criteria.defer_surface_initialization; 1145 | 1146 | out_device.extensions_to_enable.insert (out_device.extensions_to_enable.end (), 1147 | criteria.required_extensions.begin (), 1148 | criteria.required_extensions.end ()); 1149 | auto desired_extensions_supported = 1150 | detail::check_device_extension_support (out_device.physical_device, criteria.desired_extensions); 1151 | out_device.extensions_to_enable.insert (out_device.extensions_to_enable.end (), 1152 | desired_extensions_supported.begin (), 1153 | desired_extensions_supported.end ()); 1154 | return out_device; 1155 | } 1156 | 1157 | PhysicalDeviceSelector& PhysicalDeviceSelector::set_surface (VkSurfaceKHR surface) { 1158 | system_info.surface = surface; 1159 | system_info.headless = false; 1160 | return *this; 1161 | } 1162 | PhysicalDeviceSelector& PhysicalDeviceSelector::prefer_gpu_device_type (PreferredDeviceType type) { 1163 | criteria.preferred_type = type; 1164 | return *this; 1165 | } 1166 | PhysicalDeviceSelector& PhysicalDeviceSelector::allow_any_gpu_device_type (bool allow_any_type) { 1167 | criteria.allow_any_type = allow_any_type; 1168 | return *this; 1169 | } 1170 | PhysicalDeviceSelector& PhysicalDeviceSelector::require_present (bool require) { 1171 | criteria.require_present = require; 1172 | return *this; 1173 | } 1174 | PhysicalDeviceSelector& PhysicalDeviceSelector::require_dedicated_transfer_queue () { 1175 | criteria.require_dedicated_transfer_queue = true; 1176 | return *this; 1177 | } 1178 | PhysicalDeviceSelector& PhysicalDeviceSelector::require_dedicated_compute_queue () { 1179 | criteria.require_dedicated_compute_queue = true; 1180 | return *this; 1181 | } 1182 | PhysicalDeviceSelector& PhysicalDeviceSelector::require_separate_transfer_queue () { 1183 | criteria.require_separate_transfer_queue = true; 1184 | return *this; 1185 | } 1186 | PhysicalDeviceSelector& PhysicalDeviceSelector::require_separate_compute_queue () { 1187 | criteria.require_separate_compute_queue = true; 1188 | return *this; 1189 | } 1190 | PhysicalDeviceSelector& PhysicalDeviceSelector::required_device_memory_size (VkDeviceSize size) { 1191 | criteria.required_mem_size = size; 1192 | return *this; 1193 | } 1194 | PhysicalDeviceSelector& PhysicalDeviceSelector::desired_device_memory_size (VkDeviceSize size) { 1195 | criteria.desired_mem_size = size; 1196 | return *this; 1197 | } 1198 | PhysicalDeviceSelector& PhysicalDeviceSelector::add_required_extension (const char* extension) { 1199 | criteria.required_extensions.push_back (extension); 1200 | return *this; 1201 | } 1202 | PhysicalDeviceSelector& PhysicalDeviceSelector::add_required_extensions (std::vector extensions) { 1203 | criteria.required_extensions.insert ( 1204 | criteria.required_extensions.end (), extensions.begin (), extensions.end ()); 1205 | return *this; 1206 | } 1207 | PhysicalDeviceSelector& PhysicalDeviceSelector::add_desired_extension (const char* extension) { 1208 | criteria.desired_extensions.push_back (extension); 1209 | return *this; 1210 | } 1211 | PhysicalDeviceSelector& PhysicalDeviceSelector::add_desired_extensions (std::vector extensions) { 1212 | criteria.desired_extensions.insert ( 1213 | criteria.desired_extensions.end (), extensions.begin (), extensions.end ()); 1214 | return *this; 1215 | } 1216 | PhysicalDeviceSelector& PhysicalDeviceSelector::set_minimum_version (uint32_t major, uint32_t minor) { 1217 | criteria.required_version = VK_MAKE_VERSION (major, minor, 0); 1218 | return *this; 1219 | } 1220 | PhysicalDeviceSelector& PhysicalDeviceSelector::set_desired_version (uint32_t major, uint32_t minor) { 1221 | criteria.desired_version = VK_MAKE_VERSION (major, minor, 0); 1222 | return *this; 1223 | } 1224 | PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features (VkPhysicalDeviceFeatures features) { 1225 | criteria.required_features = features; 1226 | return *this; 1227 | } 1228 | PhysicalDeviceSelector& PhysicalDeviceSelector::defer_surface_initialization () { 1229 | criteria.defer_surface_initialization = true; 1230 | return *this; 1231 | } 1232 | PhysicalDeviceSelector& PhysicalDeviceSelector::select_first_device_unconditionally (bool unconditionally) { 1233 | criteria.use_first_gpu_unconditionally = unconditionally; 1234 | return *this; 1235 | } 1236 | 1237 | bool PhysicalDevice::has_dedicated_compute_queue () const { 1238 | return detail::get_dedicated_compute_queue_index (queue_families) >= 0; 1239 | } 1240 | bool PhysicalDevice::has_separate_compute_queue () const { 1241 | return detail::get_separate_compute_queue_index (queue_families) >= 0; 1242 | } 1243 | bool PhysicalDevice::has_dedicated_transfer_queue () const { 1244 | return detail::get_dedicated_transfer_queue_index (queue_families) >= 0; 1245 | } 1246 | bool PhysicalDevice::has_separate_transfer_queue () const { 1247 | return detail::get_separate_transfer_queue_index (queue_families) >= 0; 1248 | } 1249 | std::vector PhysicalDevice::get_queue_families () const { 1250 | return queue_families; 1251 | } 1252 | 1253 | // ---- Queues ---- // 1254 | 1255 | detail::Result Device::get_queue_index (QueueType type) const { 1256 | int index = -1; 1257 | switch (type) { 1258 | case QueueType::present: 1259 | index = detail::get_present_queue_index (physical_device.physical_device, surface, queue_families); 1260 | if (index < 0) return detail::Result{ QueueError::present_unavailable }; 1261 | break; 1262 | case QueueType::graphics: 1263 | index = detail::get_graphics_queue_index (queue_families); 1264 | if (index < 0) return detail::Result{ QueueError::graphics_unavailable }; 1265 | break; 1266 | case QueueType::compute: 1267 | index = detail::get_separate_compute_queue_index (queue_families); 1268 | if (index < 0) return detail::Result{ QueueError::compute_unavailable }; 1269 | break; 1270 | case QueueType::transfer: 1271 | index = detail::get_separate_transfer_queue_index (queue_families); 1272 | if (index < 0) return detail::Result{ QueueError::transfer_unavailable }; 1273 | break; 1274 | default: 1275 | return detail::Result{ QueueError::invalid_queue_family_index }; 1276 | } 1277 | return static_cast (index); 1278 | } 1279 | detail::Result Device::get_dedicated_queue_index (QueueType type) const { 1280 | int index = -1; 1281 | switch (type) { 1282 | case QueueType::compute: 1283 | index = detail::get_dedicated_compute_queue_index (queue_families); 1284 | if (index < 0) return detail::Result{ QueueError::compute_unavailable }; 1285 | break; 1286 | case QueueType::transfer: 1287 | index = detail::get_dedicated_transfer_queue_index (queue_families); 1288 | if (index < 0) return detail::Result{ QueueError::transfer_unavailable }; 1289 | break; 1290 | default: 1291 | return detail::Result{ QueueError::invalid_queue_family_index }; 1292 | } 1293 | return static_cast (index); 1294 | } 1295 | namespace detail { 1296 | VkQueue get_queue (VkDevice device, uint32_t family) { 1297 | VkQueue out_queue; 1298 | detail::vulkan_functions ().fp_vkGetDeviceQueue (device, family, 0, &out_queue); 1299 | return out_queue; 1300 | } 1301 | } // namespace detail 1302 | detail::Result Device::get_queue (QueueType type) const { 1303 | auto index = get_queue_index (type); 1304 | if (!index.has_value ()) return { index.error () }; 1305 | return detail::get_queue (device, index.value ()); 1306 | } 1307 | detail::Result Device::get_dedicated_queue (QueueType type) const { 1308 | auto index = get_dedicated_queue_index (type); 1309 | if (!index.has_value ()) return { index.error () }; 1310 | return detail::get_queue (device, index.value ()); 1311 | } 1312 | 1313 | // ---- Device ---- // 1314 | 1315 | CustomQueueDescription::CustomQueueDescription (uint32_t index, uint32_t count, std::vector priorities) 1316 | : index (index), count (count), priorities (priorities) { 1317 | assert (count == priorities.size ()); 1318 | } 1319 | 1320 | void destroy_device (Device device) { 1321 | detail::vulkan_functions ().fp_vkDestroyDevice (device.device, device.allocation_callbacks); 1322 | } 1323 | 1324 | DeviceBuilder::DeviceBuilder (PhysicalDevice phys_device) { 1325 | info.physical_device = phys_device; 1326 | info.surface = phys_device.surface; 1327 | info.queue_families = phys_device.queue_families; 1328 | info.features = phys_device.features; 1329 | info.extensions_to_enable = phys_device.extensions_to_enable; 1330 | info.defer_surface_initialization = phys_device.defer_surface_initialization; 1331 | } 1332 | 1333 | detail::Result DeviceBuilder::build () const { 1334 | 1335 | std::vector queue_descriptions; 1336 | queue_descriptions.insert ( 1337 | queue_descriptions.end (), info.queue_descriptions.begin (), info.queue_descriptions.end ()); 1338 | 1339 | if (queue_descriptions.size () == 0) { 1340 | for (uint32_t i = 0; i < info.queue_families.size (); i++) { 1341 | queue_descriptions.push_back (CustomQueueDescription{ i, 1, std::vector{ 1.0f } }); 1342 | } 1343 | } 1344 | 1345 | std::vector queueCreateInfos; 1346 | for (auto& desc : queue_descriptions) { 1347 | VkDeviceQueueCreateInfo queue_create_info = {}; 1348 | queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 1349 | queue_create_info.queueFamilyIndex = desc.index; 1350 | queue_create_info.queueCount = desc.count; 1351 | queue_create_info.pQueuePriorities = desc.priorities.data (); 1352 | queueCreateInfos.push_back (queue_create_info); 1353 | } 1354 | 1355 | std::vector extensions = info.extensions_to_enable; 1356 | if (info.surface != VK_NULL_HANDLE || info.defer_surface_initialization) 1357 | extensions.push_back ({ VK_KHR_SWAPCHAIN_EXTENSION_NAME }); 1358 | 1359 | // VUID-VkDeviceCreateInfo-pNext-00373 - don't add pEnabledFeatures if the phys_dev_features_2 is present 1360 | bool has_phys_dev_features_2 = false; 1361 | for (auto& pNext_struct : info.pNext_chain) { 1362 | if (pNext_struct->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) { 1363 | has_phys_dev_features_2 = true; 1364 | } 1365 | } 1366 | 1367 | VkDeviceCreateInfo device_create_info = {}; 1368 | device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 1369 | detail::setup_pNext_chain (device_create_info, info.pNext_chain); 1370 | device_create_info.flags = info.flags; 1371 | device_create_info.queueCreateInfoCount = static_cast (queueCreateInfos.size ()); 1372 | device_create_info.pQueueCreateInfos = queueCreateInfos.data (); 1373 | device_create_info.enabledExtensionCount = static_cast (extensions.size ()); 1374 | device_create_info.ppEnabledExtensionNames = extensions.data (); 1375 | if (!has_phys_dev_features_2) { 1376 | device_create_info.pEnabledFeatures = &info.features; 1377 | } 1378 | 1379 | Device device; 1380 | VkResult res = detail::vulkan_functions ().fp_vkCreateDevice (info.physical_device.physical_device, 1381 | &device_create_info, 1382 | info.allocation_callbacks, 1383 | &device.device); 1384 | if (res != VK_SUCCESS) { 1385 | return { DeviceError::failed_create_device, res }; 1386 | } 1387 | device.physical_device = info.physical_device; 1388 | device.surface = info.surface; 1389 | device.queue_families = info.queue_families; 1390 | device.allocation_callbacks = info.allocation_callbacks; 1391 | return device; 1392 | } 1393 | DeviceBuilder& DeviceBuilder::custom_queue_setup (std::vector queue_descriptions) { 1394 | info.queue_descriptions = queue_descriptions; 1395 | return *this; 1396 | } 1397 | DeviceBuilder& DeviceBuilder::set_allocation_callbacks (VkAllocationCallbacks* callbacks) { 1398 | info.allocation_callbacks = callbacks; 1399 | return *this; 1400 | } 1401 | 1402 | // ---- Swapchain ---- // 1403 | 1404 | namespace detail { 1405 | struct SurfaceSupportDetails { 1406 | VkSurfaceCapabilitiesKHR capabilities; 1407 | std::vector formats; 1408 | std::vector present_modes; 1409 | }; 1410 | 1411 | enum class SurfaceSupportError { 1412 | surface_handle_null, 1413 | failed_get_surface_capabilities, 1414 | failed_enumerate_surface_formats, 1415 | failed_enumerate_present_modes 1416 | }; 1417 | 1418 | struct SurfaceSupportErrorCategory : std::error_category { 1419 | const char* name () const noexcept override { return "vbk_surface_support"; } 1420 | std::string message (int err) const override { 1421 | switch (static_cast (err)) { 1422 | case SurfaceSupportError::surface_handle_null: 1423 | return "surface_handle_null"; 1424 | case SurfaceSupportError::failed_get_surface_capabilities: 1425 | return "failed_get_surface_capabilities"; 1426 | case SurfaceSupportError::failed_enumerate_surface_formats: 1427 | return "failed_enumerate_surface_formats"; 1428 | case SurfaceSupportError::failed_enumerate_present_modes: 1429 | return "failed_enumerate_present_modes"; 1430 | default: 1431 | return ""; 1432 | } 1433 | } 1434 | }; 1435 | const SurfaceSupportErrorCategory surface_support_error_category; 1436 | 1437 | std::error_code make_error_code (SurfaceSupportError surface_support_error) { 1438 | return { static_cast (surface_support_error), detail::surface_support_error_category }; 1439 | } 1440 | 1441 | Result query_surface_support_details (VkPhysicalDevice phys_device, VkSurfaceKHR surface) { 1442 | if (surface == VK_NULL_HANDLE) 1443 | return make_error_code (SurfaceSupportError::surface_handle_null); 1444 | 1445 | VkSurfaceCapabilitiesKHR capabilities; 1446 | VkResult res = detail::vulkan_functions ().fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR ( 1447 | phys_device, surface, &capabilities); 1448 | if (res != VK_SUCCESS) { 1449 | return { make_error_code (SurfaceSupportError::failed_get_surface_capabilities), res }; 1450 | } 1451 | 1452 | std::vector formats; 1453 | std::vector present_modes; 1454 | 1455 | auto formats_ret = detail::get_vector ( 1456 | formats, detail::vulkan_functions ().fp_vkGetPhysicalDeviceSurfaceFormatsKHR, phys_device, surface); 1457 | if (formats_ret != VK_SUCCESS) 1458 | return { make_error_code (SurfaceSupportError::failed_enumerate_surface_formats), formats_ret }; 1459 | auto present_modes_ret = detail::get_vector ( 1460 | present_modes, detail::vulkan_functions ().fp_vkGetPhysicalDeviceSurfacePresentModesKHR, phys_device, surface); 1461 | if (present_modes_ret != VK_SUCCESS) 1462 | return { make_error_code (SurfaceSupportError::failed_enumerate_present_modes), present_modes_ret }; 1463 | 1464 | return SurfaceSupportDetails{ capabilities, formats, present_modes }; 1465 | } 1466 | 1467 | VkSurfaceFormatKHR find_surface_format (std::vector const& available_formats, 1468 | std::vector const& desired_formats) { 1469 | for (auto const& desired_format : desired_formats) { 1470 | for (auto const& available_format : available_formats) { 1471 | // finds the first format that is desired and available 1472 | if (desired_format.format == available_format.format && 1473 | desired_format.colorSpace == available_format.colorSpace) { 1474 | return desired_format; 1475 | } 1476 | } 1477 | } 1478 | 1479 | // use the first available one if any desired formats aren't found 1480 | return available_formats[0]; 1481 | } 1482 | 1483 | VkPresentModeKHR find_present_mode (std::vector const& available_resent_modes, 1484 | std::vector const& desired_present_modes) { 1485 | for (auto const& desired_pm : desired_present_modes) { 1486 | for (auto const& available_pm : available_resent_modes) { 1487 | // finds the first present mode that is desired and available 1488 | if (desired_pm == available_pm) return desired_pm; 1489 | } 1490 | } 1491 | // only present mode required, use as a fallback 1492 | return VK_PRESENT_MODE_FIFO_KHR; 1493 | } 1494 | 1495 | template T minimum (T a, T b) { return a < b ? a : b; } 1496 | template T maximum (T a, T b) { return a > b ? a : b; } 1497 | 1498 | VkExtent2D find_extent ( 1499 | VkSurfaceCapabilitiesKHR const& capabilities, uint32_t desired_width, uint32_t desired_height) { 1500 | if (capabilities.currentExtent.width != UINT32_MAX) { 1501 | return capabilities.currentExtent; 1502 | } else { 1503 | VkExtent2D actualExtent = { desired_width, desired_height }; 1504 | 1505 | actualExtent.width = maximum (capabilities.minImageExtent.width, 1506 | minimum (capabilities.maxImageExtent.width, actualExtent.width)); 1507 | actualExtent.height = maximum (capabilities.minImageExtent.height, 1508 | minimum (capabilities.maxImageExtent.height, actualExtent.height)); 1509 | 1510 | return actualExtent; 1511 | } 1512 | } 1513 | } // namespace detail 1514 | 1515 | void destroy_swapchain (Swapchain const& swapchain) { 1516 | if (swapchain.device != VK_NULL_HANDLE && swapchain.swapchain != VK_NULL_HANDLE) { 1517 | detail::vulkan_functions ().fp_vkDestroySwapchainKHR ( 1518 | swapchain.device, swapchain.swapchain, swapchain.allocation_callbacks); 1519 | } 1520 | } 1521 | 1522 | SwapchainBuilder::SwapchainBuilder (Device const& device) { 1523 | info.device = device.device; 1524 | info.physical_device = device.physical_device.physical_device; 1525 | info.surface = device.surface; 1526 | auto present = device.get_queue_index (QueueType::present); 1527 | auto graphics = device.get_queue_index (QueueType::graphics); 1528 | // TODO: handle error of queue's not available 1529 | info.graphics_queue_index = present.value (); 1530 | info.present_queue_index = graphics.value (); 1531 | } 1532 | SwapchainBuilder::SwapchainBuilder (Device const& device, VkSurfaceKHR const surface) { 1533 | info.device = device.device; 1534 | info.physical_device = device.physical_device.physical_device; 1535 | info.surface = surface; 1536 | Device temp_device = device; 1537 | temp_device.surface = surface; 1538 | auto present = temp_device.get_queue_index (QueueType::present); 1539 | auto graphics = temp_device.get_queue_index (QueueType::graphics); 1540 | // TODO: handle error of queue's not available 1541 | info.graphics_queue_index = present.value (); 1542 | info.present_queue_index = graphics.value (); 1543 | } 1544 | SwapchainBuilder::SwapchainBuilder (VkPhysicalDevice const physical_device, 1545 | VkDevice const device, 1546 | VkSurfaceKHR const surface, 1547 | int32_t graphics_queue_index, 1548 | int32_t present_queue_index) { 1549 | info.physical_device = physical_device; 1550 | info.device = device; 1551 | info.surface = surface; 1552 | info.graphics_queue_index = static_cast (graphics_queue_index); 1553 | info.present_queue_index = static_cast (present_queue_index); 1554 | if (graphics_queue_index < 0 || present_queue_index < 0) { 1555 | auto queue_families = detail::get_vector_noerror ( 1556 | detail::vulkan_functions ().fp_vkGetPhysicalDeviceQueueFamilyProperties, physical_device); 1557 | if (graphics_queue_index < 0) 1558 | info.graphics_queue_index = 1559 | static_cast (detail::get_graphics_queue_index (queue_families)); 1560 | if (present_queue_index < 0) 1561 | info.present_queue_index = static_cast ( 1562 | detail::get_present_queue_index (physical_device, surface, queue_families)); 1563 | } 1564 | } 1565 | detail::Result SwapchainBuilder::build () const { 1566 | if (info.surface == VK_NULL_HANDLE) { 1567 | return detail::Error{ SwapchainError::surface_handle_not_provided }; 1568 | } 1569 | 1570 | auto desired_formats = info.desired_formats; 1571 | if (desired_formats.size () == 0) add_desired_formats (desired_formats); 1572 | auto desired_present_modes = info.desired_present_modes; 1573 | if (desired_present_modes.size () == 0) add_desired_present_modes (desired_present_modes); 1574 | 1575 | auto surface_support_ret = detail::query_surface_support_details (info.physical_device, info.surface); 1576 | if (!surface_support_ret.has_value ()) 1577 | return detail::Error{ SwapchainError::failed_query_surface_support_details, 1578 | surface_support_ret.vk_result () }; 1579 | auto surface_support = surface_support_ret.value (); 1580 | 1581 | uint32_t image_count = surface_support.capabilities.minImageCount + 1; 1582 | if (surface_support.capabilities.maxImageCount > 0 && image_count > surface_support.capabilities.maxImageCount) { 1583 | image_count = surface_support.capabilities.maxImageCount; 1584 | } 1585 | VkSurfaceFormatKHR surface_format = detail::find_surface_format (surface_support.formats, desired_formats); 1586 | 1587 | VkExtent2D extent = 1588 | detail::find_extent (surface_support.capabilities, info.desired_width, info.desired_height); 1589 | 1590 | uint32_t image_array_layers = info.array_layer_count; 1591 | if (surface_support.capabilities.maxImageArrayLayers < info.array_layer_count) 1592 | image_array_layers = surface_support.capabilities.maxImageArrayLayers; 1593 | if (info.array_layer_count == 0) image_array_layers = 1; 1594 | 1595 | uint32_t queue_family_indices[] = { info.graphics_queue_index, info.present_queue_index }; 1596 | 1597 | 1598 | VkPresentModeKHR present_mode = 1599 | detail::find_present_mode (surface_support.present_modes, desired_present_modes); 1600 | 1601 | VkSurfaceTransformFlagBitsKHR pre_transform = info.pre_transform; 1602 | if (info.pre_transform == static_cast (0)) 1603 | pre_transform = surface_support.capabilities.currentTransform; 1604 | 1605 | VkSwapchainCreateInfoKHR swapchain_create_info = {}; 1606 | swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; 1607 | detail::setup_pNext_chain (swapchain_create_info, info.pNext_chain); 1608 | swapchain_create_info.flags = info.create_flags; 1609 | swapchain_create_info.surface = info.surface; 1610 | swapchain_create_info.minImageCount = image_count; 1611 | swapchain_create_info.imageFormat = surface_format.format; 1612 | swapchain_create_info.imageColorSpace = surface_format.colorSpace; 1613 | swapchain_create_info.imageExtent = extent; 1614 | swapchain_create_info.imageArrayLayers = image_array_layers; 1615 | swapchain_create_info.imageUsage = info.image_usage_flags; 1616 | 1617 | if (info.graphics_queue_index != info.present_queue_index) { 1618 | swapchain_create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; 1619 | swapchain_create_info.queueFamilyIndexCount = 2; 1620 | swapchain_create_info.pQueueFamilyIndices = queue_family_indices; 1621 | } else { 1622 | swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; 1623 | } 1624 | 1625 | swapchain_create_info.preTransform = pre_transform; 1626 | swapchain_create_info.compositeAlpha = info.composite_alpha; 1627 | swapchain_create_info.presentMode = present_mode; 1628 | swapchain_create_info.clipped = info.clipped; 1629 | swapchain_create_info.oldSwapchain = info.old_swapchain; 1630 | Swapchain swapchain{}; 1631 | VkResult res = detail::vulkan_functions ().fp_vkCreateSwapchainKHR ( 1632 | info.device, &swapchain_create_info, info.allocation_callbacks, &swapchain.swapchain); 1633 | if (res != VK_SUCCESS) { 1634 | return detail::Error{ SwapchainError::failed_create_swapchain, res }; 1635 | } 1636 | swapchain.device = info.device; 1637 | swapchain.image_format = surface_format.format; 1638 | swapchain.extent = extent; 1639 | auto images = swapchain.get_images (); 1640 | if (!images) { 1641 | return detail::Error{ SwapchainError::failed_get_swapchain_images }; 1642 | } 1643 | swapchain.image_count = static_cast (images.value ().size ()); 1644 | swapchain.allocation_callbacks = info.allocation_callbacks; 1645 | return swapchain; 1646 | } 1647 | detail::Result> Swapchain::get_images () { 1648 | std::vector swapchain_images; 1649 | 1650 | auto swapchain_images_ret = detail::get_vector ( 1651 | swapchain_images, detail::vulkan_functions ().fp_vkGetSwapchainImagesKHR, device, swapchain); 1652 | if (swapchain_images_ret != VK_SUCCESS) { 1653 | return detail::Error{ SwapchainError::failed_get_swapchain_images, swapchain_images_ret }; 1654 | } 1655 | return swapchain_images; 1656 | } 1657 | detail::Result> Swapchain::get_image_views () { 1658 | 1659 | auto swapchain_images_ret = get_images (); 1660 | if (!swapchain_images_ret) return swapchain_images_ret.error (); 1661 | auto swapchain_images = swapchain_images_ret.value (); 1662 | 1663 | std::vector views (swapchain_images.size ()); 1664 | 1665 | for (size_t i = 0; i < swapchain_images.size (); i++) { 1666 | VkImageViewCreateInfo createInfo = {}; 1667 | createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; 1668 | createInfo.image = swapchain_images[i]; 1669 | createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; 1670 | createInfo.format = image_format; 1671 | createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; 1672 | createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; 1673 | createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; 1674 | createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; 1675 | createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 1676 | createInfo.subresourceRange.baseMipLevel = 0; 1677 | createInfo.subresourceRange.levelCount = 1; 1678 | createInfo.subresourceRange.baseArrayLayer = 0; 1679 | createInfo.subresourceRange.layerCount = 1; 1680 | 1681 | VkResult res = detail::vulkan_functions ().fp_vkCreateImageView ( 1682 | device, &createInfo, allocation_callbacks, &views[i]); 1683 | if (res != VK_SUCCESS) 1684 | return detail::Error{ SwapchainError::failed_create_swapchain_image_views, res }; 1685 | } 1686 | return views; 1687 | } 1688 | void Swapchain::destroy_image_views (std::vector const& image_views) { 1689 | for (auto& image_view : image_views) { 1690 | detail::vulkan_functions ().fp_vkDestroyImageView (device, image_view, allocation_callbacks); 1691 | } 1692 | } 1693 | SwapchainBuilder& SwapchainBuilder::set_old_swapchain (VkSwapchainKHR old_swapchain) { 1694 | info.old_swapchain = old_swapchain; 1695 | return *this; 1696 | } 1697 | SwapchainBuilder& SwapchainBuilder::set_old_swapchain (Swapchain const& swapchain) { 1698 | info.old_swapchain = swapchain.swapchain; 1699 | return *this; 1700 | } 1701 | SwapchainBuilder& SwapchainBuilder::set_desired_extent (uint32_t width, uint32_t height) { 1702 | info.desired_width = width; 1703 | info.desired_height = height; 1704 | return *this; 1705 | } 1706 | SwapchainBuilder& SwapchainBuilder::set_desired_format (VkSurfaceFormatKHR format) { 1707 | info.desired_formats.insert (info.desired_formats.begin (), format); 1708 | return *this; 1709 | } 1710 | SwapchainBuilder& SwapchainBuilder::add_fallback_format (VkSurfaceFormatKHR format) { 1711 | info.desired_formats.push_back (format); 1712 | return *this; 1713 | } 1714 | SwapchainBuilder& SwapchainBuilder::use_default_format_selection () { 1715 | info.desired_formats.clear (); 1716 | add_desired_formats (info.desired_formats); 1717 | return *this; 1718 | } 1719 | 1720 | SwapchainBuilder& SwapchainBuilder::set_desired_present_mode (VkPresentModeKHR present_mode) { 1721 | info.desired_present_modes.insert (info.desired_present_modes.begin (), present_mode); 1722 | return *this; 1723 | } 1724 | SwapchainBuilder& SwapchainBuilder::add_fallback_present_mode (VkPresentModeKHR present_mode) { 1725 | info.desired_present_modes.push_back (present_mode); 1726 | return *this; 1727 | } 1728 | SwapchainBuilder& SwapchainBuilder::use_default_present_mode_selection () { 1729 | info.desired_present_modes.clear (); 1730 | add_desired_present_modes (info.desired_present_modes); 1731 | return *this; 1732 | } 1733 | SwapchainBuilder& SwapchainBuilder::set_allocation_callbacks (VkAllocationCallbacks* callbacks) { 1734 | info.allocation_callbacks = callbacks; 1735 | return *this; 1736 | } 1737 | SwapchainBuilder& SwapchainBuilder::set_image_usage_flags (VkImageUsageFlags usage_flags) { 1738 | info.image_usage_flags = usage_flags; 1739 | return *this; 1740 | } 1741 | SwapchainBuilder& SwapchainBuilder::add_image_usage_flags (VkImageUsageFlags usage_flags) { 1742 | info.image_usage_flags = info.image_usage_flags | usage_flags; 1743 | return *this; 1744 | } 1745 | SwapchainBuilder& SwapchainBuilder::use_default_image_usage_flags () { 1746 | info.image_usage_flags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 1747 | return *this; 1748 | } 1749 | SwapchainBuilder& SwapchainBuilder::set_image_array_layer_count (uint32_t array_layer_count) { 1750 | info.array_layer_count = array_layer_count; 1751 | return *this; 1752 | } 1753 | SwapchainBuilder& SwapchainBuilder::set_clipped (bool clipped) { 1754 | info.clipped = clipped; 1755 | return *this; 1756 | } 1757 | SwapchainBuilder& SwapchainBuilder::set_create_flags (VkSwapchainCreateFlagBitsKHR create_flags) { 1758 | info.create_flags = create_flags; 1759 | return *this; 1760 | } 1761 | SwapchainBuilder& SwapchainBuilder::set_pre_transform_flags (VkSurfaceTransformFlagBitsKHR pre_transform_flags) { 1762 | info.pre_transform = pre_transform_flags; 1763 | return *this; 1764 | } 1765 | SwapchainBuilder& SwapchainBuilder::set_composite_alpha_flags (VkCompositeAlphaFlagBitsKHR composite_alpha_flags) { 1766 | info.composite_alpha = composite_alpha_flags; 1767 | return *this; 1768 | } 1769 | 1770 | void SwapchainBuilder::add_desired_formats (std::vector& formats) const { 1771 | formats.push_back ({ VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }); 1772 | formats.push_back ({ VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }); 1773 | } 1774 | void SwapchainBuilder::add_desired_present_modes (std::vector& modes) const { 1775 | modes.push_back (VK_PRESENT_MODE_MAILBOX_KHR); 1776 | modes.push_back (VK_PRESENT_MODE_FIFO_KHR); 1777 | } 1778 | } // namespace vkb -------------------------------------------------------------------------------- /VkBootstrap.h: -------------------------------------------------------------------------------- 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 20 | 21 | #include 22 | #include 23 | 24 | #include 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 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 get_system_info (); 178 | static detail::Result 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 available_layers; 186 | std::vector 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 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 layers; 288 | std::vector extensions; 289 | VkInstanceCreateFlags flags = 0; 290 | std::vector 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 disabled_validation_checks; 302 | std::vector enabled_validation_features; 303 | std::vector 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 get_queue_families () const; 348 | 349 | private: 350 | std::vector extensions_to_enable; 351 | std::vector 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 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 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 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 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 required_extensions; 452 | std::vector 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 queue_families; 478 | VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; 479 | 480 | detail::Result 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 get_dedicated_queue_index (QueueType type) const; 483 | 484 | detail::Result 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 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 priorities); 492 | uint32_t index = 0; 493 | uint32_t count = 0; 494 | std::vector 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 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 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 DeviceBuilder& add_pNext (T* structure) { 513 | info.pNext_chain.push_back (reinterpret_cast (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 pNext_chain; 524 | PhysicalDevice physical_device; 525 | VkSurfaceKHR surface = VK_NULL_HANDLE; 526 | bool defer_surface_initialization = false; 527 | std::vector queue_families; 528 | VkPhysicalDeviceFeatures features{}; 529 | std::vector extensions_to_enable; 530 | std::vector 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> 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> get_image_views (); 550 | void destroy_image_views (std::vector 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 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 SwapchainBuilder& add_pNext (T* structure) { 615 | info.pNext_chain.push_back (reinterpret_cast (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& formats) const; 624 | void add_desired_present_modes (std::vector& modes) const; 625 | 626 | struct SwapchainInfo { 627 | VkPhysicalDevice physical_device = VK_NULL_HANDLE; 628 | VkDevice device = VK_NULL_HANDLE; 629 | std::vector pNext_chain; 630 | VkSwapchainCreateFlagBitsKHR create_flags = static_cast (0); 631 | VkSurfaceKHR surface = VK_NULL_HANDLE; 632 | std::vector 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 (0); 640 | VkCompositeAlphaFlagBitsKHR composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 641 | std::vector 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 : true_type {}; 653 | template <> struct is_error_code_enum : true_type {}; 654 | template <> struct is_error_code_enum : true_type {}; 655 | template <> struct is_error_code_enum : true_type {}; 656 | template <> struct is_error_code_enum : true_type {}; 657 | } // namespace std -------------------------------------------------------------------------------- /compute.cxx: -------------------------------------------------------------------------------- 1 | #include "context.hxx" 2 | #include "transform.hxx" 3 | #include "launch.hxx" 4 | #include 5 | 6 | template 7 | [[using spirv: comp, local_size(NT), push]] 8 | void saxpy(int count, type_t a, type_t* x, type_t* y) { 9 | int gid = glcomp_GlobalInvocationID.x; 10 | if(gid < count) 11 | y[gid] += a * x[gid]; 12 | } 13 | 14 | int main() { 15 | context_t context; 16 | 17 | // Allocate test data storage. 18 | int count = 100; 19 | float* x = context.alloc_gpu(count); 20 | float* y = context.alloc_gpu(count); 21 | 22 | // Create a command buffer. 23 | cmd_buffer_t cmd_buffer(context); 24 | cmd_buffer.begin(); 25 | 26 | // Initialize the data on the GPU using lambda closure syntax. This is 27 | // better for embarrassingly parallel launches. The lambda is invoked once 28 | // for each index < count. 29 | vk_transform(count, cmd_buffer, [=](int index) { 30 | x[index] = index; 31 | y[index] = 2 * index + 1; 32 | }); 33 | 34 | // Perform SAXPY with a chevron launch. This launches the compute shader on 35 | // thread blocks. This is best for kernels requiring cooperative parallel 36 | // programming. You control the block size. The first chevron argument is 37 | // the number of blocks, not the number of threads. 38 | float a = 3; 39 | int num_blocks = (count + 127) / 128; 40 | saxpy<<>>(count, a, x, y); 41 | 42 | // Copy the data to host memory. 43 | float* host = context.alloc_cpu(count); 44 | 45 | cmd_buffer.host_barrier(); 46 | context.memcpy(cmd_buffer, host, y, sizeof(float) * count); 47 | 48 | // End and submit the command buffer. 49 | cmd_buffer.end(); 50 | context.submit(cmd_buffer); 51 | 52 | // And wait for it to be done. 53 | vkQueueWaitIdle(context.queue); 54 | 55 | // Print our results. 56 | for(int i = 0; i < count; ++i) 57 | printf("%3d: %f\n", i, host[i]); 58 | 59 | context.free(x); 60 | context.free(y); 61 | context.free(host); 62 | 63 | return 0; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /context.cxx: -------------------------------------------------------------------------------- 1 | // #define USE_VALIDATION 2 | 3 | #include "VkBootstrap.h" 4 | 5 | #define VMA_IMPLEMENTATION 6 | #include "vk_mem_alloc.h" 7 | 8 | #include 9 | #include 10 | #include "context.hxx" 11 | 12 | context_t::context_t() { 13 | // Create the instance. 14 | vkb::InstanceBuilder builder; 15 | auto inst_ret = builder.set_app_name("saxpy") 16 | .require_api_version(1, 2) 17 | #ifdef USE_VALIDATION 18 | .request_validation_layers () 19 | .use_default_debug_messenger () 20 | .add_debug_messenger_severity(VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) 21 | .add_validation_feature_enable(VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT) 22 | #endif 23 | .set_headless() 24 | .build (); 25 | if (!inst_ret) { 26 | std::cerr << "Failed to create Vulkan instance. Error: " << inst_ret.error().message() << "\n"; 27 | exit(1); 28 | } 29 | vkb::Instance vkb_inst = inst_ret.value(); 30 | instance = vkb_inst.instance; 31 | 32 | // Create the physical device. 33 | 34 | vkb::PhysicalDeviceSelector selector{ vkb_inst }; 35 | auto phys_ret = selector 36 | .set_minimum_version (1, 2) 37 | .add_required_extension("VK_KHR_buffer_device_address") 38 | .add_required_extension("VK_KHR_shader_non_semantic_info") 39 | .require_dedicated_transfer_queue() 40 | .select(); 41 | if (!phys_ret) { 42 | std::cerr << "Failed to select Vulkan Physical Device. Error: " << phys_ret.error().message() << "\n"; 43 | exit(1); 44 | } 45 | 46 | vkb::PhysicalDevice vkb_phys_device = phys_ret.value(); 47 | physical_device = vkb_phys_device.physical_device; 48 | 49 | // Create the device. 50 | vkb::DeviceBuilder device_builder { vkb_phys_device }; 51 | VkPhysicalDeviceBufferDeviceAddressFeaturesKHR feature1 { 52 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR, 53 | nullptr, 54 | true 55 | }; 56 | device_builder.add_pNext(&feature1); 57 | 58 | VkPhysicalDeviceFloat16Int8FeaturesKHR feature2 { 59 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, 60 | nullptr, 61 | false, 62 | true 63 | }; 64 | device_builder.add_pNext(&feature2); 65 | 66 | // automatically propagate needed data from instance & physical device 67 | auto dev_ret = device_builder.build(); 68 | if (!dev_ret) { 69 | std::cerr << "Failed to create Vulkan device. Error: " << dev_ret.error().message() << "\n"; 70 | exit(1); 71 | } 72 | 73 | vkb::Device vkb_device = dev_ret.value(); 74 | device = vkb_device.device; 75 | 76 | // Create the compute queue. 77 | // Get the graphics queue with a helper function 78 | auto queue_ret = vkb_device.get_queue(vkb::QueueType::compute); 79 | if (!queue_ret) { 80 | std::cerr << "Failed to get queue. Error: " << queue_ret.error().message() << "\n"; 81 | exit(1); 82 | } 83 | queue = queue_ret.value(); 84 | queue_index = vkb_device.get_queue_index(vkb::QueueType::compute).value(); 85 | 86 | // Create a command pool. 87 | VkCommandPoolCreateInfo cmdPoolInfo { 88 | VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 89 | nullptr, 90 | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, 91 | queue_index 92 | }; 93 | vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &command_pool); 94 | 95 | // Create the pipeline cache. 96 | VkPipelineCacheCreateInfo pipelineCacheCreateInfo { 97 | VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO 98 | }; 99 | vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, 100 | &pipeline_cache); 101 | 102 | // Create the allocator. 103 | VmaAllocatorCreateInfo allocatorInfo = {}; 104 | allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_2; 105 | allocatorInfo.physicalDevice = physical_device; 106 | allocatorInfo.device = device; 107 | allocatorInfo.instance = instance; 108 | allocatorInfo.flags = 109 | VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT | 110 | VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; 111 | 112 | vmaCreateAllocator(&allocatorInfo, &allocator); 113 | 114 | // Allocate a 16MB staging buffer. 115 | staging = alloc_cpu(16<< 20, VK_BUFFER_USAGE_TRANSFER_DST_BIT | 116 | VK_BUFFER_USAGE_TRANSFER_SRC_BIT); 117 | } 118 | 119 | context_t::~context_t() { 120 | // Destroy the staging memory. 121 | free(staging); 122 | 123 | // Destroy the allocator. 124 | assert(!buffer_map.size()); 125 | vmaDestroyAllocator(allocator); 126 | 127 | // Destroy the pipelines. 128 | for(auto it : transforms) { 129 | transform_t& transform = it.second; 130 | vkDestroyPipeline(device, transform.pipeline, nullptr); 131 | vkDestroyPipelineLayout(device, transform.pipeline_layout, nullptr); 132 | } 133 | 134 | // Destroy the shader modules. 135 | for(auto it : modules) 136 | vkDestroyShaderModule(device, it.second, nullptr); 137 | 138 | // Destroy the cache and command pool. 139 | vkDestroyPipelineCache(device, pipeline_cache, nullptr); 140 | vkDestroyCommandPool(device, command_pool, nullptr); 141 | vkDestroyDevice(device, nullptr); 142 | 143 | // Destroy the messenger. 144 | // TODO: 145 | 146 | // Destroy the instance. 147 | vkDestroyInstance(instance, nullptr); 148 | } 149 | 150 | //////////////////////////////////////////////////////////////////////////////// 151 | 152 | void* context_t::alloc_gpu(size_t size, uint32_t usage) { 153 | VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; 154 | bufferInfo.size = size; 155 | bufferInfo.usage = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | usage; 156 | 157 | VmaAllocationCreateInfo allocInfo = {}; 158 | allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; 159 | 160 | VkBuffer buffer; 161 | VmaAllocation allocation; 162 | vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, 163 | &allocation, nullptr); 164 | 165 | VkBufferDeviceAddressInfo addressInfo { 166 | VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, 167 | nullptr, 168 | buffer 169 | }; 170 | VkDeviceAddress address = vkGetBufferDeviceAddress(device, &addressInfo); 171 | void* p = (void*)address; 172 | 173 | buffer_map.insert(std::make_pair(p, buffer_t { size, usage, buffer, allocation })); 174 | return p; 175 | } 176 | 177 | void* context_t::alloc_cpu(size_t size, uint32_t usage) { 178 | VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; 179 | bufferInfo.size = size; 180 | bufferInfo.usage = usage; 181 | 182 | VmaAllocationCreateInfo allocInfo = {}; 183 | allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; 184 | 185 | VkBuffer buffer; 186 | VmaAllocation allocation; 187 | vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, 188 | &allocation, nullptr); 189 | 190 | void* p; 191 | vmaMapMemory(allocator, allocation, &p); 192 | 193 | buffer_map.insert(std::make_pair(p, buffer_t { size, usage | 194 | 0x8000'0000, buffer, allocation })); 195 | return p; 196 | } 197 | void context_t::free(void* p) { 198 | auto it = buffer_map.find(p); 199 | assert(buffer_map.end() != it && p == it->first); 200 | 201 | if(it->second.is_cpu()) 202 | vmaUnmapMemory(allocator, it->second.allocation); 203 | 204 | vmaDestroyBuffer(allocator, it->second.buffer, it->second.allocation); 205 | buffer_map.erase(it); 206 | } 207 | 208 | context_t::buffer_it_t context_t::find_buffer(void* p) { 209 | buffer_it_t it = buffer_map.lower_bound(p); 210 | if(buffer_map.end() != it) { 211 | // Check the range. 212 | const char* p2 = (const char*)it->first + it->second.size; 213 | if(p >= p2) 214 | it = buffer_map.end(); 215 | } 216 | return it; 217 | } 218 | 219 | void context_t::memcpy(VkCommandBuffer cmd_buffer, void* dest, void* source, 220 | size_t size) { 221 | 222 | buffer_it_t dest_it = find_buffer(dest); 223 | buffer_it_t source_it = find_buffer(source); 224 | 225 | // At least one side must refer to buffer memory. Otherwise we'd just use 226 | // normal memcpy. 227 | assert(buffer_map.end() != dest_it || buffer_map.end() != source_it); 228 | 229 | if(buffer_map.end() == dest_it) { 230 | if(source_it->second.is_cpu()) { 231 | ::memcpy(dest, source, size); 232 | 233 | } else { 234 | // The source is GPU memory. First copy into the staging buffer. 235 | memcpy(cmd_buffer, staging, source, size); 236 | 237 | // FENCE. 238 | 239 | // Then memcpy to the dest. 240 | ::memcpy(dest, staging, size); 241 | } 242 | 243 | } else if(buffer_map.end() == source_it) { 244 | if(dest_it->second.is_cpu()) { 245 | ::memcpy(dest, source, size); 246 | 247 | } else { 248 | // The dest is GPU memory. First copy into the staging buffer. 249 | ::memcpy(staging, source, size); 250 | 251 | // FENCE 252 | 253 | // Now copy the staging buffer into the dest. 254 | memcpy(cmd_buffer, dest, staging, size); 255 | } 256 | 257 | } else { 258 | // Copy between buffers. 259 | VkBufferCopy copyRegion { }; 260 | copyRegion.srcOffset = ((const char*)source - (const char*)source_it->first); 261 | copyRegion.dstOffset = ((const char*)dest - (const char*)dest_it->first); 262 | copyRegion.size = size; 263 | vkCmdCopyBuffer(cmd_buffer, source_it->second.buffer, 264 | dest_it->second.buffer, 1, ©Region); 265 | } 266 | } 267 | 268 | //////////////////////////////////////////////////////////////////////////////// 269 | 270 | VkShaderModule context_t::create_module(const char* data, size_t size) { 271 | auto it = modules.find(data); 272 | if(modules.end() == it) { 273 | VkShaderModuleCreateInfo createInfo { 274 | VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, 275 | nullptr, 276 | 0, 277 | size, 278 | (const uint32_t*)data 279 | }; 280 | 281 | VkShaderModule module; 282 | vkCreateShaderModule(device, &createInfo, nullptr, &module); 283 | it = modules.insert(std::pair(data, module)).first; 284 | } 285 | 286 | return it->second; 287 | } 288 | 289 | //////////////////////////////////////////////////////////////////////////////// 290 | 291 | void context_t::dispatch_compute(VkCommandBuffer cmd_buffer, const char* name, 292 | VkShaderModule module, int num_blocks, uint32_t push_size, 293 | const void* push_data) { 294 | 295 | auto it = transforms.find(name); 296 | if(transforms.end() == it) { 297 | // Define a pipeline layout that takes only a push constant. 298 | VkPipelineLayoutCreateInfo create_info { 299 | VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO 300 | }; 301 | create_info.pushConstantRangeCount = 1; 302 | 303 | VkPushConstantRange range { VK_SHADER_STAGE_COMPUTE_BIT, 0, push_size }; 304 | create_info.pPushConstantRanges = ⦥ 305 | 306 | VkPipelineLayout pipeline_layout; 307 | vkCreatePipelineLayout(device, &create_info, nullptr, 308 | &pipeline_layout); 309 | 310 | // Create the compute pipeline. 311 | VkComputePipelineCreateInfo computePipelineCreateInfo { 312 | VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, 313 | nullptr, 314 | 0, 315 | { 316 | VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 317 | 0, 318 | 0, 319 | VK_SHADER_STAGE_COMPUTE_BIT, 320 | module, 321 | name 322 | }, 323 | pipeline_layout 324 | }; 325 | 326 | VkPipeline pipeline; 327 | vkCreateComputePipelines(device, pipeline_cache, 1, 328 | &computePipelineCreateInfo, nullptr, &pipeline); 329 | 330 | transform_t transform { 331 | pipeline_layout, 332 | pipeline 333 | }; 334 | 335 | it = transforms.insert(std::make_pair(name, transform)).first; 336 | } 337 | 338 | transform_t transform = it->second; 339 | 340 | vkCmdBindPipeline(cmd_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, 341 | transform.pipeline); 342 | 343 | vkCmdPushConstants(cmd_buffer, transform.pipeline_layout, 344 | VK_SHADER_STAGE_COMPUTE_BIT, 0, push_size, push_data); 345 | 346 | vkCmdDispatch(cmd_buffer, num_blocks, 1, 1); 347 | 348 | 349 | VkMemoryBarrier memoryBarrier = { 350 | VK_STRUCTURE_TYPE_MEMORY_BARRIER, 351 | nullptr, 352 | VK_ACCESS_SHADER_WRITE_BIT, 353 | VK_ACCESS_SHADER_READ_BIT 354 | }; 355 | vkCmdPipelineBarrier(cmd_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 356 | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &memoryBarrier, 0, nullptr, 0, 357 | nullptr); 358 | } 359 | 360 | void context_t::submit(VkCommandBuffer cmd_buffer) { 361 | // Submit the command buffer. 362 | VkSubmitInfo submitInfo { 363 | VK_STRUCTURE_TYPE_SUBMIT_INFO 364 | }; 365 | submitInfo.commandBufferCount = 1; 366 | submitInfo.pCommandBuffers = &cmd_buffer; 367 | 368 | vkQueueSubmit(queue, 1, &submitInfo, 0); 369 | } 370 | 371 | cmd_buffer_t::cmd_buffer_t(context_t& context) : context(context) { 372 | VkCommandBufferAllocateInfo allocInfo { 373 | VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 374 | nullptr, 375 | context.command_pool, 376 | VK_COMMAND_BUFFER_LEVEL_PRIMARY, 377 | 1 378 | }; 379 | vkAllocateCommandBuffers(context.device, &allocInfo, &vkCommandBuffer); 380 | } 381 | 382 | cmd_buffer_t::~cmd_buffer_t() { 383 | vkFreeCommandBuffers(context.device, context.command_pool, 1, 384 | &vkCommandBuffer); 385 | } 386 | 387 | void cmd_buffer_t::begin() { 388 | VkCommandBufferBeginInfo beginInfo { 389 | VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 390 | nullptr, 391 | VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT 392 | }; 393 | vkBeginCommandBuffer(vkCommandBuffer, &beginInfo); 394 | } 395 | 396 | void cmd_buffer_t::end() { 397 | vkEndCommandBuffer(vkCommandBuffer); 398 | } 399 | 400 | void cmd_buffer_t::host_barrier() { 401 | VkMemoryBarrier memoryBarrier { 402 | VK_STRUCTURE_TYPE_MEMORY_BARRIER, 403 | nullptr, 404 | VK_ACCESS_SHADER_WRITE_BIT, 405 | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_HOST_READ_BIT 406 | }; 407 | vkCmdPipelineBarrier(vkCommandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 408 | VK_PIPELINE_STAGE_HOST_BIT, 0, 1, &memoryBarrier, 0, nullptr, 0, nullptr); 409 | } 410 | -------------------------------------------------------------------------------- /context.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "vk_mem_alloc.h" 5 | 6 | template 7 | struct tuple_t { 8 | types_t @(int...) ...; 9 | }; 10 | 11 | struct context_t { 12 | context_t(); 13 | ~context_t(); 14 | 15 | // Make it non-copyable. 16 | context_t(const context_t&) = delete; 17 | context_t& operator=(const context_t&) = delete; 18 | 19 | operator VkInstance() const noexcept { return instance; } 20 | operator VkDevice() const noexcept { return device; } 21 | operator VkPhysicalDevice() const noexcept { return physical_device; } 22 | 23 | VkInstance instance; 24 | 25 | VkPhysicalDevice physical_device; 26 | VkDevice device; 27 | 28 | uint32_t queue_index; 29 | VkQueue queue; 30 | 31 | VkCommandPool command_pool; 32 | 33 | VkPipelineCache pipeline_cache; 34 | 35 | VmaAllocator allocator; 36 | 37 | struct buffer_t { 38 | uint32_t size; 39 | uint32_t usage; 40 | VkBuffer buffer; 41 | VmaAllocation allocation; 42 | 43 | bool is_cpu() const noexcept { 44 | return 0x8000'0000 & usage; 45 | } 46 | }; 47 | typedef std::map buffer_map_t; 48 | typedef buffer_map_t::iterator buffer_it_t; 49 | buffer_map_t buffer_map; 50 | void* staging; 51 | 52 | void* alloc_gpu(size_t size, 53 | uint32_t usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT); 54 | 55 | template 56 | type_t* alloc_gpu(size_t count, 57 | uint32_t usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT) { 58 | return (type_t*)alloc_gpu(sizeof(type_t) * count, usage); 59 | } 60 | 61 | void* alloc_cpu(size_t size, 62 | uint32_t usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT); 63 | 64 | template 65 | type_t* alloc_cpu(size_t count, 66 | uint32_t usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT) { 67 | return (type_t*)alloc_cpu(sizeof(type_t) * count, usage); 68 | } 69 | 70 | void free(void* p); 71 | buffer_it_t find_buffer(void* p); 72 | 73 | // Copy between buffer memory. At least one operand must map to a buffer. 74 | void memcpy(VkCommandBuffer cmd_buffer, void* dest, void* source, 75 | size_t size); 76 | 77 | std::map modules; 78 | VkShaderModule create_module(const char* data, size_t size); 79 | 80 | struct transform_t { 81 | VkPipelineLayout pipeline_layout; 82 | VkPipeline pipeline; 83 | }; 84 | std::map transforms; 85 | 86 | void dispatch_compute(VkCommandBuffer cmd_buffer, const char* name, 87 | VkShaderModule module, int num_blocks, uint32_t push_size, 88 | const void* push_data); 89 | 90 | void submit(VkCommandBuffer cmd_buffer); 91 | }; 92 | 93 | struct cmd_buffer_t { 94 | cmd_buffer_t(context_t& context); 95 | ~cmd_buffer_t(); 96 | 97 | operator VkCommandBuffer() { return vkCommandBuffer; } 98 | 99 | void begin(); 100 | void end(); 101 | void host_barrier(); 102 | 103 | context_t& context; 104 | VkCommandBuffer vkCommandBuffer; 105 | }; 106 | -------------------------------------------------------------------------------- /launch.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "context.hxx" 3 | 4 | // Chevron launch on a SPIR-V compute shader performs ADL lookup to find 5 | // this symbol, and overload resolution to select this overload. 6 | template 7 | void spirv_compute_dispatch(int num_blocks, cmd_buffer_t& cmd_buffer, 8 | params_t... params) { 9 | 10 | static_assert((... && std::is_trivially_copyable_v)); 11 | tuple_t storage { params... }; 12 | 13 | cmd_buffer.context.dispatch_compute( 14 | cmd_buffer, 15 | @spirv(F), 16 | cmd_buffer.context.create_module(__spirv_data, __spirv_size), 17 | num_blocks, 18 | sizeof(storage), 19 | &storage 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /transform.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "context.hxx" 3 | 4 | namespace cvk { 5 | 6 | template 7 | [[using spirv: comp, local_size(256), push]] 8 | void transform_shader(int count, func_t func) { 9 | int gid = glcomp_GlobalInvocationID.x; 10 | 11 | if(gid >= count) 12 | return; 13 | 14 | func(gid); 15 | } 16 | 17 | } // namespace cvk 18 | 19 | template 20 | static void vk_transform(int count, cmd_buffer_t& cmd_buffer, 21 | func_t func) { 22 | 23 | int num_blocks = (count + 255) / 256; 24 | cvk::transform_shader<<>>(count, func); 25 | } 26 | --------------------------------------------------------------------------------