diff options
| -rw-r--r-- | meson.build | 23 | ||||
| -rw-r--r-- | src/main.c | 10 | ||||
| -rw-r--r-- | src/platform.h | 26 | ||||
| -rw-r--r-- | src/platform_sdl.c | 17 | ||||
| -rw-r--r-- | src/renderer.c | 934 | ||||
| -rw-r--r-- | src/renderer.h | 27 | 
6 files changed, 1035 insertions, 2 deletions
diff --git a/meson.build b/meson.build index e3997cf..6aa0ceb 100644 --- a/meson.build +++ b/meson.build @@ -1,13 +1,32 @@  project('visible-gltf', 'c', default_options: ['warning_level=3', 'c_std=c23']) +build_type = get_option('buildtype') +  sdl3_dep = dependency('sdl3') +if host_machine.system() == 'darwin' +moltenvk_library_path = '/Users/clements/dev/VulkanSDK/1.4.309.0/macOS/lib' +moltenvk_include_path = '/Users/clements/dev/VulkanSDK/1.4.309.0/macOS/include' +vulkan_dep = declare_dependency( +  link_args: ['-L' + moltenvk_library_path, '-lvulkan'], +  include_directories: include_directories(moltenvk_include_path) +) +else  +vulkan_dep = dependency('vulkan') +endif + +vgltf_c_args = [] +if build_type == 'debug' +  vgltf_c_args += '-DVGLTF_DEBUG' +endif  executable(    'vgltf',    [      'src/main.c',      'src/log.c', -    'src/platform_sdl.c' +    'src/platform_sdl.c', +    'src/renderer.c',    ], -  dependencies: [sdl3_dep] +  c_args: vgltf_c_args, +  dependencies: [sdl3_dep, vulkan_dep],  ) @@ -1,5 +1,6 @@  #include "log.h"  #include "platform.h" +#include "renderer.h"  int main(void) {    struct vgltf_platform platform = {}; @@ -8,6 +9,12 @@ int main(void) {      goto err;    } +  struct vgltf_renderer renderer = {}; +  if (!vgltf_renderer_init(&renderer, &platform)) { +    VGLTF_LOG_ERR("Couldn't initialize the renderer"); +    goto deinit_platform; +  } +    while (true) {      struct vgltf_event event;      while (vgltf_platform_poll_event(&platform, &event)) { @@ -20,8 +27,11 @@ int main(void) {    }  out_main_loop: +  vgltf_renderer_deinit(&renderer);    vgltf_platform_deinit(&platform);    return 0; +deinit_platform: +  vgltf_platform_deinit(&platform);  err:    return 1;  } diff --git a/src/platform.h b/src/platform.h index baf0165..2f8bc19 100644 --- a/src/platform.h +++ b/src/platform.h @@ -1,6 +1,16 @@  #ifndef VGLTF_PLATFORM_H  #define VGLTF_PLATFORM_H +#include "log.h" +#include <stdint.h> +#include <stdlib.h> + +#define VGLTF_PANIC(...)                                                       \ +  do {                                                                         \ +    VGLTF_LOG_ERR("panic: " __VA_ARGS__);                                      \ +    exit(1);                                                                   \ +  } while (0) +  enum vgltf_event_type {    VGLTF_EVENT_QUIT,    VGLTF_EVENT_KEY_DOWN, @@ -49,11 +59,27 @@ struct vgltf_event {    };  }; +struct vgltf_window_size { +  int width; +  int height; +}; +  struct vgltf_platform;  bool vgltf_platform_init(struct vgltf_platform *platform);  void vgltf_platform_deinit(struct vgltf_platform *platform);  bool vgltf_platform_poll_event(struct vgltf_platform *platform,                                 struct vgltf_event *event); +bool vgltf_platform_get_window_size(struct vgltf_platform *platform, +                                    struct vgltf_window_size *window_size); + +// Vulkan specifics +#include "vulkan/vulkan_core.h" +char const *const * +vgltf_platform_get_vulkan_instance_extensions(struct vgltf_platform *platform, +                                              uint32_t *count); +bool vgltf_platform_create_vulkan_surface(struct vgltf_platform *platform, +                                          VkInstance instance, +                                          VkSurfaceKHR *surface);  #include "platform_sdl.h" diff --git a/src/platform_sdl.c b/src/platform_sdl.c index 27ed4c3..281ccb5 100644 --- a/src/platform_sdl.c +++ b/src/platform_sdl.c @@ -1,6 +1,7 @@  #include "log.h"  #include "platform.h"  #include "platform_sdl.h" +#include <SDL3/SDL_vulkan.h>  bool vgltf_platform_init(struct vgltf_platform *platform) {    if (!SDL_Init(SDL_INIT_VIDEO)) { @@ -111,3 +112,19 @@ bool vgltf_platform_poll_event(struct vgltf_platform *platform,    }    return pending_events;  } +bool vgltf_platform_get_window_size(struct vgltf_platform *platform, +                                    struct vgltf_window_size *window_size) { +  return SDL_GetWindowSize(platform->window, &window_size->width, +                           &window_size->height); +} +char const *const * +vgltf_platform_get_vulkan_instance_extensions(struct vgltf_platform *platform, +                                              uint32_t *count) { +  (void)platform; +  return SDL_Vulkan_GetInstanceExtensions(count); +} +bool vgltf_platform_create_vulkan_surface(struct vgltf_platform *platform, +                                          VkInstance instance, +                                          VkSurfaceKHR *surface) { +  return SDL_Vulkan_CreateSurface(platform->window, instance, nullptr, surface); +} diff --git a/src/renderer.c b/src/renderer.c new file mode 100644 index 0000000..cacd3bd --- /dev/null +++ b/src/renderer.c @@ -0,0 +1,934 @@ +#include "log.h" +#include "renderer.h" +#include "src/platform.h" +#include "vulkan/vulkan_core.h" +#include <assert.h> + +static const char *VALIDATION_LAYERS[] = {"VK_LAYER_KHRONOS_validation"}; +static constexpr int VALIDATION_LAYER_COUNT = +    sizeof(VALIDATION_LAYERS) / sizeof(VALIDATION_LAYERS[0]); + +#ifdef VGLTF_DEBUG +static constexpr bool enable_validation_layers = true; +#else +static constexpr bool enable_validation_layers = false; +#endif + +static VKAPI_ATTR VkBool32 VKAPI_CALL +debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, +               VkDebugUtilsMessageTypeFlagBitsEXT message_type, +               const VkDebugUtilsMessengerCallbackDataEXT *callback_data, +               void *user_data) { +  (void)message_severity; +  (void)message_type; +  (void)user_data; +  VGLTF_LOG_DBG("validation layer: %s", callback_data->pMessage); +  return VK_FALSE; +} + +static constexpr int REQUIRED_INSTANCE_EXTENSIONS_ARRAY_CAPACITY = 10; +struct required_instance_extensions { +  const char *extensions[REQUIRED_INSTANCE_EXTENSIONS_ARRAY_CAPACITY]; +  uint32_t count; +}; +void required_instance_extensions_push( +    struct required_instance_extensions *required_instance_extensions, +    const char *required_instance_extension) { +  if (required_instance_extensions->count == +      REQUIRED_INSTANCE_EXTENSIONS_ARRAY_CAPACITY) { +    VGLTF_PANIC("required instance extensions array is full"); +  } +  required_instance_extensions +      ->extensions[required_instance_extensions->count++] = +      required_instance_extension; +} + +static constexpr int SUPPORTED_INSTANCE_EXTENSIONS_ARRAY_CAPACITY = 128; +struct supported_instance_extensions { +  VkExtensionProperties +      properties[SUPPORTED_INSTANCE_EXTENSIONS_ARRAY_CAPACITY]; +  uint32_t count; +}; +bool supported_instance_extensions_init( +    struct supported_instance_extensions *supported_instance_extensions) { +  if (vkEnumerateInstanceExtensionProperties( +          nullptr, &supported_instance_extensions->count, nullptr) != +      VK_SUCCESS) { +    goto err; +  } + +  if (supported_instance_extensions->count > +      SUPPORTED_INSTANCE_EXTENSIONS_ARRAY_CAPACITY) { +    VGLTF_LOG_ERR("supported instance extensions array cannot fit all the " +                  "VkExtensionProperties"); +    goto err; +  } + +  if (vkEnumerateInstanceExtensionProperties( +          nullptr, &supported_instance_extensions->count, +          supported_instance_extensions->properties) != VK_SUCCESS) { +    goto err; +  } +  return true; +err: +  return false; +} +void supported_instance_extensions_debug_print( +    const struct supported_instance_extensions *supported_instance_extensions) { +  VGLTF_LOG_DBG("Supported instance extensions:"); +  for (uint32_t i = 0; i < supported_instance_extensions->count; i++) { +    VGLTF_LOG_DBG("\t- %s", +                  supported_instance_extensions->properties[i].extensionName); +  } +} +bool supported_instance_extensions_includes( +    const struct supported_instance_extensions *supported_instance_extensions, +    const char *extension_name) { +  for (uint32_t supported_instance_extension_index = 0; +       supported_instance_extension_index < +       supported_instance_extensions->count; +       supported_instance_extension_index++) { +    const VkExtensionProperties *extension_properties = +        &supported_instance_extensions +             ->properties[supported_instance_extension_index]; +    if (strcmp(extension_properties->extensionName, extension_name) == 0) { +      return true; +    } +  } + +  return false; +} + +static constexpr uint32_t SUPPORTED_VALIDATION_LAYERS_ARRAY_CAPACITY = 64; +struct supported_validation_layers { +  VkLayerProperties properties[SUPPORTED_VALIDATION_LAYERS_ARRAY_CAPACITY]; +  uint32_t count; +}; +bool supported_validation_layers_init( +    struct supported_validation_layers *supported_validation_layers) { +  if (vkEnumerateInstanceLayerProperties(&supported_validation_layers->count, +                                         nullptr) != VK_SUCCESS) { +    goto err; +  } + +  if (supported_validation_layers->count > +      SUPPORTED_VALIDATION_LAYERS_ARRAY_CAPACITY) { +    VGLTF_LOG_ERR("supported validation layers array cannot fit all the " +                  "VkLayerProperties"); +    goto err; +  } + +  if (vkEnumerateInstanceLayerProperties( +          &supported_validation_layers->count, +          supported_validation_layers->properties) != VK_SUCCESS) { +    goto err; +  } + +  return true; +err: +  return false; +} + +static bool are_validation_layer_supported() { +  struct supported_validation_layers supported_layers = {}; +  if (!supported_validation_layers_init(&supported_layers)) { +    goto err; +  } + +  for (int requested_layer_index = 0; +       requested_layer_index < VALIDATION_LAYER_COUNT; +       requested_layer_index++) { +    const char *requested_layer_name = VALIDATION_LAYERS[requested_layer_index]; +    bool requested_layer_found = false; +    for (uint32_t supported_layer_index = 0; +         supported_layer_index < supported_layers.count; +         supported_layer_index++) { +      VkLayerProperties *supported_layer = +          &supported_layers.properties[supported_layer_index]; +      if (strcmp(requested_layer_name, supported_layer->layerName) == 0) { +        requested_layer_found = true; +        break; +      } +    } + +    if (!requested_layer_found) { +      goto err; +    } +  } + +  return true; +err: +  return false; +} + +static bool fetch_required_instance_extensions( +    struct required_instance_extensions *required_extensions, +    struct vgltf_platform *platform) { +  struct supported_instance_extensions supported_extensions = {}; +  if (!supported_instance_extensions_init(&supported_extensions)) { +    VGLTF_LOG_ERR( +        "Couldn't fetch supported instance extensions details (OOM?)"); +    goto err; +  } +  supported_instance_extensions_debug_print(&supported_extensions); + +  uint32_t platform_required_extension_count = 0; +  const char *const *platform_required_extensions = +      vgltf_platform_get_vulkan_instance_extensions( +          platform, &platform_required_extension_count); +  for (uint32_t platform_required_extension_index = 0; +       platform_required_extension_index < platform_required_extension_count; +       platform_required_extension_index++) { +    required_instance_extensions_push( +        required_extensions, +        platform_required_extensions[platform_required_extension_index]); +  } +  required_instance_extensions_push( +      required_extensions, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + +  if (enable_validation_layers) { +    required_instance_extensions_push(required_extensions, +                                      VK_EXT_DEBUG_UTILS_EXTENSION_NAME); +  } + +  bool all_extensions_supported = true; +  for (uint32_t required_extension_index = 0; +       required_extension_index < required_extensions->count; +       required_extension_index++) { +    const char *required_extension_name = +        required_extensions->extensions[required_extension_index]; +    if (!supported_instance_extensions_includes(&supported_extensions, +                                                required_extension_name)) { +      VGLTF_LOG_ERR("Unsupported instance extension: %s", +                    required_extension_name); +      all_extensions_supported = false; +    } +  } + +  if (!all_extensions_supported) { +    VGLTF_LOG_ERR("Some required extensions are unsupported."); +    goto err; +  } + +  return true; +err: +  return false; +} + +static void populate_debug_messenger_create_info( +    VkDebugUtilsMessengerCreateInfoEXT *create_info) { +  *create_info = (VkDebugUtilsMessengerCreateInfoEXT){}; +  create_info->sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; +  create_info->messageSeverity = +      VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | +      VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | +      VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; +  create_info->messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | +                             VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | +                             VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; +  create_info->pfnUserCallback = debug_callback; +} + +static bool vgltf_renderer_create_instance(struct vgltf_renderer *renderer, +                                           struct vgltf_platform *platform) { +  VGLTF_LOG_INFO("Creating vulkan instance..."); +  if (enable_validation_layers && !are_validation_layer_supported()) { +    VGLTF_LOG_ERR("Requested validation layers aren't supported"); +    goto err; +  } + +  VkApplicationInfo application_info = { +      .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, +      .pApplicationName = "Visible GLTF", +      .applicationVersion = VK_MAKE_VERSION(0, 1, 0), +      .pEngineName = "No Engine", +      .engineVersion = VK_MAKE_VERSION(1, 0, 0), +      .apiVersion = VK_API_VERSION_1_2}; + +  struct required_instance_extensions required_extensions = {}; +  fetch_required_instance_extensions(&required_extensions, platform); + +  VkInstanceCreateInfo create_info = { +      .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, +      .pApplicationInfo = &application_info, +      .enabledExtensionCount = required_extensions.count, +      .ppEnabledExtensionNames = required_extensions.extensions, +      .flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR}; + +  VkDebugUtilsMessengerCreateInfoEXT debug_create_info; +  if (enable_validation_layers) { +    create_info.enabledLayerCount = VALIDATION_LAYER_COUNT; +    create_info.ppEnabledLayerNames = VALIDATION_LAYERS; +    populate_debug_messenger_create_info(&debug_create_info); +    create_info.pNext = &debug_create_info; +  } + +  if (vkCreateInstance(&create_info, nullptr, &renderer->instance) != +      VK_SUCCESS) { +    VGLTF_LOG_ERR("Failed to create VkInstance"); +    goto err; +  } + +  return true; +err: +  return false; +} + +static VkResult create_debug_utils_messenger_ext( +    VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT *create_info, +    const VkAllocationCallbacks *allocator, +    VkDebugUtilsMessengerEXT *debug_messenger) { +  auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( +      instance, "vkCreateDebugUtilsMessengerEXT"); +  if (func != nullptr) { +    return func(instance, create_info, allocator, debug_messenger); +  } + +  return VK_ERROR_EXTENSION_NOT_PRESENT; +} + +static void +destroy_debug_utils_messenger_ext(VkInstance instance, +                                  VkDebugUtilsMessengerEXT debug_messenger, +                                  const VkAllocationCallbacks *allocator) { +  auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( +      instance, "vkDestroyDebugUtilsMessengerEXT"); +  if (func != nullptr) { +    func(instance, debug_messenger, allocator); +  } +} + +static void +vgltf_renderer_setup_debug_messenger(struct vgltf_renderer *renderer) { +  if (!enable_validation_layers) +    return; +  VkDebugUtilsMessengerCreateInfoEXT create_info; +  populate_debug_messenger_create_info(&create_info); +  create_debug_utils_messenger_ext(renderer->instance, &create_info, nullptr, +                                   &renderer->debug_messenger); +} + +static constexpr int AVAILABLE_PHYSICAL_DEVICE_ARRAY_CAPACITY = 128; +struct available_physical_devices { +  VkPhysicalDevice devices[AVAILABLE_PHYSICAL_DEVICE_ARRAY_CAPACITY]; +  uint32_t count; +}; +static bool +available_physical_devices_init(VkInstance instance, +                                struct available_physical_devices *devices) { + +  if (vkEnumeratePhysicalDevices(instance, &devices->count, nullptr) != +      VK_SUCCESS) { +    VGLTF_LOG_ERR("Couldn't enumerate physical devices"); +    goto err; +  } + +  if (devices->count == 0) { +    VGLTF_LOG_ERR("Failed to find any GPU with Vulkan support"); +    goto err; +  } + +  if (devices->count > AVAILABLE_PHYSICAL_DEVICE_ARRAY_CAPACITY) { +    VGLTF_LOG_ERR("available physical devices array cannot fit all available " +                  "physical devices"); +    goto err; +  } + +  if (vkEnumeratePhysicalDevices(instance, &devices->count, devices->devices) != +      VK_SUCCESS) { +    VGLTF_LOG_ERR("Couldn't enumerate physical devices"); +    goto err; +  } + +  return true; +err: +  return false; +} + +struct queue_family_indices { +  uint32_t graphics_family; +  uint32_t present_family; +  bool has_graphics_family; +  bool has_present_family; +}; +bool queue_family_indices_is_complete( +    const struct queue_family_indices *indices) { +  return indices->has_graphics_family && indices->has_present_family; +} +bool queue_family_indices_for_device(struct queue_family_indices *indices, +                                     VkPhysicalDevice device, +                                     VkSurfaceKHR surface) { +  static constexpr uint32_t QUEUE_FAMILY_PROPERTIES_ARRAY_CAPACITY = 64; +  uint32_t queue_family_count = 0; +  vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, +                                           nullptr); + +  if (queue_family_count > QUEUE_FAMILY_PROPERTIES_ARRAY_CAPACITY) { +    VGLTF_LOG_ERR( +        "Queue family properties array cannot fit all queue family properties"); +    goto err; +  } + +  VkQueueFamilyProperties +      queue_family_properties[QUEUE_FAMILY_PROPERTIES_ARRAY_CAPACITY] = {}; +  vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, +                                           queue_family_properties); + +  for (uint32_t queue_family_index = 0; queue_family_index < queue_family_count; +       queue_family_index++) { +    VkQueueFamilyProperties *queue_family = +        &queue_family_properties[queue_family_index]; + +    VkBool32 present_support; +    vkGetPhysicalDeviceSurfaceSupportKHR(device, queue_family_index, surface, +                                         &present_support); + +    if (queue_family->queueFlags & VK_QUEUE_GRAPHICS_BIT) { +      indices->graphics_family = queue_family_index; +      indices->has_graphics_family = true; +    } + +    if (present_support) { +      indices->present_family = queue_family_index; +      indices->has_present_family = true; +    } + +    if (queue_family_indices_is_complete(indices)) { +      break; +    } +  } + +  return true; +err: +  return false; +} + +static bool is_in_array(uint32_t *array, int length, uint32_t value) { +  for (int i = 0; i < length; i++) { +    if (array[i] == value) { +      return true; +    } +  } + +  return false; +} + +static constexpr uint32_t SUPPORTED_EXTENSIONS_ARRAY_CAPACITY = 128; +struct supported_extensions { +  VkExtensionProperties properties[SUPPORTED_EXTENSIONS_ARRAY_CAPACITY]; +  uint32_t count; +}; +bool supported_extensions_init( +    struct supported_extensions *supported_extensions, +    VkPhysicalDevice device) { +  if (vkEnumerateDeviceExtensionProperties(device, nullptr, +                                           &supported_extensions->count, +                                           nullptr) != VK_SUCCESS) { +    goto err; +  } + +  if (supported_extensions->count > SUPPORTED_EXTENSIONS_ARRAY_CAPACITY) { +    VGLTF_LOG_ERR( +        "supported extensions aarray cannot fit all the VkExtensionProperties"); +    goto err; +  } + +  if (vkEnumerateDeviceExtensionProperties( +          device, nullptr, &supported_extensions->count, +          supported_extensions->properties) != VK_SUCCESS) { +    goto err; +  } + +  return true; +err: +  return false; +} + +static bool supported_extensions_includes_extension( +    struct supported_extensions *supported_extensions, +    const char *extension_name) { +  for (uint32_t supported_extension_index = 0; +       supported_extension_index < supported_extensions->count; +       supported_extension_index++) { +    if (strcmp(supported_extensions->properties[supported_extension_index] +                   .extensionName, +               extension_name) == 0) { +      return true; +    } +  } +  return false; +} + +static const char *DEVICE_EXTENSIONS[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME, +                                          "VK_KHR_portability_subset"}; +static constexpr int DEVICE_EXTENSION_COUNT = +    sizeof(DEVICE_EXTENSIONS) / sizeof(DEVICE_EXTENSIONS[0]); +static bool are_device_extensions_supported(VkPhysicalDevice device) { +  struct supported_extensions supported_extensions = {}; +  if (!supported_extensions_init(&supported_extensions, device)) { +    goto err; +  } + +  for (uint32_t required_extension_index = 0; +       required_extension_index < DEVICE_EXTENSION_COUNT; +       required_extension_index++) { +    if (!supported_extensions_includes_extension( +            &supported_extensions, +            DEVICE_EXTENSIONS[required_extension_index])) { +      VGLTF_LOG_DBG("Unsupported: %s", +                    DEVICE_EXTENSIONS[required_extension_index]); +      goto err; +    } +  } + +  return true; + +err: +  return false; +} + +static constexpr int SWAPCHAIN_SUPPORT_DETAILS_MAX_SURFACE_FORMAT_COUNT = 256; +static constexpr int SWAPCHAIN_SUPPORT_DETAILS_MAX_PRESENT_MODE_COUNT = 256; +struct swapchain_support_details { +  VkSurfaceCapabilitiesKHR capabilities; +  VkSurfaceFormatKHR +      formats[SWAPCHAIN_SUPPORT_DETAILS_MAX_SURFACE_FORMAT_COUNT]; +  VkPresentModeKHR +      present_modes[SWAPCHAIN_SUPPORT_DETAILS_MAX_PRESENT_MODE_COUNT]; +  uint32_t format_count; +  uint32_t present_mode_count; +}; +bool swapchain_support_details_query_from_device( +    struct swapchain_support_details *swapchain_support_details, +    VkPhysicalDevice device, VkSurfaceKHR surface) { +  if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR( +          device, surface, &swapchain_support_details->capabilities) != +      VK_SUCCESS) { +    goto err; +  } + +  if (vkGetPhysicalDeviceSurfaceFormatsKHR( +          device, surface, &swapchain_support_details->format_count, nullptr) != +      VK_SUCCESS) { +    goto err; +  } + +  if (swapchain_support_details->format_count != 0 && +      swapchain_support_details->format_count < +          SWAPCHAIN_SUPPORT_DETAILS_MAX_SURFACE_FORMAT_COUNT) { +    if (vkGetPhysicalDeviceSurfaceFormatsKHR( +            device, surface, &swapchain_support_details->format_count, +            swapchain_support_details->formats) != VK_SUCCESS) { +      goto err; +    } +  } + +  if (vkGetPhysicalDeviceSurfacePresentModesKHR( +          device, surface, &swapchain_support_details->present_mode_count, +          nullptr) != VK_SUCCESS) { +    goto err; +  } + +  if (swapchain_support_details->present_mode_count != 0 && +      swapchain_support_details->present_mode_count < +          SWAPCHAIN_SUPPORT_DETAILS_MAX_PRESENT_MODE_COUNT) { +    if (vkGetPhysicalDeviceSurfacePresentModesKHR( +            device, surface, &swapchain_support_details->present_mode_count, +            swapchain_support_details->present_modes) != VK_SUCCESS) { +      goto err; +    } +  } + +  return true; +err: +  return false; +} + +static bool is_physical_device_suitable(VkPhysicalDevice device, +                                        VkSurfaceKHR surface) { +  struct queue_family_indices indices = {}; +  queue_family_indices_for_device(&indices, device, surface); + +  VGLTF_LOG_DBG("Checking for physical device extension support"); +  bool extensions_supported = are_device_extensions_supported(device); +  VGLTF_LOG_DBG("Supported: %d", extensions_supported); + +  bool swapchain_adequate = false; +  if (extensions_supported) { + +    VGLTF_LOG_DBG("Checking for swapchain support details"); +    struct swapchain_support_details swapchain_support_details = {}; +    if (!swapchain_support_details_query_from_device(&swapchain_support_details, +                                                     device, surface)) { +      VGLTF_LOG_ERR("Couldn't query swapchain support details from device"); +      goto err; +    } + +    swapchain_adequate = swapchain_support_details.format_count > 0 && +                         swapchain_support_details.present_mode_count > 0; +  } + +  return queue_family_indices_is_complete(&indices) && extensions_supported && +         swapchain_adequate; +err: +  return false; +} + +static bool +vgltf_renderer_pick_physical_device(struct vgltf_renderer *renderer) { +  VkPhysicalDevice physical_device = VK_NULL_HANDLE; + +  struct available_physical_devices available_physical_devices = {}; +  if (!available_physical_devices_init(renderer->instance, +                                       &available_physical_devices)) { +    VGLTF_LOG_ERR("Couldn't fetch available physical devices"); +    goto err; +  } + +  for (uint32_t available_physical_device_index = 0; +       available_physical_device_index < available_physical_devices.count; +       available_physical_device_index++) { +    VkPhysicalDevice available_physical_device = +        available_physical_devices.devices[available_physical_device_index]; +    if (is_physical_device_suitable(available_physical_device, +                                    renderer->surface)) { +      physical_device = available_physical_device; +      break; +    } +  } + +  if (physical_device == VK_NULL_HANDLE) { +    VGLTF_LOG_ERR("Failed to find a suitable GPU"); +    goto err; +  } + +  renderer->physical_device = physical_device; + +  return true; +err: +  return false; +} + +static bool +vgltf_renderer_create_logical_device(struct vgltf_renderer *renderer) { +  struct queue_family_indices queue_family_indices = {}; +  queue_family_indices_for_device(&queue_family_indices, +                                  renderer->physical_device, renderer->surface); +  static constexpr int MAX_QUEUE_FAMILY_COUNT = 2; + +  uint32_t unique_queue_families[MAX_QUEUE_FAMILY_COUNT] = {}; +  int unique_queue_family_count = 0; + +  if (!is_in_array(unique_queue_families, unique_queue_family_count, +                   queue_family_indices.graphics_family)) { +    assert(unique_queue_family_count < MAX_QUEUE_FAMILY_COUNT); +    unique_queue_families[unique_queue_family_count++] = +        queue_family_indices.graphics_family; +  } +  if (!is_in_array(unique_queue_families, unique_queue_family_count, +                   queue_family_indices.present_family)) { +    assert(unique_queue_family_count < MAX_QUEUE_FAMILY_COUNT); +    unique_queue_families[unique_queue_family_count++] = +        queue_family_indices.present_family; +  } + +  float queue_priority = 1.f; +  VkDeviceQueueCreateInfo queue_create_infos[MAX_QUEUE_FAMILY_COUNT] = {}; +  int queue_create_info_count = 0; +  for (int unique_queue_family_index = 0; +       unique_queue_family_index < unique_queue_family_count; +       unique_queue_family_index++) { +    queue_create_infos[queue_create_info_count++] = (VkDeviceQueueCreateInfo){ +        .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, +        .queueFamilyIndex = unique_queue_families[unique_queue_family_index], +        .queueCount = 1, +        .pQueuePriorities = &queue_priority}; +  } + +  VkPhysicalDeviceFeatures device_features = {}; +  VkDeviceCreateInfo create_info = { +      .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, +      .pQueueCreateInfos = queue_create_infos, +      .queueCreateInfoCount = queue_create_info_count, +      .pEnabledFeatures = &device_features, +      .ppEnabledExtensionNames = DEVICE_EXTENSIONS, +      .enabledExtensionCount = DEVICE_EXTENSION_COUNT}; +  if (vkCreateDevice(renderer->physical_device, &create_info, nullptr, +                     &renderer->device) != VK_SUCCESS) { +    VGLTF_LOG_ERR("Failed to create logical device"); +    goto err; +  } + +  vkGetDeviceQueue(renderer->device, queue_family_indices.graphics_family, 0, +                   &renderer->graphics_queue); +  vkGetDeviceQueue(renderer->device, queue_family_indices.present_family, 0, +                   &renderer->present_queue); + +  return true; +err: +  return false; +} + +static bool vgltf_renderer_create_surface(struct vgltf_renderer *renderer, +                                          struct vgltf_platform *platform) { +  if (!vgltf_platform_create_vulkan_surface(platform, renderer->instance, +                                            &renderer->surface)) { +    VGLTF_LOG_ERR("Couldn't create surface"); +    goto err; +  } + +  return true; +err: +  return false; +} + +static 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]; +} + +static VkPresentModeKHR +choose_swapchain_present_mode(VkPresentModeKHR *available_modes, +                              uint32_t available_mode_count) { +  for (uint32_t available_mode_index = 0; +       available_mode_index < available_mode_count; available_mode_index++) { +    VkPresentModeKHR available_mode = available_modes[available_mode_index]; +    if (available_mode == VK_PRESENT_MODE_MAILBOX_KHR) { +      return available_mode; +    } +  } + +  return VK_PRESENT_MODE_FIFO_KHR; +} + +static uint32_t clamp_uint32(uint32_t min, uint32_t max, uint32_t value) { +  return value < min ? min : value > max ? max : value; +} + +static 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; +  } +} + +static bool vgltf_renderer_create_swapchain(struct vgltf_renderer *renderer, +                                            int window_width_px, +                                            int window_height_px) { +  struct swapchain_support_details swapchain_support_details = {}; +  swapchain_support_details_query_from_device( +      &swapchain_support_details, renderer->physical_device, renderer->surface); + +  VkSurfaceFormatKHR surface_format = +      choose_swapchain_surface_format(swapchain_support_details.formats, +                                      swapchain_support_details.format_count); +  VkPresentModeKHR present_mode = choose_swapchain_present_mode( +      swapchain_support_details.present_modes, +      swapchain_support_details.present_mode_count); + +  VkExtent2D extent = +      choose_swapchain_extent(&swapchain_support_details.capabilities, +                              window_width_px, window_height_px); +  uint32_t image_count = +      swapchain_support_details.capabilities.minImageCount + 1; +  if (swapchain_support_details.capabilities.maxImageCount > 0 && +      image_count > swapchain_support_details.capabilities.maxImageCount) { +    image_count = swapchain_support_details.capabilities.maxImageCount; +  } + +  VkSwapchainCreateInfoKHR create_info = { +      .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, +      .surface = renderer->surface, +      .minImageCount = image_count, +      .imageFormat = surface_format.format, +      .imageColorSpace = surface_format.colorSpace, +      .imageExtent = extent, +      .imageArrayLayers = 1, +      .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT}; +  struct queue_family_indices indices = {}; +  queue_family_indices_for_device(&indices, 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_details.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, nullptr, +                           &renderer->swapchain) != VK_SUCCESS) { +    VGLTF_LOG_ERR("Swapchain creation failed!"); +    goto err; +  } + +  if (vkGetSwapchainImagesKHR(renderer->device, renderer->swapchain, +                              &renderer->swapchain_image_count, +                              nullptr) != VK_SUCCESS) { +    VGLTF_LOG_ERR("Couldn't get swapchain image count"); +    goto destroy_swapchain; +  } + +  if (renderer->swapchain_image_count > +      VGLTF_RENDERER_MAX_SWAPCHAIN_IMAGE_COUNT) { +    VGLTF_LOG_ERR("Swapchain image array cannot fit all %d swapchain images", +                  renderer->swapchain_image_count); +    goto destroy_swapchain; +  } + +  if (vkGetSwapchainImagesKHR(renderer->device, renderer->swapchain, +                              &renderer->swapchain_image_count, +                              renderer->swapchain_images) != VK_SUCCESS) { +    VGLTF_LOG_ERR("Couldn't get swapchain images"); +    goto destroy_swapchain; +  } + +  renderer->swapchain_image_format = surface_format.format; +  renderer->swapchain_extent = extent; + +  return true; +destroy_swapchain: +  vkDestroySwapchainKHR(renderer->device, renderer->swapchain, nullptr); +err: +  return false; +} + +static bool vgltf_renderer_create_image_views(struct vgltf_renderer *renderer) { +  uint32_t swapchain_image_index; +  for (swapchain_image_index = 0; +       swapchain_image_index < renderer->swapchain_image_count; +       swapchain_image_index++) { +    VkImage swapchain_image = renderer->swapchain_images[swapchain_image_index]; + +    VkImageViewCreateInfo create_info = { +        .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, +        .image = swapchain_image, +        .viewType = VK_IMAGE_VIEW_TYPE_2D, +        .format = renderer->swapchain_image_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, +                             .levelCount = 1, +                             .layerCount = 1}}; + +    if (vkCreateImageView( +            renderer->device, &create_info, nullptr, +            &renderer->swapchain_image_views[swapchain_image_index]) != +        VK_SUCCESS) { +      goto err; +    } +  } +  return true; +err: +  for (uint32_t to_remove_index = 0; to_remove_index < swapchain_image_index; +       to_remove_index++) { +    vkDestroyImageView(renderer->device, +                       renderer->swapchain_image_views[to_remove_index], +                       nullptr); +  } +  return false; +} + +bool vgltf_renderer_init(struct vgltf_renderer *renderer, +                         struct vgltf_platform *platform) { +  if (!vgltf_renderer_create_instance(renderer, platform)) { +    VGLTF_LOG_ERR("instance creation failed"); +    goto err; +  } +  vgltf_renderer_setup_debug_messenger(renderer); +  if (!vgltf_renderer_create_surface(renderer, platform)) { +    goto destroy_instance; +  } + +  if (!vgltf_renderer_pick_physical_device(renderer)) { +    VGLTF_LOG_ERR("Couldn't pick physical device"); +    goto destroy_surface; +  } +  if (!vgltf_renderer_create_logical_device(renderer)) { +    VGLTF_LOG_ERR("Couldn't create logical device"); +    goto destroy_device; +  } + +  struct vgltf_window_size window_size = {800, 600}; +  if (!vgltf_platform_get_window_size(platform, &window_size)) { +    VGLTF_LOG_ERR("Couldn't get window size"); +    goto destroy_device; +  } + +  if (!vgltf_renderer_create_swapchain(renderer, window_size.width, +                                       window_size.height)) { +    VGLTF_LOG_ERR("Couldn't create swapchain"); +    goto destroy_device; +  } + +  if (!vgltf_renderer_create_image_views(renderer)) { +    VGLTF_LOG_ERR("Couldn't create image views"); +    goto destroy_swapchain; +  } + +  return true; +destroy_swapchain: +  vkDestroySwapchainKHR(renderer->device, renderer->swapchain, nullptr); +destroy_device: +  vkDestroyDevice(renderer->device, nullptr); +destroy_surface: +  vkDestroySurfaceKHR(renderer->instance, renderer->surface, nullptr); +destroy_instance: +  if (enable_validation_layers) { +    destroy_debug_utils_messenger_ext(renderer->instance, +                                      renderer->debug_messenger, nullptr); +  } +  vkDestroyInstance(renderer->instance, nullptr); +err: +  return false; +} +void vgltf_renderer_deinit(struct vgltf_renderer *renderer) { +  for (uint32_t swapchain_image_view_index = 0; +       swapchain_image_view_index < renderer->swapchain_image_count; +       swapchain_image_view_index++) { +    vkDestroyImageView( +        renderer->device, +        renderer->swapchain_image_views[swapchain_image_view_index], nullptr); +  } +  vkDestroySwapchainKHR(renderer->device, renderer->swapchain, nullptr); +  vkDestroyDevice(renderer->device, nullptr); +  vkDestroySurfaceKHR(renderer->instance, renderer->surface, nullptr); +  if (enable_validation_layers) { +    destroy_debug_utils_messenger_ext(renderer->instance, +                                      renderer->debug_messenger, nullptr); +  } +  vkDestroyInstance(renderer->instance, nullptr); +} diff --git a/src/renderer.h b/src/renderer.h new file mode 100644 index 0000000..5e08762 --- /dev/null +++ b/src/renderer.h @@ -0,0 +1,27 @@ +#ifndef VGLTF_RENDERER_H +#define VGLTF_RENDERER_H + +#include "platform.h" +#include <vulkan/vulkan.h> + +constexpr int VGLTF_RENDERER_MAX_SWAPCHAIN_IMAGE_COUNT = 32; +struct vgltf_renderer { +  VkInstance instance; +  VkPhysicalDevice physical_device; +  VkDevice device; +  VkQueue graphics_queue; +  VkQueue present_queue; +  VkDebugUtilsMessengerEXT debug_messenger; +  VkSurfaceKHR surface; +  VkSwapchainKHR swapchain; +  VkImage swapchain_images[VGLTF_RENDERER_MAX_SWAPCHAIN_IMAGE_COUNT]; +  VkImageView swapchain_image_views[VGLTF_RENDERER_MAX_SWAPCHAIN_IMAGE_COUNT]; +  VkFormat swapchain_image_format; +  VkExtent2D swapchain_extent; +  uint32_t swapchain_image_count; +}; +bool vgltf_renderer_init(struct vgltf_renderer *renderer, +                         struct vgltf_platform *platform); +void vgltf_renderer_deinit(struct vgltf_renderer *renderer); + +#endif // VGLTF_RENDERER_H  | 
