SDL

Форк
0
/
testvulkan.c 
1173 строки · 45.7 Кб
1
/*
2
  Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
3

4
  This software is provided 'as-is', without any express or implied
5
  warranty.  In no event will the authors be held liable for any damages
6
  arising from the use of this software.
7

8
  Permission is granted to anyone to use this software for any purpose,
9
  including commercial applications, and to alter it and redistribute it
10
  freely.
11
*/
12
#include <stdlib.h>
13

14
#include <SDL3/SDL_test_common.h>
15
#include <SDL3/SDL_main.h>
16

17
#if defined(SDL_PLATFORM_ANDROID) && defined(__ARM_EABI__) && !defined(__ARM_ARCH_7A__)
18

19
int main(int argc, char *argv[])
20
{
21
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No Vulkan support on this system\n");
22
    return 1;
23
}
24

25
#else
26

27
#define VK_NO_PROTOTYPES
28
#ifdef HAVE_VULKAN_H
29
#include <vulkan/vulkan.h>
30
#else
31
/* SDL includes a copy for building on systems without the Vulkan SDK */
32
#include "../src/video/khronos/vulkan/vulkan.h"
33
#endif
34
#include <SDL3/SDL_vulkan.h>
35

36
#ifndef UINT64_MAX /* VS2008 */
37
#define UINT64_MAX 18446744073709551615
38
#endif
39

40
#define VULKAN_FUNCTIONS()                                              \
41
    VULKAN_DEVICE_FUNCTION(vkAcquireNextImageKHR)                       \
42
    VULKAN_DEVICE_FUNCTION(vkAllocateCommandBuffers)                    \
43
    VULKAN_DEVICE_FUNCTION(vkBeginCommandBuffer)                        \
44
    VULKAN_DEVICE_FUNCTION(vkCmdClearColorImage)                        \
45
    VULKAN_DEVICE_FUNCTION(vkCmdPipelineBarrier)                        \
46
    VULKAN_DEVICE_FUNCTION(vkCreateCommandPool)                         \
47
    VULKAN_DEVICE_FUNCTION(vkCreateFence)                               \
48
    VULKAN_DEVICE_FUNCTION(vkCreateImageView)                           \
49
    VULKAN_DEVICE_FUNCTION(vkCreateSemaphore)                           \
50
    VULKAN_DEVICE_FUNCTION(vkCreateSwapchainKHR)                        \
51
    VULKAN_DEVICE_FUNCTION(vkDestroyCommandPool)                        \
52
    VULKAN_DEVICE_FUNCTION(vkDestroyDevice)                             \
53
    VULKAN_DEVICE_FUNCTION(vkDestroyFence)                              \
54
    VULKAN_DEVICE_FUNCTION(vkDestroyImageView)                          \
55
    VULKAN_DEVICE_FUNCTION(vkDestroySemaphore)                          \
56
    VULKAN_DEVICE_FUNCTION(vkDestroySwapchainKHR)                       \
57
    VULKAN_DEVICE_FUNCTION(vkDeviceWaitIdle)                            \
58
    VULKAN_DEVICE_FUNCTION(vkEndCommandBuffer)                          \
59
    VULKAN_DEVICE_FUNCTION(vkFreeCommandBuffers)                        \
60
    VULKAN_DEVICE_FUNCTION(vkGetDeviceQueue)                            \
61
    VULKAN_DEVICE_FUNCTION(vkGetFenceStatus)                            \
62
    VULKAN_DEVICE_FUNCTION(vkGetSwapchainImagesKHR)                     \
63
    VULKAN_DEVICE_FUNCTION(vkQueuePresentKHR)                           \
64
    VULKAN_DEVICE_FUNCTION(vkQueueSubmit)                               \
65
    VULKAN_DEVICE_FUNCTION(vkResetCommandBuffer)                        \
66
    VULKAN_DEVICE_FUNCTION(vkResetFences)                               \
67
    VULKAN_DEVICE_FUNCTION(vkWaitForFences)                             \
68
    VULKAN_GLOBAL_FUNCTION(vkCreateInstance)                            \
69
    VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceExtensionProperties)      \
70
    VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceLayerProperties)          \
71
    VULKAN_INSTANCE_FUNCTION(vkCreateDevice)                            \
72
    VULKAN_INSTANCE_FUNCTION(vkDestroyInstance)                         \
73
    VULKAN_INSTANCE_FUNCTION(vkDestroySurfaceKHR)                       \
74
    VULKAN_INSTANCE_FUNCTION(vkEnumerateDeviceExtensionProperties)      \
75
    VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices)                \
76
    VULKAN_INSTANCE_FUNCTION(vkGetDeviceProcAddr)                       \
77
    VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures)               \
78
    VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties)             \
79
    VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties)  \
80
    VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \
81
    VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR)      \
82
    VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR) \
83
    VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR)
84

85
#define VULKAN_DEVICE_FUNCTION(name)   static PFN_##name name = NULL;
86
#define VULKAN_GLOBAL_FUNCTION(name)   static PFN_##name name = NULL;
87
#define VULKAN_INSTANCE_FUNCTION(name) static PFN_##name name = NULL;
88
VULKAN_FUNCTIONS()
89
#undef VULKAN_DEVICE_FUNCTION
90
#undef VULKAN_GLOBAL_FUNCTION
91
#undef VULKAN_INSTANCE_FUNCTION
92
static PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
93

94
/* Based on the headers found in
95
 * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers
96
 */
97
#if VK_HEADER_VERSION < 22
98
enum
99
{
100
    VK_ERROR_FRAGMENTED_POOL = -12,
101
};
102
#endif
103
#if VK_HEADER_VERSION < 38
104
enum
105
{
106
    VK_ERROR_OUT_OF_POOL_MEMORY_KHR = -1000069000
107
};
108
#endif
109

