diff options
| author | Clement Sibille <clements@lisible.xyz> | 2024-12-10 08:28:01 +0100 |
|---|---|---|
| committer | Clement Sibille <clements@lisible.xyz> | 2024-12-10 08:28:01 +0100 |
| commit | 584fe916ee7be68856a605ed99cd01693b710356 (patch) | |
| tree | 22e0cb1e72f9c0539877302ada8855fa008af4ce | |
Initialize instance and validation layers
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | meson.build | 11 | ||||
| -rw-r--r-- | src/main.c | 277 |
3 files changed, 290 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc95448 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.cache/ +build/ diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..ebfbfff --- /dev/null +++ b/meson.build @@ -0,0 +1,11 @@ +project('vkguide', 'c', default_options: ['c_std=c17', 'warning_level=3']) + +cc = meson.get_compiler('c') +sdl3_dep = dependency('SDL3') +vulkan_dep = dependency('vulkan') + +executable( + 'vkguide', + ['src/main.c'], + dependencies: [sdl3_dep, vulkan_dep] +) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..102ddca --- /dev/null +++ b/src/main.c @@ -0,0 +1,277 @@ +#include <SDL3/SDL.h> +#include <SDL3/SDL_vulkan.h> +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <vulkan/vulkan.h> +#include <vulkan/vulkan_core.h> + +#ifdef NDEBUG +#define LOG(...) +#else +#define LOG(...) \ + do { \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } while (0) +#endif + +struct vulkan_renderer { + VkInstance instance; + VkDebugUtilsMessengerEXT debug_messenger; + bool enable_validation_layers; +}; + +#define MAX_EXTENSION_COUNT 256 +#define MAX_ADDITIONAL_EXTENSION_COUNT 100 + +VKAPI_ATTR VkBool32 VKAPI_CALL +vulkan_debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, + VkDebugUtilsMessageTypeFlagsEXT message_type, + const VkDebugUtilsMessengerCallbackDataEXT *callback_data, + void *user_data) { + (void)message_severity; + (void)message_type; + (void)user_data; + LOG("Validation layer: %s", callback_data->pMessage); + return VK_FALSE; +} + +bool vulkan_renderer_create_instance(struct vulkan_renderer *renderer) { + + VkApplicationInfo application_info = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pApplicationName = "vkguide", + .applicationVersion = VK_MAKE_VERSION(1, 0, 0), + .pEngineName = "None", + .engineVersion = VK_MAKE_VERSION(1, 0, 0), + .apiVersion = VK_API_VERSION_1_0}; + + const char *requested_extensions[MAX_EXTENSION_COUNT] = {0}; + uint32_t requested_extension_count = 0; + uint32_t required_instance_extension_count; + const char *const *required_instance_extensions = + SDL_Vulkan_GetInstanceExtensions(&required_instance_extension_count); + + assert(requested_extension_count + required_instance_extension_count < + MAX_EXTENSION_COUNT); + memcpy(requested_extensions, required_instance_extensions, + required_instance_extension_count * sizeof(const char *)); + requested_extension_count += required_instance_extension_count; + + const char *additional_extensions[MAX_ADDITIONAL_EXTENSION_COUNT] = {0}; + uint32_t additional_extension_count = 0; + + if (renderer->enable_validation_layers) { + additional_extensions[additional_extension_count++] = + VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + } + + assert(requested_extension_count + additional_extension_count < + MAX_EXTENSION_COUNT); + memcpy(requested_extensions + requested_extension_count, + additional_extensions, + additional_extension_count * sizeof(const char *)); + requested_extension_count += additional_extension_count; + + uint32_t enabled_layer_count = 0; + const char *const *enabled_layers = NULL; + + VkDebugUtilsMessengerCreateInfoEXT debug_create_info = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .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, + .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, + .pfnUserCallback = vulkan_debug_callback}; + if (renderer->enable_validation_layers) { + static const char *validation_layers[] = {"VK_LAYER_KHRONOS_validation"}; + static const uint32_t validation_layer_count = + sizeof(validation_layers) / sizeof(const char *); + uint32_t available_layer_count; + vkEnumerateInstanceLayerProperties(&available_layer_count, NULL); + + VkLayerProperties *available_layers = + malloc(available_layer_count * sizeof(VkLayerProperties)); + vkEnumerateInstanceLayerProperties(&available_layer_count, + available_layers); + + bool requested_layers_found = true; + for (uint32_t validation_layer_index = 0; + validation_layer_index < validation_layer_count; + validation_layer_index++) { + bool layer_found = false; + for (uint32_t available_layer_index = 0; + available_layer_index < available_layer_count; + available_layer_index++) { + if (strcmp(validation_layers[validation_layer_index], + available_layers[available_layer_index].layerName) == 0) { + layer_found = true; + break; + } + } + + if (!layer_found) { + requested_layers_found = false; + break; + } + } + + free(available_layers); + if (!requested_layers_found) { + LOG("Not all requested layers are available."); + goto err; + } + enabled_layer_count = validation_layer_count; + enabled_layers = validation_layers; + } + + VkInstanceCreateInfo instance_create_info = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &application_info, + .enabledExtensionCount = requested_extension_count, + .ppEnabledExtensionNames = requested_extensions, + .enabledLayerCount = enabled_layer_count, + .ppEnabledLayerNames = enabled_layers}; + if (renderer->enable_validation_layers) { + instance_create_info.pNext = + (VkDebugUtilsMessengerCreateInfoEXT *)&debug_create_info; + } + + VkResult create_instance_result = + vkCreateInstance(&instance_create_info, NULL, &renderer->instance); + if (create_instance_result != VK_SUCCESS) { + LOG("Vulkan instance creation failed, VkResult=%d", create_instance_result); + goto err; + } + return true; +err: + return false; +} + +void vulkan_renderer_destroy_instance(struct vulkan_renderer *renderer) { + vkDestroyInstance(renderer->instance, NULL); +} +VkResult vkCreateDebugUtilsMessengerEXT( + VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT *create_info, + const VkAllocationCallbacks *allocation_callbacks, + VkDebugUtilsMessengerEXT *debug_messenger) { + PFN_vkCreateDebugUtilsMessengerEXT func = + (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkCreateDebugUtilsMessengerEXT"); + if (func != NULL) { + return func(instance, create_info, allocation_callbacks, debug_messenger); + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +void vkDestroyDebugUtilsMessengerEXT(VkInstance instance, + VkDebugUtilsMessengerEXT debugMessenger, + const VkAllocationCallbacks *pAllocator) { + PFN_vkDestroyDebugUtilsMessengerEXT func = + (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkDestroyDebugUtilsMessengerEXT"); + if (func != NULL) { + func(instance, debugMessenger, pAllocator); + } +} + +bool vulkan_renderer_create_debug_messenger(struct vulkan_renderer *renderer) { + return vkCreateDebugUtilsMessengerEXT( + renderer->instance, + &(const VkDebugUtilsMessengerCreateInfoEXT){ + .sType = + VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .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, + .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, + .pfnUserCallback = vulkan_debug_callback}, + NULL, &renderer->debug_messenger) == VK_SUCCESS; +} + +bool vulkan_renderer_init(struct vulkan_renderer *renderer) { + assert(renderer); +#ifdef NDEBUG + renderer->enable_validation_layers = false; +#else + renderer->enable_validation_layers = true; +#endif + + if (!vulkan_renderer_create_instance(renderer)) { + goto err; + } + + if (renderer->enable_validation_layers) { + if (!vulkan_renderer_create_debug_messenger(renderer)) { + LOG("Couldn't create Vulkan renderer debug messenger."); + } + } + + return true; + +err: + return false; +} + +void vulkan_renderer_deinit(struct vulkan_renderer *renderer) { + if (renderer->enable_validation_layers) { + vkDestroyDebugUtilsMessengerEXT(renderer->instance, + renderer->debug_messenger, NULL); + } + vulkan_renderer_destroy_instance(renderer); +} + +int main(void) { + if (!SDL_Init(SDL_INIT_VIDEO)) { + LOG("Couldn't initialize SDL: %s", SDL_GetError()); + goto err; + } + + SDL_Window *window = + SDL_CreateWindow("vkguide", 1280, 720, SDL_WINDOW_VULKAN); + if (!window) { + LOG("Couldn't create window: %s", SDL_GetError()); + goto quit_sdl; + } + + struct vulkan_renderer renderer; + if (!vulkan_renderer_init(&renderer)) { + LOG("Couldn't init vulkan renderer"); + goto destroy_window; + } + + while (true) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + bool quit = event.type == SDL_EVENT_QUIT; + bool escape_pressed = + event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE; + + if (quit || escape_pressed) { + goto out_main_loop; + } + } + + // Render things + } +out_main_loop: + + vulkan_renderer_deinit(&renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; + +destroy_window: + SDL_DestroyWindow(window); +quit_sdl: + SDL_Quit(); +err: + return 1; +} |
