diff options
| author | Clement Sibille <clements@lisible.xyz> | 2024-12-11 06:57:28 +0100 | 
|---|---|---|
| committer | Clement Sibille <clements@lisible.xyz> | 2024-12-11 06:57:28 +0100 | 
| commit | 20c7d98ee852533bf784f58dd22c688a8ed33c15 (patch) | |
| tree | 8c67793cfa26b6187f9a556500f7405e2c72c7b4 | |
| parent | 9747d8d77352e33ec6117c34d679ec6d6b8a361a (diff) | |
Add swapchain creation
| -rw-r--r-- | src/main.c | 227 | 
1 files changed, 222 insertions, 5 deletions
@@ -1,6 +1,7 @@  #include <SDL3/SDL.h>  #include <SDL3/SDL_vulkan.h>  #include <assert.h> +#include <stdint.h>  #include <stdio.h>  #include <stdlib.h>  #include <vulkan/vulkan.h> @@ -24,6 +25,7 @@ struct vulkan_renderer {    VkDebugUtilsMessengerEXT debug_messenger;    VkSurfaceKHR surface;    VkQueue present_queue; +  VkSwapchainKHR swapchain;    bool enable_validation_layers;  }; @@ -253,14 +255,104 @@ struct queue_family_indices find_queue_families(VkPhysicalDevice device,    return indices;  } -bool is_device_suitable(VkPhysicalDevice device, VkSurfaceKHR surface) { +bool extension_with_name_is_in_array(VkExtensionProperties *array, +                                     uint32_t length, +                                     const char *extension_name) { +  for (uint32_t index = 0; index < length; index++) { +    if (strcmp(array[index].extensionName, extension_name) == 0) { +      return true; +    } +  } + +  return false; +} + +bool device_supports_requested_extensions(VkPhysicalDevice device, +                                          const char **required_extensions, +                                          uint32_t required_extension_count) { +  uint32_t supported_extension_count; +  vkEnumerateDeviceExtensionProperties(device, NULL, &supported_extension_count, +                                       NULL); + +  VkExtensionProperties supported_extensions[MAX_EXTENSION_COUNT] = {0}; +  vkEnumerateDeviceExtensionProperties(device, NULL, &supported_extension_count, +                                       supported_extensions); +  for (uint32_t required_extension_index = 0; +       required_extension_index < required_extension_count; +       required_extension_index++) { +    if (!extension_with_name_is_in_array( +            supported_extensions, supported_extension_count, +            required_extensions[required_extension_index])) { +      return false; +    } +  } + +  return true; +} + +#define MAX_SWAPCHAIN_SURFACE_FORMAT_COUNT 10 +#define MAX_SWAPCHAIN_SURFACE_PRESENT_MODE_COUNT 10 + +struct swapchain_support_details { +  VkSurfaceCapabilitiesKHR capabilities; +  VkSurfaceFormatKHR formats[MAX_SWAPCHAIN_SURFACE_FORMAT_COUNT]; +  VkPresentModeKHR present_modes[MAX_SWAPCHAIN_SURFACE_PRESENT_MODE_COUNT]; +  uint32_t format_count; +  uint32_t present_mode_count; +}; +struct swapchain_support_details +query_swapchain_support(VkPhysicalDevice device, VkSurfaceKHR surface) { +  struct swapchain_support_details details; +  vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, +                                            &details.capabilities); + +  vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &details.format_count, +                                       NULL); +  if (details.format_count != 0) { +    assert(details.format_count < MAX_SWAPCHAIN_SURFACE_FORMAT_COUNT); +    vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &details.format_count, +                                         details.formats); +  } + +  vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, +                                            &details.present_mode_count, NULL); +  if (details.present_mode_count != 0) { +    assert(details.present_mode_count < +           MAX_SWAPCHAIN_SURFACE_PRESENT_MODE_COUNT); +    vkGetPhysicalDeviceSurfacePresentModesKHR( +        device, surface, &details.present_mode_count, details.present_modes); +  } + +  return details; +} + +bool is_device_suitable(VkPhysicalDevice device, VkSurfaceKHR surface, +                        const char **required_extensions, +                        uint32_t required_extension_count) {    struct queue_family_indices queue_family_indices =        find_queue_families(device, surface); -  return queue_family_indices_is_complete(&queue_family_indices); +  bool extensions_supported = device_supports_requested_extensions( +      device, required_extensions, required_extension_count); + +  bool swapchain_adequate = false; +  if (extensions_supported) { +    struct swapchain_support_details swapchain_support_details = +        query_swapchain_support(device, surface); +    swapchain_adequate = swapchain_support_details.format_count != 0 && +                         swapchain_support_details.present_mode_count != 0; +  } + +  return queue_family_indices_is_complete(&queue_family_indices) && +         extensions_supported && swapchain_adequate;  } +static const char *required_extensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; +static uint32_t required_extension_count = +    sizeof(required_extensions) / sizeof(const char *); +  bool vulkan_renderer_pick_physical_device(struct vulkan_renderer *renderer) { +    VkPhysicalDevice physical_device = VK_NULL_HANDLE;    uint32_t device_count = 0;    vkEnumeratePhysicalDevices(renderer->instance, &device_count, NULL); @@ -274,7 +366,8 @@ bool vulkan_renderer_pick_physical_device(struct vulkan_renderer *renderer) {    vkEnumeratePhysicalDevices(renderer->instance, &device_count, devices);    for (uint32_t device_index = 0; device_index < device_count; device_index++) { -    if (is_device_suitable(devices[device_index], renderer->surface)) { +    if (is_device_suitable(devices[device_index], renderer->surface, +                           required_extensions, required_extension_count)) {        physical_device = devices[device_index];        break;      } @@ -305,7 +398,6 @@ bool is_in_array(uint32_t *array, int length, uint32_t value) {  bool vulkan_renderer_create_logical_device(struct vulkan_renderer *renderer) {    struct queue_family_indices indices =        find_queue_families(renderer->physical_device, renderer->surface); -    VkDeviceQueueCreateInfo queue_create_infos[MAX_QUEUE_FAMILY_COUNT] = {0};    int queue_create_info_count = 0; @@ -343,7 +435,8 @@ bool vulkan_renderer_create_logical_device(struct vulkan_renderer *renderer) {                           .pQueueCreateInfos = queue_create_infos,                           .queueCreateInfoCount = queue_create_info_count,                           .pEnabledFeatures = &device_features, -                         .enabledExtensionCount = 0, +                         .ppEnabledExtensionNames = required_extensions, +                         .enabledExtensionCount = required_extension_count,                           // TODO maybe add the validation layers                           // Not required according to vulkan-tutorial, but might                           // be good for compatibility @@ -363,6 +456,114 @@ bool vulkan_renderer_create_logical_device(struct vulkan_renderer *renderer) {    return true;  } +VkSurfaceFormatKHR +choose_swapchain_surface_format(VkSurfaceFormatKHR *available_formats, +                                uint32_t available_format_count) { +  for (uint32_t available_format_index = 0; +       available_format_index < available_format_count; +       available_format_index++) { +    VkSurfaceFormatKHR available_format = +        available_formats[available_format_index]; +    if (available_format.format == VK_FORMAT_B8G8R8A8_SRGB && +        available_format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { +      return available_format; +    } +  } + +  return available_formats[0]; +} + +VkPresentModeKHR +choose_swapchain_present_mode(VkPresentModeKHR *available_present_modes, +                              uint32_t available_present_mode_count) { +  for (uint32_t available_present_mode_index = 0; +       available_present_mode_index < available_present_mode_count; +       available_present_mode_index++) { +    VkPresentModeKHR present_mode = +        available_present_modes[available_present_mode_index]; +    if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR) { +      return present_mode; +    } +  } +  return VK_PRESENT_MODE_FIFO_KHR; +} + +uint32_t clamp_uint32(uint32_t min, uint32_t max, uint32_t value) { +  return value < min ? min : value > max ? max : value; +} + +VkExtent2D choose_swapchain_extent(const VkSurfaceCapabilitiesKHR *capabilities, +                                   int width, int height) { +  if (capabilities->currentExtent.width != UINT32_MAX) { +    return capabilities->currentExtent; +  } else { +    VkExtent2D actual_extent = {width, height}; +    actual_extent.width = +        clamp_uint32(capabilities->minImageExtent.width, +                     capabilities->maxImageExtent.width, actual_extent.width); +    actual_extent.height = +        clamp_uint32(capabilities->minImageExtent.height, +                     capabilities->maxImageExtent.height, actual_extent.height); +    return actual_extent; +  } +} + +bool vulkan_renderer_create_swapchain(struct vulkan_renderer *renderer, +                                      int window_width_px, +                                      int window_height_px) { +  struct swapchain_support_details swapchain_support = +      query_swapchain_support(renderer->physical_device, renderer->surface); + +  VkSurfaceFormatKHR surface_format = choose_swapchain_surface_format( +      swapchain_support.formats, swapchain_support.format_count); +  VkPresentModeKHR present_mode = choose_swapchain_present_mode( +      swapchain_support.present_modes, swapchain_support.present_mode_count); +  VkExtent2D extent = choose_swapchain_extent( +      &swapchain_support.capabilities, window_width_px, window_height_px); +  uint32_t image_count = swapchain_support.capabilities.minImageCount + 1; +  if (swapchain_support.capabilities.maxImageCount > 0 && +      image_count > swapchain_support.capabilities.maxImageCount) { +    image_count = swapchain_support.capabilities.maxImageCount; +  } + +  VkSwapchainCreateInfoKHR create_info = {0}; +  create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; +  create_info.surface = renderer->surface; +  create_info.minImageCount = image_count; +  create_info.imageFormat = surface_format.format; +  create_info.imageColorSpace = surface_format.colorSpace; +  create_info.imageExtent = extent; +  create_info.imageArrayLayers = 1; +  create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + +  struct queue_family_indices indices = +      find_queue_families(renderer->physical_device, renderer->surface); +  uint32_t queue_family_indices[] = {indices.graphics_family, +                                     indices.present_family}; + +  if (indices.graphics_family != indices.present_family) { +    create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; +    create_info.queueFamilyIndexCount = 2; +    create_info.pQueueFamilyIndices = queue_family_indices; +  } else { +    create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; +  } + +  create_info.preTransform = swapchain_support.capabilities.currentTransform; +  create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; +  create_info.presentMode = present_mode; +  create_info.clipped = VK_TRUE; +  create_info.oldSwapchain = VK_NULL_HANDLE; + +  if (vkCreateSwapchainKHR(renderer->device, &create_info, NULL, +                           &renderer->swapchain) != VK_SUCCESS) { +    LOG("Couldn't create swapchain"); +    return false; +  } + +  return true; +} +  bool vulkan_renderer_init(struct vulkan_renderer *renderer,                            SDL_Window *window) {    assert(renderer); @@ -399,8 +600,23 @@ bool vulkan_renderer_init(struct vulkan_renderer *renderer,      goto destroy_surface;    } +  int window_width_px; +  int window_height_px; +  if (!SDL_GetWindowSizeInPixels(window, &window_width_px, &window_height_px)) { +    LOG("Couldn't get window size"); +    goto destroy_surface; +  } + +  if (!vulkan_renderer_create_swapchain(renderer, window_width_px, +                                        window_height_px)) { +    LOG("Couldn't create swapchain"); +    goto destroy_logical_device; +  } +    return true; +destroy_logical_device: +  vkDestroyDevice(renderer->device, NULL);  destroy_surface:    vkDestroySurfaceKHR(renderer->instance, renderer->surface, NULL);  destroy_instance: @@ -414,6 +630,7 @@ err:  }  void vulkan_renderer_deinit(struct vulkan_renderer *renderer) { +  vkDestroySwapchainKHR(renderer->device, renderer->swapchain, NULL);    vkDestroyDevice(renderer->device, NULL);    vkDestroySurfaceKHR(renderer->instance, renderer->surface, NULL);    if (renderer->enable_validation_layers) {  | 