110
static const char *getVulkanResultString(VkResult result)
111
{
112
    switch ((int)result) {
113
#define RESULT_CASE(x) \
114
    case x:            \
115
        return #x
116
        RESULT_CASE(VK_SUCCESS);
117
        RESULT_CASE(VK_NOT_READY);
118
        RESULT_CASE(VK_TIMEOUT);
119
        RESULT_CASE(VK_EVENT_SET);
120
        RESULT_CASE(VK_EVENT_RESET);
121
        RESULT_CASE(VK_INCOMPLETE);
122
        RESULT_CASE(VK_ERROR_OUT_OF_HOST_MEMORY);
123
        RESULT_CASE(VK_ERROR_OUT_OF_DEVICE_MEMORY);
124
        RESULT_CASE(VK_ERROR_INITIALIZATION_FAILED);
125
        RESULT_CASE(VK_ERROR_DEVICE_LOST);
126
        RESULT_CASE(VK_ERROR_MEMORY_MAP_FAILED);
127
        RESULT_CASE(VK_ERROR_LAYER_NOT_PRESENT);
128
        RESULT_CASE(VK_ERROR_EXTENSION_NOT_PRESENT);
129
        RESULT_CASE(VK_ERROR_FEATURE_NOT_PRESENT);
130
        RESULT_CASE(VK_ERROR_INCOMPATIBLE_DRIVER);
131
        RESULT_CASE(VK_ERROR_TOO_MANY_OBJECTS);
132
        RESULT_CASE(VK_ERROR_FORMAT_NOT_SUPPORTED);
133
        RESULT_CASE(VK_ERROR_FRAGMENTED_POOL);
134
        RESULT_CASE(VK_ERROR_SURFACE_LOST_KHR);
135
        RESULT_CASE(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR);
136
        RESULT_CASE(VK_SUBOPTIMAL_KHR);
137
        RESULT_CASE(VK_ERROR_OUT_OF_DATE_KHR);
138
        RESULT_CASE(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);
139
        RESULT_CASE(VK_ERROR_VALIDATION_FAILED_EXT);
140
        RESULT_CASE(VK_ERROR_OUT_OF_POOL_MEMORY_KHR);
141
        RESULT_CASE(VK_ERROR_INVALID_SHADER_NV);
142
#undef RESULT_CASE
143
    default:
144
        break;
145
    }
146
    return (result < 0) ? "VK_ERROR_<Unknown>" : "VK_<Unknown>";
147
}
148

149
typedef struct VulkanContext
150
{
151
    SDL_Window *window;
152
    VkInstance instance;
153
    VkDevice device;
154
    VkSurfaceKHR surface;
155
    VkSwapchainKHR swapchain;
156
    VkPhysicalDeviceProperties physicalDeviceProperties;
157
    VkPhysicalDeviceFeatures physicalDeviceFeatures;
158
    uint32_t graphicsQueueFamilyIndex;
159
    uint32_t presentQueueFamilyIndex;
160
    VkPhysicalDevice physicalDevice;
161
    VkQueue graphicsQueue;
162
    VkQueue presentQueue;
163
    VkSemaphore imageAvailableSemaphore;
164
    VkSemaphore renderingFinishedSemaphore;
165
    VkSurfaceCapabilitiesKHR surfaceCapabilities;
166
    VkSurfaceFormatKHR *surfaceFormats;
167
    uint32_t surfaceFormatsAllocatedCount;
168
    uint32_t surfaceFormatsCount;
169
    uint32_t swapchainDesiredImageCount;
170
    VkSurfaceFormatKHR surfaceFormat;
171
    VkExtent2D swapchainSize;
172
    VkCommandPool commandPool;
173
    uint32_t swapchainImageCount;
174
    VkImage *swapchainImages;
175
    VkCommandBuffer *commandBuffers;
176
    VkFence *fences;
177
} VulkanContext;
178

179
static SDLTest_CommonState *state;
180
static VulkanContext *vulkanContexts = NULL; /* an array of state->num_windows items */
181
static VulkanContext *vulkanContext = NULL;  /* for the currently-rendering window */
182

183
static void shutdownVulkan(SDL_bool doDestroySwapchain);
184

185
/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
186
static void quit(int rc)
187
{
188
    shutdownVulkan(SDL_TRUE);
189
    SDLTest_CommonQuit(state);
190
    /* Let 'main()' return normally */
191
    if (rc != 0) {
192
        exit(rc);
193
    }
194
}
195

196
static void loadGlobalFunctions(void)
197
{
198
    vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr();
199
    if (!vkGetInstanceProcAddr) {
200
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
201
                     "SDL_Vulkan_GetVkGetInstanceProcAddr(): %s\n",
202
                     SDL_GetError());
203
        quit(2);
204
    }
205

206
#define VULKAN_DEVICE_FUNCTION(name)
207
#define VULKAN_GLOBAL_FUNCTION(name)                                                   \
208
    name = (PFN_##name)vkGetInstanceProcAddr(VK_NULL_HANDLE, #name);                   \
209
    if (!name) {                                                                       \
210
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,                                     \
211
                     "vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed\n"); \
212
        quit(2);                                                                       \
213
    }
214
#define VULKAN_INSTANCE_FUNCTION(name)
215
    VULKAN_FUNCTIONS()
216
#undef VULKAN_DEVICE_FUNCTION
217
#undef VULKAN_GLOBAL_FUNCTION
218
#undef VULKAN_INSTANCE_FUNCTION
219
}
220

221
static void createInstance(void)
222
{
223
    VkApplicationInfo appInfo = { 0 };
224
    VkInstanceCreateInfo instanceCreateInfo = { 0 };
225
    VkResult result;
226

227
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
228
    appInfo.apiVersion = VK_API_VERSION_1_0;
229
    instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
230
    instanceCreateInfo.pApplicationInfo = &appInfo;
231
#ifdef __APPLE__
232
    instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
233
#endif
234

235
    instanceCreateInfo.ppEnabledExtensionNames = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount);
236
    result = vkCreateInstance(&instanceCreateInfo, NULL, &vulkanContext->instance);
237
    if (result != VK_SUCCESS) {
238
        vulkanContext->instance = VK_NULL_HANDLE;
239
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
240
                     "vkCreateInstance(): %s\n",
241
                     getVulkanResultString(result));
242
        quit(2);
243
    }
244
}
245

