swapchain creation

pull/8/head
cosmonaut 2020-12-16 19:50:31 -08:00
parent 36eb134c9f
commit 458bd6049d
2 changed files with 443 additions and 110 deletions

View File

@ -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,

View File

@ -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;
}