| /* |
| Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely. |
| */ |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <math.h> |
| |
| #include "SDL_test_common.h" |
| |
| #if defined(__ANDROID__) && defined(__ARM_EABI__) && !defined(__ARM_ARCH_7A__) |
| |
| int main(int argc, char *argv[]) |
| { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No Vulkan support on this system\n"); |
| return 1; |
| } |
| |
| #else |
| |
| #define VK_NO_PROTOTYPES |
| #ifdef HAVE_VULKAN_H |
| #include <vulkan/vulkan.h> |
| #else |
| /* SDL includes a copy for building on systems without the Vulkan SDK */ |
| #include "../src/video/khronos/vulkan/vulkan.h" |
| #endif |
| #include "SDL_vulkan.h" |
| |
| #ifndef UINT64_MAX /* VS2008 */ |
| #define UINT64_MAX 18446744073709551615 |
| #endif |
| |
| #define VULKAN_FUNCTIONS() \ |
| VULKAN_DEVICE_FUNCTION(vkAcquireNextImageKHR) \ |
| VULKAN_DEVICE_FUNCTION(vkAllocateCommandBuffers) \ |
| VULKAN_DEVICE_FUNCTION(vkBeginCommandBuffer) \ |
| VULKAN_DEVICE_FUNCTION(vkCmdClearColorImage) \ |
| VULKAN_DEVICE_FUNCTION(vkCmdPipelineBarrier) \ |
| VULKAN_DEVICE_FUNCTION(vkCreateCommandPool) \ |
| VULKAN_DEVICE_FUNCTION(vkCreateFence) \ |
| VULKAN_DEVICE_FUNCTION(vkCreateImageView) \ |
| VULKAN_DEVICE_FUNCTION(vkCreateSemaphore) \ |
| VULKAN_DEVICE_FUNCTION(vkCreateSwapchainKHR) \ |
| VULKAN_DEVICE_FUNCTION(vkDestroyCommandPool) \ |
| VULKAN_DEVICE_FUNCTION(vkDestroyDevice) \ |
| VULKAN_DEVICE_FUNCTION(vkDestroyFence) \ |
| VULKAN_DEVICE_FUNCTION(vkDestroyImageView) \ |
| VULKAN_DEVICE_FUNCTION(vkDestroySemaphore) \ |
| VULKAN_DEVICE_FUNCTION(vkDestroySwapchainKHR) \ |
| VULKAN_DEVICE_FUNCTION(vkDeviceWaitIdle) \ |
| VULKAN_DEVICE_FUNCTION(vkEndCommandBuffer) \ |
| VULKAN_DEVICE_FUNCTION(vkFreeCommandBuffers) \ |
| VULKAN_DEVICE_FUNCTION(vkGetDeviceQueue) \ |
| VULKAN_DEVICE_FUNCTION(vkGetFenceStatus) \ |
| VULKAN_DEVICE_FUNCTION(vkGetSwapchainImagesKHR) \ |
| VULKAN_DEVICE_FUNCTION(vkQueuePresentKHR) \ |
| VULKAN_DEVICE_FUNCTION(vkQueueSubmit) \ |
| VULKAN_DEVICE_FUNCTION(vkResetCommandBuffer) \ |
| VULKAN_DEVICE_FUNCTION(vkResetFences) \ |
| VULKAN_DEVICE_FUNCTION(vkWaitForFences) \ |
| VULKAN_GLOBAL_FUNCTION(vkCreateInstance) \ |
| VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceExtensionProperties) \ |
| VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceLayerProperties) \ |
| VULKAN_INSTANCE_FUNCTION(vkCreateDevice) \ |
| VULKAN_INSTANCE_FUNCTION(vkDestroyInstance) \ |
| VULKAN_INSTANCE_FUNCTION(vkDestroySurfaceKHR) \ |
| VULKAN_INSTANCE_FUNCTION(vkEnumerateDeviceExtensionProperties) \ |
| VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices) \ |
| VULKAN_INSTANCE_FUNCTION(vkGetDeviceProcAddr) \ |
| VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures) \ |
| VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties) \ |
| VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties) \ |
| VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ |
| VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR) \ |
| VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR) \ |
| VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR) |
| |
| #define VULKAN_DEVICE_FUNCTION(name) static PFN_##name name = NULL; |
| #define VULKAN_GLOBAL_FUNCTION(name) static PFN_##name name = NULL; |
| #define VULKAN_INSTANCE_FUNCTION(name) static PFN_##name name = NULL; |
| VULKAN_FUNCTIONS() |
| #undef VULKAN_DEVICE_FUNCTION |
| #undef VULKAN_GLOBAL_FUNCTION |
| #undef VULKAN_INSTANCE_FUNCTION |
| static PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; |
| |
| /* Based on the headers found in |
| * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers |
| */ |
| #if VK_HEADER_VERSION < 22 |
| enum |
| { |
| VK_ERROR_FRAGMENTED_POOL = -12, |
| }; |
| #endif |
| #if VK_HEADER_VERSION < 38 |
| enum { |
| VK_ERROR_OUT_OF_POOL_MEMORY_KHR = -1000069000 |
| }; |
| #endif |
| |
| static const char *getVulkanResultString(VkResult result) |
| { |
| switch((int) result) |
| { |
| #define RESULT_CASE(x) case x: return #x |
| RESULT_CASE(VK_SUCCESS); |
| RESULT_CASE(VK_NOT_READY); |
| RESULT_CASE(VK_TIMEOUT); |
| RESULT_CASE(VK_EVENT_SET); |
| RESULT_CASE(VK_EVENT_RESET); |
| RESULT_CASE(VK_INCOMPLETE); |
| RESULT_CASE(VK_ERROR_OUT_OF_HOST_MEMORY); |
| RESULT_CASE(VK_ERROR_OUT_OF_DEVICE_MEMORY); |
| RESULT_CASE(VK_ERROR_INITIALIZATION_FAILED); |
| RESULT_CASE(VK_ERROR_DEVICE_LOST); |
| RESULT_CASE(VK_ERROR_MEMORY_MAP_FAILED); |
| RESULT_CASE(VK_ERROR_LAYER_NOT_PRESENT); |
| RESULT_CASE(VK_ERROR_EXTENSION_NOT_PRESENT); |
| RESULT_CASE(VK_ERROR_FEATURE_NOT_PRESENT); |
| RESULT_CASE(VK_ERROR_INCOMPATIBLE_DRIVER); |
| RESULT_CASE(VK_ERROR_TOO_MANY_OBJECTS); |
| RESULT_CASE(VK_ERROR_FORMAT_NOT_SUPPORTED); |
| RESULT_CASE(VK_ERROR_FRAGMENTED_POOL); |
| RESULT_CASE(VK_ERROR_SURFACE_LOST_KHR); |
| RESULT_CASE(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); |
| RESULT_CASE(VK_SUBOPTIMAL_KHR); |
| RESULT_CASE(VK_ERROR_OUT_OF_DATE_KHR); |
| RESULT_CASE(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); |
| RESULT_CASE(VK_ERROR_VALIDATION_FAILED_EXT); |
| RESULT_CASE(VK_ERROR_OUT_OF_POOL_MEMORY_KHR); |
| RESULT_CASE(VK_ERROR_INVALID_SHADER_NV); |
| #undef RESULT_CASE |
| default: break; |
| } |
| return (result < 0) ? "VK_ERROR_<Unknown>" : "VK_<Unknown>"; |
| } |
| |
| typedef struct VulkanContext |
| { |
| SDL_Window *window; |
| VkInstance instance; |
| VkDevice device; |
| VkSurfaceKHR surface; |
| VkSwapchainKHR swapchain; |
| VkPhysicalDeviceProperties physicalDeviceProperties; |
| VkPhysicalDeviceFeatures physicalDeviceFeatures; |
| uint32_t graphicsQueueFamilyIndex; |
| uint32_t presentQueueFamilyIndex; |
| VkPhysicalDevice physicalDevice; |
| VkQueue graphicsQueue; |
| VkQueue presentQueue; |
| VkSemaphore imageAvailableSemaphore; |
| VkSemaphore renderingFinishedSemaphore; |
| VkSurfaceCapabilitiesKHR surfaceCapabilities; |
| VkSurfaceFormatKHR *surfaceFormats; |
| uint32_t surfaceFormatsAllocatedCount; |
| uint32_t surfaceFormatsCount; |
| uint32_t swapchainDesiredImageCount; |
| VkSurfaceFormatKHR surfaceFormat; |
| VkExtent2D swapchainSize; |
| VkCommandPool commandPool; |
| uint32_t swapchainImageCount; |
| VkImage *swapchainImages; |
| VkCommandBuffer *commandBuffers; |
| VkFence *fences; |
| } VulkanContext; |
| |
| static SDLTest_CommonState *state; |
| static VulkanContext *vulkanContexts = NULL; // an array of state->num_windows items |
| static VulkanContext *vulkanContext = NULL; // for the currently-rendering window |
| |
| static void shutdownVulkan(SDL_bool doDestroySwapchain); |
| |
| /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ |
| static void quit(int rc) |
| { |
| shutdownVulkan(SDL_TRUE); |
| SDLTest_CommonQuit(state); |
| exit(rc); |
| } |
| |
| static void loadGlobalFunctions(void) |
| { |
| vkGetInstanceProcAddr = SDL_Vulkan_GetVkGetInstanceProcAddr(); |
| if (!vkGetInstanceProcAddr) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "SDL_Vulkan_GetVkGetInstanceProcAddr(): %s\n", |
| SDL_GetError()); |
| quit(2); |
| } |
| |
| #define VULKAN_DEVICE_FUNCTION(name) |
| #define VULKAN_GLOBAL_FUNCTION(name) \ |
| name = (PFN_##name)vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \ |
| if (!name) { \ |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \ |
| "vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed\n"); \ |
| quit(2); \ |
| } |
| #define VULKAN_INSTANCE_FUNCTION(name) |
| VULKAN_FUNCTIONS() |
| #undef VULKAN_DEVICE_FUNCTION |
| #undef VULKAN_GLOBAL_FUNCTION |
| #undef VULKAN_INSTANCE_FUNCTION |
| } |
| |
| static void createInstance(void) |
| { |
| VkApplicationInfo appInfo = {0}; |
| VkInstanceCreateInfo instanceCreateInfo = {0}; |
| const char **extensions = NULL; |
| unsigned extensionCount = 0; |
| VkResult result; |
| |
| appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; |
| appInfo.apiVersion = VK_API_VERSION_1_0; |
| instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
| instanceCreateInfo.pApplicationInfo = &appInfo; |
| if (!SDL_Vulkan_GetInstanceExtensions(NULL, &extensionCount, NULL)) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "SDL_Vulkan_GetInstanceExtensions(): %s\n", |
| SDL_GetError()); |
| quit(2); |
| } |
| extensions = (const char **) SDL_malloc(sizeof(const char *) * extensionCount); |
| if (!extensions) { |
| SDL_OutOfMemory(); |
| quit(2); |
| } |
| if (!SDL_Vulkan_GetInstanceExtensions(NULL, &extensionCount, extensions)) { |
| SDL_free((void*)extensions); |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "SDL_Vulkan_GetInstanceExtensions(): %s\n", |
| SDL_GetError()); |
| quit(2); |
| } |
| instanceCreateInfo.enabledExtensionCount = extensionCount; |
| instanceCreateInfo.ppEnabledExtensionNames = extensions; |
| result = vkCreateInstance(&instanceCreateInfo, NULL, &vulkanContext->instance); |
| SDL_free((void*)extensions); |
| if (result != VK_SUCCESS) { |
| vulkanContext->instance = VK_NULL_HANDLE; |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkCreateInstance(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| } |
| |
| static void loadInstanceFunctions(void) |
| { |
| #define VULKAN_DEVICE_FUNCTION(name) |
| #define VULKAN_GLOBAL_FUNCTION(name) |
| #define VULKAN_INSTANCE_FUNCTION(name) \ |
| name = (PFN_##name)vkGetInstanceProcAddr(vulkanContext->instance, #name); \ |
| if (!name) { \ |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \ |
| "vkGetInstanceProcAddr(instance, \"" #name "\") failed\n"); \ |
| quit(2); \ |
| } |
| VULKAN_FUNCTIONS() |
| #undef VULKAN_DEVICE_FUNCTION |
| #undef VULKAN_GLOBAL_FUNCTION |
| #undef VULKAN_INSTANCE_FUNCTION |
| } |
| |
| static void createSurface(void) |
| { |
| if (!SDL_Vulkan_CreateSurface(vulkanContext->window, |
| vulkanContext->instance, |
| &vulkanContext->surface)) { |
| vulkanContext->surface = VK_NULL_HANDLE; |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_Vulkan_CreateSurface(): %s\n", SDL_GetError()); |
| quit(2); |
| } |
| } |
| |
| static void findPhysicalDevice(void) |
| { |
| uint32_t physicalDeviceCount = 0; |
| VkPhysicalDevice *physicalDevices; |
| VkQueueFamilyProperties *queueFamiliesProperties = NULL; |
| uint32_t queueFamiliesPropertiesAllocatedSize = 0; |
| VkExtensionProperties *deviceExtensions = NULL; |
| uint32_t deviceExtensionsAllocatedSize = 0; |
| uint32_t physicalDeviceIndex; |
| VkResult result; |
| |
| result = vkEnumeratePhysicalDevices(vulkanContext->instance, &physicalDeviceCount, NULL); |
| if (result != VK_SUCCESS) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkEnumeratePhysicalDevices(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| if (physicalDeviceCount == 0) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkEnumeratePhysicalDevices(): no physical devices\n"); |
| quit(2); |
| } |
| physicalDevices = (VkPhysicalDevice *) SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount); |
| if (!physicalDevices) { |
| SDL_OutOfMemory(); |
| quit(2); |
| } |
| result = vkEnumeratePhysicalDevices(vulkanContext->instance, &physicalDeviceCount, physicalDevices); |
| if (result != VK_SUCCESS) { |
| SDL_free(physicalDevices); |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkEnumeratePhysicalDevices(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| vulkanContext->physicalDevice = NULL; |
| for (physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount; physicalDeviceIndex++) { |
| uint32_t queueFamiliesCount = 0; |
| uint32_t queueFamilyIndex; |
| uint32_t deviceExtensionCount = 0; |
| SDL_bool hasSwapchainExtension = SDL_FALSE; |
| uint32_t i; |
| |
| VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex]; |
| vkGetPhysicalDeviceProperties(physicalDevice, &vulkanContext->physicalDeviceProperties); |
| if(VK_VERSION_MAJOR(vulkanContext->physicalDeviceProperties.apiVersion) < 1) { |
| continue; |
| } |
| vkGetPhysicalDeviceFeatures(physicalDevice, &vulkanContext->physicalDeviceFeatures); |
| vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, NULL); |
| if (queueFamiliesCount == 0) { |
| continue; |
| } |
| if (queueFamiliesPropertiesAllocatedSize < queueFamiliesCount) { |
| SDL_free(queueFamiliesProperties); |
| queueFamiliesPropertiesAllocatedSize = queueFamiliesCount; |
| queueFamiliesProperties = (VkQueueFamilyProperties *) SDL_malloc(sizeof(VkQueueFamilyProperties) * queueFamiliesPropertiesAllocatedSize); |
| if (!queueFamiliesProperties) { |
| SDL_free(physicalDevices); |
| SDL_free(deviceExtensions); |
| SDL_OutOfMemory(); |
| quit(2); |
| } |
| } |
| vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, queueFamiliesProperties); |
| vulkanContext->graphicsQueueFamilyIndex = queueFamiliesCount; |
| vulkanContext->presentQueueFamilyIndex = queueFamiliesCount; |
| for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; queueFamilyIndex++) { |
| VkBool32 supported = 0; |
| |
| if (queueFamiliesProperties[queueFamilyIndex].queueCount == 0) { |
| continue; |
| } |
| |
| if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) { |
| vulkanContext->graphicsQueueFamilyIndex = queueFamilyIndex; |
| } |
| |
| result = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, vulkanContext->surface, &supported); |
| if (result != VK_SUCCESS) { |
| SDL_free(physicalDevices); |
| SDL_free(queueFamiliesProperties); |
| SDL_free(deviceExtensions); |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkGetPhysicalDeviceSurfaceSupportKHR(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| if (supported) { |
| vulkanContext->presentQueueFamilyIndex = queueFamilyIndex; |
| if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) { |
| break; // use this queue because it can present and do graphics |
| } |
| } |
| } |
| |
| if (vulkanContext->graphicsQueueFamilyIndex == queueFamiliesCount) { // no good queues found |
| continue; |
| } |
| if (vulkanContext->presentQueueFamilyIndex == queueFamiliesCount) { // no good queues found |
| continue; |
| } |
| result = vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, NULL); |
| if (result != VK_SUCCESS) |
| { |
| SDL_free(physicalDevices); |
| SDL_free(queueFamiliesProperties); |
| SDL_free(deviceExtensions); |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkEnumerateDeviceExtensionProperties(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| if (deviceExtensionCount == 0) { |
| continue; |
| } |
| if (deviceExtensionsAllocatedSize < deviceExtensionCount) { |
| SDL_free(deviceExtensions); |
| deviceExtensionsAllocatedSize = deviceExtensionCount; |
| deviceExtensions = SDL_malloc(sizeof(VkExtensionProperties) * deviceExtensionsAllocatedSize); |
| if (!deviceExtensions) { |
| SDL_free(physicalDevices); |
| SDL_free(queueFamiliesProperties); |
| SDL_OutOfMemory(); |
| quit(2); |
| } |
| } |
| result = vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, deviceExtensions); |
| if (result != VK_SUCCESS) { |
| SDL_free(physicalDevices); |
| SDL_free(queueFamiliesProperties); |
| SDL_free(deviceExtensions); |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkEnumerateDeviceExtensionProperties(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| for (i = 0; i < deviceExtensionCount; i++) { |
| if(SDL_strcmp(deviceExtensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) { |
| hasSwapchainExtension = SDL_TRUE; |
| break; |
| } |
| } |
| if (!hasSwapchainExtension) { |
| continue; |
| } |
| vulkanContext->physicalDevice = physicalDevice; |
| break; |
| } |
| SDL_free(physicalDevices); |
| SDL_free(queueFamiliesProperties); |
| SDL_free(deviceExtensions); |
| if (!vulkanContext->physicalDevice) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Vulkan: no viable physical devices found"); |
| quit(2); |
| } |
| } |
| |
| static void createDevice(void) |
| { |
| VkDeviceQueueCreateInfo deviceQueueCreateInfo[1] = { {0} }; |
| static const float queuePriority[] = {1.0f}; |
| VkDeviceCreateInfo deviceCreateInfo = {0}; |
| static const char *const deviceExtensionNames[] = { |
| VK_KHR_SWAPCHAIN_EXTENSION_NAME, |
| }; |
| VkResult result; |
| |
| deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
| deviceQueueCreateInfo->queueFamilyIndex = vulkanContext->graphicsQueueFamilyIndex; |
| deviceQueueCreateInfo->queueCount = 1; |
| deviceQueueCreateInfo->pQueuePriorities = &queuePriority[0]; |
| |
| deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; |
| deviceCreateInfo.queueCreateInfoCount = 1; |
| deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo; |
| deviceCreateInfo.pEnabledFeatures = NULL; |
| deviceCreateInfo.enabledExtensionCount = SDL_arraysize(deviceExtensionNames); |
| deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames; |
| result = vkCreateDevice(vulkanContext->physicalDevice, &deviceCreateInfo, NULL, &vulkanContext->device); |
| if (result != VK_SUCCESS) { |
| vulkanContext->device = VK_NULL_HANDLE; |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkCreateDevice(): %s\n", getVulkanResultString(result)); |
| quit(2); |
| } |
| } |
| |
| static void loadDeviceFunctions(void) |
| { |
| #define VULKAN_DEVICE_FUNCTION(name) \ |
| name = (PFN_##name)vkGetDeviceProcAddr(vulkanContext->device, #name); \ |
| if (!name) { \ |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \ |
| "vkGetDeviceProcAddr(device, \"" #name "\") failed\n"); \ |
| quit(2); \ |
| } |
| #define VULKAN_GLOBAL_FUNCTION(name) |
| #define VULKAN_INSTANCE_FUNCTION(name) |
| VULKAN_FUNCTIONS() |
| #undef VULKAN_DEVICE_FUNCTION |
| #undef VULKAN_GLOBAL_FUNCTION |
| #undef VULKAN_INSTANCE_FUNCTION |
| } |
| |
| #undef VULKAN_FUNCTIONS |
| |
| static void getQueues(void) |
| { |
| vkGetDeviceQueue(vulkanContext->device, |
| vulkanContext->graphicsQueueFamilyIndex, |
| 0, |
| &vulkanContext->graphicsQueue); |
| if (vulkanContext->graphicsQueueFamilyIndex != vulkanContext->presentQueueFamilyIndex) { |
| vkGetDeviceQueue(vulkanContext->device, |
| vulkanContext->presentQueueFamilyIndex, |
| 0, |
| &vulkanContext->presentQueue); |
| } else { |
| vulkanContext->presentQueue = vulkanContext->graphicsQueue; |
| } |
| } |
| |
| static void createSemaphore(VkSemaphore *semaphore) |
| { |
| VkResult result; |
| |
| VkSemaphoreCreateInfo createInfo = {0}; |
| createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; |
| result = vkCreateSemaphore(vulkanContext->device, &createInfo, NULL, semaphore); |
| if (result != VK_SUCCESS) { |
| *semaphore = VK_NULL_HANDLE; |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkCreateSemaphore(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| } |
| |
| static void createSemaphores(void) |
| { |
| createSemaphore(&vulkanContext->imageAvailableSemaphore); |
| createSemaphore(&vulkanContext->renderingFinishedSemaphore); |
| } |
| |
| static void getSurfaceCaps(void) |
| { |
| VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkanContext->physicalDevice, vulkanContext->surface, &vulkanContext->surfaceCapabilities); |
| if (result != VK_SUCCESS) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkGetPhysicalDeviceSurfaceCapabilitiesKHR(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| |
| // check surface usage |
| if (!(vulkanContext->surfaceCapabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "Vulkan surface doesn't support VK_IMAGE_USAGE_TRANSFER_DST_BIT\n"); |
| quit(2); |
| } |
| } |
| |
| static void getSurfaceFormats(void) |
| { |
| VkResult result = vkGetPhysicalDeviceSurfaceFormatsKHR(vulkanContext->physicalDevice, |
| vulkanContext->surface, |
| &vulkanContext->surfaceFormatsCount, |
| NULL); |
| if (result != VK_SUCCESS) { |
| vulkanContext->surfaceFormatsCount = 0; |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkGetPhysicalDeviceSurfaceFormatsKHR(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| if (vulkanContext->surfaceFormatsCount > vulkanContext->surfaceFormatsAllocatedCount) { |
| vulkanContext->surfaceFormatsAllocatedCount = vulkanContext->surfaceFormatsCount; |
| SDL_free(vulkanContext->surfaceFormats); |
| vulkanContext->surfaceFormats = (VkSurfaceFormatKHR *) SDL_malloc(sizeof(VkSurfaceFormatKHR) * vulkanContext->surfaceFormatsAllocatedCount); |
| if (!vulkanContext->surfaceFormats) { |
| vulkanContext->surfaceFormatsCount = 0; |
| SDL_OutOfMemory(); |
| quit(2); |
| } |
| } |
| result = vkGetPhysicalDeviceSurfaceFormatsKHR(vulkanContext->physicalDevice, |
| vulkanContext->surface, |
| &vulkanContext->surfaceFormatsCount, |
| vulkanContext->surfaceFormats); |
| if (result != VK_SUCCESS) { |
| vulkanContext->surfaceFormatsCount = 0; |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkGetPhysicalDeviceSurfaceFormatsKHR(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| } |
| |
| static void getSwapchainImages(void) |
| { |
| VkResult result; |
| |
| SDL_free(vulkanContext->swapchainImages); |
| vulkanContext->swapchainImages = NULL; |
| result = vkGetSwapchainImagesKHR(vulkanContext->device, vulkanContext->swapchain, &vulkanContext->swapchainImageCount, NULL); |
| if (result != VK_SUCCESS) { |
| vulkanContext->swapchainImageCount = 0; |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkGetSwapchainImagesKHR(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| vulkanContext->swapchainImages = SDL_malloc(sizeof(VkImage) * vulkanContext->swapchainImageCount); |
| if (!vulkanContext->swapchainImages) { |
| SDL_OutOfMemory(); |
| quit(2); |
| } |
| result = vkGetSwapchainImagesKHR(vulkanContext->device, |
| vulkanContext->swapchain, |
| &vulkanContext->swapchainImageCount, |
| vulkanContext->swapchainImages); |
| if (result != VK_SUCCESS) { |
| SDL_free(vulkanContext->swapchainImages); |
| vulkanContext->swapchainImages = NULL; |
| vulkanContext->swapchainImageCount = 0; |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkGetSwapchainImagesKHR(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| } |
| |
| static SDL_bool createSwapchain(void) |
| { |
| uint32_t i; |
| int w, h; |
| VkSwapchainCreateInfoKHR createInfo = {0}; |
| VkResult result; |
| |
| // pick an image count |
| vulkanContext->swapchainDesiredImageCount = vulkanContext->surfaceCapabilities.minImageCount + 1; |
| if ( (vulkanContext->swapchainDesiredImageCount > vulkanContext->surfaceCapabilities.maxImageCount) && |
| (vulkanContext->surfaceCapabilities.maxImageCount > 0) ) { |
| vulkanContext->swapchainDesiredImageCount = vulkanContext->surfaceCapabilities.maxImageCount; |
| } |
| |
| // pick a format |
| if ( (vulkanContext->surfaceFormatsCount == 1) && |
| (vulkanContext->surfaceFormats[0].format == VK_FORMAT_UNDEFINED) ) { |
| // aren't any preferred formats, so we pick |
| vulkanContext->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; |
| vulkanContext->surfaceFormat.format = VK_FORMAT_R8G8B8A8_UNORM; |
| } else { |
| vulkanContext->surfaceFormat = vulkanContext->surfaceFormats[0]; |
| for (i = 0; i < vulkanContext->surfaceFormatsCount; i++) { |
| if(vulkanContext->surfaceFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM) { |
| vulkanContext->surfaceFormat = vulkanContext->surfaceFormats[i]; |
| break; |
| } |
| } |
| } |
| |
| // get size |
| SDL_Vulkan_GetDrawableSize(vulkanContext->window, &w, &h); |
| |
| // Clamp the size to the allowable image extent. |
| // SDL_Vulkan_GetDrawableSize()'s result it not always in this range (bug #3287) |
| vulkanContext->swapchainSize.width = SDL_clamp((uint32_t) w, |
| vulkanContext->surfaceCapabilities.minImageExtent.width, |
| vulkanContext->surfaceCapabilities.maxImageExtent.width); |
| |
| vulkanContext->swapchainSize.height = SDL_clamp((uint32_t) h, |
| vulkanContext->surfaceCapabilities.minImageExtent.height, |
| vulkanContext->surfaceCapabilities.maxImageExtent.height); |
| |
| if (w == 0 || h == 0) { |
| return SDL_FALSE; |
| } |
| |
| getSurfaceCaps(); |
| |
| createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; |
| createInfo.surface = vulkanContext->surface; |
| createInfo.minImageCount = vulkanContext->swapchainDesiredImageCount; |
| createInfo.imageFormat = vulkanContext->surfaceFormat.format; |
| createInfo.imageColorSpace = vulkanContext->surfaceFormat.colorSpace; |
| createInfo.imageExtent = vulkanContext->swapchainSize; |
| createInfo.imageArrayLayers = 1; |
| createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; |
| createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| createInfo.preTransform = vulkanContext->surfaceCapabilities.currentTransform; |
| createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; |
| createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; |
| createInfo.clipped = VK_TRUE; |
| createInfo.oldSwapchain = vulkanContext->swapchain; |
| result = vkCreateSwapchainKHR(vulkanContext->device, &createInfo, NULL, &vulkanContext->swapchain); |
| |
| if (createInfo.oldSwapchain) { |
| vkDestroySwapchainKHR(vulkanContext->device, createInfo.oldSwapchain, NULL); |
| } |
| |
| if(result != VK_SUCCESS) { |
| vulkanContext->swapchain = VK_NULL_HANDLE; |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkCreateSwapchainKHR(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| |
| getSwapchainImages(); |
| return SDL_TRUE; |
| } |
| |
| static void destroySwapchain(void) |
| { |
| if (vulkanContext->swapchain) { |
| vkDestroySwapchainKHR(vulkanContext->device, vulkanContext->swapchain, NULL); |
| vulkanContext->swapchain = VK_NULL_HANDLE; |
| } |
| SDL_free(vulkanContext->swapchainImages); |
| vulkanContext->swapchainImages = NULL; |
| } |
| |
| static void destroyCommandBuffers(void) |
| { |
| if (vulkanContext->commandBuffers) { |
| vkFreeCommandBuffers(vulkanContext->device, |
| vulkanContext->commandPool, |
| vulkanContext->swapchainImageCount, |
| vulkanContext->commandBuffers); |
| SDL_free(vulkanContext->commandBuffers); |
| vulkanContext->commandBuffers = NULL; |
| } |
| } |
| |
| static void destroyCommandPool(void) |
| { |
| if (vulkanContext->commandPool) { |
| vkDestroyCommandPool(vulkanContext->device, vulkanContext->commandPool, NULL); |
| } |
| vulkanContext->commandPool = VK_NULL_HANDLE; |
| } |
| |
| static void createCommandPool(void) |
| { |
| VkResult result; |
| VkCommandPoolCreateInfo createInfo = {0}; |
| createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; |
| createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; |
| createInfo.queueFamilyIndex = vulkanContext->graphicsQueueFamilyIndex; |
| result = vkCreateCommandPool(vulkanContext->device, &createInfo, NULL, &vulkanContext->commandPool); |
| if (result != VK_SUCCESS) { |
| vulkanContext->commandPool = VK_NULL_HANDLE; |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkCreateCommandPool(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| } |
| |
| static void createCommandBuffers(void) |
| { |
| VkResult result; |
| VkCommandBufferAllocateInfo allocateInfo = {0}; |
| allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
| allocateInfo.commandPool = vulkanContext->commandPool; |
| allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
| allocateInfo.commandBufferCount = vulkanContext->swapchainImageCount; |
| vulkanContext->commandBuffers = (VkCommandBuffer *) SDL_malloc(sizeof(VkCommandBuffer) * vulkanContext->swapchainImageCount); |
| result = vkAllocateCommandBuffers(vulkanContext->device, &allocateInfo, vulkanContext->commandBuffers); |
| if(result != VK_SUCCESS) { |
| SDL_free(vulkanContext->commandBuffers); |
| vulkanContext->commandBuffers = NULL; |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkAllocateCommandBuffers(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| } |
| |
| static void createFences(void) |
| { |
| uint32_t i; |
| |
| vulkanContext->fences = SDL_malloc(sizeof(VkFence) * vulkanContext->swapchainImageCount); |
| if (!vulkanContext->fences) { |
| SDL_OutOfMemory(); |
| quit(2); |
| } |
| for (i = 0; i < vulkanContext->swapchainImageCount; i++) { |
| VkResult result; |
| VkFenceCreateInfo createInfo = {0}; |
| createInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; |
| createInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; |
| result = vkCreateFence(vulkanContext->device, &createInfo, NULL, &vulkanContext->fences[i]); |
| if(result != VK_SUCCESS) { |
| for(; i > 0; i--) { |
| vkDestroyFence(vulkanContext->device, vulkanContext->fences[i - 1], NULL); |
| } |
| SDL_free(vulkanContext->fences); |
| vulkanContext->fences = NULL; |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkCreateFence(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| } |
| } |
| |
| static void destroyFences(void) |
| { |
| uint32_t i; |
| |
| if (!vulkanContext->fences) { |
| return; |
| } |
| |
| for (i = 0; i < vulkanContext->swapchainImageCount; i++) { |
| vkDestroyFence(vulkanContext->device, vulkanContext->fences[i], NULL); |
| } |
| SDL_free(vulkanContext->fences); |
| vulkanContext->fences = NULL; |
| } |
| |
| static void recordPipelineImageBarrier(VkCommandBuffer commandBuffer, |
| VkAccessFlags sourceAccessMask, |
| VkAccessFlags destAccessMask, |
| VkImageLayout sourceLayout, |
| VkImageLayout destLayout, |
| VkImage image) |
| { |
| VkImageMemoryBarrier barrier = {0}; |
| barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; |
| barrier.srcAccessMask = sourceAccessMask; |
| barrier.dstAccessMask = destAccessMask; |
| barrier.oldLayout = sourceLayout; |
| barrier.newLayout = destLayout; |
| barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| barrier.image = image; |
| barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| barrier.subresourceRange.baseMipLevel = 0; |
| barrier.subresourceRange.levelCount = 1; |
| barrier.subresourceRange.baseArrayLayer = 0; |
| barrier.subresourceRange.layerCount = 1; |
| vkCmdPipelineBarrier(commandBuffer, |
| VK_PIPELINE_STAGE_TRANSFER_BIT, |
| VK_PIPELINE_STAGE_TRANSFER_BIT, |
| 0, |
| 0, |
| NULL, |
| 0, |
| NULL, |
| 1, |
| &barrier); |
| } |
| |
| static void rerecordCommandBuffer(uint32_t frameIndex, const VkClearColorValue *clearColor) |
| { |
| VkCommandBuffer commandBuffer = vulkanContext->commandBuffers[frameIndex]; |
| VkImage image = vulkanContext->swapchainImages[frameIndex]; |
| VkCommandBufferBeginInfo beginInfo = {0}; |
| VkImageSubresourceRange clearRange = {0}; |
| |
| VkResult result = vkResetCommandBuffer(commandBuffer, 0); |
| if(result != VK_SUCCESS) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkResetCommandBuffer(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
| beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; |
| result = vkBeginCommandBuffer(commandBuffer, &beginInfo); |
| if (result != VK_SUCCESS) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkBeginCommandBuffer(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| recordPipelineImageBarrier(commandBuffer, |
| 0, |
| VK_ACCESS_TRANSFER_WRITE_BIT, |
| VK_IMAGE_LAYOUT_UNDEFINED, |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, |
| image); |
| clearRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| clearRange.baseMipLevel = 0; |
| clearRange.levelCount = 1; |
| clearRange.baseArrayLayer = 0; |
| clearRange.layerCount = 1; |
| vkCmdClearColorImage(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clearColor, 1, &clearRange); |
| recordPipelineImageBarrier(commandBuffer, |
| VK_ACCESS_TRANSFER_WRITE_BIT, |
| VK_ACCESS_MEMORY_READ_BIT, |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, |
| VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, |
| image); |
| result = vkEndCommandBuffer(commandBuffer); |
| if (result != VK_SUCCESS) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkEndCommandBuffer(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| } |
| |
| static void destroySwapchainAndSwapchainSpecificStuff(SDL_bool doDestroySwapchain) |
| { |
| vkDeviceWaitIdle(vulkanContext->device); |
| destroyFences(); |
| destroyCommandBuffers(); |
| destroyCommandPool(); |
| if (doDestroySwapchain) { |
| destroySwapchain(); |
| } |
| } |
| |
| static SDL_bool createNewSwapchainAndSwapchainSpecificStuff(void) |
| { |
| destroySwapchainAndSwapchainSpecificStuff(SDL_FALSE); |
| getSurfaceCaps(); |
| getSurfaceFormats(); |
| if(!createSwapchain()) { |
| return SDL_FALSE; |
| } |
| createCommandPool(); |
| createCommandBuffers(); |
| createFences(); |
| return SDL_TRUE; |
| } |
| |
| static void initVulkan(void) |
| { |
| int i; |
| |
| SDL_Vulkan_LoadLibrary(NULL); |
| |
| vulkanContexts = (VulkanContext *) SDL_calloc(state->num_windows, sizeof (VulkanContext)); |
| if (!vulkanContexts) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!"); |
| quit(2); |
| } |
| |
| for (i = 0; i < state->num_windows; ++i) { |
| vulkanContext = &vulkanContexts[i]; |
| vulkanContext->window = state->windows[i]; |
| loadGlobalFunctions(); |
| createInstance(); |
| loadInstanceFunctions(); |
| createSurface(); |
| findPhysicalDevice(); |
| createDevice(); |
| loadDeviceFunctions(); |
| getQueues(); |
| createSemaphores(); |
| createNewSwapchainAndSwapchainSpecificStuff(); |
| } |
| } |
| |
| static void shutdownVulkan(SDL_bool doDestroySwapchain) |
| { |
| if (vulkanContexts) { |
| int i; |
| for (i = 0; i < state->num_windows; ++i) { |
| vulkanContext = &vulkanContexts[i]; |
| if (vulkanContext->device && vkDeviceWaitIdle) { |
| vkDeviceWaitIdle(vulkanContext->device); |
| } |
| |
| destroySwapchainAndSwapchainSpecificStuff(doDestroySwapchain); |
| |
| if (vulkanContext->imageAvailableSemaphore && vkDestroySemaphore) { |
| vkDestroySemaphore(vulkanContext->device, vulkanContext->imageAvailableSemaphore, NULL); |
| } |
| |
| if (vulkanContext->renderingFinishedSemaphore && vkDestroySemaphore) { |
| vkDestroySemaphore(vulkanContext->device, vulkanContext->renderingFinishedSemaphore, NULL); |
| } |
| |
| if (vulkanContext->device && vkDestroyDevice) { |
| vkDestroyDevice(vulkanContext->device, NULL); |
| } |
| |
| if (vulkanContext->surface && vkDestroySurfaceKHR) { |
| vkDestroySurfaceKHR(vulkanContext->instance, vulkanContext->surface, NULL); |
| } |
| |
| if (vulkanContext->instance && vkDestroyInstance) { |
| vkDestroyInstance(vulkanContext->instance, NULL); |
| } |
| |
| SDL_free(vulkanContext->surfaceFormats); |
| } |
| SDL_free(vulkanContexts); |
| vulkanContexts = NULL; |
| } |
| |
| SDL_Vulkan_UnloadLibrary(); |
| } |
| |
| static SDL_bool render(void) |
| { |
| uint32_t frameIndex; |
| VkResult result; |
| double currentTime; |
| VkClearColorValue clearColor = { {0} }; |
| VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; |
| VkSubmitInfo submitInfo = {0}; |
| VkPresentInfoKHR presentInfo = {0}; |
| int w, h; |
| |
| if (!vulkanContext->swapchain) { |
| SDL_bool retval = createNewSwapchainAndSwapchainSpecificStuff(); |
| if(!retval) |
| SDL_Delay(100); |
| return retval; |
| } |
| result = vkAcquireNextImageKHR(vulkanContext->device, |
| vulkanContext->swapchain, |
| UINT64_MAX, |
| vulkanContext->imageAvailableSemaphore, |
| VK_NULL_HANDLE, |
| &frameIndex); |
| if (result == VK_ERROR_OUT_OF_DATE_KHR) { |
| return createNewSwapchainAndSwapchainSpecificStuff(); |
| } |
| |
| if ((result != VK_SUBOPTIMAL_KHR) && (result != VK_SUCCESS)) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkAcquireNextImageKHR(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| result = vkWaitForFences(vulkanContext->device, 1, &vulkanContext->fences[frameIndex], VK_FALSE, UINT64_MAX); |
| if (result != VK_SUCCESS) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkWaitForFences(): %s\n", getVulkanResultString(result)); |
| quit(2); |
| } |
| result = vkResetFences(vulkanContext->device, 1, &vulkanContext->fences[frameIndex]); |
| if (result != VK_SUCCESS) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkResetFences(): %s\n", getVulkanResultString(result)); |
| quit(2); |
| } |
| currentTime = (double)SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency(); |
| clearColor.float32[0] = (float)(0.5 + 0.5 * SDL_sin(currentTime)); |
| clearColor.float32[1] = (float)(0.5 + 0.5 * SDL_sin(currentTime + M_PI * 2 / 3)); |
| clearColor.float32[2] = (float)(0.5 + 0.5 * SDL_sin(currentTime + M_PI * 4 / 3)); |
| clearColor.float32[3] = 1; |
| rerecordCommandBuffer(frameIndex, &clearColor); |
| submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
| submitInfo.waitSemaphoreCount = 1; |
| submitInfo.pWaitSemaphores = &vulkanContext->imageAvailableSemaphore; |
| submitInfo.pWaitDstStageMask = &waitDestStageMask; |
| submitInfo.commandBufferCount = 1; |
| submitInfo.pCommandBuffers = &vulkanContext->commandBuffers[frameIndex]; |
| submitInfo.signalSemaphoreCount = 1; |
| submitInfo.pSignalSemaphores = &vulkanContext->renderingFinishedSemaphore; |
| result = vkQueueSubmit(vulkanContext->graphicsQueue, 1, &submitInfo, vulkanContext->fences[frameIndex]); |
| |
| if (result != VK_SUCCESS) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkQueueSubmit(): %s\n", getVulkanResultString(result)); |
| quit(2); |
| } |
| presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; |
| presentInfo.waitSemaphoreCount = 1; |
| presentInfo.pWaitSemaphores = &vulkanContext->renderingFinishedSemaphore; |
| presentInfo.swapchainCount = 1; |
| presentInfo.pSwapchains = &vulkanContext->swapchain; |
| presentInfo.pImageIndices = &frameIndex; |
| result = vkQueuePresentKHR(vulkanContext->presentQueue, &presentInfo); |
| if ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)) { |
| return createNewSwapchainAndSwapchainSpecificStuff(); |
| } |
| |
| if (result != VK_SUCCESS) { |
| SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, |
| "vkQueuePresentKHR(): %s\n", |
| getVulkanResultString(result)); |
| quit(2); |
| } |
| SDL_Vulkan_GetDrawableSize(vulkanContext->window, &w, &h); |
| if(w != (int)vulkanContext->swapchainSize.width || h != (int)vulkanContext->swapchainSize.height) { |
| return createNewSwapchainAndSwapchainSpecificStuff(); |
| } |
| return SDL_TRUE; |
| } |
| |
| int main(int argc, char **argv) |
| { |
| int done; |
| SDL_DisplayMode mode; |
| SDL_Event event; |
| Uint32 then, now, frames; |
| int dw, dh; |
| |
| /* Enable standard application logging */ |
| SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); |
| |
| /* Initialize test framework */ |
| state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO); |
| if(!state) { |
| return 1; |
| } |
| |
| /* Set Vulkan parameters */ |
| state->window_flags |= SDL_WINDOW_VULKAN; |
| state->skip_renderer = 1; |
| |
| if (!SDLTest_CommonDefaultArgs(state, argc, argv) || !SDLTest_CommonInit(state)) { |
| SDLTest_CommonQuit(state); |
| return 1; |
| } |
| |
| SDL_GetCurrentDisplayMode(0, &mode); |
| SDL_Log("Screen BPP : %d\n", SDL_BITSPERPIXEL(mode.format)); |
| SDL_GetWindowSize(state->windows[0], &dw, &dh); |
| SDL_Log("Window Size : %d,%d\n", dw, dh); |
| SDL_Vulkan_GetDrawableSize(state->windows[0], &dw, &dh); |
| SDL_Log("Draw Size : %d,%d\n", dw, dh); |
| SDL_Log("\n"); |
| |
| initVulkan(); |
| |
| /* Main render loop */ |
| frames = 0; |
| then = SDL_GetTicks(); |
| done = 0; |
| while (!done) { |
| /* Check for events */ |
| frames++; |
| while(SDL_PollEvent(&event)) { |
| /* Need to destroy the swapchain before the window created |
| * by SDL. |
| */ |
| if (event.type == SDL_WINDOWEVENT_CLOSE) { |
| destroySwapchainAndSwapchainSpecificStuff(SDL_TRUE); |
| } |
| SDLTest_CommonEvent(state, &event, &done); |
| } |
| |
| if (!done) { |
| int i; |
| for (i = 0; i < state->num_windows; ++i) { |
| if (state->windows[i]) { |
| vulkanContext = &vulkanContexts[i]; |
| render(); |
| } |
| } |
| } |
| } |
| |
| /* Print out some timing information */ |
| now = SDL_GetTicks(); |
| if (now > then) { |
| SDL_Log("%2.2f frames per second\n", ((double)frames * 1000) / (now - then)); |
| } |
| |
| shutdownVulkan(SDL_TRUE); |
| SDLTest_CommonQuit(state); |
| return 0; |
| } |
| |
| #endif |