246
static void loadInstanceFunctions(void)
247
{
248
#define VULKAN_DEVICE_FUNCTION(name)
249
#define VULKAN_GLOBAL_FUNCTION(name)
250
#define VULKAN_INSTANCE_FUNCTION(name)                                           \
251
    name = (PFN_##name)vkGetInstanceProcAddr(vulkanContext->instance, #name);    \
252
    if (!name) {                                                                 \
253
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,                               \
254
                     "vkGetInstanceProcAddr(instance, \"" #name "\") failed\n"); \
255
        quit(2);                                                                 \
256
    }
257
    VULKAN_FUNCTIONS()
258
#undef VULKAN_DEVICE_FUNCTION
259
#undef VULKAN_GLOBAL_FUNCTION
260
#undef VULKAN_INSTANCE_FUNCTION
261
}
262

263
static void createSurface(void)
264
{
265
    if (SDL_Vulkan_CreateSurface(vulkanContext->window, vulkanContext->instance, NULL, &vulkanContext->surface) < 0) {
266
        vulkanContext->surface = VK_NULL_HANDLE;
267
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_Vulkan_CreateSurface(): %s\n", SDL_GetError());
268
        quit(2);
269
    }
270
}
271

272
static void findPhysicalDevice(void)
273
{
274
    uint32_t physicalDeviceCount = 0;
275
    VkPhysicalDevice *physicalDevices;
276
    VkQueueFamilyProperties *queueFamiliesProperties = NULL;
277
    uint32_t queueFamiliesPropertiesAllocatedSize = 0;
278
    VkExtensionProperties *deviceExtensions = NULL;
279
    uint32_t deviceExtensionsAllocatedSize = 0;
280
    uint32_t physicalDeviceIndex;
281
    VkResult result;
282

283
    result = vkEnumeratePhysicalDevices(vulkanContext->instance, &physicalDeviceCount, NULL);
284
    if (result != VK_SUCCESS) {
285
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
286
                     "vkEnumeratePhysicalDevices(): %s\n",
287
                     getVulkanResultString(result));
288
        quit(2);
289
    }
290
    if (physicalDeviceCount == 0) {
291
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
292
                     "vkEnumeratePhysicalDevices(): no physical devices\n");
293
        quit(2);
294
    }
295
    physicalDevices = (VkPhysicalDevice *)SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
296
    if (!physicalDevices) {
297
        quit(2);
298
    }
299
    result = vkEnumeratePhysicalDevices(vulkanContext->instance, &physicalDeviceCount, physicalDevices);
300
    if (result != VK_SUCCESS) {
301
        SDL_free(physicalDevices);
302
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
303
                     "vkEnumeratePhysicalDevices(): %s\n",
304
                     getVulkanResultString(result));
305
        quit(2);
306
    }
307
    vulkanContext->physicalDevice = NULL;
308
    for (physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount; physicalDeviceIndex++) {
309
        uint32_t queueFamiliesCount = 0;
310
        uint32_t queueFamilyIndex;
311
        uint32_t deviceExtensionCount = 0;
312
        SDL_bool hasSwapchainExtension = SDL_FALSE;
313
        SDL_bool supportsPresent;
314
        uint32_t i;
315

316
        VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex];
317
        vkGetPhysicalDeviceProperties(physicalDevice, &vulkanContext->physicalDeviceProperties);
318
        if (VK_VERSION_MAJOR(vulkanContext->physicalDeviceProperties.apiVersion) < 1) {
319
            continue;
320
        }
321
        vkGetPhysicalDeviceFeatures(physicalDevice, &vulkanContext->physicalDeviceFeatures);
322
        vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, NULL);
323
        if (queueFamiliesCount == 0) {
324
            continue;
325
        }
326
        if (queueFamiliesPropertiesAllocatedSize < queueFamiliesCount) {
327
            SDL_free(queueFamiliesProperties);
328
            queueFamiliesPropertiesAllocatedSize = queueFamiliesCount;
329
            queueFamiliesProperties = (VkQueueFamilyProperties *)SDL_malloc(sizeof(VkQueueFamilyProperties) * queueFamiliesPropertiesAllocatedSize);
330
            if (!queueFamiliesProperties) {
331
                SDL_free(physicalDevices);
332
                SDL_free(deviceExtensions);
333
                quit(2);
334
            }
335
        }
336
        vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, queueFamiliesProperties);
337
        vulkanContext->graphicsQueueFamilyIndex = queueFamiliesCount;
338
        vulkanContext->presentQueueFamilyIndex = queueFamiliesCount;
339
        for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; queueFamilyIndex++) {
340
            VkBool32 supported = 0;
341

342
            if (queueFamiliesProperties[queueFamilyIndex].queueCount == 0) {
343
                continue;
344
            }
345

346
            if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
347
                vulkanContext->graphicsQueueFamilyIndex = queueFamilyIndex;
348
            }
349

350
            result = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, vulkanContext->surface, &supported);
351
            if (result != VK_SUCCESS) {
352
                SDL_free(physicalDevices);
353
                SDL_free(queueFamiliesProperties);
354
                SDL_free(deviceExtensions);
355
                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
356
                             "vkGetPhysicalDeviceSurfaceSupportKHR(): %s\n",
357
                             getVulkanResultString(result));
358
                quit(2);
359
            }
360
            if (supported) {
361
                /* This check isn't necessary if you are able to check a
362
                 * VkSurfaceKHR like above, but just as a sanity check we do
363
                 * this here as part of testing the API.
364
                 * -flibit
365
                 */
366
                supportsPresent = SDL_Vulkan_GetPresentationSupport(vulkanContext->instance, physicalDevice, queueFamilyIndex);
367
                if (!supportsPresent) {
368
                    SDL_free(physicalDevices);
369
                    SDL_free(queueFamiliesProperties);
370
                    SDL_free(deviceExtensions);
371
                    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
372
                                 "SDL_Vulkan_GetPresentationSupport(): %s\n",
373
                                 SDL_GetError());
374
                    quit(2);
375
                }
376

377
                vulkanContext->presentQueueFamilyIndex = queueFamilyIndex;
378
                if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
379
                    break; // use this queue because it can present and do graphics
380
                }
381
            }
382
        }
383

384
        if (vulkanContext->graphicsQueueFamilyIndex == queueFamiliesCount) { // no good queues found
385
            continue;
386
        }
387
        if (vulkanContext->presentQueueFamilyIndex == queueFamiliesCount) { // no good queues found
388
            continue;
389
        }
390
        result = vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, NULL);
391
        if (result != VK_SUCCESS) {
392
            SDL_free(physicalDevices);
393
            SDL_free(queueFamiliesProperties);
394
            SDL_free(deviceExtensions);
395
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
396
                         "vkEnumerateDeviceExtensionProperties(): %s\n",
397
                         getVulkanResultString(result));
398
            quit(2);
399
        }
400
        if (deviceExtensionCount == 0) {
401
            continue;
402
        }
403
        if (deviceExtensionsAllocatedSize < deviceExtensionCount) {
404
            SDL_free(deviceExtensions);
405
            deviceExtensionsAllocatedSize = deviceExtensionCount;
406
            deviceExtensions = SDL_malloc(sizeof(VkExtensionProperties) * deviceExtensionsAllocatedSize);
407
            if (!deviceExtensions) {
408
                SDL_free(physicalDevices);
409
                SDL_free(queueFamiliesProperties);
410
                quit(2);
411
            }
412
        }
