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