From 3a7bca385c05c500741068d0813fe3a2f86f3911 Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Wed, 31 Jul 2024 10:07:49 +0200 Subject: [PATCH] feat: Create the swapchain --- src/renderer/backend_api.h | 4 +- src/renderer/vk/device.c | 39 ++++- src/renderer/vk/device.h | 14 +- src/renderer/vk/init.c | 6 +- src/renderer/vk/meson.build | 4 +- src/renderer/vk/physical_resource_manager.c | 1 + src/renderer/vk/physical_resource_manager.h | 10 ++ src/renderer/vk/swapchain.c | 176 ++++++++++++++++++++ vk_layer_settings.txt | 2 +- 9 files changed, 242 insertions(+), 14 deletions(-) create mode 100644 src/renderer/vk/physical_resource_manager.c create mode 100644 src/renderer/vk/physical_resource_manager.h create mode 100644 src/renderer/vk/swapchain.c diff --git a/src/renderer/backend_api.h b/src/renderer/backend_api.h index 4dc775e..445185c 100644 --- a/src/renderer/backend_api.h +++ b/src/renderer/backend_api.h @@ -1,12 +1,12 @@ #ifndef RT_RENCOM_RENDERER_API_H #define RT_RENCOM_RENDERER_API_H -#include "runtime/runtime.h" +#include #include "renderer.h" typedef struct rt_physical_resource_manager_i rt_physical_resource_manager_i; -typedef rt_physical_resource_manager_i *rt_render_device_get_physical_resource_manager_fn(void *o); +typedef rt_physical_resource_manager_i rt_render_device_get_physical_resource_manager_fn(void *o); /* Interface for the render device. * The device is responsible for executing command lists. */ diff --git a/src/renderer/vk/device.c b/src/renderer/vk/device.c index 7d98008..c5209fd 100644 --- a/src/renderer/vk/device.c +++ b/src/renderer/vk/device.c @@ -5,6 +5,7 @@ #include #include +#include #include "device.h" @@ -439,6 +440,7 @@ static rt_result CreateDevice(rt_vk_device *dev) { queue_info[0].queueCount = 1; queue_info[0].queueFamilyIndex = queue_indices.graphics; queue_info[0].pQueuePriorities = &priority; + dev->unique_families[0] = queue_indices.graphics; if (queue_indices.compute != queue_indices.graphics) { queue_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_info[1].pNext = NULL; @@ -446,6 +448,7 @@ static rt_result CreateDevice(rt_vk_device *dev) { queue_info[1].queueCount = 1; queue_info[1].queueFamilyIndex = queue_indices.compute; queue_info[1].pQueuePriorities = &priority; + dev->unique_families[distinct_queue_count] = queue_indices.compute; ++distinct_queue_count; } if (queue_indices.present != queue_indices.graphics && @@ -456,18 +459,23 @@ static rt_result CreateDevice(rt_vk_device *dev) { queue_info[distinct_queue_count].queueCount = 1; queue_info[distinct_queue_count].queueFamilyIndex = queue_indices.present; queue_info[distinct_queue_count].pQueuePriorities = &priority; + dev->unique_families[distinct_queue_count] = queue_indices.present; ++distinct_queue_count; } if (queue_indices.transfer != queue_indices.graphics && - queue_indices.transfer != queue_indices.compute) { + queue_indices.transfer != queue_indices.compute && + queue_indices.transfer != queue_indices.present) { queue_info[distinct_queue_count].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_info[distinct_queue_count].pNext = NULL; queue_info[distinct_queue_count].flags = 0; queue_info[distinct_queue_count].queueCount = 1; queue_info[distinct_queue_count].queueFamilyIndex = queue_indices.transfer; queue_info[distinct_queue_count].pQueuePriorities = &priority; + dev->unique_families[distinct_queue_count] = queue_indices.transfer; ++distinct_queue_count; } + dev->unique_family_count = distinct_queue_count; + VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, }; @@ -619,9 +627,15 @@ void DestroyPerFrameObjects(rt_vk_device *dev) { } } -rt_create_vk_device_result rtCreateVkDevice(const rt_renderer_window_info *info) { - rtLog("vk", "Init"); +extern rt_result CreateSwapchain(rt_vk_device *dev); +extern rt_result RecreateSwapchain(rt_vk_device *dev); +extern void DestroySwapchain(rt_vk_device *dev); + +rt_create_vk_device_result rtCreateVkDevice(const rt_renderer_window_info *info) { + rtLog("VK", "Init"); + + rt_timestamp initBegin = rtTimeNow(); rt_vk_device dev; _tracking_alloc_cbs.pUserData = NULL; @@ -635,6 +649,13 @@ rt_create_vk_device_result rtCreateVkDevice(const rt_renderer_window_info *info) dev.alloc_cb = NULL; } + dev.max_frames_in_flight = RT_RESTRICT_VALUE_TO_BOUNDS(r_VkMaxFramesInFlight.i, 2, 3); + rtLog("VK", "Max frames in flight: %u", dev.max_frames_in_flight); + if ((int)dev.max_frames_in_flight != r_VkMaxFramesInFlight.i) { + r_VkMaxFramesInFlight.i = (int)dev.max_frames_in_flight; + rtNotifyCVARChange(&r_VkMaxFramesInFlight); + } + rt_result res; if ((res = CreateInstance(&dev)) != RT_SUCCESS) goto out; @@ -646,6 +667,11 @@ rt_create_vk_device_result rtCreateVkDevice(const rt_renderer_window_info *info) goto out; if ((res = CreatePerFrameObjects(&dev)) != RT_SUCCESS) goto out; + if ((res = CreateSwapchain(&dev)) != RT_SUCCESS) + goto out; + + rt_time_delta initTime = rtTimeBetween(initBegin, rtTimeNow()); + rtLog("VK", "Init complete. Took %lf seconds.", initTime); out: return (rt_create_vk_device_result) {.result = res, .device = dev}; @@ -654,6 +680,7 @@ out: void rtDestroyVkDevice(rt_vk_device *dev) { rtLog("VK", "Shutdown"); vkDeviceWaitIdle(dev->device); + DestroySwapchain(dev); DestroyPerFrameObjects(dev); // DestroyAllocator(); vkDestroyDevice(dev->device, dev->alloc_cb); @@ -663,3 +690,9 @@ void rtDestroyVkDevice(rt_vk_device *dev) { #endif vkDestroyInstance(dev->instance, dev->alloc_cb); } + +/* rt_render_device_i functions */ +rt_physical_resource_manager_i rtVkDevGetPhysicalResourceManager(void *o) { + rt_physical_resource_manager_i iface = {}; + return iface; +} \ No newline at end of file diff --git a/src/renderer/vk/device.h b/src/renderer/vk/device.h index 992a480..ad95201 100644 --- a/src/renderer/vk/device.h +++ b/src/renderer/vk/device.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef _WIN32 struct HINSTANCE__; @@ -29,7 +30,7 @@ typedef struct { VkSemaphore swapchain_transitioned; } rt_vk_frame_data; -typedef struct { +typedef struct rt_vk_device { /* *** Vulkan objects *** */ VkInstance instance; VkDevice device; @@ -42,11 +43,14 @@ typedef struct { uint32_t compute_family; uint32_t transfer_family; uint32_t present_family; + uint32_t unique_families[4]; + uint32_t unique_family_count; VkQueue graphics_queue; VkQueue compute_queue; VkQueue transfer_queue; VkQueue present_queue; + /* *** Properties and features *** */ VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props; VkPhysicalDeviceDescriptorIndexingFeatures descriptor_indexing_features; @@ -59,6 +63,10 @@ typedef struct { /* *** Windowing system *** */ rt_vk_native_window native_window; + VkSwapchainKHR swapchain; + VkImage swapchain_images[4]; + VkImageView swapchain_image_views[4]; + uint32_t swapchain_image_count; /* *** Debug utils *** */ #ifdef RT_DEBUG @@ -75,5 +83,7 @@ rt_create_vk_device_result rtCreateVkDevice(const rt_renderer_window_info *info) void rtDestroyVkDevice(rt_vk_device *dev); -#endif +/* rt_render_device_i functions */ +rt_physical_resource_manager_i rtVkDevGetPhysicalResourceManager(void *o); +#endif diff --git a/src/renderer/vk/init.c b/src/renderer/vk/init.c index aaa7d9b..2ce00c2 100644 --- a/src/renderer/vk/init.c +++ b/src/renderer/vk/init.c @@ -7,11 +7,7 @@ extern rt_cvar r_VkEnableAPIAllocTracking; extern rt_cvar r_VkPhysDeviceName; extern rt_cvar r_VkMaxFramesInFlight; - -#if 0 -extern rt_cvar r_VkPreferredSwapchainImages; extern rt_cvar r_VkPreferMailboxMode; -#endif void VkRegisterCVARs(void) { rtRegisterCVAR(&r_VkEnableAPIAllocTracking); @@ -37,7 +33,7 @@ rt_render_backend_init_result VkInit(const rt_renderer_window_info *info) { _device = device_result.device; rt_render_device_i device_i = { .o = &_device, - .GetPhysicalResourceManager = NULL + .GetPhysicalResourceManager = rtVkDevGetPhysicalResourceManager, }; res.device = device_i; diff --git a/src/renderer/vk/meson.build b/src/renderer/vk/meson.build index e5c3d11..147b7a0 100644 --- a/src/renderer/vk/meson.build +++ b/src/renderer/vk/meson.build @@ -14,10 +14,12 @@ if get_option('build_vk') vk_renderer_lib = library('rtvk', 'device.h', + 'physical_resource_manager.h', 'device.c', 'init.c', - + 'physical_resource_manager.c', + 'swapchain.c', '../../../contrib/volk/volk.c', '../../../contrib/volk/volk.h', diff --git a/src/renderer/vk/physical_resource_manager.c b/src/renderer/vk/physical_resource_manager.c new file mode 100644 index 0000000..4ed6184 --- /dev/null +++ b/src/renderer/vk/physical_resource_manager.c @@ -0,0 +1 @@ +#include "physical_resource_manager.h" \ No newline at end of file diff --git a/src/renderer/vk/physical_resource_manager.h b/src/renderer/vk/physical_resource_manager.h new file mode 100644 index 0000000..d4481bf --- /dev/null +++ b/src/renderer/vk/physical_resource_manager.h @@ -0,0 +1,10 @@ +#ifndef RT_VK_PHYSICAL_RESOURCE_MANAGER_H +#define RT_VK_PHYSICAL_RESOURCE_MANAGER_H + +struct rt_vk_device; + +typedef struct { + struct rt_vk_device *dev; +} rt_vk_physical_resource_manager; + +#endif diff --git a/src/renderer/vk/swapchain.c b/src/renderer/vk/swapchain.c new file mode 100644 index 0000000..27b9ead --- /dev/null +++ b/src/renderer/vk/swapchain.c @@ -0,0 +1,176 @@ +#include +#include + +#include "device.h" + +RT_CVAR_I(r_VkPreferMailboxMode, "Prefer the mailbox present mode. [0/1] (Default: 1)", 1); +RT_CVAR_I(r_VkPreferRelaxedMode, "Prefer the relaxed FIFO present mode. [0/1] (Default: 0)", 0); +RT_CVAR_I(r_VkEnableVSync, "Enable VSync. [0/1] (Default: 0)", 0); + +rt_result CreateSwapchain(rt_vk_device *dev) { + rt_temp_arena temp = rtGetTemporaryArena(NULL, 0); + uint32_t format_count = 0; + if (vkGetPhysicalDeviceSurfaceFormatsKHR(dev->phys_device, dev->surface, &format_count, NULL) != VK_SUCCESS) { + rtReturnTemporaryArena(temp); + return RT_UNKNOWN_ERROR; + } + VkSurfaceFormatKHR *formats = RT_ARENA_PUSH_ARRAY(temp.arena, VkSurfaceFormatKHR, format_count); + if (vkGetPhysicalDeviceSurfaceFormatsKHR(dev->phys_device, dev->surface, &format_count, formats) != VK_SUCCESS) { + rtReturnTemporaryArena(temp); + return RT_UNKNOWN_ERROR; + } + + /* Pick a format */ + VkFormat surface_format = formats[0].format; + VkColorSpaceKHR color_space = formats[0].colorSpace; + for (uint32_t i = 0; i < format_count; ++i) { + if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB && formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + surface_format = formats[i].format; + color_space = formats[i].colorSpace; + break; + } + } + + /* Determine the number of images to create */ + uint32_t num_images = dev->max_frames_in_flight + 1; + VkSurfaceCapabilitiesKHR surface_capabilities; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev->phys_device, dev->surface, &surface_capabilities); + if (surface_capabilities.maxImageCount > 0 && surface_capabilities.maxImageCount < dev->max_frames_in_flight) { + dev->max_frames_in_flight = surface_capabilities.maxImageCount; + num_images = dev->max_frames_in_flight; + rtLog("VK", "Limiting number of frames in flight to maximum number of swapchain images %u.", dev->max_frames_in_flight); + } + if (surface_capabilities.maxImageCount > 0 && num_images > surface_capabilities.maxImageCount) { + num_images = surface_capabilities.maxImageCount; + } + + + /* Determine the extent */ + VkExtent2D extent = surface_capabilities.currentExtent; + if (surface_capabilities.currentExtent.width == UINT32_MAX && surface_capabilities.currentExtent.height == UINT32_MAX) { + extent = surface_capabilities.maxImageExtent; + } + + /* Determine the present mode */ + uint32_t present_mode_count = 0; + if (vkGetPhysicalDeviceSurfacePresentModesKHR(dev->phys_device, dev->surface, &present_mode_count, NULL) != VK_SUCCESS) { + rtReturnTemporaryArena(temp); + return RT_UNKNOWN_ERROR; + } + VkPresentModeKHR *present_modes = RT_ARENA_PUSH_ARRAY(temp.arena, VkPresentModeKHR, present_mode_count); + if (vkGetPhysicalDeviceSurfacePresentModesKHR(dev->phys_device, dev->surface, &present_mode_count, present_modes) != VK_SUCCESS) { + rtReturnTemporaryArena(temp); + return RT_UNKNOWN_ERROR; + } + + VkPresentModeKHR present_mode = present_modes[0]; + if (!r_VkEnableVSync.i) { + if (r_VkPreferMailboxMode.i) { + rtLog("VK", "r_VkPreferMailboxMode has no effect, if VSync is disabled."); + r_VkPreferMailboxMode.i = 0; + rtNotifyCVARChange(&r_VkPreferMailboxMode); + } + if (r_VkPreferRelaxedMode.i) { + rtLog("VK", "r_VkPreferRelaxedMode has no effect, if VSync is disabled."); + r_VkPreferRelaxedMode.i = 0; + rtNotifyCVARChange(&r_VkPreferRelaxedMode); + } + + for (uint32_t i = 0; i < present_mode_count; ++i) { + if (present_modes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) { + present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; + break; + } + } + + if (present_mode != VK_PRESENT_MODE_IMMEDIATE_KHR) { + rtLog("VK", "VSync was disabled, but the physical device/surface combination does not support VK_PRESENT_MODE_IMMEDIATE_KHR"); + r_VkEnableVSync.i = 1; + rtNotifyCVARChange(&r_VkEnableVSync); + } + } + /* NOT an else if, because we fall back to this if disabled vsync is not supported. */ + if (r_VkEnableVSync.i) { + /* Required to be supported */ + present_mode = VK_PRESENT_MODE_FIFO_KHR; + if (r_VkPreferMailboxMode.i || r_VkPreferRelaxedMode.i) { + for (uint32_t i = 0; i < present_mode_count; ++i) { + if (present_modes[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR && r_VkPreferRelaxedMode.i) { + present_mode = present_modes[i]; + break; + } + if (present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR && r_VkPreferMailboxMode.i) { + present_mode = present_modes[i]; + break; + } + } + } + } + VkSwapchainCreateInfoKHR swapchain_info = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext = NULL, + .flags = 0, + .surface = dev->surface, + .minImageCount = num_images, + .imageFormat = surface_format, + .imageColorSpace = color_space, + .imageExtent = extent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .pQueueFamilyIndices = NULL, + .queueFamilyIndexCount = 0, + .preTransform = surface_capabilities.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = present_mode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + }; + + if (dev->graphics_family != dev->present_family) { + uint32_t queue_families[2]; + queue_families[0] = dev->graphics_family; + queue_families[1] = dev->present_family; + swapchain_info.pQueueFamilyIndices = &queue_families[0]; + swapchain_info.queueFamilyIndexCount = 2; + } + + if (vkCreateSwapchainKHR(dev->device, &swapchain_info, dev->alloc_cb, &dev->swapchain) != VK_SUCCESS) { + rtReturnTemporaryArena(temp); + return RT_UNKNOWN_ERROR; + } + + vkGetSwapchainImagesKHR(dev->device, dev->swapchain, &dev->swapchain_image_count, NULL); + RT_ASSERT(dev->swapchain_image_count <= RT_ARRAY_COUNT(dev->swapchain_images), "Unexpectedly high number of swapchain images."); + vkGetSwapchainImagesKHR(dev->device, dev->swapchain, &dev->swapchain_image_count, &dev->swapchain_images[0]); + for (uint32_t i = 0; i < dev->swapchain_image_count; ++i) { + VkImageViewCreateInfo view_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = dev->swapchain_images[i], + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = surface_format, + .components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }, + .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseArrayLayer = 0,.baseMipLevel = 0, + .layerCount = 1, .levelCount = 1}, + }; + vkCreateImageView(dev->device, &view_info, dev->alloc_cb, &dev->swapchain_image_views[i]); + } + + return RT_SUCCESS; +} + +void DestroySwapchain(rt_vk_device *dev) { + vkDestroySwapchainKHR(dev->device, dev->swapchain, dev->alloc_cb); + for (uint32_t i = 0; i < dev->swapchain_image_count; ++i) { + vkDestroyImageView(dev->device, dev->swapchain_image_views[i], dev->alloc_cb); + } +} + +rt_result RecreateSwapchain(rt_vk_device *dev) { + /* TODO(Kevin): Do this in a more perfomant way involving oldSwapchain */ + vkDeviceWaitIdle(dev->device); + DestroySwapchain(dev); + return CreateSwapchain(dev); +} diff --git a/vk_layer_settings.txt b/vk_layer_settings.txt index 938e454..9350562 100644 --- a/vk_layer_settings.txt +++ b/vk_layer_settings.txt @@ -16,7 +16,7 @@ khronos_validation.validate_sync = true khronos_validation.thread_safety = true # Specifies what action is to be taken when a layer reports information -khronos_validation.debug_action = VK_DBG_LAYER_ACTION_LOG_MSG +#khronos_validation.debug_action = VK_DBG_LAYER_ACTION_LOG_MSG # Comma-delineated list of options specifying the types of messages to be reported khronos_validation.report_flags = debug,error,perf,info,warn