413
        result = vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, deviceExtensions);
414
        if (result != VK_SUCCESS) {
415
            SDL_free(physicalDevices);
416
            SDL_free(queueFamiliesProperties);
417
            SDL_free(deviceExtensions);
418
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
419
                         "vkEnumerateDeviceExtensionProperties(): %s\n",
420
                         getVulkanResultString(result));
421
            quit(2);
422
        }
423
        for (i = 0; i < deviceExtensionCount; i++) {
424
            if (SDL_strcmp(deviceExtensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) {
425
                hasSwapchainExtension = SDL_TRUE;
426
                break;
427
            }
428
        }
429
        if (!hasSwapchainExtension) {
430
            continue;
431
        }
432
        vulkanContext->physicalDevice = physicalDevice;
433
        break;
434
    }
435
    SDL_free(physicalDevices);
436
    SDL_free(queueFamiliesProperties);
437
    SDL_free(deviceExtensions);
438
    if (!vulkanContext->physicalDevice) {
439
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Vulkan: no viable physical devices found");
440
        quit(2);
441
    }
442
}
443

444
static void createDevice(void)
445
{
446
    VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { { 0 }, { 0 } };
447
    static const float queuePriority[] = { 1.0f };
448
    VkDeviceCreateInfo deviceCreateInfo = { 0 };
449
    static const char *const deviceExtensionNames[] = {
450
        VK_KHR_SWAPCHAIN_EXTENSION_NAME,
451
#ifdef __APPLE__        
452
        "VK_KHR_portability_subset"
453
#endif        
454
    };
455
    VkResult result;
456

457
    deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
458
    deviceCreateInfo.queueCreateInfoCount = 0;
459
    deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
460
    deviceCreateInfo.pEnabledFeatures = NULL;
461
    deviceCreateInfo.enabledExtensionCount = SDL_arraysize(deviceExtensionNames);
462
    deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames;
463

464
    deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
465
    deviceQueueCreateInfo[0].queueFamilyIndex = vulkanContext->graphicsQueueFamilyIndex;
466
    deviceQueueCreateInfo[0].queueCount = 1;
467
    deviceQueueCreateInfo[0].pQueuePriorities = queuePriority;
468
    ++deviceCreateInfo.queueCreateInfoCount;
469

470
    if (vulkanContext->presentQueueFamilyIndex != vulkanContext->graphicsQueueFamilyIndex) {
471
        deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
472
        deviceQueueCreateInfo[1].queueFamilyIndex = vulkanContext->presentQueueFamilyIndex;
473
        deviceQueueCreateInfo[1].queueCount = 1;
474
        deviceQueueCreateInfo[1].pQueuePriorities = queuePriority;
475
        ++deviceCreateInfo.queueCreateInfoCount;
476
    }
477

478
    result = vkCreateDevice(vulkanContext->physicalDevice, &deviceCreateInfo, NULL, &vulkanContext->device);
479
    if (result != VK_SUCCESS) {
480
        vulkanContext->device = VK_NULL_HANDLE;
481
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkCreateDevice(): %s\n", getVulkanResultString(result));
482
        quit(2);
483
    }
484
}
485

486
static void loadDeviceFunctions(void)
487
{
488
#define VULKAN_DEVICE_FUNCTION(name)                                         \
489
    name = (PFN_##name)vkGetDeviceProcAddr(vulkanContext->device, #name);    \
490
    if (!name) {                                                             \
491
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,                           \
492
                     "vkGetDeviceProcAddr(device, \"" #name "\") failed\n"); \
493
        quit(2);                                                             \
494
    }
495
#define VULKAN_GLOBAL_FUNCTION(name)
496
#define VULKAN_INSTANCE_FUNCTION(name)
497
    VULKAN_FUNCTIONS()
498
#undef VULKAN_DEVICE_FUNCTION
499
#undef VULKAN_GLOBAL_FUNCTION
500
#undef VULKAN_INSTANCE_FUNCTION
501
}
502

503
#undef VULKAN_FUNCTIONS
504

505
static void getQueues(void)
506
{
507
    vkGetDeviceQueue(vulkanContext->device,
508
                     vulkanContext->graphicsQueueFamilyIndex,
509
                     0,
510
                     &vulkanContext->graphicsQueue);
511
    if (vulkanContext->graphicsQueueFamilyIndex != vulkanContext->presentQueueFamilyIndex) {
512
        vkGetDeviceQueue(vulkanContext->device,
513
                         vulkanContext->presentQueueFamilyIndex,
514
                         0,
515
                         &vulkanContext->presentQueue);
516
    } else {
517
        vulkanContext->presentQueue = vulkanContext->graphicsQueue;
518
    }
519
}
520

521
static void createSemaphore(VkSemaphore *semaphore)
522
{
523
    VkResult result;
524

525
    VkSemaphoreCreateInfo createInfo = { 0 };
526
    createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
527
    result = vkCreateSemaphore(vulkanContext->device, &createInfo, NULL, semaphore);
528
    if (result != VK_SUCCESS) {
529
        *semaphore = VK_NULL_HANDLE;
530
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
531
                     "vkCreateSemaphore(): %s\n",
532
                     getVulkanResultString(result));
533
        quit(2);
534
    }
535
}
536

537
static void createSemaphores(void)
538
{
539
    createSemaphore(&vulkanContext->imageAvailableSemaphore);
540
    createSemaphore(&vulkanContext->renderingFinishedSemaphore);
541
}
542

543
static void getSurfaceCaps(void)
544
{
545
    VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkanContext->physicalDevice, vulkanContext->surface, &vulkanContext->surfaceCapabilities);
546
    if (result != VK_SUCCESS) {
547
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
548
                     "vkGetPhysicalDeviceSurfaceCapabilitiesKHR(): %s\n",
549
                     getVulkanResultString(result));
550
        quit(2);
551
    }
552

553
    // check surface usage
554
    if (!(vulkanContext->surfaceCapabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) {
555
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
556
                     "Vulkan surface doesn't support VK_IMAGE_USAGE_TRANSFER_DST_BIT\n");
557
        quit(2);
558
    }
559
}
560

