Atlas - testvulkan.c

Home / ext / SDL / test Lines: 1 | Size: 48298 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely. 11*/ 12#include <stdlib.h> 13 14#include <SDL3/SDL_test_common.h> 15#include <SDL3/SDL_main.h> 16 17#if defined(SDL_PLATFORM_ANDROID) && defined(__ARM_EABI__) && !defined(__ARM_ARCH_7A__) 18 19int main(int argc, char *argv[]) 20{ 21 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No Vulkan support on this system"); 22 return 1; 23} 24 25#else 26 27#define VK_NO_PROTOTYPES 28#ifdef HAVE_VULKAN_H 29#include <vulkan/vulkan.h> 30#else 31/* SDL includes a copy for building on systems without the Vulkan SDK */ 32#include "../src/video/khronos/vulkan/vulkan.h" 33#endif 34#include <SDL3/SDL_vulkan.h> 35 36#ifndef UINT64_MAX /* VS2008 */ 37#define UINT64_MAX 18446744073709551615 38#endif 39 40#define VULKAN_FUNCTIONS() \ 41 VULKAN_DEVICE_FUNCTION(vkAcquireNextImageKHR) \ 42 VULKAN_DEVICE_FUNCTION(vkAllocateCommandBuffers) \ 43 VULKAN_DEVICE_FUNCTION(vkBeginCommandBuffer) \ 44 VULKAN_DEVICE_FUNCTION(vkCmdClearColorImage) \ 45 VULKAN_DEVICE_FUNCTION(vkCmdPipelineBarrier) \ 46 VULKAN_DEVICE_FUNCTION(vkCreateCommandPool) \ 47 VULKAN_DEVICE_FUNCTION(vkCreateFence) \ 48 VULKAN_DEVICE_FUNCTION(vkCreateImageView) \ 49 VULKAN_DEVICE_FUNCTION(vkCreateSemaphore) \ 50 VULKAN_DEVICE_FUNCTION(vkCreateSwapchainKHR) \ 51 VULKAN_DEVICE_FUNCTION(vkDestroyCommandPool) \ 52 VULKAN_DEVICE_FUNCTION(vkDestroyDevice) \ 53 VULKAN_DEVICE_FUNCTION(vkDestroyFence) \ 54 VULKAN_DEVICE_FUNCTION(vkDestroyImageView) \ 55 VULKAN_DEVICE_FUNCTION(vkDestroySemaphore) \ 56 VULKAN_DEVICE_FUNCTION(vkDestroySwapchainKHR) \ 57 VULKAN_DEVICE_FUNCTION(vkDeviceWaitIdle) \ 58 VULKAN_DEVICE_FUNCTION(vkEndCommandBuffer) \ 59 VULKAN_DEVICE_FUNCTION(vkFreeCommandBuffers) \ 60 VULKAN_DEVICE_FUNCTION(vkGetDeviceQueue) \ 61 VULKAN_DEVICE_FUNCTION(vkGetFenceStatus) \ 62 VULKAN_DEVICE_FUNCTION(vkGetSwapchainImagesKHR) \ 63 VULKAN_DEVICE_FUNCTION(vkQueuePresentKHR) \ 64 VULKAN_DEVICE_FUNCTION(vkQueueSubmit) \ 65 VULKAN_DEVICE_FUNCTION(vkResetCommandBuffer) \ 66 VULKAN_DEVICE_FUNCTION(vkResetFences) \ 67 VULKAN_DEVICE_FUNCTION(vkWaitForFences) \ 68 VULKAN_GLOBAL_FUNCTION(vkCreateInstance) \ 69 VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceExtensionProperties) \ 70 VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceLayerProperties) \ 71 VULKAN_INSTANCE_FUNCTION(vkCreateDevice) \ 72 VULKAN_INSTANCE_FUNCTION(vkDestroyInstance) \ 73 VULKAN_INSTANCE_FUNCTION(vkDestroySurfaceKHR) \ 74 VULKAN_INSTANCE_FUNCTION(vkEnumerateDeviceExtensionProperties) \ 75 VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices) \ 76 VULKAN_INSTANCE_FUNCTION(vkGetDeviceProcAddr) \ 77 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures) \ 78 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties) \ 79 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties) \ 80 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ 81 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR) \ 82 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR) \ 83 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR) 84 85#define VULKAN_DEVICE_FUNCTION(name) static PFN_##name name = NULL; 86#define VULKAN_GLOBAL_FUNCTION(name) static PFN_##name name = NULL; 87#define VULKAN_INSTANCE_FUNCTION(name) static PFN_##name name = NULL; 88VULKAN_FUNCTIONS() 89#undef VULKAN_DEVICE_FUNCTION 90#undef VULKAN_GLOBAL_FUNCTION 91#undef VULKAN_INSTANCE_FUNCTION 92static PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; 93 94/* Based on the headers found in 95 * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers 96 */ 97#if VK_HEADER_VERSION < 22 98enum 99{ 100 VK_ERROR_FRAGMENTED_POOL = -12, 101}; 102#endif 103#if VK_HEADER_VERSION < 38 104enum 105{ 106 VK_ERROR_OUT_OF_POOL_MEMORY_KHR = -1000069000 107}; 108#endif 109 110static const char *getVulkanResultString(VkResult result) 111{ 112 switch ((int)result) { 113#define RESULT_CASE(x) \ 114 case x: \ 115 return #x 116 RESULT_CASE(VK_SUCCESS); 117 RESULT_CASE(VK_NOT_READY); 118 RESULT_CASE(VK_TIMEOUT); 119 RESULT_CASE(VK_EVENT_SET); 120 RESULT_CASE(VK_EVENT_RESET); 121 RESULT_CASE(VK_INCOMPLETE); 122 RESULT_CASE(VK_ERROR_OUT_OF_HOST_MEMORY); 123 RESULT_CASE(VK_ERROR_OUT_OF_DEVICE_MEMORY); 124 RESULT_CASE(VK_ERROR_INITIALIZATION_FAILED); 125 RESULT_CASE(VK_ERROR_DEVICE_LOST); 126 RESULT_CASE(VK_ERROR_MEMORY_MAP_FAILED); 127 RESULT_CASE(VK_ERROR_LAYER_NOT_PRESENT); 128 RESULT_CASE(VK_ERROR_EXTENSION_NOT_PRESENT); 129 RESULT_CASE(VK_ERROR_FEATURE_NOT_PRESENT); 130 RESULT_CASE(VK_ERROR_INCOMPATIBLE_DRIVER); 131 RESULT_CASE(VK_ERROR_TOO_MANY_OBJECTS); 132 RESULT_CASE(VK_ERROR_FORMAT_NOT_SUPPORTED); 133 RESULT_CASE(VK_ERROR_FRAGMENTED_POOL); 134 RESULT_CASE(VK_ERROR_SURFACE_LOST_KHR); 135 RESULT_CASE(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); 136 RESULT_CASE(VK_SUBOPTIMAL_KHR); 137 RESULT_CASE(VK_ERROR_OUT_OF_DATE_KHR); 138 RESULT_CASE(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); 139 RESULT_CASE(VK_ERROR_VALIDATION_FAILED_EXT); 140 RESULT_CASE(VK_ERROR_OUT_OF_POOL_MEMORY_KHR); 141 RESULT_CASE(VK_ERROR_INVALID_SHADER_NV); 142#undef RESULT_CASE 143 default: 144 break; 145 } 146 return (result < 0) ? "VK_ERROR_<Unknown>" : "VK_<Unknown>"; 147} 148 149typedef struct VulkanContext 150{ 151 SDL_Window *window; 152 VkInstance instance; 153 VkDevice device; 154 VkSurfaceKHR surface; 155 VkSwapchainKHR swapchain; 156 VkPhysicalDeviceProperties physicalDeviceProperties; 157 VkPhysicalDeviceFeatures physicalDeviceFeatures; 158 uint32_t graphicsQueueFamilyIndex; 159 uint32_t presentQueueFamilyIndex; 160 VkPhysicalDevice physicalDevice; 161 VkQueue graphicsQueue; 162 VkQueue presentQueue; 163 VkSemaphore imageAvailableSemaphore; 164 VkSemaphore renderingFinishedSemaphore; 165 VkSurfaceCapabilitiesKHR surfaceCapabilities; 166 VkSurfaceFormatKHR *surfaceFormats; 167 uint32_t surfaceFormatsAllocatedCount; 168 uint32_t surfaceFormatsCount; 169 uint32_t swapchainDesiredImageCount; 170 VkSurfaceFormatKHR surfaceFormat; 171 VkExtent2D swapchainSize; 172 VkCommandPool commandPool; 173 uint32_t swapchainImageCount; 174 VkImage *swapchainImages; 175 VkCommandBuffer *commandBuffers; 176 VkFence *fences; 177} VulkanContext; 178 179static SDLTest_CommonState *state; 180static VulkanContext *vulkanContexts = NULL; /* an array of state->num_windows items */ 181static VulkanContext *vulkanContext = NULL; /* for the currently-rendering window */ 182 183static void shutdownVulkan(bool doDestroySwapchain); 184 185/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ 186static void quit(int rc) 187{ 188 shutdownVulkan(true); 189 SDLTest_CommonQuit(state); 190 /* Let 'main()' return normally */ 191 if (rc != 0) { 192 exit(rc); 193 } 194} 195 196static void loadGlobalFunctions(void) 197{ 198 vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr(); 199 if (!vkGetInstanceProcAddr) { 200 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 201 "SDL_Vulkan_GetVkGetInstanceProcAddr(): %s", 202 SDL_GetError()); 203 quit(2); 204 } 205 206#define VULKAN_DEVICE_FUNCTION(name) 207#define VULKAN_GLOBAL_FUNCTION(name) \ 208 name = (PFN_##name)vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \ 209 if (!name) { \ 210 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \ 211 "vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed"); \ 212 quit(2); \ 213 } 214#define VULKAN_INSTANCE_FUNCTION(name) 215 VULKAN_FUNCTIONS() 216#undef VULKAN_DEVICE_FUNCTION 217#undef VULKAN_GLOBAL_FUNCTION 218#undef VULKAN_INSTANCE_FUNCTION 219} 220 221static bool checkVulkanPortability(void) 222{ 223 Uint32 extensionCount, i; 224 VkExtensionProperties *availableExtensions; 225 bool supported = false; 226 227 vkEnumerateInstanceExtensionProperties( 228 NULL, 229 &extensionCount, 230 NULL); 231 availableExtensions = SDL_malloc( 232 extensionCount * sizeof(VkExtensionProperties)); 233 vkEnumerateInstanceExtensionProperties( 234 NULL, 235 &extensionCount, 236 availableExtensions); 237 238 for (i = 0; i < extensionCount; i += 1) { 239 if (SDL_strcmp(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, availableExtensions[i].extensionName) == 0) { 240 supported = true; 241 break; 242 } 243 } 244 245 SDL_free(availableExtensions); 246 return supported; 247} 248 249static void createInstance(void) 250{ 251 VkApplicationInfo appInfo = { 0 }; 252 VkInstanceCreateInfo instanceCreateInfo = { 0 }; 253 bool supportsPortabilityEnumeration; 254 const char **instanceExtensions; 255 VkResult result; 256 257 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 258 appInfo.apiVersion = VK_API_VERSION_1_0; 259 instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 260 instanceCreateInfo.pApplicationInfo = &appInfo; 261 262 supportsPortabilityEnumeration = checkVulkanPortability(); 263 if (supportsPortabilityEnumeration) { 264 // Allocate our own extension array so that we can add the KHR_portability extensions for MoltenVK 265 Uint32 count_instance_extensions; 266 const char * const *instance_extensions = SDL_Vulkan_GetInstanceExtensions(&count_instance_extensions); 267 268 int count_extensions = count_instance_extensions + 1; 269 instanceExtensions = SDL_malloc(count_extensions * sizeof(const char *)); 270 instanceExtensions[0] = VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME; 271 SDL_memcpy((char **) &instanceExtensions[1], instance_extensions, count_instance_extensions * sizeof(const char*)); 272 273 instanceCreateInfo.enabledExtensionCount = count_extensions; 274 instanceCreateInfo.ppEnabledExtensionNames = instanceExtensions; 275 instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; 276 } else { 277 // No need to allocate anything, just use SDL's array directly 278 instanceExtensions = NULL; 279 instanceCreateInfo.ppEnabledExtensionNames = 280 SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); 281 } 282 283 result = vkCreateInstance(&instanceCreateInfo, NULL, &vulkanContext->instance); 284 285 if (instanceExtensions != NULL) { 286 SDL_free((char **) instanceExtensions); 287 } 288 289 if (result != VK_SUCCESS) { 290 vulkanContext->instance = VK_NULL_HANDLE; 291 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 292 "vkCreateInstance(): %s", 293 getVulkanResultString(result)); 294 quit(2); 295 } 296} 297 298static void loadInstanceFunctions(void) 299{ 300#define VULKAN_DEVICE_FUNCTION(name) 301#define VULKAN_GLOBAL_FUNCTION(name) 302#define VULKAN_INSTANCE_FUNCTION(name) \ 303 name = (PFN_##name)vkGetInstanceProcAddr(vulkanContext->instance, #name); \ 304 if (!name) { \ 305 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \ 306 "vkGetInstanceProcAddr(instance, \"" #name "\") failed"); \ 307 quit(2); \ 308 } 309 VULKAN_FUNCTIONS() 310#undef VULKAN_DEVICE_FUNCTION 311#undef VULKAN_GLOBAL_FUNCTION 312#undef VULKAN_INSTANCE_FUNCTION 313} 314 315static void createSurface(void) 316{ 317 if (!SDL_Vulkan_CreateSurface(vulkanContext->window, vulkanContext->instance, NULL, &vulkanContext->surface)) { 318 vulkanContext->surface = VK_NULL_HANDLE; 319 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_Vulkan_CreateSurface(): %s", SDL_GetError()); 320 quit(2); 321 } 322} 323 324static void findPhysicalDevice(void) 325{ 326 uint32_t physicalDeviceCount = 0; 327 VkPhysicalDevice *physicalDevices; 328 VkQueueFamilyProperties *queueFamiliesProperties = NULL; 329 uint32_t queueFamiliesPropertiesAllocatedSize = 0; 330 VkExtensionProperties *deviceExtensions = NULL; 331 uint32_t deviceExtensionsAllocatedSize = 0; 332 uint32_t physicalDeviceIndex; 333 VkResult result; 334 335 result = vkEnumeratePhysicalDevices(vulkanContext->instance, &physicalDeviceCount, NULL); 336 if (result != VK_SUCCESS) { 337 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 338 "vkEnumeratePhysicalDevices(): %s", 339 getVulkanResultString(result)); 340 quit(2); 341 } 342 if (physicalDeviceCount == 0) { 343 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 344 "vkEnumeratePhysicalDevices(): no physical devices"); 345 quit(2); 346 } 347 physicalDevices = (VkPhysicalDevice *)SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount); 348 if (!physicalDevices) { 349 quit(2); 350 } 351 result = vkEnumeratePhysicalDevices(vulkanContext->instance, &physicalDeviceCount, physicalDevices); 352 if (result != VK_SUCCESS) { 353 SDL_free(physicalDevices); 354 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 355 "vkEnumeratePhysicalDevices(): %s", 356 getVulkanResultString(result)); 357 quit(2); 358 } 359 vulkanContext->physicalDevice = NULL; 360 for (physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount; physicalDeviceIndex++) { 361 uint32_t queueFamiliesCount = 0; 362 uint32_t queueFamilyIndex; 363 uint32_t deviceExtensionCount = 0; 364 bool hasSwapchainExtension = false; 365 bool supportsPresent; 366 uint32_t i; 367 368 VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex]; 369 vkGetPhysicalDeviceProperties(physicalDevice, &vulkanContext->physicalDeviceProperties); 370 if (VK_VERSION_MAJOR(vulkanContext->physicalDeviceProperties.apiVersion) < 1) { 371 continue; 372 } 373 vkGetPhysicalDeviceFeatures(physicalDevice, &vulkanContext->physicalDeviceFeatures); 374 vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, NULL); 375 if (queueFamiliesCount == 0) { 376 continue; 377 } 378 if (queueFamiliesPropertiesAllocatedSize < queueFamiliesCount) { 379 SDL_free(queueFamiliesProperties); 380 queueFamiliesPropertiesAllocatedSize = queueFamiliesCount; 381 queueFamiliesProperties = (VkQueueFamilyProperties *)SDL_malloc(sizeof(VkQueueFamilyProperties) * queueFamiliesPropertiesAllocatedSize); 382 if (!queueFamiliesProperties) { 383 SDL_free(physicalDevices); 384 SDL_free(deviceExtensions); 385 quit(2); 386 } 387 } 388 vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, queueFamiliesProperties); 389 vulkanContext->graphicsQueueFamilyIndex = queueFamiliesCount; 390 vulkanContext->presentQueueFamilyIndex = queueFamiliesCount; 391 for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; queueFamilyIndex++) { 392 VkBool32 supported = 0; 393 394 if (queueFamiliesProperties[queueFamilyIndex].queueCount == 0) { 395 continue; 396 } 397 398 if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) { 399 vulkanContext->graphicsQueueFamilyIndex = queueFamilyIndex; 400 } 401 402 result = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, vulkanContext->surface, &supported); 403 if (result != VK_SUCCESS) { 404 SDL_free(physicalDevices); 405 SDL_free(queueFamiliesProperties); 406 SDL_free(deviceExtensions); 407 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 408 "vkGetPhysicalDeviceSurfaceSupportKHR(): %s", 409 getVulkanResultString(result)); 410 quit(2); 411 } 412 if (supported) { 413 /* This check isn't necessary if you are able to check a 414 * VkSurfaceKHR like above, but just as a sanity check we do 415 * this here as part of testing the API. 416 * -flibit 417 */ 418 supportsPresent = SDL_Vulkan_GetPresentationSupport(vulkanContext->instance, physicalDevice, queueFamilyIndex); 419 if (!supportsPresent) { 420 SDL_free(physicalDevices); 421 SDL_free(queueFamiliesProperties); 422 SDL_free(deviceExtensions); 423 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 424 "SDL_Vulkan_GetPresentationSupport(): %s", 425 SDL_GetError()); 426 quit(2); 427 } 428 429 vulkanContext->presentQueueFamilyIndex = queueFamilyIndex; 430 if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) { 431 break; // use this queue because it can present and do graphics 432 } 433 } 434 } 435 436 if (vulkanContext->graphicsQueueFamilyIndex == queueFamiliesCount) { // no good queues found 437 continue; 438 } 439 if (vulkanContext->presentQueueFamilyIndex == queueFamiliesCount) { // no good queues found 440 continue; 441 } 442 result = vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, NULL); 443 if (result != VK_SUCCESS) { 444 SDL_free(physicalDevices); 445 SDL_free(queueFamiliesProperties); 446 SDL_free(deviceExtensions); 447 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 448 "vkEnumerateDeviceExtensionProperties(): %s", 449 getVulkanResultString(result)); 450 quit(2); 451 } 452 if (deviceExtensionCount == 0) { 453 continue; 454 } 455 if (deviceExtensionsAllocatedSize < deviceExtensionCount) { 456 SDL_free(deviceExtensions); 457 deviceExtensionsAllocatedSize = deviceExtensionCount; 458 deviceExtensions = SDL_malloc(sizeof(VkExtensionProperties) * deviceExtensionsAllocatedSize); 459 if (!deviceExtensions) { 460 SDL_free(physicalDevices); 461 SDL_free(queueFamiliesProperties); 462 quit(2); 463 } 464 } 465 result = vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, deviceExtensions); 466 if (result != VK_SUCCESS) { 467 SDL_free(physicalDevices); 468 SDL_free(queueFamiliesProperties); 469 SDL_free(deviceExtensions); 470 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 471 "vkEnumerateDeviceExtensionProperties(): %s", 472 getVulkanResultString(result)); 473 quit(2); 474 } 475 for (i = 0; i < deviceExtensionCount; i++) { 476 if (SDL_strcmp(deviceExtensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) { 477 hasSwapchainExtension = true; 478 break; 479 } 480 } 481 if (!hasSwapchainExtension) { 482 continue; 483 } 484 vulkanContext->physicalDevice = physicalDevice; 485 break; 486 } 487 SDL_free(physicalDevices); 488 SDL_free(queueFamiliesProperties); 489 SDL_free(deviceExtensions); 490 if (!vulkanContext->physicalDevice) { 491 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Vulkan: no viable physical devices found"); 492 quit(2); 493 } 494} 495 496static void createDevice(void) 497{ 498 VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { { 0 }, { 0 } }; 499 static const float queuePriority[] = { 1.0f }; 500 VkDeviceCreateInfo deviceCreateInfo = { 0 }; 501 static const char *const deviceExtensionNames[] = { 502 VK_KHR_SWAPCHAIN_EXTENSION_NAME, 503#ifdef __APPLE__ 504 "VK_KHR_portability_subset" 505#endif 506 }; 507 VkResult result; 508 509 deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 510 deviceCreateInfo.queueCreateInfoCount = 0; 511 deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo; 512 deviceCreateInfo.pEnabledFeatures = NULL; 513 deviceCreateInfo.enabledExtensionCount = SDL_arraysize(deviceExtensionNames); 514 deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames; 515 516 deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 517 deviceQueueCreateInfo[0].queueFamilyIndex = vulkanContext->graphicsQueueFamilyIndex; 518 deviceQueueCreateInfo[0].queueCount = 1; 519 deviceQueueCreateInfo[0].pQueuePriorities = queuePriority; 520 ++deviceCreateInfo.queueCreateInfoCount; 521 522 if (vulkanContext->presentQueueFamilyIndex != vulkanContext->graphicsQueueFamilyIndex) { 523 deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 524 deviceQueueCreateInfo[1].queueFamilyIndex = vulkanContext->presentQueueFamilyIndex; 525 deviceQueueCreateInfo[1].queueCount = 1; 526 deviceQueueCreateInfo[1].pQueuePriorities = queuePriority; 527 ++deviceCreateInfo.queueCreateInfoCount; 528 } 529 530 result = vkCreateDevice(vulkanContext->physicalDevice, &deviceCreateInfo, NULL, &vulkanContext->device); 531 if (result != VK_SUCCESS) { 532 vulkanContext->device = VK_NULL_HANDLE; 533 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkCreateDevice(): %s", getVulkanResultString(result)); 534 quit(2); 535 } 536} 537 538static void loadDeviceFunctions(void) 539{ 540#define VULKAN_DEVICE_FUNCTION(name) \ 541 name = (PFN_##name)vkGetDeviceProcAddr(vulkanContext->device, #name); \ 542 if (!name) { \ 543 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \ 544 "vkGetDeviceProcAddr(device, \"" #name "\") failed"); \ 545 quit(2); \ 546 } 547#define VULKAN_GLOBAL_FUNCTION(name) 548#define VULKAN_INSTANCE_FUNCTION(name) 549 VULKAN_FUNCTIONS() 550#undef VULKAN_DEVICE_FUNCTION 551#undef VULKAN_GLOBAL_FUNCTION 552#undef VULKAN_INSTANCE_FUNCTION 553} 554 555#undef VULKAN_FUNCTIONS 556 557static void getQueues(void) 558{ 559 vkGetDeviceQueue(vulkanContext->device, 560 vulkanContext->graphicsQueueFamilyIndex, 561 0, 562 &vulkanContext->graphicsQueue); 563 if (vulkanContext->graphicsQueueFamilyIndex != vulkanContext->presentQueueFamilyIndex) { 564 vkGetDeviceQueue(vulkanContext->device, 565 vulkanContext->presentQueueFamilyIndex, 566 0, 567 &vulkanContext->presentQueue); 568 } else { 569 vulkanContext->presentQueue = vulkanContext->graphicsQueue; 570 } 571} 572 573static void createSemaphore(VkSemaphore *semaphore) 574{ 575 VkResult result; 576 577 VkSemaphoreCreateInfo createInfo = { 0 }; 578 createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; 579 result = vkCreateSemaphore(vulkanContext->device, &createInfo, NULL, semaphore); 580 if (result != VK_SUCCESS) { 581 *semaphore = VK_NULL_HANDLE; 582 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 583 "vkCreateSemaphore(): %s", 584 getVulkanResultString(result)); 585 quit(2); 586 } 587} 588 589static void createSemaphores(void) 590{ 591 createSemaphore(&vulkanContext->imageAvailableSemaphore); 592 createSemaphore(&vulkanContext->renderingFinishedSemaphore); 593} 594 595static void getSurfaceCaps(void) 596{ 597 VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkanContext->physicalDevice, vulkanContext->surface, &vulkanContext->surfaceCapabilities); 598 if (result != VK_SUCCESS) { 599 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 600 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR(): %s", 601 getVulkanResultString(result)); 602 quit(2); 603 } 604 605 // check surface usage 606 if (!(vulkanContext->surfaceCapabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) { 607 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 608 "Vulkan surface doesn't support VK_IMAGE_USAGE_TRANSFER_DST_BIT"); 609 quit(2); 610 } 611} 612 613static void getSurfaceFormats(void) 614{ 615 VkResult result = vkGetPhysicalDeviceSurfaceFormatsKHR(vulkanContext->physicalDevice, 616 vulkanContext->surface, 617 &vulkanContext->surfaceFormatsCount, 618 NULL); 619 if (result != VK_SUCCESS) { 620 vulkanContext->surfaceFormatsCount = 0; 621 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 622 "vkGetPhysicalDeviceSurfaceFormatsKHR(): %s", 623 getVulkanResultString(result)); 624 quit(2); 625 } 626 if (vulkanContext->surfaceFormatsCount > vulkanContext->surfaceFormatsAllocatedCount) { 627 vulkanContext->surfaceFormatsAllocatedCount = vulkanContext->surfaceFormatsCount; 628 SDL_free(vulkanContext->surfaceFormats); 629 vulkanContext->surfaceFormats = (VkSurfaceFormatKHR *)SDL_malloc(sizeof(VkSurfaceFormatKHR) * vulkanContext->surfaceFormatsAllocatedCount); 630 if (!vulkanContext->surfaceFormats) { 631 vulkanContext->surfaceFormatsCount = 0; 632 quit(2); 633 } 634 } 635 result = vkGetPhysicalDeviceSurfaceFormatsKHR(vulkanContext->physicalDevice, 636 vulkanContext->surface, 637 &vulkanContext->surfaceFormatsCount, 638 vulkanContext->surfaceFormats); 639 if (result != VK_SUCCESS) { 640 vulkanContext->surfaceFormatsCount = 0; 641 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 642 "vkGetPhysicalDeviceSurfaceFormatsKHR(): %s", 643 getVulkanResultString(result)); 644 quit(2); 645 } 646} 647 648static void getSwapchainImages(void) 649{ 650 VkResult result; 651 652 SDL_free(vulkanContext->swapchainImages); 653 vulkanContext->swapchainImages = NULL; 654 result = vkGetSwapchainImagesKHR(vulkanContext->device, vulkanContext->swapchain, &vulkanContext->swapchainImageCount, NULL); 655 if (result != VK_SUCCESS) { 656 vulkanContext->swapchainImageCount = 0; 657 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 658 "vkGetSwapchainImagesKHR(): %s", 659 getVulkanResultString(result)); 660 quit(2); 661 } 662 vulkanContext->swapchainImages = SDL_malloc(sizeof(VkImage) * vulkanContext->swapchainImageCount); 663 if (!vulkanContext->swapchainImages) { 664 quit(2); 665 } 666 result = vkGetSwapchainImagesKHR(vulkanContext->device, 667 vulkanContext->swapchain, 668 &vulkanContext->swapchainImageCount, 669 vulkanContext->swapchainImages); 670 if (result != VK_SUCCESS) { 671 SDL_free(vulkanContext->swapchainImages); 672 vulkanContext->swapchainImages = NULL; 673 vulkanContext->swapchainImageCount = 0; 674 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 675 "vkGetSwapchainImagesKHR(): %s", 676 getVulkanResultString(result)); 677 quit(2); 678 } 679} 680 681static bool createSwapchain(void) 682{ 683 uint32_t i; 684 int w, h; 685 VkSwapchainCreateInfoKHR createInfo = { 0 }; 686 VkResult result; 687 SDL_WindowFlags flags; 688 689 // pick an image count 690 vulkanContext->swapchainDesiredImageCount = vulkanContext->surfaceCapabilities.minImageCount + 1; 691 if ((vulkanContext->swapchainDesiredImageCount > vulkanContext->surfaceCapabilities.maxImageCount) && 692 (vulkanContext->surfaceCapabilities.maxImageCount > 0)) { 693 vulkanContext->swapchainDesiredImageCount = vulkanContext->surfaceCapabilities.maxImageCount; 694 } 695 696 // pick a format 697 if ((vulkanContext->surfaceFormatsCount == 1) && 698 (vulkanContext->surfaceFormats[0].format == VK_FORMAT_UNDEFINED)) { 699 // aren't any preferred formats, so we pick 700 vulkanContext->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; 701 vulkanContext->surfaceFormat.format = VK_FORMAT_R8G8B8A8_UNORM; 702 } else { 703 vulkanContext->surfaceFormat = vulkanContext->surfaceFormats[0]; 704 for (i = 0; i < vulkanContext->surfaceFormatsCount; i++) { 705 if (vulkanContext->surfaceFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM) { 706 vulkanContext->surfaceFormat = vulkanContext->surfaceFormats[i]; 707 break; 708 } 709 } 710 } 711 712 // get size 713 SDL_GetWindowSizeInPixels(vulkanContext->window, &w, &h); 714 715 // get flags 716 flags = SDL_GetWindowFlags(vulkanContext->window); 717 718 // Clamp the size to the allowable image extent. 719 // SDL_GetWindowSizeInPixels()'s result it not always in this range (bug #3287) 720 vulkanContext->swapchainSize.width = SDL_clamp((uint32_t)w, 721 vulkanContext->surfaceCapabilities.minImageExtent.width, 722 vulkanContext->surfaceCapabilities.maxImageExtent.width); 723 724 vulkanContext->swapchainSize.height = SDL_clamp((uint32_t)h, 725 vulkanContext->surfaceCapabilities.minImageExtent.height, 726 vulkanContext->surfaceCapabilities.maxImageExtent.height); 727 728 if (w == 0 || h == 0) { 729 return false; 730 } 731 732 getSurfaceCaps(); 733 734 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; 735 createInfo.surface = vulkanContext->surface; 736 createInfo.minImageCount = vulkanContext->swapchainDesiredImageCount; 737 createInfo.imageFormat = vulkanContext->surfaceFormat.format; 738 createInfo.imageColorSpace = vulkanContext->surfaceFormat.colorSpace; 739 createInfo.imageExtent = vulkanContext->swapchainSize; 740 createInfo.imageArrayLayers = 1; 741 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; 742 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; 743 createInfo.preTransform = vulkanContext->surfaceCapabilities.currentTransform; 744 if (flags & SDL_WINDOW_TRANSPARENT) { 745 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; 746 } else { 747 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 748 } 749 createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; 750 createInfo.clipped = VK_TRUE; 751 createInfo.oldSwapchain = vulkanContext->swapchain; 752 result = vkCreateSwapchainKHR(vulkanContext->device, &createInfo, NULL, &vulkanContext->swapchain); 753 754 if (createInfo.oldSwapchain) { 755 vkDestroySwapchainKHR(vulkanContext->device, createInfo.oldSwapchain, NULL); 756 } 757 758 if (result != VK_SUCCESS) { 759 vulkanContext->swapchain = VK_NULL_HANDLE; 760 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 761 "vkCreateSwapchainKHR(): %s", 762 getVulkanResultString(result)); 763 quit(2); 764 } 765 766 getSwapchainImages(); 767 return true; 768} 769 770static void destroySwapchain(void) 771{ 772 if (vulkanContext->swapchain) { 773 vkDestroySwapchainKHR(vulkanContext->device, vulkanContext->swapchain, NULL); 774 vulkanContext->swapchain = VK_NULL_HANDLE; 775 } 776 SDL_free(vulkanContext->swapchainImages); 777 vulkanContext->swapchainImages = NULL; 778} 779 780static void destroyCommandBuffers(void) 781{ 782 if (vulkanContext->commandBuffers) { 783 vkFreeCommandBuffers(vulkanContext->device, 784 vulkanContext->commandPool, 785 vulkanContext->swapchainImageCount, 786 vulkanContext->commandBuffers); 787 SDL_free(vulkanContext->commandBuffers); 788 vulkanContext->commandBuffers = NULL; 789 } 790} 791 792static void destroyCommandPool(void) 793{ 794 if (vulkanContext->commandPool) { 795 vkDestroyCommandPool(vulkanContext->device, vulkanContext->commandPool, NULL); 796 } 797 vulkanContext->commandPool = VK_NULL_HANDLE; 798} 799 800static void createCommandPool(void) 801{ 802 VkResult result; 803 VkCommandPoolCreateInfo createInfo = { 0 }; 804 createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; 805 createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; 806 createInfo.queueFamilyIndex = vulkanContext->graphicsQueueFamilyIndex; 807 result = vkCreateCommandPool(vulkanContext->device, &createInfo, NULL, &vulkanContext->commandPool); 808 if (result != VK_SUCCESS) { 809 vulkanContext->commandPool = VK_NULL_HANDLE; 810 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 811 "vkCreateCommandPool(): %s", 812 getVulkanResultString(result)); 813 quit(2); 814 } 815} 816 817static void createCommandBuffers(void) 818{ 819 VkResult result; 820 VkCommandBufferAllocateInfo allocateInfo = { 0 }; 821 allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; 822 allocateInfo.commandPool = vulkanContext->commandPool; 823 allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; 824 allocateInfo.commandBufferCount = vulkanContext->swapchainImageCount; 825 vulkanContext->commandBuffers = (VkCommandBuffer *)SDL_malloc(sizeof(VkCommandBuffer) * vulkanContext->swapchainImageCount); 826 result = vkAllocateCommandBuffers(vulkanContext->device, &allocateInfo, vulkanContext->commandBuffers); 827 if (result != VK_SUCCESS) { 828 SDL_free(vulkanContext->commandBuffers); 829 vulkanContext->commandBuffers = NULL; 830 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 831 "vkAllocateCommandBuffers(): %s", 832 getVulkanResultString(result)); 833 quit(2); 834 } 835} 836 837static void createFences(void) 838{ 839 uint32_t i; 840 841 vulkanContext->fences = SDL_malloc(sizeof(VkFence) * vulkanContext->swapchainImageCount); 842 if (!vulkanContext->fences) { 843 quit(2); 844 } 845 for (i = 0; i < vulkanContext->swapchainImageCount; i++) { 846 VkResult result; 847 VkFenceCreateInfo createInfo = { 0 }; 848 createInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; 849 createInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; 850 result = vkCreateFence(vulkanContext->device, &createInfo, NULL, &vulkanContext->fences[i]); 851 if (result != VK_SUCCESS) { 852 for (; i > 0; i--) { 853 vkDestroyFence(vulkanContext->device, vulkanContext->fences[i - 1], NULL); 854 } 855 SDL_free(vulkanContext->fences); 856 vulkanContext->fences = NULL; 857 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 858 "vkCreateFence(): %s", 859 getVulkanResultString(result)); 860 quit(2); 861 } 862 } 863} 864 865static void destroyFences(void) 866{ 867 uint32_t i; 868 869 if (!vulkanContext->fences) { 870 return; 871 } 872 873 for (i = 0; i < vulkanContext->swapchainImageCount; i++) { 874 vkDestroyFence(vulkanContext->device, vulkanContext->fences[i], NULL); 875 } 876 SDL_free(vulkanContext->fences); 877 vulkanContext->fences = NULL; 878} 879 880static void recordPipelineImageBarrier(VkCommandBuffer commandBuffer, 881 VkAccessFlags sourceAccessMask, 882 VkAccessFlags destAccessMask, 883 VkImageLayout sourceLayout, 884 VkImageLayout destLayout, 885 VkImage image) 886{ 887 VkImageMemoryBarrier barrier = { 0 }; 888 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; 889 barrier.srcAccessMask = sourceAccessMask; 890 barrier.dstAccessMask = destAccessMask; 891 barrier.oldLayout = sourceLayout; 892 barrier.newLayout = destLayout; 893 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 894 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 895 barrier.image = image; 896 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 897 barrier.subresourceRange.baseMipLevel = 0; 898 barrier.subresourceRange.levelCount = 1; 899 barrier.subresourceRange.baseArrayLayer = 0; 900 barrier.subresourceRange.layerCount = 1; 901 vkCmdPipelineBarrier(commandBuffer, 902 VK_PIPELINE_STAGE_TRANSFER_BIT, 903 VK_PIPELINE_STAGE_TRANSFER_BIT, 904 0, 905 0, 906 NULL, 907 0, 908 NULL, 909 1, 910 &barrier); 911} 912 913static void rerecordCommandBuffer(uint32_t frameIndex, const VkClearColorValue *clearColor) 914{ 915 VkCommandBuffer commandBuffer = vulkanContext->commandBuffers[frameIndex]; 916 VkImage image = vulkanContext->swapchainImages[frameIndex]; 917 VkCommandBufferBeginInfo beginInfo = { 0 }; 918 VkImageSubresourceRange clearRange = { 0 }; 919 920 VkResult result = vkResetCommandBuffer(commandBuffer, 0); 921 if (result != VK_SUCCESS) { 922 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 923 "vkResetCommandBuffer(): %s", 924 getVulkanResultString(result)); 925 quit(2); 926 } 927 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 928 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; 929 result = vkBeginCommandBuffer(commandBuffer, &beginInfo); 930 if (result != VK_SUCCESS) { 931 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 932 "vkBeginCommandBuffer(): %s", 933 getVulkanResultString(result)); 934 quit(2); 935 } 936 recordPipelineImageBarrier(commandBuffer, 937 0, 938 VK_ACCESS_TRANSFER_WRITE_BIT, 939 VK_IMAGE_LAYOUT_UNDEFINED, 940 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 941 image); 942 clearRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 943 clearRange.baseMipLevel = 0; 944 clearRange.levelCount = 1; 945 clearRange.baseArrayLayer = 0; 946 clearRange.layerCount = 1; 947 vkCmdClearColorImage(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clearColor, 1, &clearRange); 948 recordPipelineImageBarrier(commandBuffer, 949 VK_ACCESS_TRANSFER_WRITE_BIT, 950 VK_ACCESS_MEMORY_READ_BIT, 951 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 952 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 953 image); 954 result = vkEndCommandBuffer(commandBuffer); 955 if (result != VK_SUCCESS) { 956 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 957 "vkEndCommandBuffer(): %s", 958 getVulkanResultString(result)); 959 quit(2); 960 } 961} 962 963static void destroySwapchainAndSwapchainSpecificStuff(bool doDestroySwapchain) 964{ 965 if (vkDeviceWaitIdle != NULL) { 966 vkDeviceWaitIdle(vulkanContext->device); 967 } 968 destroyFences(); 969 destroyCommandBuffers(); 970 destroyCommandPool(); 971 if (doDestroySwapchain) { 972 destroySwapchain(); 973 } 974} 975 976static bool createNewSwapchainAndSwapchainSpecificStuff(void) 977{ 978 destroySwapchainAndSwapchainSpecificStuff(false); 979 getSurfaceCaps(); 980 getSurfaceFormats(); 981 if (!createSwapchain()) { 982 return false; 983 } 984 createCommandPool(); 985 createCommandBuffers(); 986 createFences(); 987 return true; 988} 989 990static void initVulkan(void) 991{ 992 int i; 993 994 SDL_Vulkan_LoadLibrary(NULL); 995 996 vulkanContexts = (VulkanContext *)SDL_calloc(state->num_windows, sizeof(VulkanContext)); 997 if (!vulkanContexts) { 998 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!"); 999 quit(2); 1000 } 1001 1002 for (i = 0; i < state->num_windows; ++i) { 1003 vulkanContext = &vulkanContexts[i]; 1004 vulkanContext->window = state->windows[i]; 1005 loadGlobalFunctions(); 1006 createInstance(); 1007 loadInstanceFunctions(); 1008 createSurface(); 1009 findPhysicalDevice(); 1010 createDevice(); 1011 loadDeviceFunctions(); 1012 getQueues(); 1013 createSemaphores(); 1014 createNewSwapchainAndSwapchainSpecificStuff(); 1015 } 1016} 1017 1018static void shutdownVulkan(bool doDestroySwapchain) 1019{ 1020 if (vulkanContexts) { 1021 int i; 1022 for (i = 0; i < state->num_windows; ++i) { 1023 vulkanContext = &vulkanContexts[i]; 1024 if (vulkanContext->device && vkDeviceWaitIdle) { 1025 vkDeviceWaitIdle(vulkanContext->device); 1026 } 1027 1028 destroySwapchainAndSwapchainSpecificStuff(doDestroySwapchain); 1029 1030 if (vulkanContext->imageAvailableSemaphore && vkDestroySemaphore) { 1031 vkDestroySemaphore(vulkanContext->device, vulkanContext->imageAvailableSemaphore, NULL); 1032 } 1033 1034 if (vulkanContext->renderingFinishedSemaphore && vkDestroySemaphore) { 1035 vkDestroySemaphore(vulkanContext->device, vulkanContext->renderingFinishedSemaphore, NULL); 1036 } 1037 1038 if (vulkanContext->device && vkDestroyDevice) { 1039 vkDestroyDevice(vulkanContext->device, NULL); 1040 } 1041 1042 if (vulkanContext->surface && vkDestroySurfaceKHR) { 1043 vkDestroySurfaceKHR(vulkanContext->instance, vulkanContext->surface, NULL); 1044 } 1045 1046 if (vulkanContext->instance && vkDestroyInstance) { 1047 vkDestroyInstance(vulkanContext->instance, NULL); 1048 } 1049 1050 SDL_free(vulkanContext->surfaceFormats); 1051 } 1052 SDL_free(vulkanContexts); 1053 vulkanContexts = NULL; 1054 } 1055 1056 SDL_Vulkan_UnloadLibrary(); 1057} 1058 1059static bool render(void) 1060{ 1061 uint32_t frameIndex; 1062 VkResult rc; 1063 double currentTime; 1064 VkClearColorValue clearColor = { { 0 } }; 1065 VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; 1066 VkSubmitInfo submitInfo = { 0 }; 1067 VkPresentInfoKHR presentInfo = { 0 }; 1068 int w, h; 1069 1070 if (!vulkanContext->swapchain) { 1071 bool result = createNewSwapchainAndSwapchainSpecificStuff(); 1072 if (!result) { 1073 SDL_Delay(100); 1074 } 1075 return result; 1076 } 1077 rc = vkAcquireNextImageKHR(vulkanContext->device, 1078 vulkanContext->swapchain, 1079 UINT64_MAX, 1080 vulkanContext->imageAvailableSemaphore, 1081 VK_NULL_HANDLE, 1082 &frameIndex); 1083 if (rc == VK_ERROR_OUT_OF_DATE_KHR) { 1084 return createNewSwapchainAndSwapchainSpecificStuff(); 1085 } 1086 1087 if ((rc != VK_SUBOPTIMAL_KHR) && (rc != VK_SUCCESS)) { 1088 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 1089 "vkAcquireNextImageKHR(): %s", 1090 getVulkanResultString(rc)); 1091 quit(2); 1092 } 1093 rc = vkWaitForFences(vulkanContext->device, 1, &vulkanContext->fences[frameIndex], VK_FALSE, UINT64_MAX); 1094 if (rc != VK_SUCCESS) { 1095 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkWaitForFences(): %s", getVulkanResultString(rc)); 1096 quit(2); 1097 } 1098 rc = vkResetFences(vulkanContext->device, 1, &vulkanContext->fences[frameIndex]); 1099 if (rc != VK_SUCCESS) { 1100 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkResetFences(): %s", getVulkanResultString(rc)); 1101 quit(2); 1102 } 1103 currentTime = (double)SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency(); 1104 clearColor.float32[0] = (float)(0.5 + 0.5 * SDL_sin(currentTime)); 1105 clearColor.float32[1] = (float)(0.5 + 0.5 * SDL_sin(currentTime + SDL_PI_D * 2 / 3)); 1106 clearColor.float32[2] = (float)(0.5 + 0.5 * SDL_sin(currentTime + SDL_PI_D * 4 / 3)); 1107 clearColor.float32[3] = 0.5; // for SDL_WINDOW_TRANSPARENT, ignored with VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR 1108 rerecordCommandBuffer(frameIndex, &clearColor); 1109 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 1110 submitInfo.waitSemaphoreCount = 1; 1111 submitInfo.pWaitSemaphores = &vulkanContext->imageAvailableSemaphore; 1112 submitInfo.pWaitDstStageMask = &waitDestStageMask; 1113 submitInfo.commandBufferCount = 1; 1114 submitInfo.pCommandBuffers = &vulkanContext->commandBuffers[frameIndex]; 1115 submitInfo.signalSemaphoreCount = 1; 1116 submitInfo.pSignalSemaphores = &vulkanContext->renderingFinishedSemaphore; 1117 rc = vkQueueSubmit(vulkanContext->graphicsQueue, 1, &submitInfo, vulkanContext->fences[frameIndex]); 1118 1119 if (rc != VK_SUCCESS) { 1120 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkQueueSubmit(): %s", getVulkanResultString(rc)); 1121 quit(2); 1122 } 1123 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; 1124 presentInfo.waitSemaphoreCount = 1; 1125 presentInfo.pWaitSemaphores = &vulkanContext->renderingFinishedSemaphore; 1126 presentInfo.swapchainCount = 1; 1127 presentInfo.pSwapchains = &vulkanContext->swapchain; 1128 presentInfo.pImageIndices = &frameIndex; 1129 rc = vkQueuePresentKHR(vulkanContext->presentQueue, &presentInfo); 1130 if ((rc == VK_ERROR_OUT_OF_DATE_KHR) || (rc == VK_SUBOPTIMAL_KHR)) { 1131 return createNewSwapchainAndSwapchainSpecificStuff(); 1132 } 1133 1134 if (rc != VK_SUCCESS) { 1135 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, 1136 "vkQueuePresentKHR(): %s", 1137 getVulkanResultString(rc)); 1138 quit(2); 1139 } 1140 SDL_GetWindowSizeInPixels(vulkanContext->window, &w, &h); 1141 if (w != (int)vulkanContext->swapchainSize.width || h != (int)vulkanContext->swapchainSize.height) { 1142 return createNewSwapchainAndSwapchainSpecificStuff(); 1143 } 1144 return true; 1145} 1146 1147int main(int argc, char **argv) 1148{ 1149 int done; 1150 const SDL_DisplayMode *mode; 1151 SDL_Event event; 1152 Uint64 then, now; 1153 Uint32 frames; 1154 int dw, dh; 1155 1156 /* Initialize test framework */ 1157 state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO); 1158 if (!state) { 1159 return 1; 1160 } 1161 1162 /* Set Vulkan parameters */ 1163 state->window_flags |= SDL_WINDOW_VULKAN; 1164 state->skip_renderer = 1; 1165 1166 if (!SDLTest_CommonDefaultArgs(state, argc, argv) || !SDLTest_CommonInit(state)) { 1167 SDLTest_CommonQuit(state); 1168 return 1; 1169 } 1170 1171 mode = SDL_GetCurrentDisplayMode(SDL_GetPrimaryDisplay()); 1172 if (mode) { 1173 SDL_Log("Screen BPP : %d", SDL_BITSPERPIXEL(mode->format)); 1174 } 1175 SDL_GetWindowSize(state->windows[0], &dw, &dh); 1176 SDL_Log("Window Size : %d,%d", dw, dh); 1177 SDL_GetWindowSizeInPixels(state->windows[0], &dw, &dh); 1178 SDL_Log("Draw Size : %d,%d", dw, dh); 1179 SDL_Log("%s", ""); 1180 1181 initVulkan(); 1182 1183 /* Main render loop */ 1184 frames = 0; 1185 then = SDL_GetTicks(); 1186 done = 0; 1187 while (!done) { 1188 /* Check for events */ 1189 frames++; 1190 while (SDL_PollEvent(&event)) { 1191 /* Need to destroy the swapchain before the window created 1192 * by SDL. 1193 */ 1194 if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) { 1195 destroySwapchainAndSwapchainSpecificStuff(true); 1196 } 1197 SDLTest_CommonEvent(state, &event, &done); 1198 } 1199 1200 if (!done) { 1201 int i; 1202 for (i = 0; i < state->num_windows; ++i) { 1203 if (state->windows[i]) { 1204 vulkanContext = &vulkanContexts[i]; 1205 render(); 1206 } 1207 } 1208 } 1209 } 1210 1211 /* Print out some timing information */ 1212 now = SDL_GetTicks(); 1213 if (now > then) { 1214 SDL_Log("%2.2f frames per second", ((double)frames * 1000) / (now - then)); 1215 } 1216 1217 shutdownVulkan(true); 1218 SDLTest_CommonQuit(state); 1219 return 0; 1220} 1221 1222#endif 1223
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.