test/testvulkan.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 27 Aug 2017 22:15:57 -0400
changeset 11365 a9bd2625fa01
child 11366 1e4fe1678a00
permissions -rw-r--r--
vulkan: Initial Vulkan support!

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