561
static void getSurfaceFormats(void)
562
{
563
    VkResult result = vkGetPhysicalDeviceSurfaceFormatsKHR(vulkanContext->physicalDevice,
564
                                                           vulkanContext->surface,
565
                                                           &vulkanContext->surfaceFormatsCount,
566
                                                           NULL);
567
    if (result != VK_SUCCESS) {
568
        vulkanContext->surfaceFormatsCount = 0;
569
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
570
                     "vkGetPhysicalDeviceSurfaceFormatsKHR(): %s\n",
571
                     getVulkanResultString(result));
572
        quit(2);
573
    }
574
    if (vulkanContext->surfaceFormatsCount > vulkanContext->surfaceFormatsAllocatedCount) {
575
        vulkanContext->surfaceFormatsAllocatedCount = vulkanContext->surfaceFormatsCount;
576
        SDL_free(vulkanContext->surfaceFormats);
577
        vulkanContext->surfaceFormats = (VkSurfaceFormatKHR *)SDL_malloc(sizeof(VkSurfaceFormatKHR) * vulkanContext->surfaceFormatsAllocatedCount);
578
        if (!vulkanContext->surfaceFormats) {
579
            vulkanContext->surfaceFormatsCount = 0;
580
            quit(2);
581
        }
582
    }
583
    result = vkGetPhysicalDeviceSurfaceFormatsKHR(vulkanContext->physicalDevice,
584
                                                  vulkanContext->surface,
585
                                                  &vulkanContext->surfaceFormatsCount,
586
                                                  vulkanContext->surfaceFormats);
587
    if (result != VK_SUCCESS) {
588
        vulkanContext->surfaceFormatsCount = 0;
589
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
590
                     "vkGetPhysicalDeviceSurfaceFormatsKHR(): %s\n",
591
                     getVulkanResultString(result));
592
        quit(2);
593
    }
594
}
595

596
static void getSwapchainImages(void)
597
{
598
    VkResult result;
599

600
    SDL_free(vulkanContext->swapchainImages);
601
    vulkanContext->swapchainImages = NULL;
602
    result = vkGetSwapchainImagesKHR(vulkanContext->device, vulkanContext->swapchain, &vulkanContext->swapchainImageCount, NULL);
603
    if (result != VK_SUCCESS) {
604
        vulkanContext->swapchainImageCount = 0;
605
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
606
                     "vkGetSwapchainImagesKHR(): %s\n",
607
                     getVulkanResultString(result));
608
        quit(2);
609
    }
610
    vulkanContext->swapchainImages = SDL_malloc(sizeof(VkImage) * vulkanContext->swapchainImageCount);
611
    if (!vulkanContext->swapchainImages) {
612
        quit(2);
613
    }
614
    result = vkGetSwapchainImagesKHR(vulkanContext->device,
615
                                     vulkanContext->swapchain,
616
                                     &vulkanContext->swapchainImageCount,
617
                                     vulkanContext->swapchainImages);
618
    if (result != VK_SUCCESS) {
619
        SDL_free(vulkanContext->swapchainImages);
620
        vulkanContext->swapchainImages = NULL;
621
        vulkanContext->swapchainImageCount = 0;
622
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
623
                     "vkGetSwapchainImagesKHR(): %s\n",
624
                     getVulkanResultString(result));
625
        quit(2);
626
    }
627
}
628

629
static SDL_bool createSwapchain(void)
630
{
631
    uint32_t i;
632
    int w, h;
633
    VkSwapchainCreateInfoKHR createInfo = { 0 };
634
    VkResult result;
635
    SDL_WindowFlags flags;
636

637
    // pick an image count
638
    vulkanContext->swapchainDesiredImageCount = vulkanContext->surfaceCapabilities.minImageCount + 1;
639
    if ((vulkanContext->swapchainDesiredImageCount > vulkanContext->surfaceCapabilities.maxImageCount) &&
640
        (vulkanContext->surfaceCapabilities.maxImageCount > 0)) {
641
        vulkanContext->swapchainDesiredImageCount = vulkanContext->surfaceCapabilities.maxImageCount;
642
    }
643

644
    // pick a format
645
    if ((vulkanContext->surfaceFormatsCount == 1) &&
646
        (vulkanContext->surfaceFormats[0].format == VK_FORMAT_UNDEFINED)) {
647
        // aren't any preferred formats, so we pick
648
        vulkanContext->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
649
        vulkanContext->surfaceFormat.format = VK_FORMAT_R8G8B8A8_UNORM;
650
    } else {
651
        vulkanContext->surfaceFormat = vulkanContext->surfaceFormats[0];
652
        for (i = 0; i < vulkanContext->surfaceFormatsCount; i++) {
653
            if (vulkanContext->surfaceFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM) {
654
                vulkanContext->surfaceFormat = vulkanContext->surfaceFormats[i];
655
                break;
656
            }
657
        }
658
    }
659

660
    // get size
661
    SDL_GetWindowSizeInPixels(vulkanContext->window, &w, &h);
662

663
    // get flags
664
    flags = SDL_GetWindowFlags(vulkanContext->window);
665

666
    // Clamp the size to the allowable image extent.
667
    // SDL_GetWindowSizeInPixels()'s result it not always in this range (bug #3287)
668
    vulkanContext->swapchainSize.width = SDL_clamp((uint32_t)w,
669
                                                   vulkanContext->surfaceCapabilities.minImageExtent.width,
670
                                                   vulkanContext->surfaceCapabilities.maxImageExtent.width);
671

672
    vulkanContext->swapchainSize.height = SDL_clamp((uint32_t)h,
673
                                                    vulkanContext->surfaceCapabilities.minImageExtent.height,
674
                                                    vulkanContext->surfaceCapabilities.maxImageExtent.height);
675

676
    if (w == 0 || h == 0) {
677
        return SDL_FALSE;
678
    }
679

680
    getSurfaceCaps();
681

682
    createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
683
    createInfo.surface = vulkanContext->surface;
684
    createInfo.minImageCount = vulkanContext->swapchainDesiredImageCount;
685
    createInfo.imageFormat = vulkanContext->surfaceFormat.format;
686
    createInfo.imageColorSpace = vulkanContext->surfaceFormat.colorSpace;
687
    createInfo.imageExtent = vulkanContext->swapchainSize;
688
    createInfo.imageArrayLayers = 1;
689
    createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
690
    createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
691
    createInfo.preTransform = vulkanContext->surfaceCapabilities.currentTransform;
692
    if (flags & SDL_WINDOW_TRANSPARENT) {
693
        createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
694
    } else {
695
        createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
696
    }
697
    createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
698
    createInfo.clipped = VK_TRUE;
699
    createInfo.oldSwapchain = vulkanContext->swapchain;
700
    result = vkCreateSwapchainKHR(vulkanContext->device, &createInfo, NULL, &vulkanContext->swapchain);
701

702
    if (createInfo.oldSwapchain) {
703
        vkDestroySwapchainKHR(vulkanContext->device, createInfo.oldSwapchain, NULL);
704
    }
705

706
    if (result != VK_SUCCESS) {
707
        vulkanContext->swapchain = VK_NULL_HANDLE;
708
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
709
                     "vkCreateSwapchainKHR(): %s\n",
710
                     getVulkanResultString(result));
711
        quit(2);
712
    }
