From 91ed8c6e9bad94692814c7e59707889b7ba73815 Mon Sep 17 00:00:00 2001 From: Sara Date: Sat, 27 Jan 2024 14:01:04 +0100 Subject: [PATCH] feat: now setting up vulkan swapchain in RenderContext --- src/render_context.c | 167 ++++++++++++++++++++++++++++++++++++++++--- src/render_context.h | 12 ++++ 2 files changed, 171 insertions(+), 8 deletions(-) diff --git a/src/render_context.c b/src/render_context.c index 1080ff4..aa58d17 100644 --- a/src/render_context.c +++ b/src/render_context.c @@ -4,22 +4,112 @@ #include #include "debug.h" #include "list.h" +#include "mathext.h" + +static const List requiredVulkanExtensions = { + .data = (char*[]){ + VK_KHR_SWAPCHAIN_EXTENSION_NAME + }, + .cap = 1, + .len = 1, + .element_size = sizeof(char*) +}; // get the list of supported vulkan protocol extensions static List RenderContextGetExtensions(const RenderContext *self) { // get the number of available extensions - unsigned extCount = 0; - SDL_Vulkan_GetInstanceExtensions(self->window, &extCount, 0); - List extensions = list_from_type_with_len(char*, extCount); - if(!SDL_Vulkan_GetInstanceExtensions(self->window, &extCount, extensions.data)) { + uint32_t count = 0; + SDL_Vulkan_GetInstanceExtensions(self->window, &count, 0); + List extensions = list_from_type_with_len(char*, count); + if(!SDL_Vulkan_GetInstanceExtensions(self->window, &count, extensions.data)) { LOG_WARNING("Failed to fetch vulkan instance extensions '%s'", SDL_GetError()); } return extensions; } +static List GetDeviceExtensions(VkPhysicalDevice device) { + uint32_t count = 0; + vkEnumerateDeviceExtensionProperties(device, NULL, &count, NULL); + List extensions = list_with_len(sizeof(VkExtensionProperties), count); + if(extensions.len > 0) + vkEnumerateDeviceExtensionProperties(device, NULL, &count, extensions.data); + return extensions; +} + +static VkSurfaceFormatKHR SelectSurfaceFormat(List availableFormats) { + list_foreach(VkSurfaceFormatKHR*, format, &availableFormats) { + if(format->colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + && format->format == VK_FORMAT_B8G8R8A8_SRGB) + return *format; + } + return *list_at_as(VkSurfaceFormatKHR, &availableFormats, 0); +} + +static VkPresentModeKHR SelectPresentMode(List availableModes) { + list_foreach(VkPresentModeKHR*, mode, &availableModes) { + if(*mode == VK_PRESENT_MODE_MAILBOX_KHR) + return *mode; + } + return VK_PRESENT_MODE_FIFO_KHR; +} + +static VkExtent2D SelectSwapExtent(RenderContext *self, const VkSurfaceCapabilitiesKHR *capabilities) { + if(capabilities->currentExtent.width != UINT32_MAX) { + return capabilities->currentExtent; + } + int width, height; + SDL_Vulkan_GetDrawableSize(self->window, &width, &height); + return (VkExtent2D) { + .width = CLAMP((uint32_t)width, capabilities->minImageExtent.width, capabilities->maxImageExtent.width), + .height = CLAMP((uint32_t)height, capabilities->minImageExtent.height, capabilities->maxImageExtent.height) + };; +} + +static int CheckExtensionSupport(VkPhysicalDevice device) { + List supported = GetDeviceExtensions(device); + int isValid = 1; + for(size_t i = 0; isValid && i < requiredVulkanExtensions.len; ++i) { + if(list_contains(&supported, ((VkExtensionProperties*)requiredVulkanExtensions.data) + i) == 0) + isValid = 0; + } + list_empty(&supported); + ASSERT_RETURN(isValid, 0, "Not all required vulkan extensions are supported"); + return isValid; +} + +static SwapChainSupportInfo RenderContextGetSwapChainSupport(RenderContext *self, VkPhysicalDevice device) { + SwapChainSupportInfo support = {0}; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, self->windowSurface, &support.capabilities); + uint32_t count = 0; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, self->windowSurface, &count, NULL); + if(count > 0) { + support.formats = list_with_len(sizeof(VkSurfaceFormatKHR), count); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, self->windowSurface, &count, support.formats.data); + } + vkGetPhysicalDeviceSurfacePresentModesKHR(device, self->windowSurface, &count, NULL); + if(count > 0) { + support.presentModes = list_with_len(sizeof(VkPresentModeKHR), count); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, self->windowSurface, &count, support.presentModes.data); + } + return support; +} + +static void SwapChainSupportInfoClean(SwapChainSupportInfo *self) { + list_empty(&self->formats); + list_empty(&self->presentModes); +} + +static int CheckSupportedSwapChain(RenderContext *self, VkPhysicalDevice device) { + SwapChainSupportInfo swapchain = RenderContextGetSwapChainSupport(self, device); + int validSwapChain = swapchain.formats.len > 0 + && swapchain.presentModes.len > 0; + SwapChainSupportInfoClean(&swapchain); + return validSwapChain; +} + // returns 1 if the GPU reference by device is usable for this application // 0 otherwise -static int GpuIsUsable(VkPhysicalDevice device) { +static int GpuIsUsable(RenderContext* self, VkPhysicalDevice device) { // get properties and features VkPhysicalDeviceProperties properties; VkPhysicalDeviceFeatures features; @@ -28,7 +118,9 @@ static int GpuIsUsable(VkPhysicalDevice device) { // integrated or discrete works return (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) // needs geometry shader support - && (features.geometryShader); + && (features.geometryShader) + && CheckExtensionSupport(device) + && CheckSupportedSwapChain(self, device); } // initialize the SDL window for this application @@ -70,7 +162,7 @@ static void RenderContextSelectPhysicalDevice(RenderContext *self) { vkEnumeratePhysicalDevices(self->vulkanInstance, &deviceCount, devices.data); // search through the available devices for one suitable to our usecase list_foreach(VkPhysicalDevice*, device, &devices) { - if(GpuIsUsable(*device)) { + if(GpuIsUsable(self, *device)) { self->physicalDevice = *device; break; } @@ -148,15 +240,20 @@ static void RenderContextCreateLogicalDevice(RenderContext *self) { const float priorities = 1.f; list_foreach(VkDeviceQueueCreateInfo*, info, &queueCreateInfo) info->pQueuePriorities = &priorities; + + List extensions = list_copy(&requiredVulkanExtensions); VkDeviceCreateInfo createInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pQueueCreateInfos = queueCreateInfo.data, .queueCreateInfoCount = queueCreateInfo.len, + .enabledExtensionCount = (uint32_t)extensions.len, + .ppEnabledExtensionNames = extensions.data }; VkPhysicalDeviceFeatures features = {0}; if(vkCreateDevice(self->physicalDevice, &createInfo, NULL, &self->device) != VK_SUCCESS) { LOG_ERROR("Failed to create vulkan logical device"); } + list_empty(&extensions); list_empty(&queueCreateInfo); vkGetDeviceQueue(self->device, queueFamilies.graphicsQueue, 0, &self->graphicsQueue); vkGetDeviceQueue(self->device, queueFamilies.presentQueue, 0, &self->presentQueue); @@ -168,17 +265,71 @@ static void RenderContextCreateSurface(RenderContext *self) { } } +static void RenderContextCreateSwapchain(RenderContext *self) { + // convert supported capabilities to prefered settings + SwapChainSupportInfo support = RenderContextGetSwapChainSupport(self, self->physicalDevice); + self->swapchainExtent = SelectSwapExtent(self, &support.capabilities); + VkSurfaceFormatKHR surfaceFormat = SelectSurfaceFormat(support.formats); + self->swapchainFormat = surfaceFormat.format; + VkPresentModeKHR presentMode = SelectPresentMode(support.presentModes); + uint32_t chainLength = support.capabilities.minImageCount; + if(support.capabilities.maxImageCount > 0) + chainLength = MAX(chainLength, support.capabilities.maxImageCount); + VkSwapchainCreateInfoKHR createInfo = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = self->windowSurface, + .minImageCount = chainLength, + .imageFormat = surfaceFormat.format, + .imageColorSpace = surfaceFormat.colorSpace, + .imageExtent = self->swapchainExtent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .preTransform = support.capabilities.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = presentMode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + }; + // if the present and graphics queue are not the same, they need to work together + // allow this swapchain to be accessed by both + QueueFamilyIndexes families = RenderContextFindQueueFamilies(self); + if(families.graphicsQueue != families.presentQueue) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = families.as_array; + } + SwapChainSupportInfoClean(&support); + if(vkCreateSwapchainKHR(self->device, &createInfo, NULL, &self->swapchain) != VK_SUCCESS) { + LOG_ERROR("Failed to create vulkan swapchain"); + } + vkGetSwapchainImagesKHR(self->device, self->swapchain, &chainLength, NULL); + self->swapchainImages = list_with_len(sizeof(VkImage), chainLength); + vkGetSwapchainImagesKHR(self->device, self->swapchain, &chainLength, self->swapchainImages.data); +} + RenderContext *RenderContextCreate(const VkApplicationInfo *appinfo) { + LOG_INFO("Creating render context"); RenderContext* self = malloc(sizeof(RenderContext)); memset(self, 0x0, sizeof(RenderContext)); + LOG_INFO("Initializing SDL window"); RenderContextInitSdlWindow(self, appinfo); + LOG_INFO("Initializing vulkan instance"); RenderContextInitVulkanInstance(self, appinfo); - RenderContextSelectPhysicalDevice(self); + LOG_INFO("Creating window render surface"); RenderContextCreateSurface(self); + LOG_INFO("Selecting physical device"); + RenderContextSelectPhysicalDevice(self); + LOG_INFO("Creating logical device"); + RenderContextCreateLogicalDevice(self); + LOG_INFO("Set up swapchain"); + RenderContextCreateSwapchain(self); + LOG_INFO("Finished setting up vulkan render context"); return self; } void RenderContextDestroy(RenderContext *self) { + vkDestroySwapchainKHR(self->device, self->swapchain, NULL); vkDestroySurfaceKHR(self->vulkanInstance, self->windowSurface, NULL); vkDestroyDevice(self->device, NULL); vkDestroyInstance(self->vulkanInstance, NULL); diff --git a/src/render_context.h b/src/render_context.h index 407c0e7..3cca4ea 100644 --- a/src/render_context.h +++ b/src/render_context.h @@ -7,12 +7,18 @@ #include #include +#include "list.h" + typedef struct RenderContext { SDL_Window *window; VkInstance vulkanInstance; VkPhysicalDevice physicalDevice; VkDevice device; VkSurfaceKHR windowSurface; + VkSwapchainKHR swapchain; + List swapchainImages; // + VkFormat swapchainFormat; + VkExtent2D swapchainExtent; VkQueue graphicsQueue; VkQueue presentQueue; @@ -32,6 +38,12 @@ typedef struct QueueFamilyIndexes { }; } QueueFamilyIndexes; +typedef struct SwapChainSupportInfo { + VkSurfaceCapabilitiesKHR capabilities; + List formats; // + List presentModes; // +} SwapChainSupportInfo; + RenderContext *RenderContextCreate(const VkApplicationInfo *appinfo); void RenderContextDestroy(RenderContext *self);