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
21int 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;
90VULKAN_FUNCTIONS()
91#undef VULKAN_DEVICE_FUNCTION
92#undef VULKAN_GLOBAL_FUNCTION
93#undef VULKAN_INSTANCE_FUNCTION
94static 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
100enum
101{
102 VK_ERROR_FRAGMENTED_POOL = -12,
103};
104#endif
105#if VK_HEADER_VERSION < 38
106enum {
107 VK_ERROR_OUT_OF_POOL_MEMORY_KHR = -1000069000
108};
109#endif
110
111static const char *getVulkanResultString(VkResult result)
112{
113 switch((int)result)
114 {
115 case VK_SUCCESS:
116 return "VK_SUCCESS";
117 case VK_NOT_READY:
118 return "VK_NOT_READY";
119 case VK_TIMEOUT:
120 return "VK_TIMEOUT";
121 case VK_EVENT_SET:
122 return "VK_EVENT_SET";
123 case VK_EVENT_RESET:
124 return "VK_EVENT_RESET";
125 case VK_INCOMPLETE:
126 return "VK_INCOMPLETE";
127 case VK_ERROR_OUT_OF_HOST_MEMORY:
128 return "VK_ERROR_OUT_OF_HOST_MEMORY";
129 case VK_ERROR_OUT_OF_DEVICE_MEMORY:
130 return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
131 case VK_ERROR_INITIALIZATION_FAILED:
132 return "VK_ERROR_INITIALIZATION_FAILED";
133 case VK_ERROR_DEVICE_LOST:
134 return "VK_ERROR_DEVICE_LOST";
135 case VK_ERROR_MEMORY_MAP_FAILED:
136 return "VK_ERROR_MEMORY_MAP_FAILED";
137 case VK_ERROR_LAYER_NOT_PRESENT:
138 return "VK_ERROR_LAYER_NOT_PRESENT";
139 case VK_ERROR_EXTENSION_NOT_PRESENT:
140 return "VK_ERROR_EXTENSION_NOT_PRESENT";
141 case VK_ERROR_FEATURE_NOT_PRESENT:
142 return "VK_ERROR_FEATURE_NOT_PRESENT";
143 case VK_ERROR_INCOMPATIBLE_DRIVER:
144 return "VK_ERROR_INCOMPATIBLE_DRIVER";
145 case VK_ERROR_TOO_MANY_OBJECTS:
146 return "VK_ERROR_TOO_MANY_OBJECTS";
147 case VK_ERROR_FORMAT_NOT_SUPPORTED:
148 return "VK_ERROR_FORMAT_NOT_SUPPORTED";
149 case VK_ERROR_FRAGMENTED_POOL:
150 return "VK_ERROR_FRAGMENTED_POOL";
151 case VK_ERROR_SURFACE_LOST_KHR:
152 return "VK_ERROR_SURFACE_LOST_KHR";
153 case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
154 return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
155 case VK_SUBOPTIMAL_KHR:
156 return "VK_SUBOPTIMAL_KHR";
157 case VK_ERROR_OUT_OF_DATE_KHR:
158 return "VK_ERROR_OUT_OF_DATE_KHR";
159 case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
160 return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
161 case VK_ERROR_VALIDATION_FAILED_EXT:
162 return "VK_ERROR_VALIDATION_FAILED_EXT";
163 case VK_ERROR_OUT_OF_POOL_MEMORY_KHR:
164 return "VK_ERROR_OUT_OF_POOL_MEMORY_KHR";
165 case VK_ERROR_INVALID_SHADER_NV:
166 return "VK_ERROR_INVALID_SHADER_NV";
167 default:
168 break;
169 }
170 if(result < 0)
171 return "VK_ERROR_<Unknown>";
172 return "VK_<Unknown>";
173}
174
175typedef 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
204static SDLTest_CommonState *state;
205static VulkanContext vulkanContext = {0};
206
207static void shutdownVulkan(void);
208
209/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
210static void quit(int rc)
211{
212 shutdownVulkan();
213 SDLTest_CommonQuit(state);
214 exit(rc);
215}
216
217static 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
244static void createInstance(void)
245{
246 VkApplicationInfo appInfo = {0};
247 VkInstanceCreateInfo instanceCreateInfo = {0};
248 const char **extensions = NULL;
249 unsigned extensionCount = 0;
250 VkResult result;
251
252
253 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
254 appInfo.apiVersion = VK_API_VERSION_1_0;
255 instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
256 instanceCreateInfo.pApplicationInfo = &appInfo;
257 if(!SDL_Vulkan_GetInstanceExtensions(NULL, &extensionCount, NULL))
258 {
259 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
260 "SDL_Vulkan_GetInstanceExtensions(): %s\n",
261 SDL_GetError());
262 quit(2);
263 }
264 extensions = SDL_malloc(sizeof(const char *) * extensionCount);
265 if(!extensions)
266 {
267 SDL_OutOfMemory();
268 quit(2);
269 }
270 if(!SDL_Vulkan_GetInstanceExtensions(NULL, &extensionCount, extensions))
271 {
272 SDL_free((void*)extensions);
273 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
274 "SDL_Vulkan_GetInstanceExtensions(): %s\n",
275 SDL_GetError());
276 quit(2);
277 }
278 instanceCreateInfo.enabledExtensionCount = extensionCount;
279 instanceCreateInfo.ppEnabledExtensionNames = extensions;
280 result = vkCreateInstance(&instanceCreateInfo, NULL, &vulkanContext.instance);
281 SDL_free((void*)extensions);
282 if(result != VK_SUCCESS)
283 {
284 vulkanContext.instance = VK_NULL_HANDLE;
285 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
286 "vkCreateInstance(): %s\n",
287 getVulkanResultString(result));
288 quit(2);
289 }
290}
291
292static 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
310static 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
323static 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
496static 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
528static 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
548static 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
563static 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
580static void createSemaphores(void)
581{
582 createSemaphore(&vulkanContext.imageAvailableSemaphore);
583 createSemaphore(&vulkanContext.renderingFinishedSemaphore);
584}
585
586static 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
607static 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
648static 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
686static 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
757static 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
766static 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
777static void destroyCommandPool(void)
778{
779 if(vulkanContext.commandPool)
780 vkDestroyCommandPool(vulkanContext.device, vulkanContext.commandPool, NULL);
781 vulkanContext.commandPool = VK_NULL_HANDLE;
782}
783
784static 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
805static 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
829static 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
864static 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
878static 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
911static 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
965static void destroySwapchainAndSwapchainSpecificStuff(SDL_bool doDestroySwapchain)
966{
967 destroyFences();
968 destroyCommandBuffers();
969 destroyCommandPool();
970 if(doDestroySwapchain)
971 destroySwapchain();
972}
973
974static 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
987static 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
1003static 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
1022static 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
1118int main(int argc, char *argv[])
1119{
1120 int done;
1121 SDL_DisplayMode mode;
1122 SDL_Event event;
1123 Uint32 then, now, frames;
1124 int dw, dh;
1125
1126 /* Enable standard application logging */
1127 SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
1128
1129 /* Initialize test framework */
1130 state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
1131 if(!state)
1132 {
1133 return 1;
1134 }
1135
1136 /* Set Vulkan parameters */
1137 state->window_flags |= SDL_WINDOW_VULKAN;
1138 state->num_windows = 1;
1139 state->skip_renderer = 1;
1140
1141 if (!SDLTest_CommonDefaultArgs(state, argc, argv) || !SDLTest_CommonInit(state)) {
1142 SDLTest_CommonQuit(state);
1143 return 1;
1144 }
1145
1146 SDL_GetCurrentDisplayMode(0, &mode);
1147 SDL_Log("Screen BPP : %d\n", SDL_BITSPERPIXEL(mode.format));
1148 SDL_GetWindowSize(state->windows[0], &dw, &dh);
1149 SDL_Log("Window Size : %d,%d\n", dw, dh);
1150 SDL_Vulkan_GetDrawableSize(state->windows[0], &dw, &dh);
1151 SDL_Log("Draw Size : %d,%d\n", dw, dh);
1152 SDL_Log("\n");
1153
1154 initVulkan();
1155
1156 /* Main render loop */
1157 frames = 0;
1158 then = SDL_GetTicks();
1159 done = 0;
1160 while(!done)
1161 {
1162 /* Check for events */
1163 ++frames;
1164 while(SDL_PollEvent(&event))
1165 {
1166 SDLTest_CommonEvent(state, &event, &done);
1167 }
1168
1169 if(!done)
1170 render();
1171 }
1172
1173 /* Print out some timing information */
1174 now = SDL_GetTicks();
1175 if(now > then)
1176 {
1177 SDL_Log("%2.2f frames per second\n", ((double)frames * 1000) / (now - then));
1178 }
1179 quit(0);
1180 return 0;
1181}
1182
1183#endif
1184