713

714
    getSwapchainImages();
715
    return SDL_TRUE;
716
}
717

718
static void destroySwapchain(void)
719
{
720
    if (vulkanContext->swapchain) {
721
        vkDestroySwapchainKHR(vulkanContext->device, vulkanContext->swapchain, NULL);
722
        vulkanContext->swapchain = VK_NULL_HANDLE;
723
    }
724
    SDL_free(vulkanContext->swapchainImages);
725
    vulkanContext->swapchainImages = NULL;
726
}
727

728
static void destroyCommandBuffers(void)
729
{
730
    if (vulkanContext->commandBuffers) {
731
        vkFreeCommandBuffers(vulkanContext->device,
732
                             vulkanContext->commandPool,
733
                             vulkanContext->swapchainImageCount,
734
                             vulkanContext->commandBuffers);
735
        SDL_free(vulkanContext->commandBuffers);
736
        vulkanContext->commandBuffers = NULL;
737
    }
738
}
739

740
static void destroyCommandPool(void)
741
{
742
    if (vulkanContext->commandPool) {
743
        vkDestroyCommandPool(vulkanContext->device, vulkanContext->commandPool, NULL);
744
    }
745
    vulkanContext->commandPool = VK_NULL_HANDLE;
746
}
747

748
static void createCommandPool(void)
749
{
750
    VkResult result;
751
    VkCommandPoolCreateInfo createInfo = { 0 };
752
    createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
753
    createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
754
    createInfo.queueFamilyIndex = vulkanContext->graphicsQueueFamilyIndex;
755
    result = vkCreateCommandPool(vulkanContext->device, &createInfo, NULL, &vulkanContext->commandPool);
756
    if (result != VK_SUCCESS) {
757
        vulkanContext->commandPool = VK_NULL_HANDLE;
758
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
759
                     "vkCreateCommandPool(): %s\n",
760
                     getVulkanResultString(result));
761
        quit(2);
762
    }
763
}
764

765
static void createCommandBuffers(void)
766
{
767
    VkResult result;
768
    VkCommandBufferAllocateInfo allocateInfo = { 0 };
769
    allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
770
    allocateInfo.commandPool = vulkanContext->commandPool;
771
    allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
772
    allocateInfo.commandBufferCount = vulkanContext->swapchainImageCount;
773
    vulkanContext->commandBuffers = (VkCommandBuffer *)SDL_malloc(sizeof(VkCommandBuffer) * vulkanContext->swapchainImageCount);
774
    result = vkAllocateCommandBuffers(vulkanContext->device, &allocateInfo, vulkanContext->commandBuffers);
775
    if (result != VK_SUCCESS) {
776
        SDL_free(vulkanContext->commandBuffers);
777
        vulkanContext->commandBuffers = NULL;
778
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
779
                     "vkAllocateCommandBuffers(): %s\n",
780
                     getVulkanResultString(result));
781
        quit(2);
782
    }
783
}
784

785
static void createFences(void)
786
{
787
    uint32_t i;
788

789
    vulkanContext->fences = SDL_malloc(sizeof(VkFence) * vulkanContext->swapchainImageCount);
790
    if (!vulkanContext->fences) {
791
        quit(2);
792
    }
793
    for (i = 0; i < vulkanContext->swapchainImageCount; i++) {
794
        VkResult result;
795
        VkFenceCreateInfo createInfo = { 0 };
796
        createInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
797
        createInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
798
        result = vkCreateFence(vulkanContext->device, &createInfo, NULL, &vulkanContext->fences[i]);
799
        if (result != VK_SUCCESS) {
800
            for (; i > 0; i--) {
801
                vkDestroyFence(vulkanContext->device, vulkanContext->fences[i - 1], NULL);
802
            }
803
            SDL_free(vulkanContext->fences);
804
            vulkanContext->fences = NULL;
805
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
806
                         "vkCreateFence(): %s\n",
807
                         getVulkanResultString(result));
808
            quit(2);
809
        }
810
    }
811
}
812

813
static void destroyFences(void)
814
{
815
    uint32_t i;
816

817
    if (!vulkanContext->fences) {
818
        return;
819
    }
820

821
    for (i = 0; i < vulkanContext->swapchainImageCount; i++) {
822
        vkDestroyFence(vulkanContext->device, vulkanContext->fences[i], NULL);
823
    }
824
    SDL_free(vulkanContext->fences);
825
    vulkanContext->fences = NULL;
826
}
827

828
static void recordPipelineImageBarrier(VkCommandBuffer commandBuffer,
829
                                       VkAccessFlags sourceAccessMask,
830
                                       VkAccessFlags destAccessMask,
831
                                       VkImageLayout sourceLayout,
832
                                       VkImageLayout destLayout,
833
                                       VkImage image)
834
{
835
    VkImageMemoryBarrier barrier = { 0 };
836
    barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
837
    barrier.srcAccessMask = sourceAccessMask;
838
    barrier.dstAccessMask = destAccessMask;
839
    barrier.oldLayout = sourceLayout;
840
    barrier.newLayout = destLayout;
841
    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
842
    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
843
    barrier.image = image;
844
    barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
845
    barrier.subresourceRange.baseMipLevel = 0;
846
    barrier.subresourceRange.levelCount = 1;
847
    barrier.subresourceRange.baseArrayLayer = 0;
848
    barrier.subresourceRange.layerCount = 1;
849
    vkCmdPipelineBarrier(commandBuffer,
850
                         VK_PIPELINE_STAGE_TRANSFER_BIT,
851
                         VK_PIPELINE_STAGE_TRANSFER_BIT,
852
                         0,
853
                         0,
854
                         NULL,
855
                         0,
856
                         NULL,
857
                         1,
858
                         &barrier);
859
}
860

