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