From 458bd6049d07b243b99f5cf4f979b29526b1e63e Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Wed, 16 Dec 2020 19:50:31 -0800 Subject: [PATCH] swapchain creation --- include/Refresh.h | 8 + src/Refresh_Driver_Vulkan.c | 545 ++++++++++++++++++++++++++++-------- 2 files changed, 443 insertions(+), 110 deletions(-) diff --git a/include/Refresh.h b/include/Refresh.h index 4683198..46a6967 100644 --- a/include/Refresh.h +++ b/include/Refresh.h @@ -65,6 +65,14 @@ typedef struct REFRESH_ShaderModule REFRESH_ShaderModule; typedef struct REFRESH_RenderPass REFRESH_RenderPass; typedef struct REFRESH_GraphicsPipeline REFRESH_GraphicsPipeline; +typedef enum REFRESH_PresentMode +{ + REFRESH_PRESENTMODE_IMMEDIATE, + REFRESH_PRESENTMODE_MAILBOX, + REFRESH_PRESENTMODE_FIFO, + REFRESH_PRESENTMODE_FIFO_RELAXED +} REFRESH_PresentMode; + typedef enum REFRESH_PrimitiveType { REFRESH_PRIMITIVETYPE_POINTLIST, diff --git a/src/Refresh_Driver_Vulkan.c b/src/Refresh_Driver_Vulkan.c index ed4c547..6dd2f1d 100644 --- a/src/Refresh_Driver_Vulkan.c +++ b/src/Refresh_Driver_Vulkan.c @@ -106,6 +106,13 @@ typedef enum VulkanResourceAccessType RESOURCE_ACCESS_TYPES_COUNT } VulkanResourceAccessType; +typedef enum CreateSwapchainResult +{ + CREATE_SWAPCHAIN_FAIL, + CREATE_SWAPCHAIN_SUCCESS, + CREATE_SWAPCHAIN_SURFACE_ZERO, +} CreateSwapchainResult; + /* Structures */ typedef struct QueueFamilyIndices @@ -135,14 +142,18 @@ typedef struct VulkanRenderer uint8_t supportsDebugUtils; uint8_t debugMode; + uint8_t headless; + REFRESH_PresentMode presentMode; VkSurfaceKHR surface; - VkSwapchainKHR swapchain; - VkImage *swapchainImages; - VkImageView *swapchainImageViews; + VkSwapchainKHR swapChain; + VkFormat swapChainFormat; + VkComponentMapping swapChainSwizzle; + VkImage *swapChainImages; + VkImageView *swapChainImageViews; VulkanResourceAccessType *swapChainResourceAccessTypes; - uint32_t swapchainImageCount; - VkExtent2D swapchainExtent; + uint32_t swapChainImageCount; + VkExtent2D swapChainExtent; QueueFamilyIndices queueFamilyIndices; VkQueue graphicsQueue; @@ -614,6 +625,411 @@ static void VULKAN_BindGraphicsPipeline( SDL_assert(0); } +/* Swapchain */ + +static inline VkExtent2D VULKAN_INTERNAL_ChooseSwapExtent( + void* windowHandle, + const VkSurfaceCapabilitiesKHR capabilities +) { + VkExtent2D actualExtent; + int32_t drawableWidth, drawableHeight; + + if (capabilities.currentExtent.width != UINT32_MAX) + { + return capabilities.currentExtent; + } + else + { + SDL_Vulkan_GetDrawableSize( + (SDL_Window*) windowHandle, + &drawableWidth, + &drawableHeight + ); + + actualExtent.width = drawableWidth; + actualExtent.height = drawableHeight; + + return actualExtent; + } +} + +static uint8_t VULKAN_INTERNAL_QuerySwapChainSupport( + VulkanRenderer *renderer, + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + SwapChainSupportDetails *outputDetails +) { + VkResult result; + uint32_t formatCount; + uint32_t presentModeCount; + + result = renderer->vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + physicalDevice, + surface, + &outputDetails->capabilities + ); + if (result != VK_SUCCESS) + { + REFRESH_LogError( + "vkGetPhysicalDeviceSurfaceCapabilitiesKHR: %s", + VkErrorMessages(result) + ); + + return 0; + } + + renderer->vkGetPhysicalDeviceSurfaceFormatsKHR( + physicalDevice, + surface, + &formatCount, + NULL + ); + + if (formatCount != 0) + { + outputDetails->formats = (VkSurfaceFormatKHR*) SDL_malloc( + sizeof(VkSurfaceFormatKHR) * formatCount + ); + outputDetails->formatsLength = formatCount; + + if (!outputDetails->formats) + { + SDL_OutOfMemory(); + return 0; + } + + result = renderer->vkGetPhysicalDeviceSurfaceFormatsKHR( + physicalDevice, + surface, + &formatCount, + outputDetails->formats + ); + if (result != VK_SUCCESS) + { + REFRESH_LogError( + "vkGetPhysicalDeviceSurfaceFormatsKHR: %s", + VkErrorMessages(result) + ); + + SDL_free(outputDetails->formats); + return 0; + } + } + + renderer->vkGetPhysicalDeviceSurfacePresentModesKHR( + physicalDevice, + surface, + &presentModeCount, + NULL + ); + + if (presentModeCount != 0) + { + outputDetails->presentModes = (VkPresentModeKHR*) SDL_malloc( + sizeof(VkPresentModeKHR) * presentModeCount + ); + outputDetails->presentModesLength = presentModeCount; + + if (!outputDetails->presentModes) + { + SDL_OutOfMemory(); + return 0; + } + + result = renderer->vkGetPhysicalDeviceSurfacePresentModesKHR( + physicalDevice, + surface, + &presentModeCount, + outputDetails->presentModes + ); + if (result != VK_SUCCESS) + { + REFRESH_LogError( + "vkGetPhysicalDeviceSurfacePresentModesKHR: %s", + VkErrorMessages(result) + ); + + SDL_free(outputDetails->formats); + SDL_free(outputDetails->presentModes); + return 0; + } + } + + return 1; +} + +static uint8_t VULKAN_INTERNAL_ChooseSwapSurfaceFormat( + VkFormat desiredFormat, + VkSurfaceFormatKHR *availableFormats, + uint32_t availableFormatsLength, + VkSurfaceFormatKHR *outputFormat +) { + uint32_t i; + for (i = 0; i < availableFormatsLength; i += 1) + { + if ( availableFormats[i].format == desiredFormat && + availableFormats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR ) + { + *outputFormat = availableFormats[i]; + return 1; + } + } + + REFRESH_LogError("Desired surface format is unavailable."); + return 0; +} + +static uint8_t VULKAN_INTERNAL_ChooseSwapPresentMode( + REFRESH_PresentMode desiredPresentInterval, + VkPresentModeKHR *availablePresentModes, + uint32_t availablePresentModesLength, + VkPresentModeKHR *outputPresentMode +) { + #define CHECK_MODE(m) \ + for (i = 0; i < availablePresentModesLength; i += 1) \ + { \ + if (availablePresentModes[i] == m) \ + { \ + *outputPresentMode = m; \ + REFRESH_LogInfo("Using " #m "!"); \ + return 1; \ + } \ + } \ + REFRESH_LogInfo(#m " unsupported."); + + uint32_t i; + if (desiredPresentInterval == REFRESH_PRESENTMODE_IMMEDIATE) + { + CHECK_MODE(VK_PRESENT_MODE_IMMEDIATE_KHR) + } + else if (desiredPresentInterval == REFRESH_PRESENTMODE_MAILBOX) + { + CHECK_MODE(VK_PRESENT_MODE_MAILBOX_KHR) + } + else if (desiredPresentInterval == REFRESH_PRESENTMODE_FIFO) + { + CHECK_MODE(VK_PRESENT_MODE_FIFO_KHR) + } + else if (desiredPresentInterval == REFRESH_PRESENTMODE_FIFO_RELAXED) + { + CHECK_MODE(VK_PRESENT_MODE_FIFO_RELAXED_KHR) + } + else + { + REFRESH_LogError( + "Unrecognized PresentInterval: %d", + desiredPresentInterval + ); + return 0; + } + + #undef CHECK_MODE + + REFRESH_LogInfo("Fall back to VK_PRESENT_MODE_FIFO_KHR."); + *outputPresentMode = VK_PRESENT_MODE_FIFO_KHR; + return 1; +} + +static CreateSwapchainResult VULKAN_INTERNAL_CreateSwapchain( + VulkanRenderer *renderer +) { + VkResult vulkanResult; + SwapChainSupportDetails swapChainSupportDetails; + VkSurfaceFormatKHR surfaceFormat; + VkPresentModeKHR presentMode; + VkExtent2D extent; + uint32_t imageCount, swapChainImageCount, i; + VkSwapchainCreateInfoKHR swapChainCreateInfo; + VkImage *swapChainImages; + VkImageViewCreateInfo createInfo; + VkImageView swapChainImageView; + + if (!VULKAN_INTERNAL_QuerySwapChainSupport( + renderer, + renderer->physicalDevice, + renderer->surface, + &swapChainSupportDetails + )) { + REFRESH_LogError("Device does not support swap chain creation"); + return CREATE_SWAPCHAIN_FAIL; + } + + renderer->swapChainFormat = VK_FORMAT_B8G8R8A8_UNORM; + renderer->swapChainSwizzle.r = VK_COMPONENT_SWIZZLE_IDENTITY; + renderer->swapChainSwizzle.g = VK_COMPONENT_SWIZZLE_IDENTITY; + renderer->swapChainSwizzle.b = VK_COMPONENT_SWIZZLE_IDENTITY; + renderer->swapChainSwizzle.a = VK_COMPONENT_SWIZZLE_IDENTITY; + if (!VULKAN_INTERNAL_ChooseSwapSurfaceFormat( + renderer->swapChainFormat, + swapChainSupportDetails.formats, + swapChainSupportDetails.formatsLength, + &surfaceFormat + )) { + SDL_free(swapChainSupportDetails.formats); + SDL_free(swapChainSupportDetails.presentModes); + REFRESH_LogError("Device does not support swap chain format"); + return CREATE_SWAPCHAIN_FAIL; + } + + if (!VULKAN_INTERNAL_ChooseSwapPresentMode( + renderer->presentMode, + swapChainSupportDetails.presentModes, + swapChainSupportDetails.presentModesLength, + &presentMode + )) { + SDL_free(swapChainSupportDetails.formats); + SDL_free(swapChainSupportDetails.presentModes); + REFRESH_LogError("Device does not support swap chain present mode"); + return CREATE_SWAPCHAIN_FAIL; + } + + extent = VULKAN_INTERNAL_ChooseSwapExtent( + renderer->deviceWindowHandle, + swapChainSupportDetails.capabilities + ); + + if (extent.width == 0 || extent.height == 0) + { + return CREATE_SWAPCHAIN_SURFACE_ZERO; + } + + imageCount = swapChainSupportDetails.capabilities.minImageCount + 1; + + if ( swapChainSupportDetails.capabilities.maxImageCount > 0 && + imageCount > swapChainSupportDetails.capabilities.maxImageCount ) + { + imageCount = swapChainSupportDetails.capabilities.maxImageCount; + } + + if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR) + { + /* Required for proper triple-buffering. + * Note that this is below the above maxImageCount check! + * If the driver advertises MAILBOX but does not support 3 swap + * images, it's not real mailbox support, so let it fail hard. + * -flibit + */ + imageCount = SDL_max(imageCount, 3); + } + + swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapChainCreateInfo.pNext = NULL; + swapChainCreateInfo.flags = 0; + swapChainCreateInfo.surface = renderer->surface; + swapChainCreateInfo.minImageCount = imageCount; + swapChainCreateInfo.imageFormat = surfaceFormat.format; + swapChainCreateInfo.imageColorSpace = surfaceFormat.colorSpace; + swapChainCreateInfo.imageExtent = extent; + swapChainCreateInfo.imageArrayLayers = 1; + swapChainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapChainCreateInfo.queueFamilyIndexCount = 0; + swapChainCreateInfo.pQueueFamilyIndices = NULL; + swapChainCreateInfo.preTransform = swapChainSupportDetails.capabilities.currentTransform; + swapChainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapChainCreateInfo.presentMode = presentMode; + swapChainCreateInfo.clipped = VK_TRUE; + swapChainCreateInfo.oldSwapchain = VK_NULL_HANDLE; + + vulkanResult = renderer->vkCreateSwapchainKHR( + renderer->logicalDevice, + &swapChainCreateInfo, + NULL, + &renderer->swapChain + ); + + SDL_free(swapChainSupportDetails.formats); + SDL_free(swapChainSupportDetails.presentModes); + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResult("vkCreateSwapchainKHR", vulkanResult); + + return CREATE_SWAPCHAIN_FAIL; + } + + renderer->vkGetSwapchainImagesKHR( + renderer->logicalDevice, + renderer->swapChain, + &swapChainImageCount, + NULL + ); + + renderer->swapChainImages = (VkImage*) SDL_malloc( + sizeof(VkImage) * swapChainImageCount + ); + if (!renderer->swapChainImages) + { + SDL_OutOfMemory(); + return CREATE_SWAPCHAIN_FAIL; + } + + renderer->swapChainImageViews = (VkImageView*) SDL_malloc( + sizeof(VkImageView) * swapChainImageCount + ); + if (!renderer->swapChainImageViews) + { + SDL_OutOfMemory(); + return CREATE_SWAPCHAIN_FAIL; + } + + renderer->swapChainResourceAccessTypes = (VulkanResourceAccessType*) SDL_malloc( + sizeof(VulkanResourceAccessType) * swapChainImageCount + ); + if (!renderer->swapChainResourceAccessTypes) + { + SDL_OutOfMemory(); + return CREATE_SWAPCHAIN_FAIL; + } + + swapChainImages = SDL_stack_alloc(VkImage, swapChainImageCount); + renderer->vkGetSwapchainImagesKHR( + renderer->logicalDevice, + renderer->swapChain, + &swapChainImageCount, + swapChainImages + ); + renderer->swapChainImageCount = swapChainImageCount; + renderer->swapChainExtent = extent; + + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.pNext = NULL; + createInfo.flags = 0; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = surfaceFormat.format; + createInfo.components = renderer->swapChainSwizzle; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + for (i = 0; i < swapChainImageCount; i += 1) + { + createInfo.image = swapChainImages[i]; + + vulkanResult = renderer->vkCreateImageView( + renderer->logicalDevice, + &createInfo, + NULL, + &swapChainImageView + ); + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResult("vkCreateImageView", vulkanResult); + SDL_stack_free(swapChainImages); + return CREATE_SWAPCHAIN_FAIL; + } + + renderer->swapChainImages[i] = swapChainImages[i]; + renderer->swapChainImageViews[i] = swapChainImageView; + renderer->swapChainResourceAccessTypes[i] = RESOURCE_ACCESS_NONE; + } + + SDL_stack_free(swapChainImages); + return CREATE_SWAPCHAIN_SUCCESS; +} + /* Device instantiation */ static inline uint8_t VULKAN_INTERNAL_SupportsExtension( @@ -883,111 +1299,6 @@ static uint8_t VULKAN_INTERNAL_CheckDeviceExtensions( return allExtensionsSupported; } -static uint8_t VULKAN_INTERNAL_QuerySwapChainSupport( - VulkanRenderer *renderer, - VkPhysicalDevice physicalDevice, - VkSurfaceKHR surface, - SwapChainSupportDetails *outputDetails -) { - VkResult result; - uint32_t formatCount; - uint32_t presentModeCount; - - result = renderer->vkGetPhysicalDeviceSurfaceCapabilitiesKHR( - physicalDevice, - surface, - &outputDetails->capabilities - ); - if (result != VK_SUCCESS) - { - REFRESH_LogError( - "vkGetPhysicalDeviceSurfaceCapabilitiesKHR: %s", - VkErrorMessages(result) - ); - - return 0; - } - - renderer->vkGetPhysicalDeviceSurfaceFormatsKHR( - physicalDevice, - surface, - &formatCount, - NULL - ); - - if (formatCount != 0) - { - outputDetails->formats = (VkSurfaceFormatKHR*) SDL_malloc( - sizeof(VkSurfaceFormatKHR) * formatCount - ); - outputDetails->formatsLength = formatCount; - - if (!outputDetails->formats) - { - SDL_OutOfMemory(); - return 0; - } - - result = renderer->vkGetPhysicalDeviceSurfaceFormatsKHR( - physicalDevice, - surface, - &formatCount, - outputDetails->formats - ); - if (result != VK_SUCCESS) - { - REFRESH_LogError( - "vkGetPhysicalDeviceSurfaceFormatsKHR: %s", - VkErrorMessages(result) - ); - - SDL_free(outputDetails->formats); - return 0; - } - } - - renderer->vkGetPhysicalDeviceSurfacePresentModesKHR( - physicalDevice, - surface, - &presentModeCount, - NULL - ); - - if (presentModeCount != 0) - { - outputDetails->presentModes = (VkPresentModeKHR*) SDL_malloc( - sizeof(VkPresentModeKHR) * presentModeCount - ); - outputDetails->presentModesLength = presentModeCount; - - if (!outputDetails->presentModes) - { - SDL_OutOfMemory(); - return 0; - } - - result = renderer->vkGetPhysicalDeviceSurfacePresentModesKHR( - physicalDevice, - surface, - &presentModeCount, - outputDetails->presentModes - ); - if (result != VK_SUCCESS) - { - REFRESH_LogError( - "vkGetPhysicalDeviceSurfacePresentModesKHR: %s", - VkErrorMessages(result) - ); - - SDL_free(outputDetails->formats); - SDL_free(outputDetails->presentModes); - return 0; - } - } - - return 1; -} - static uint8_t VULKAN_INTERNAL_IsDeviceSuitable( VulkanRenderer *renderer, VkPhysicalDevice physicalDevice, @@ -1316,6 +1627,7 @@ static REFRESH_Device* VULKAN_CreateDevice( renderer = (VulkanRenderer*) SDL_malloc(sizeof(VulkanRenderer)); result->driverData = (REFRESH_Renderer*) renderer; renderer->debugMode = debugMode; + renderer->headless = deviceWindowHandle == NULL; /* Create the VkInstance */ if (!VULKAN_INTERNAL_CreateInstance(renderer, deviceWindowHandle)) @@ -1400,6 +1712,19 @@ static REFRESH_Device* VULKAN_CreateDevice( return NULL; } + /* + * Create initial swapchain + */ + + if (!renderer->headless) + { + if (VULKAN_INTERNAL_CreateSwapchain(renderer) != CREATE_SWAPCHAIN_SUCCESS) + { + REFRESH_LogError("Failed to create swap chain"); + return NULL; + } + } + return result; }