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