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