sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

testvulkan.c (44570B)


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