2 Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
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.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
17 #include "SDL_test_common.h"
19 #if defined(__ANDROID__) && defined(__ARM_EABI__) && !defined(__ARM_ARCH_7A__)
21 int main(int argc, char *argv[])
23 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No Vulkan support on this system\n");
29 #define VK_NO_PROTOTYPES
31 #include <vulkan/vulkan.h>
33 /* SDL includes a copy for building on systems without the Vulkan SDK */
34 #include "../src/video/khronos/vulkan/vulkan.h"
36 #include "SDL_vulkan.h"
38 #ifndef UINT64_MAX /* VS2008 */
39 #define UINT64_MAX 18446744073709551615
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)
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;
91 #undef VULKAN_DEVICE_FUNCTION
92 #undef VULKAN_GLOBAL_FUNCTION
93 #undef VULKAN_INSTANCE_FUNCTION
94 static PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
96 /* Based on the headers found in
97 * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers
99 #if VK_HEADER_VERSION < 22
102 VK_ERROR_FRAGMENTED_POOL = -12,
105 #if VK_HEADER_VERSION < 38
107 VK_ERROR_OUT_OF_POOL_MEMORY_KHR = -1000069000
111 static const char *getVulkanResultString(VkResult result)
118 return "VK_NOT_READY";
122 return "VK_EVENT_SET";
124 return "VK_EVENT_RESET";
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";
171 return "VK_ERROR_<Unknown>";
172 return "VK_<Unknown>";
175 typedef struct VulkanContext
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;
204 static SDLTest_CommonState *state;
205 static VulkanContext vulkanContext = {0};
207 static void shutdownVulkan(void);
209 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
210 static void quit(int rc)
213 SDLTest_CommonQuit(state);
217 static void loadGlobalFunctions(void)
219 vkGetInstanceProcAddr = SDL_Vulkan_GetVkGetInstanceProcAddr();
220 if(!vkGetInstanceProcAddr)
222 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
223 "SDL_Vulkan_GetVkGetInstanceProcAddr(): %s\n",
228 #define VULKAN_DEVICE_FUNCTION(name)
229 #define VULKAN_GLOBAL_FUNCTION(name) \
230 name = (PFN_##name)vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \
233 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \
234 "vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed\n"); \
237 #define VULKAN_INSTANCE_FUNCTION(name)
239 #undef VULKAN_DEVICE_FUNCTION
240 #undef VULKAN_GLOBAL_FUNCTION
241 #undef VULKAN_INSTANCE_FUNCTION
244 static void createInstance(void)
246 VkApplicationInfo appInfo = {0};
247 VkInstanceCreateInfo instanceCreateInfo = {0};
248 const char **extensions = NULL;
249 unsigned extensionCount = 0;
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))
259 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
260 "SDL_Vulkan_GetInstanceExtensions(): %s\n",
264 extensions = SDL_malloc(sizeof(const char *) * extensionCount);
270 if(!SDL_Vulkan_GetInstanceExtensions(NULL, &extensionCount, extensions))
272 SDL_free((void*)extensions);
273 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
274 "SDL_Vulkan_GetInstanceExtensions(): %s\n",
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)
284 vulkanContext.instance = VK_NULL_HANDLE;
285 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
286 "vkCreateInstance(): %s\n",
287 getVulkanResultString(result));
292 static void loadInstanceFunctions(void)
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); \
300 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \
301 "vkGetInstanceProcAddr(instance, \"" #name "\") failed\n"); \
305 #undef VULKAN_DEVICE_FUNCTION
306 #undef VULKAN_GLOBAL_FUNCTION
307 #undef VULKAN_INSTANCE_FUNCTION
310 static void createSurface(void)
312 if(!SDL_Vulkan_CreateSurface(state->windows[0],
313 vulkanContext.instance,
314 &vulkanContext.surface))
316 vulkanContext.surface = VK_NULL_HANDLE;
318 SDL_LOG_CATEGORY_APPLICATION, "SDL_Vulkan_CreateSurface(): %s\n", SDL_GetError());
323 static void findPhysicalDevice(void)
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;
334 vkEnumeratePhysicalDevices(vulkanContext.instance, &physicalDeviceCount, NULL);
335 if(result != VK_SUCCESS)
337 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
338 "vkEnumeratePhysicalDevices(): %s\n",
339 getVulkanResultString(result));
342 if(physicalDeviceCount == 0)
344 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
345 "vkEnumeratePhysicalDevices(): no physical devices\n");
348 physicalDevices = SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
355 vkEnumeratePhysicalDevices(vulkanContext.instance, &physicalDeviceCount, physicalDevices);
356 if(result != VK_SUCCESS)
358 SDL_free(physicalDevices);
359 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
360 "vkEnumeratePhysicalDevices(): %s\n",
361 getVulkanResultString(result));
364 vulkanContext.physicalDevice = NULL;
365 for(physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount;
366 physicalDeviceIndex++)
368 uint32_t queueFamiliesCount = 0;
369 uint32_t queueFamilyIndex;
370 uint32_t deviceExtensionCount = 0;
371 SDL_bool hasSwapchainExtension = SDL_FALSE;
375 VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex];
376 vkGetPhysicalDeviceProperties(physicalDevice, &vulkanContext.physicalDeviceProperties);
377 if(VK_VERSION_MAJOR(vulkanContext.physicalDeviceProperties.apiVersion) < 1)
379 vkGetPhysicalDeviceFeatures(physicalDevice, &vulkanContext.physicalDeviceFeatures);
380 vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, NULL);
381 if(queueFamiliesCount == 0)
383 if(queueFamiliesPropertiesAllocatedSize < queueFamiliesCount)
385 SDL_free(queueFamiliesProperties);
386 queueFamiliesPropertiesAllocatedSize = queueFamiliesCount;
387 queueFamiliesProperties =
388 SDL_malloc(sizeof(VkQueueFamilyProperties) * queueFamiliesPropertiesAllocatedSize);
389 if(!queueFamiliesProperties)
391 SDL_free(physicalDevices);
392 SDL_free(deviceExtensions);
397 vkGetPhysicalDeviceQueueFamilyProperties(
398 physicalDevice, &queueFamiliesCount, queueFamiliesProperties);
399 vulkanContext.graphicsQueueFamilyIndex = queueFamiliesCount;
400 vulkanContext.presentQueueFamilyIndex = queueFamiliesCount;
401 for(queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount;
404 VkBool32 supported = 0;
406 if(queueFamiliesProperties[queueFamilyIndex].queueCount == 0)
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)
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));
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
429 if(vulkanContext.graphicsQueueFamilyIndex == queueFamiliesCount) // no good queues found
431 if(vulkanContext.presentQueueFamilyIndex == queueFamiliesCount) // no good queues found
434 vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, NULL);
435 if(result != VK_SUCCESS)
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));
445 if(deviceExtensionCount == 0)
447 if(deviceExtensionsAllocatedSize < deviceExtensionCount)
449 SDL_free(deviceExtensions);
450 deviceExtensionsAllocatedSize = deviceExtensionCount;
452 SDL_malloc(sizeof(VkExtensionProperties) * deviceExtensionsAllocatedSize);
453 if(!deviceExtensions)
455 SDL_free(physicalDevices);
456 SDL_free(queueFamiliesProperties);
461 result = vkEnumerateDeviceExtensionProperties(
462 physicalDevice, NULL, &deviceExtensionCount, deviceExtensions);
463 if(result != VK_SUCCESS)
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));
473 for(i = 0; i < deviceExtensionCount; i++)
475 if(0 == SDL_strcmp(deviceExtensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME))
477 hasSwapchainExtension = SDL_TRUE;
481 if(!hasSwapchainExtension)
483 vulkanContext.physicalDevice = physicalDevice;
486 SDL_free(physicalDevices);
487 SDL_free(queueFamiliesProperties);
488 SDL_free(deviceExtensions);
489 if(!vulkanContext.physicalDevice)
491 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Vulkan: no viable physical devices found");
496 static void createDevice(void)
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,
506 deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
507 deviceQueueCreateInfo->queueFamilyIndex = vulkanContext.graphicsQueueFamilyIndex;
508 deviceQueueCreateInfo->queueCount = 1;
509 deviceQueueCreateInfo->pQueuePriorities = &queuePriority[0];
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)
521 vulkanContext.device = VK_NULL_HANDLE;
523 SDL_LOG_CATEGORY_APPLICATION, "vkCreateDevice(): %s\n", getVulkanResultString(result));
528 static void loadDeviceFunctions(void)
530 #define VULKAN_DEVICE_FUNCTION(name) \
531 name = (PFN_##name)vkGetDeviceProcAddr(vulkanContext.device, #name); \
534 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \
535 "vkGetDeviceProcAddr(device, \"" #name "\") failed\n"); \
538 #define VULKAN_GLOBAL_FUNCTION(name)
539 #define VULKAN_INSTANCE_FUNCTION(name)
541 #undef VULKAN_DEVICE_FUNCTION
542 #undef VULKAN_GLOBAL_FUNCTION
543 #undef VULKAN_INSTANCE_FUNCTION
546 #undef VULKAN_FUNCTIONS
548 static void getQueues(void)
550 vkGetDeviceQueue(vulkanContext.device,
551 vulkanContext.graphicsQueueFamilyIndex,
553 &vulkanContext.graphicsQueue);
554 if(vulkanContext.graphicsQueueFamilyIndex != vulkanContext.presentQueueFamilyIndex)
555 vkGetDeviceQueue(vulkanContext.device,
556 vulkanContext.presentQueueFamilyIndex,
558 &vulkanContext.presentQueue);
560 vulkanContext.presentQueue = vulkanContext.graphicsQueue;
563 static void createSemaphore(VkSemaphore *semaphore)
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)
572 *semaphore = VK_NULL_HANDLE;
573 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
574 "vkCreateSemaphore(): %s\n",
575 getVulkanResultString(result));
580 static void createSemaphores(void)
582 createSemaphore(&vulkanContext.imageAvailableSemaphore);
583 createSemaphore(&vulkanContext.renderingFinishedSemaphore);
586 static void getSurfaceCaps(void)
588 VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
589 vulkanContext.physicalDevice, vulkanContext.surface, &vulkanContext.surfaceCapabilities);
590 if(result != VK_SUCCESS)
592 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
593 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR(): %s\n",
594 getVulkanResultString(result));
598 // check surface usage
599 if(!(vulkanContext.surfaceCapabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
601 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
602 "Vulkan surface doesn't support VK_IMAGE_USAGE_TRANSFER_DST_BIT\n");
607 static void getSurfaceFormats(void)
609 VkResult result = vkGetPhysicalDeviceSurfaceFormatsKHR(vulkanContext.physicalDevice,
610 vulkanContext.surface,
611 &vulkanContext.surfaceFormatsCount,
613 if(result != VK_SUCCESS)
615 vulkanContext.surfaceFormatsCount = 0;
616 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
617 "vkGetPhysicalDeviceSurfaceFormatsKHR(): %s\n",
618 getVulkanResultString(result));
621 if(vulkanContext.surfaceFormatsCount > vulkanContext.surfaceFormatsAllocatedCount)
623 vulkanContext.surfaceFormatsAllocatedCount = vulkanContext.surfaceFormatsCount;
624 SDL_free(vulkanContext.surfaceFormats);
625 vulkanContext.surfaceFormats =
626 SDL_malloc(sizeof(VkSurfaceFormatKHR) * vulkanContext.surfaceFormatsAllocatedCount);
627 if(!vulkanContext.surfaceFormats)
629 vulkanContext.surfaceFormatsCount = 0;
634 result = vkGetPhysicalDeviceSurfaceFormatsKHR(vulkanContext.physicalDevice,
635 vulkanContext.surface,
636 &vulkanContext.surfaceFormatsCount,
637 vulkanContext.surfaceFormats);
638 if(result != VK_SUCCESS)
640 vulkanContext.surfaceFormatsCount = 0;
641 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
642 "vkGetPhysicalDeviceSurfaceFormatsKHR(): %s\n",
643 getVulkanResultString(result));
648 static void getSwapchainImages(void)
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)
658 vulkanContext.swapchainImageCount = 0;
659 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
660 "vkGetSwapchainImagesKHR(): %s\n",
661 getVulkanResultString(result));
664 vulkanContext.swapchainImages = SDL_malloc(sizeof(VkImage) * vulkanContext.swapchainImageCount);
665 if(!vulkanContext.swapchainImages)
670 result = vkGetSwapchainImagesKHR(vulkanContext.device,
671 vulkanContext.swapchain,
672 &vulkanContext.swapchainImageCount,
673 vulkanContext.swapchainImages);
674 if(result != VK_SUCCESS)
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));
686 static SDL_bool createSwapchain(void)
690 VkSwapchainCreateInfoKHR createInfo = {0};
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;
700 if(vulkanContext.surfaceFormatsCount == 1
701 && vulkanContext.surfaceFormats[0].format == VK_FORMAT_UNDEFINED)
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;
709 vulkanContext.surfaceFormat = vulkanContext.surfaceFormats[0];
710 for(i = 0; i < vulkanContext.surfaceFormatsCount; i++)
712 if(vulkanContext.surfaceFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM)
714 vulkanContext.surfaceFormat = vulkanContext.surfaceFormats[i];
721 SDL_Vulkan_GetDrawableSize(state->windows[0], &w, &h);
722 vulkanContext.swapchainSize.width = w;
723 vulkanContext.swapchainSize.height = h;
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;
742 vkCreateSwapchainKHR(vulkanContext.device, &createInfo, NULL, &vulkanContext.swapchain);
743 if(createInfo.oldSwapchain)
744 vkDestroySwapchainKHR(vulkanContext.device, createInfo.oldSwapchain, NULL);
745 if(result != VK_SUCCESS)
747 vulkanContext.swapchain = VK_NULL_HANDLE;
748 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
749 "vkCreateSwapchainKHR(): %s\n",
750 getVulkanResultString(result));
753 getSwapchainImages();
757 static void destroySwapchain(void)
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;
766 static void destroyCommandBuffers(void)
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;
777 static void destroyCommandPool(void)
779 if(vulkanContext.commandPool)
780 vkDestroyCommandPool(vulkanContext.device, vulkanContext.commandPool, NULL);
781 vulkanContext.commandPool = VK_NULL_HANDLE;
784 static void createCommandPool(void)
788 VkCommandPoolCreateInfo createInfo = {0};
789 createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
791 VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
792 createInfo.queueFamilyIndex = vulkanContext.graphicsQueueFamilyIndex;
794 vkCreateCommandPool(vulkanContext.device, &createInfo, NULL, &vulkanContext.commandPool);
795 if(result != VK_SUCCESS)
797 vulkanContext.commandPool = VK_NULL_HANDLE;
798 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
799 "vkCreateCommandPool(): %s\n",
800 getVulkanResultString(result));
805 static void createCommandBuffers(void)
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);
817 vkAllocateCommandBuffers(vulkanContext.device, &allocateInfo, vulkanContext.commandBuffers);
818 if(result != VK_SUCCESS)
820 SDL_free(vulkanContext.commandBuffers);
821 vulkanContext.commandBuffers = NULL;
822 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
823 "vkAllocateCommandBuffers(): %s\n",
824 getVulkanResultString(result));
829 static void createFences(void)
833 vulkanContext.fences = SDL_malloc(sizeof(VkFence) * vulkanContext.swapchainImageCount);
834 if(!vulkanContext.fences)
839 for(i = 0; i < vulkanContext.swapchainImageCount; i++)
843 VkFenceCreateInfo createInfo = {0};
844 createInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
845 createInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
847 vkCreateFence(vulkanContext.device, &createInfo, NULL, &vulkanContext.fences[i]);
848 if(result != VK_SUCCESS)
852 vkDestroyFence(vulkanContext.device, vulkanContext.fences[i - 1], NULL);
854 SDL_free(vulkanContext.fences);
855 vulkanContext.fences = NULL;
856 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
857 "vkCreateFence(): %s\n",
858 getVulkanResultString(result));
864 static void destroyFences(void)
868 if(!vulkanContext.fences)
870 for(i = 0; i < vulkanContext.swapchainImageCount; i++)
872 vkDestroyFence(vulkanContext.device, vulkanContext.fences[i], NULL);
874 SDL_free(vulkanContext.fences);
875 vulkanContext.fences = NULL;
878 static void recordPipelineImageBarrier(VkCommandBuffer commandBuffer,
879 VkAccessFlags sourceAccessMask,
880 VkAccessFlags destAccessMask,
881 VkImageLayout sourceLayout,
882 VkImageLayout destLayout,
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,
911 static void rerecordCommandBuffer(uint32_t frameIndex, const VkClearColorValue *clearColor)
913 VkCommandBuffer commandBuffer = vulkanContext.commandBuffers[frameIndex];
914 VkImage image = vulkanContext.swapchainImages[frameIndex];
915 VkCommandBufferBeginInfo beginInfo = {0};
916 VkImageSubresourceRange clearRange = {0};
918 VkResult result = vkResetCommandBuffer(commandBuffer, 0);
919 if(result != VK_SUCCESS)
921 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
922 "vkResetCommandBuffer(): %s\n",
923 getVulkanResultString(result));
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)
931 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
932 "vkBeginCommandBuffer(): %s\n",
933 getVulkanResultString(result));
936 recordPipelineImageBarrier(commandBuffer,
938 VK_ACCESS_TRANSFER_WRITE_BIT,
939 VK_IMAGE_LAYOUT_UNDEFINED,
940 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
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,
955 result = vkEndCommandBuffer(commandBuffer);
956 if(result != VK_SUCCESS)
958 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
959 "vkEndCommandBuffer(): %s\n",
960 getVulkanResultString(result));
965 static void destroySwapchainAndSwapchainSpecificStuff(SDL_bool doDestroySwapchain)
968 destroyCommandBuffers();
969 destroyCommandPool();
970 if(doDestroySwapchain)
974 static SDL_bool createNewSwapchainAndSwapchainSpecificStuff(void)
976 destroySwapchainAndSwapchainSpecificStuff(SDL_FALSE);
979 if(!createSwapchain())
982 createCommandBuffers();
987 static void initVulkan(void)
989 SDL_Vulkan_LoadLibrary(NULL);
990 SDL_memset(&vulkanContext, 0, sizeof(VulkanContext));
991 loadGlobalFunctions();
993 loadInstanceFunctions();
995 findPhysicalDevice();
997 loadDeviceFunctions();
1000 createNewSwapchainAndSwapchainSpecificStuff();
1003 static void shutdownVulkan(void)
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();
1022 static SDL_bool render(void)
1024 uint32_t frameIndex;
1027 VkClearColorValue clearColor = { {0} };
1028 VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
1029 VkSubmitInfo submitInfo = {0};
1030 VkPresentInfoKHR presentInfo = {0};
1033 if(!vulkanContext.swapchain)
1035 SDL_bool retval = createNewSwapchainAndSwapchainSpecificStuff();
1040 result = vkAcquireNextImageKHR(vulkanContext.device,
1041 vulkanContext.swapchain,
1043 vulkanContext.imageAvailableSemaphore,
1046 if(result == VK_ERROR_OUT_OF_DATE_KHR)
1047 return createNewSwapchainAndSwapchainSpecificStuff();
1048 if(result != VK_SUBOPTIMAL_KHR && result != VK_SUCCESS)
1050 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
1051 "vkAcquireNextImageKHR(): %s\n",
1052 getVulkanResultString(result));
1055 result = vkWaitForFences(
1056 vulkanContext.device, 1, &vulkanContext.fences[frameIndex], VK_FALSE, UINT64_MAX);
1057 if(result != VK_SUCCESS)
1060 SDL_LOG_CATEGORY_APPLICATION, "vkWaitForFences(): %s\n", getVulkanResultString(result));
1063 result = vkResetFences(vulkanContext.device, 1, &vulkanContext.fences[frameIndex]);
1064 if(result != VK_SUCCESS)
1067 SDL_LOG_CATEGORY_APPLICATION, "vkResetFences(): %s\n", getVulkanResultString(result));
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)
1089 SDL_LOG_CATEGORY_APPLICATION, "vkQueueSubmit(): %s\n", getVulkanResultString(result));
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)
1101 return createNewSwapchainAndSwapchainSpecificStuff();
1103 if(result != VK_SUCCESS)
1105 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
1106 "vkQueuePresentKHR(): %s\n",
1107 getVulkanResultString(result));
1110 SDL_Vulkan_GetDrawableSize(state->windows[0], &w, &h);
1111 if(w != (int)vulkanContext.swapchainSize.width || h != (int)vulkanContext.swapchainSize.height)
1113 return createNewSwapchainAndSwapchainSpecificStuff();
1118 int main(int argc, char *argv[])
1122 SDL_DisplayMode mode;
1124 Uint32 then, now, frames;
1127 /* Enable standard application logging */
1128 SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
1130 /* Initialize parameters */
1134 /* Initialize test framework */
1135 state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
1141 /* Set Vulkan parameters */
1142 state->window_flags |= SDL_WINDOW_VULKAN;
1143 state->num_windows = 1;
1144 state->skip_renderer = 1;
1146 if (!SDLTest_CommonDefaultArgs(state, argc, argv) || !SDLTest_CommonInit(state)) {
1147 SDLTest_CommonQuit(state);
1151 SDL_GetCurrentDisplayMode(0, &mode);
1152 SDL_Log("Screen BPP : %d\n", SDL_BITSPERPIXEL(mode.format));
1153 SDL_GetWindowSize(state->windows[0], &dw, &dh);
1154 SDL_Log("Window Size : %d,%d\n", dw, dh);
1155 SDL_Vulkan_GetDrawableSize(state->windows[0], &dw, &dh);
1156 SDL_Log("Draw Size : %d,%d\n", dw, dh);
1161 /* Main render loop */
1163 then = SDL_GetTicks();
1167 /* Check for events */
1169 while(SDL_PollEvent(&event))
1171 SDLTest_CommonEvent(state, &event, &done);
1178 /* Print out some timing information */
1179 now = SDL_GetTicks();
1182 SDL_Log("%2.2f frames per second\n", ((double)frames * 1000) / (now - then));