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