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