861
static void rerecordCommandBuffer(uint32_t frameIndex, const VkClearColorValue *clearColor)
862
{
863
    VkCommandBuffer commandBuffer = vulkanContext->commandBuffers[frameIndex];
864
    VkImage image = vulkanContext->swapchainImages[frameIndex];
865
    VkCommandBufferBeginInfo beginInfo = { 0 };
866
    VkImageSubresourceRange clearRange = { 0 };
867

868
    VkResult result = vkResetCommandBuffer(commandBuffer, 0);
869
    if (result != VK_SUCCESS) {
870
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
871
                     "vkResetCommandBuffer(): %s\n",
872
                     getVulkanResultString(result));
873
        quit(2);
874
    }
875
    beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
876
    beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
877
    result = vkBeginCommandBuffer(commandBuffer, &beginInfo);
878
    if (result != VK_SUCCESS) {
879
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
880
                     "vkBeginCommandBuffer(): %s\n",
881
                     getVulkanResultString(result));
882
        quit(2);
883
    }
884
    recordPipelineImageBarrier(commandBuffer,
885
                               0,
886
                               VK_ACCESS_TRANSFER_WRITE_BIT,
887
                               VK_IMAGE_LAYOUT_UNDEFINED,
888
                               VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
889
                               image);
890
    clearRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
891
    clearRange.baseMipLevel = 0;
892
    clearRange.levelCount = 1;
893
    clearRange.baseArrayLayer = 0;
894
    clearRange.layerCount = 1;
895
    vkCmdClearColorImage(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clearColor, 1, &clearRange);
896
    recordPipelineImageBarrier(commandBuffer,
897
                               VK_ACCESS_TRANSFER_WRITE_BIT,
898
                               VK_ACCESS_MEMORY_READ_BIT,
899
                               VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
900
                               VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
901
                               image);
902
    result = vkEndCommandBuffer(commandBuffer);
903
    if (result != VK_SUCCESS) {
904
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
905
                     "vkEndCommandBuffer(): %s\n",
906
                     getVulkanResultString(result));
907
        quit(2);
908
    }
909
}
910

911
static void destroySwapchainAndSwapchainSpecificStuff(SDL_bool doDestroySwapchain)
912
{
913
    if (vkDeviceWaitIdle != NULL) {
914
        vkDeviceWaitIdle(vulkanContext->device);
915
    }
916
    destroyFences();
917
    destroyCommandBuffers();
918
    destroyCommandPool();
919
    if (doDestroySwapchain) {
920
        destroySwapchain();
921
    }
922
}
923

924
static SDL_bool createNewSwapchainAndSwapchainSpecificStuff(void)
925
{
926
    destroySwapchainAndSwapchainSpecificStuff(SDL_FALSE);
927
    getSurfaceCaps();
928
    getSurfaceFormats();
929
    if (!createSwapchain()) {
930
        return SDL_FALSE;
931
    }
932
    createCommandPool();
933
    createCommandBuffers();
934
    createFences();
935
    return SDL_TRUE;
936
}
937

938
static void initVulkan(void)
939
{
940
    int i;
941

942
    SDL_Vulkan_LoadLibrary(NULL);
943

944
    vulkanContexts = (VulkanContext *)SDL_calloc(state->num_windows, sizeof(VulkanContext));
945
    if (!vulkanContexts) {
946
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!");
947
        quit(2);
948
    }
949

950
    for (i = 0; i < state->num_windows; ++i) {
951
        vulkanContext = &vulkanContexts[i];
952
        vulkanContext->window = state->windows[i];
953
        loadGlobalFunctions();
954
        createInstance();
955
        loadInstanceFunctions();
956
        createSurface();
957
        findPhysicalDevice();
958
        createDevice();
959
        loadDeviceFunctions();
960
        getQueues();
961
        createSemaphores();
962
        createNewSwapchainAndSwapchainSpecificStuff();
963
    }
964
}
965

966
static void shutdownVulkan(SDL_bool doDestroySwapchain)
967
{
968
    if (vulkanContexts) {
969
        int i;
970
        for (i = 0; i < state->num_windows; ++i) {
971
            vulkanContext = &vulkanContexts[i];
972
            if (vulkanContext->device && vkDeviceWaitIdle) {
973
                vkDeviceWaitIdle(vulkanContext->device);
974
            }
975

976
            destroySwapchainAndSwapchainSpecificStuff(doDestroySwapchain);
977

978
            if (vulkanContext->imageAvailableSemaphore && vkDestroySemaphore) {
979
                vkDestroySemaphore(vulkanContext->device, vulkanContext->imageAvailableSemaphore, NULL);
980
            }
981

982
            if (vulkanContext->renderingFinishedSemaphore && vkDestroySemaphore) {
983
                vkDestroySemaphore(vulkanContext->device, vulkanContext->renderingFinishedSemaphore, NULL);
984
            }
985

986
            if (vulkanContext->device && vkDestroyDevice) {
987
                vkDestroyDevice(vulkanContext->device, NULL);
988
            }
989

990
            if (vulkanContext->surface && vkDestroySurfaceKHR) {
991
                vkDestroySurfaceKHR(vulkanContext->instance, vulkanContext->surface, NULL);
992
            }
993

994
            if (vulkanContext->instance && vkDestroyInstance) {
995
                vkDestroyInstance(vulkanContext->instance, NULL);
996
            }
997

998
            SDL_free(vulkanContext->surfaceFormats);
999
        }
1000
        SDL_free(vulkanContexts);
1001
        vulkanContexts = NULL;
1002
    }
1003

1004
    SDL_Vulkan_UnloadLibrary();
1005
}
1006

1007
static SDL_bool render(void)
1008
{
1009
    uint32_t frameIndex;
1010
    VkResult result;
1011
    double currentTime;
1012
    VkClearColorValue clearColor = { { 0 } };
1013
    VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
1014
    VkSubmitInfo submitInfo = { 0 };
1015
    VkPresentInfoKHR presentInfo = { 0 };
1016
    int w, h;
1017

1018
    if (!vulkanContext->swapchain) {
1019
        SDL_bool retval = createNewSwapchainAndSwapchainSpecificStuff();
1020
        if (!retval) {
1021
            SDL_Delay(100);
1022
        }
1023
        return retval;
1024
    }
1025
    result = vkAcquireNextImageKHR(vulkanContext->device,
1026
                                   vulkanContext->swapchain,
1027
                                   UINT64_MAX,
1028
                                   vulkanContext->imageAvailableSemaphore,
1029
                                   VK_NULL_HANDLE,
1030
                                   &frameIndex);
1031
    if (result == VK_ERROR_OUT_OF_DATE_KHR) {
1032
        return createNewSwapchainAndSwapchainSpecificStuff();
1033
    }
1034

1035
    if ((result != VK_SUBOPTIMAL_KHR) && (result != VK_SUCCESS)) {
1036
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
1037
                     "vkAcquireNextImageKHR(): %s\n",
1038
                     getVulkanResultString(result));
1039
        quit(2);
1040
    }
