/** * @file vulkanBase.cpp * @author Piotr Krygier (everyonecancode@gmail.com) * @brief Base configuration for Vulkan API * @version 0.1 * @date 2022-05-13 * * @copyright Copyright (c) 2022 * */ #include "vulkan_base.h" #include "vulkan_errors.h" #include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wpedantic" #pragma GCC diagnostic ignored "-Wignored-qualifiers" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #pragma GCC diagnostic ignored "-Wswitch" #pragma GCC diagnostic ignored "-Wparentheses" #include "vk_mem_alloc.h" #pragma GCC diagnostic pop #include "locale_vulkan.h" #include "utilities/logger.h" #include "window.h" #include "vulkan_commons.h" #include "vulkan_buffers.h" #include "vulkan_descriptors.h" #include "vulkan_pipeline.h" #include #include #include #define APPLICATION_NAME "RedScarfEngine PoC" #define ENGINE_NAME "RedScarf Engine" /* Define Validation Layers for debug build */ #ifndef NDEBUG #define ENABLED_EXTENSIONS_COUT 4 #define ENABLED_INSTANCE_LAYERS_COUNT 1 const char* g_enabled_instance_layers_names[] = {"VK_LAYER_KHRONOS_validation"}; #else #define ENABLED_EXTENSIONS_COUT 3 #define ENABLED_INSTANCE_LAYERS_COUNT 0 const char* g_enabled_instance_layers_names = {}; #endif const char* g_enabled_instance_extensions_names[ENABLED_EXTENSIONS_COUT] = {}; #define ENABLED_DEVICE_EXTENSIONS_COUT 1 const char* g_enabled_device_extensions_names[] = { "VK_KHR_swapchain", }; VkInstance g_vulkan_instance = VK_NULL_HANDLE; VkDebugUtilsMessengerEXT g_debug_messenger = VK_NULL_HANDLE; VkQueue g_present_queue; VkSemaphore g_image_available_semaphores[SWAP_BUFFER_COUNT] = {}; VkSemaphore g_render_finished_semaphores[SWAP_BUFFER_COUNT] = {}; VkFence g_in_flight_fences[SWAP_BUFFER_COUNT] = {}; #ifndef NDEBUG static void setup_debug_messenger(); static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_type, const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data); static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* create_info, const VkAllocationCallbacks* allocator, VkDebugUtilsMessengerEXT* debug_messenger); static void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debug_messenger, const VkAllocationCallbacks* allocator); #endif static void set_enabled_extension(); static uint8_t create_instance(); static uint8_t create_surface(); static uint8_t pick_physical_device(); static uint8_t create_device(); static uint8_t create_memory_allocator(); static uint8_t create_sync_objects(); #ifndef NDEBUG /** * @brief Setup debugging system for Vulkan API * */ void setup_debug_messenger() { VkDebugUtilsMessengerCreateInfoEXT create_info; create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; create_info.pNext = NULL; 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; create_info.pUserData = NULL; // Optional create_info.flags = 0U; if (CreateDebugUtilsMessengerEXT(g_vulkan_instance, &create_info, NULL, &g_debug_messenger) != VK_SUCCESS) { fprintf(stderr, "failed to set up debug messenger!"); } } /** * @brief Calback for debug messenger * * @param message_severity Message severity * @param message_type Type of a message * @param callback_data Contains all the callback related data in the VkDebugUtilsMessengerCallbackDataEXT structure * @param user_data Data provided by the user * @return VkBool32 Should always return VK_FALSE. The VK_TRUE value is reserved for use in layer development */ VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_type, const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) { (void)message_severity; (void)message_type; (void)user_data; fprintf(stderr, "validation layer: %s\n", callback_data->pMessage); return VK_FALSE; } /** * @brief Proxy for vkCreateDebugUtilsMessengerEXT * * @param instance Vulkan instance object * @param create_info VkDebugUtilsMessengerCreateInfoEXT structure * @param allocator Memory allocator * @param debug_messenger Debug messanger handle * @return VkResult VK_SUCCESS on success */ VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* create_info, const VkAllocationCallbacks* allocator, VkDebugUtilsMessengerEXT* debug_messenger) { PFN_vkCreateDebugUtilsMessengerEXT func = NULL; func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); if (func != NULL) { return func(instance, create_info, allocator, debug_messenger); } else { return VK_ERROR_EXTENSION_NOT_PRESENT; } return VK_SUCCESS; } /** * @brief Proxy for vkDestroyDebugUtilsMessengerEXT * * @param instance Vulkan instance object * @param debug_messenger Debug messanger handle * @param allocator Memory allocator */ void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debug_messenger, const VkAllocationCallbacks* allocator) { PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); if (func != NULL) { func(instance, debug_messenger, allocator); } } #endif /** * @brief Enable instance extensions, based on system and requirements * */ void set_enabled_extension() { g_enabled_instance_extensions_names[0] = "VK_KHR_surface"; g_enabled_instance_extensions_names[1] = "VK_KHR_device_group_creation"; #ifdef __linux__ if (getenv("WAYLAND_DISPLAY") != NULL) { g_enabled_instance_extensions_names[2] = "VK_KHR_wayland_surface"; } else { g_enabled_instance_extensions_names[2] = "VK_KHR_xcb_surface"; } #elif defined(_WIN32) || defined(WIN32) g_enabled_instance_extensions_names.push_back("VK_KHR_win32_surface"); #endif #ifndef NDEBUG g_enabled_instance_extensions_names[3] = "VK_EXT_debug_utils"; #endif }; /** * @brief Create the Vulkan Instance object * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_INSTANCE_INIT_FAILED */ uint8_t create_instance() { size_t i; size_t j; uint8_t supported = 0; uint32_t extensionCount = 0; uint32_t layersCount = 0; VkApplicationInfo application_info = {}; VkInstanceCreateInfo create_info = {}; VkLayerProperties* layer_properties; VkExtensionProperties* extensions_properties; vkEnumerateInstanceLayerProperties(&layersCount, NULL); layer_properties = malloc(sizeof(VkLayerProperties) * layersCount); vkEnumerateInstanceLayerProperties(&layersCount, layer_properties); vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL); extensions_properties = malloc(sizeof(VkExtensionProperties) * extensionCount); vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensions_properties); #ifndef NDEBUG LOGD("Supported Layers"); for (i = 0; i < layersCount; i++) { LOGD("%s", layer_properties[i].layerName); } LOGD("Supported Extensions"); for (size_t i = 0; i < extensionCount; i++) { LOGD("%s", extensions_properties[i].extensionName); } #endif /* Check if selected layers are supported*/ for (i = 0; i < ENABLED_INSTANCE_LAYERS_COUNT; ++i) { for(j = 0; i < layersCount; ++j) { if(strcmp(layer_properties[j].layerName, g_enabled_instance_layers_names[i]) == 0) { supported = 1; break; } } } if(supported == 0) { LOGF(vulkan_error_messages[VULKAN_LAYER_NOT_SUPPORTED], layer_properties[j].layerName); return VULKAN_ERROR_LAYER_NOT_SUPPORTED; } supported = 0; /* Check if selected extensions are supported*/ for (i = 0; i < ENABLED_EXTENSIONS_COUT; ++i) { for(j = 0; j < extensionCount; ++j) { if(strcmp(extensions_properties[j].extensionName, g_enabled_instance_extensions_names[i]) == 0) { supported = 1; break; } } } if(supported == 0) { LOGF(vulkan_error_messages[VULKAN_EXTENSION_NOT_SUPPORTED], extensions_properties[j].extensionName); return VULKAN_ERROR_EXTENSION_NOT_SUPPORTED; } application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; application_info.pNext = NULL; application_info.pApplicationName = APPLICATION_NAME; application_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0); application_info.pEngineName = ENGINE_NAME; application_info.engineVersion = VK_MAKE_VERSION(0, 1, 0); application_info.apiVersion = VK_API_VERSION_1_3; create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; create_info.pNext = NULL; create_info.flags = 0U; create_info.pApplicationInfo = &application_info; create_info.enabledLayerCount = ENABLED_INSTANCE_LAYERS_COUNT; #ifndef NDEBUG create_info.ppEnabledLayerNames = g_enabled_instance_layers_names; #else create_info.ppEnabledLayerNames = NULL; #endif create_info.enabledExtensionCount = ENABLED_EXTENSIONS_COUT; create_info.ppEnabledExtensionNames = g_enabled_instance_extensions_names; VkResult result = vkCreateInstance(&create_info, NULL, &g_vulkan_instance); if (VK_SUCCESS != result) { LOGF(vulkan_error_messages[VULKAN_INTANCE_INIT_FAILED]); return VULKAN_ERROR_INSTANCE_INIT_FAILED; } return VULKAN_ERROR_NO_ERROR; } /** * @brief Create a Surface, connection between Vulkan and actual window * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: */ uint8_t create_surface() { if (VK_SUCCESS != glfwCreateWindowSurface(g_vulkan_instance, get_window_handle(), NULL, &g_vulkan_state.surface)) { LOGF(vulkan_error_messages[VULKAN_SURFACE_CREATION_FAILED]); return VULKAN_ERROR_SURFACE_CREATION_FAILED; } return VULKAN_ERROR_NO_ERROR; } /** * @brief Choose physical device. Used later for vkDevice * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND * VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND */ uint8_t pick_physical_device() { uint32_t physical_device_count = 0U; VkPhysicalDevice* physical_devices = NULL; /* Get number of existing physical devices */ vkEnumeratePhysicalDevices(g_vulkan_instance, &physical_device_count, NULL); if (0 == physical_device_count) { LOGF(vulkan_error_messages[VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND]); return VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND; } physical_devices = malloc(sizeof(VkPhysicalDevice) * physical_device_count); vkEnumeratePhysicalDevices(g_vulkan_instance, &physical_device_count, physical_devices); /* Check for suitability */ for (size_t physicalDeviceIdx = 0U; physicalDeviceIdx < physical_device_count; ++physicalDeviceIdx) { VkPhysicalDeviceProperties device_properties; VkPhysicalDeviceFeatures device_features; vkGetPhysicalDeviceProperties(physical_devices[physicalDeviceIdx], &device_properties); vkGetPhysicalDeviceFeatures(physical_devices[physicalDeviceIdx], &device_features); /* Expect discrete graphics device type */ if (device_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || device_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) { /* Application MUST have geometry shader */ if (device_features.geometryShader) { g_vulkan_state.physical_device = physical_devices[physicalDeviceIdx]; } } } if (VK_NULL_HANDLE == g_vulkan_state.physical_device) { /* No suitable device found */ LOGF(vulkan_error_messages[VULKAN_NO_SUITABLE_PHYSICAL_DEVICES]); return VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND; } return VULKAN_ERROR_NO_ERROR; } /** * @brief Create vkDevice object * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_QUEUE_NOT_SUPPORTED * VULKAN_ERROR_DEVICE_CREATION_FAILED */ uint8_t create_device() { size_t device_queues_count = 0; uint32_t queue_families_property_count = 0U; ssize_t graphics_family_idx = -1; ssize_t presentation_familiy_idx = -1; VkQueueFamilyProperties* queue_family_properties = NULL; VkDeviceCreateInfo device_create_info = {}; VkDeviceQueueCreateInfo device_queue_createinfos[2] = {}; /* TODO: Change when we want more queues */ float* graphics_familiy_queue_priorities = NULL; float* presentation_familiy_queue_priorities = NULL; /* Get information about supported queue families */ vkGetPhysicalDeviceQueueFamilyProperties(g_vulkan_state.physical_device, &queue_families_property_count, NULL); queue_family_properties = malloc(sizeof(VkQueueFamilyProperties) * queue_families_property_count); vkGetPhysicalDeviceQueueFamilyProperties(g_vulkan_state.physical_device, &queue_families_property_count, queue_family_properties); /* Get queue families that support graphics operations AND have most * queues. */ { size_t numberOfQueues = 0; for (size_t familyIdx = 0; familyIdx < queue_families_property_count; ++familyIdx) { LOGI("Checking queue: %ld", familyIdx); if (queue_family_properties[familyIdx].queueFlags & VK_QUEUE_GRAPHICS_BIT) { if (queue_family_properties[familyIdx].queueCount > numberOfQueues) { graphics_family_idx = familyIdx; LOGI("Setting graphics family idx to %d", familyIdx); numberOfQueues = queue_family_properties[familyIdx].queueCount; device_queues_count++; } } /* Check for family with surface support */ VkBool32 presentSupport = VK_FALSE; vkGetPhysicalDeviceSurfaceSupportKHR(g_vulkan_state.physical_device, familyIdx, g_vulkan_state.surface, &presentSupport); if (presentSupport) { presentation_familiy_idx = familyIdx; /* If this is the same queue family as in graphics family, don't increase queue count */ //TODO: This is still ugly, but fixes an error. Change later. if (presentation_familiy_idx != graphics_family_idx) { device_queues_count++; } LOGI("Setting present family idx to %d", familyIdx); } if (graphics_family_idx > 0 && presentation_familiy_idx > 0) { break; } } } /* This is not magic number ;P. We want to support exact number of required queues flags */ if (0 > graphics_family_idx) { LOGF(vulkan_error_messages[VULKAN_QUEUE_NOT_SUPPORTED]); return VULKAN_ERROR_QUEUE_NOT_SUPPORTED; } g_vulkan_state.queue_family_indices[0] = graphics_family_idx; /* TODO: Change when more families are needed */ graphics_familiy_queue_priorities = malloc(sizeof(float) * queue_family_properties[graphics_family_idx].queueCount); for (size_t i = 0; i < queue_family_properties[graphics_family_idx].queueCount; i++) { graphics_familiy_queue_priorities[i] = 1.0f; /* TODO: When queues have different tasks, change this if needed */ } presentation_familiy_queue_priorities = malloc(sizeof(float) * queue_family_properties[presentation_familiy_idx].queueCount); for (size_t i = 0; i < queue_family_properties[presentation_familiy_idx].queueCount; i++) { presentation_familiy_queue_priorities[i] = 1.0f; /* TODO: When queues have different tasks, change this if needed */ } device_queue_createinfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; device_queue_createinfos[0].pNext = NULL; device_queue_createinfos[0].flags = 0; device_queue_createinfos[0].queueFamilyIndex = graphics_family_idx; device_queue_createinfos[0].queueCount = queue_family_properties[graphics_family_idx].queueCount; device_queue_createinfos[0].pQueuePriorities = graphics_familiy_queue_priorities; device_queue_createinfos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; device_queue_createinfos[1].pNext = NULL; device_queue_createinfos[1].flags = 0; device_queue_createinfos[1].queueFamilyIndex = presentation_familiy_idx; device_queue_createinfos[1].queueCount = queue_family_properties[presentation_familiy_idx].queueCount; device_queue_createinfos[1].pQueuePriorities = presentation_familiy_queue_priorities; device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; device_create_info.pNext = NULL; device_create_info.flags = 0U; device_create_info.queueCreateInfoCount = device_queues_count; device_create_info.pQueueCreateInfos = device_queue_createinfos; device_create_info.enabledLayerCount = 0; device_create_info.ppEnabledLayerNames = NULL; device_create_info.enabledExtensionCount = ENABLED_DEVICE_EXTENSIONS_COUT; device_create_info.ppEnabledExtensionNames = g_enabled_device_extensions_names; device_create_info.pEnabledFeatures = NULL; /* TODO: Enable features, when needed */ if (VK_SUCCESS != vkCreateDevice(g_vulkan_state.physical_device, &device_create_info, NULL, &g_vulkan_state.device)) { LOGF(vulkan_error_messages[VULKAN_FAILED_TO_CREATE_DEVICE]); return VULKAN_ERROR_DEVICE_CREATION_FAILED; } vkGetDeviceQueue(g_vulkan_state.device, graphics_family_idx, 0, &g_vulkan_state.graphics_queue); vkGetDeviceQueue(g_vulkan_state.device, presentation_familiy_idx, 0, &g_present_queue); return VULKAN_ERROR_NO_ERROR; } /** * @brief Create a Memory Allocator object * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_ALLOCATOR_CREATION_FAILED */ uint8_t create_memory_allocator() { VmaVulkanFunctions vulkan_functions = {0}; vulkan_functions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; vulkan_functions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; VmaAllocatorCreateInfo allocator_create_info = {0}; allocator_create_info.vulkanApiVersion = VK_API_VERSION_1_3; allocator_create_info.physicalDevice = g_vulkan_state.physical_device; allocator_create_info.device = g_vulkan_state.device; allocator_create_info.instance = g_vulkan_instance; allocator_create_info.pVulkanFunctions = &vulkan_functions; if (VK_SUCCESS != vmaCreateAllocator(&allocator_create_info, &g_vulkan_state.allocator)) { LOGF(vulkan_error_messages[VULKAN_ERROR_ALLOCATOR_CREATION_FAILED]); return VULKAN_ERROR_ALLOCATOR_CREATION_FAILED; } return VULKAN_ERROR_NO_ERROR; } /** * @brief Create a Sync Objects * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED */ uint8_t create_sync_objects() { VkSemaphoreCreateInfo semaphore_info = {}; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; VkFenceCreateInfo fenceInfo = {}; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) { if (VK_SUCCESS != vkCreateSemaphore(g_vulkan_state.device, &semaphore_info, NULL, &g_image_available_semaphores[i]) || VK_SUCCESS != vkCreateSemaphore(g_vulkan_state.device, &semaphore_info, NULL, &g_render_finished_semaphores[i]) || VK_SUCCESS != vkCreateFence(g_vulkan_state.device, &fenceInfo, NULL, &g_in_flight_fences[i])) { LOGF(vulkan_error_messages[VULKAN_SYNCOBJCTS_CREATION_FAILED]); return VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED; } } return VULKAN_ERROR_NO_ERROR; } uint8_t init_vulkan() { uint8_t status = VULKAN_ERROR_NO_ERROR; set_enabled_extension(); STATUS_CHECK(create_instance()); #ifndef NDEBUG setup_debug_messenger(); #endif STATUS_CHECK(create_surface()); STATUS_CHECK(pick_physical_device()); STATUS_CHECK(create_device()); STATUS_CHECK(create_memory_allocator()); STATUS_CHECK(create_pipeline()); STATUS_CHECK(create_buffers()); STATUS_CHECK(create_descriptors()); STATUS_CHECK(create_sync_objects()); rse_init_time(); return status; } void draw_frame() { uint32_t image_index = 0U; VkResult result = VK_FALSE; VkSubmitInfo submit_info = {}; VkSemaphore wait_semaphores[1]; //TODO: Increase when needed VkPipelineStageFlags wait_stages[1]; VkSemaphore signal_semaphores[1]; VkPresentInfoKHR present_info = {}; VkSwapchainKHR swap_chains[1]; vkWaitForFences(g_vulkan_state.device, 1, &g_in_flight_fences[g_vulkan_state.current_frame], VK_TRUE, UINT64_MAX); rse_update_time(); result = vkAcquireNextImageKHR(g_vulkan_state.device, g_vulkan_state.swapchain, UINT64_MAX, g_image_available_semaphores[g_vulkan_state.current_frame], VK_NULL_HANDLE, &image_index); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { recreate_swapchain(); return; } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { // throw std::runtime_error("failed to acquire swap chain image!"); return; } update_uniform_buffers(); /* Only reset the fence if we are submitting work */ vkResetFences(g_vulkan_state.device, 1, &g_in_flight_fences[g_vulkan_state.current_frame]); reset_command_buffer(); record_command_buffer(image_index); submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; wait_semaphores[0] = g_image_available_semaphores[g_vulkan_state.current_frame]; wait_stages[0] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = wait_semaphores; submit_info.pWaitDstStageMask = wait_stages; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &g_vulkan_state.command_buffers[g_vulkan_state.current_frame]; signal_semaphores[0] = g_render_finished_semaphores[g_vulkan_state.current_frame]; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = signal_semaphores; if (vkQueueSubmit(g_vulkan_state.graphics_queue, 1, &submit_info, g_in_flight_fences[g_vulkan_state.current_frame]) != VK_SUCCESS) { LOGF(vulkan_error_messages[VULAKN_DRAW_FAILED]); return; } present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present_info.waitSemaphoreCount = 1; present_info.pWaitSemaphores = signal_semaphores; swap_chains[0] = g_vulkan_state.swapchain; present_info.swapchainCount = 1; present_info.pSwapchains = swap_chains; present_info.pImageIndices = &image_index; vkQueuePresentKHR(g_present_queue, &present_info); g_vulkan_state.current_frame = (g_vulkan_state.current_frame + 1) % SWAP_BUFFER_COUNT; } void deinit_vulkan() { cleanup_swapchain(); destroy_descriptors(); destroy_buffers(); for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) { vkDestroySemaphore(g_vulkan_state.device, g_image_available_semaphores[i], NULL); vkDestroySemaphore(g_vulkan_state.device, g_render_finished_semaphores[i], NULL); vkDestroyFence(g_vulkan_state.device, g_in_flight_fences[i], NULL); } destroy_pipeline(); vmaDestroyAllocator(g_vulkan_state.allocator); vkDestroyDevice(g_vulkan_state.device, NULL); #ifndef NDEBUG DestroyDebugUtilsMessengerEXT(g_vulkan_instance, g_debug_messenger, NULL); #endif vkDestroySurfaceKHR(g_vulkan_instance, g_vulkan_state.surface, NULL); vkDestroyInstance(g_vulkan_instance, NULL); }