1041
    result = vkWaitForFences(vulkanContext->device, 1, &vulkanContext->fences[frameIndex], VK_FALSE, UINT64_MAX);
1042
    if (result != VK_SUCCESS) {
1043
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkWaitForFences(): %s\n", getVulkanResultString(result));
1044
        quit(2);
1045
    }
1046
    result = vkResetFences(vulkanContext->device, 1, &vulkanContext->fences[frameIndex]);
1047
    if (result != VK_SUCCESS) {
1048
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkResetFences(): %s\n", getVulkanResultString(result));
1049
        quit(2);
1050
    }
1051
    currentTime = (double)SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency();
1052
    clearColor.float32[0] = (float)(0.5 + 0.5 * SDL_sin(currentTime));
1053
    clearColor.float32[1] = (float)(0.5 + 0.5 * SDL_sin(currentTime + SDL_PI_D * 2 / 3));
1054
    clearColor.float32[2] = (float)(0.5 + 0.5 * SDL_sin(currentTime + SDL_PI_D * 4 / 3));
1055
    clearColor.float32[3] = 0.5; // for SDL_WINDOW_TRANSPARENT, ignored with VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
1056
    rerecordCommandBuffer(frameIndex, &clearColor);
1057
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1058
    submitInfo.waitSemaphoreCount = 1;
1059
    submitInfo.pWaitSemaphores = &vulkanContext->imageAvailableSemaphore;
1060
    submitInfo.pWaitDstStageMask = &waitDestStageMask;
1061
    submitInfo.commandBufferCount = 1;
1062
    submitInfo.pCommandBuffers = &vulkanContext->commandBuffers[frameIndex];
1063
    submitInfo.signalSemaphoreCount = 1;
1064
    submitInfo.pSignalSemaphores = &vulkanContext->renderingFinishedSemaphore;
1065
    result = vkQueueSubmit(vulkanContext->graphicsQueue, 1, &submitInfo, vulkanContext->fences[frameIndex]);
1066

1067
    if (result != VK_SUCCESS) {
1068
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkQueueSubmit(): %s\n", getVulkanResultString(result));
1069
        quit(2);
1070
    }
1071
    presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1072
    presentInfo.waitSemaphoreCount = 1;
1073
    presentInfo.pWaitSemaphores = &vulkanContext->renderingFinishedSemaphore;
1074
    presentInfo.swapchainCount = 1;
1075
    presentInfo.pSwapchains = &vulkanContext->swapchain;
1076
    presentInfo.pImageIndices = &frameIndex;
1077
    result = vkQueuePresentKHR(vulkanContext->presentQueue, &presentInfo);
1078
    if ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)) {
1079
        return createNewSwapchainAndSwapchainSpecificStuff();
1080
    }
1081

1082
    if (result != VK_SUCCESS) {
1083
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
1084
                     "vkQueuePresentKHR(): %s\n",
1085
                     getVulkanResultString(result));
1086
        quit(2);
1087
    }
1088
    SDL_GetWindowSizeInPixels(vulkanContext->window, &w, &h);
1089
    if (w != (int)vulkanContext->swapchainSize.width || h != (int)vulkanContext->swapchainSize.height) {
1090
        return createNewSwapchainAndSwapchainSpecificStuff();
1091
    }
1092
    return SDL_TRUE;
1093
}
1094

1095
int main(int argc, char **argv)
1096
{
1097
    int done;
1098
    const SDL_DisplayMode *mode;
1099
    SDL_Event event;
1100
    Uint64 then, now;
1101
    Uint32 frames;
1102
    int dw, dh;
1103

1104
    /* Initialize test framework */
1105
    state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
1106
    if (!state) {
1107
        return 1;
1108
    }
1109

1110
    /* Enable standard application logging */
1111
    SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
1112

1113
    /* Set Vulkan parameters */
1114
    state->window_flags |= SDL_WINDOW_VULKAN;
1115
    state->skip_renderer = 1;
1116

1117
    if (!SDLTest_CommonDefaultArgs(state, argc, argv) || !SDLTest_CommonInit(state)) {
1118
        SDLTest_CommonQuit(state);
1119
        return 1;
1120
    }
1121

1122
    mode = SDL_GetCurrentDisplayMode(SDL_GetPrimaryDisplay());
1123
    if (mode) {
1124
        SDL_Log("Screen BPP    : %d\n", SDL_BITSPERPIXEL(mode->format));
1125
    }
1126
    SDL_GetWindowSize(state->windows[0], &dw, &dh);
1127
    SDL_Log("Window Size   : %d,%d\n", dw, dh);
1128
    SDL_GetWindowSizeInPixels(state->windows[0], &dw, &dh);
1129
    SDL_Log("Draw Size     : %d,%d\n", dw, dh);
1130
    SDL_Log("\n");
1131

1132
    initVulkan();
1133

1134
    /* Main render loop */
1135
    frames = 0;
1136
    then = SDL_GetTicks();
1137
    done = 0;
1138
    while (!done) {
1139
        /* Check for events */
1140
        frames++;
1141
        while (SDL_PollEvent(&event)) {
1142
            /* Need to destroy the swapchain before the window created
1143
             * by SDL.
1144
             */
1145
            if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
1146
                destroySwapchainAndSwapchainSpecificStuff(SDL_TRUE);
1147
            }
1148
            SDLTest_CommonEvent(state, &event, &done);
1149
        }
1150

1151
        if (!done) {
1152
            int i;
1153
            for (i = 0; i < state->num_windows; ++i) {
1154
                if (state->windows[i]) {
1155
                    vulkanContext = &vulkanContexts[i];
1156
                    render();
1157
                }
1158
            }
1159
        }
1160
    }
1161

1162
    /* Print out some timing information */
1163
    now = SDL_GetTicks();
1164
    if (now > then) {
1165
        SDL_Log("%2.2f frames per second\n", ((double)frames * 1000) / (now - then));
1166
    }
1167

1168
    shutdownVulkan(SDL_TRUE);
1169
    SDLTest_CommonQuit(state);
1170
    return 0;
1171
}
1172

1173
#endif
1174

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.