Files
RedScarfEngine/graphics/src/vulkan_base.c
T
Piotr Krygier 9cb692344f Make repo working again
I rewrote basically all pipeline specific code to make
it more customizable. I made it work, which is insane.
I never thought I would be able to go back to working state again.
2025-06-20 13:44:33 +02:00

668 lines
25 KiB
C

/**
* @file vulkanBase.cp
* @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 <GLFW/glfw3.h>
#include "utilities/localization.h"
#include <vma/vk_mem_alloc.h>
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/logger.h"
#include "vulkan/vulkan_core.h"
#include "vulkan_errors.h"
#include "vulkan_commons.h"
#include "vulkan_commands.h"
#include "vulkan_buffers.h"
#include "vulkan_image.h"
#include "vulkan_render_pass.h"
#include "vulkan_swapchain.h"
#include "pipeline_builder.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#define APPLICATION_NAME "RedScarfEngine PoC"
#define ENGINE_NAME "RedScarf Engine"
/**
* @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
*/
static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
VkDebugUtilsMessageTypeFlagsEXT message_type,
const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data)
{
enum log_level_t log_level = LOGLEVEL_TRACE;
(void)message_severity;
(void)message_type;
(void)user_data;
/* There is no guarantee, that the error message will have single severity flag set, so we can't use switch()
* here. We have to check flags and set severity to most important one */
if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
log_level = LOGLEVEL_ERROR;
} else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
log_level = LOGLEVEL_WARNING;
} else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
log_level = LOGLEVEL_INFO;
} else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
log_level = LOGLEVEL_TRACE;
}
LOGGER(log_level, 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
*/
static 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 Setup debugging system for Vulkan API
*
* @param context Graphics context
*
*/
static rse_err_t setup_debug_messenger(VkInstance instance, VkDebugUtilsMessengerEXT* messenger)
{
VkDebugUtilsMessengerCreateInfoEXT create_info = {0};
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_INFO_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(instance, &create_info, NULL, messenger) != VK_SUCCESS) {
LOGF(_("Failed to setup debug messenger\n"));
return VULKAN_ERROR_DEBUG_MESSENGER_CREATION_FAILED;
}
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Proxy for vkDestroyDebugUtilsMessengerEXT
*
* @param instance Vulkan instance object
* @param debug_messenger Debug messanger handle
* @param allocator Memory allocator
*/
static 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);
}
}
/**
* @brief Create the Vulkan Instance object
*
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_INSTANCE_INIT_FAILED
*/
static rse_err_t create_instance(VkInstance* instance)
{
size_t i = 0U;
size_t j = 0U;
uint8_t supported = 0U;
VkResult result = VK_SUCCESS;
uint32_t extensionCount = 0U;
uint32_t layersCount = 0U;
VkApplicationInfo application_info = {0};
VkInstanceCreateInfo create_info = {0};
VkLayerProperties* layer_properties;
VkExtensionProperties* extensions_properties;
const char* enabled_instance_extensions_names[] = {
"VK_KHR_surface",
"VK_KHR_device_group_creation",
#ifdef __linux__
#ifdef WAYLAND
"VK_KHR_wayland_surface",
#else
"VK_KHR_xcb_surface",
#endif /* WAYLAND */
#elif defined(_WIN32) || defined(WIN32)
"VK_KHR_win32_surface",
#endif /* _WIN32 || WIN32 */
"VK_EXT_debug_utils",
};
#ifndef NDEBUG
uint32_t enabled_instance_extensions_count = 4;
uint32_t enabled_instance_layers_count = 1;
const char* enabled_instance_layers_names[] = {
"VK_LAYER_KHRONOS_validation",
};
#else
uint32_t enabled_instance_extensions_count = 3;
uint32_t enabled_instance_layers_count = 0;
const char* enabled_instance_layers_names[] = {};
#endif /* NDEBUG */
vkEnumerateInstanceLayerProperties(&layersCount, NULL);
rse_malloc(layer_properties, sizeof(VkLayerProperties) * layersCount);
vkEnumerateInstanceLayerProperties(&layersCount, layer_properties);
vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL);
rse_malloc(extensions_properties, sizeof(VkExtensionProperties) * extensionCount);
vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensions_properties);
#ifndef NDEBUG
LOGD("Supported Layers: \n");
for (i = 0; i < layersCount; i++) {
LOGD("\t-%s\n", layer_properties[i].layerName);
}
LOGD("Supported Extensions: \n");
for (size_t i = 0; i < extensionCount; i++) {
LOGD("\t-%s\n", 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, enabled_instance_layers_names[i]) == 0) {
supported = 1;
break;
}
}
if(supported == 0) {
LOGF(_("Selected layer is not supported: %s\n"), layer_properties[j].layerName);
return VULKAN_ERROR_LAYER_NOT_SUPPORTED;
}
supported = 0;
}
/* Check if selected extensions are supported*/
for (i = 0; i < enabled_instance_extensions_count; ++i) {
for(j = 0; j < extensionCount; ++j) {
if(strcmp(extensions_properties[j].extensionName, enabled_instance_extensions_names[i]) == 0) {
supported = 1;
break;
}
}
if(supported == 0) {
LOGF(_("Selected extension is not supported: %s\n"), extensions_properties[j].extensionName);
return VULKAN_ERROR_EXTENSION_NOT_SUPPORTED;
}
supported = 0;
}
rse_free(layer_properties);
rse_free(extensions_properties);
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_4;
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;
create_info.ppEnabledLayerNames = enabled_instance_layers_names;
create_info.enabledExtensionCount = enabled_instance_extensions_count;
create_info.ppEnabledExtensionNames = enabled_instance_extensions_names;
result = vkCreateInstance(&create_info, NULL, instance);
if (VK_SUCCESS != result) {
LOGF(_("Vulkan instance initialization failed\n"));
return VULKAN_ERROR_INSTANCE_INIT_FAILED;
}
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Create a Surface, connection between Vulkan and actual window
*
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
*/
static rse_err_t create_surface(VkInstance instance, GLFWwindow* window_handle, VkSurfaceKHR* surface)
{
if (VK_SUCCESS != glfwCreateWindowSurface(instance, window_handle, NULL, surface)) {
LOGF(_("Failed to create window surface\n"));
return VULKAN_ERROR_SURFACE_CREATION_FAILED;
}
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Choose physical device. Used later for vkDevice
*
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND
* VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND
*/
static rse_err_t pick_physical_device(VkInstance instance, VkPhysicalDevice* physical_device)
{
uint32_t physical_device_count = 0U;
VkPhysicalDevice* physical_devices = NULL;
VkPhysicalDeviceProperties device_properties = {0};;
VkPhysicalDeviceFeatures device_features = {0};
/* Get number of existing physical devices */
vkEnumeratePhysicalDevices(instance, &physical_device_count, NULL);
if (0 == physical_device_count) {
LOGF(_("No physical graphical physical devices found in system\n"));
return VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND;
}
rse_malloc(physical_devices, sizeof(VkPhysicalDevice) * physical_device_count);
vkEnumeratePhysicalDevices(instance, &physical_device_count, physical_devices);
/* Check for suitability */
for (size_t physicalDeviceIdx = 0U; physicalDeviceIdx < physical_device_count; ++physicalDeviceIdx) {
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) {
*physical_device = physical_devices[physicalDeviceIdx];
}
}
}
if (VK_NULL_HANDLE == physical_device) {
/* No suitable device found */
LOGF(_("No suitable physical device found\n"));
return VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND;
}
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Create vkDevice object
*
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_QUEUE_NOT_SUPPORTED
* VULKAN_ERROR_DEVICE_CREATION_FAILED
*/
static rse_err_t create_device(struct graphics_context_t* context)
{
bool graphics_family_found = false;
bool presentation_familiy_found = false;
float* graphics_familiy_queue_priorities = NULL;
float* presentation_familiy_queue_priorities = NULL;
uint32_t device_queues_count = 0;
uint32_t graphics_family_idx = 0;
uint32_t presentation_familiy_idx = 0;
uint32_t queue_families_property_count = 0U;
uint32_t enabled_device_extensions_count = 2;
VkBool32 bindless_supported = VK_FALSE;
VkDeviceCreateInfo device_create_info = {0};
VkQueueFamilyProperties* queue_family_properties = NULL;
VkDeviceQueueCreateInfo device_queue_createinfos[2] = {0};
VkPhysicalDeviceFeatures2 physical_features2 = {0};
VkPhysicalDeviceDescriptorIndexingFeatures indexing_features = {0};
const char* enabled_device_extensions_names[] = {
"VK_KHR_swapchain",
"VK_EXT_descriptor_indexing",
};
/* Enable descriptori indexing for bindless textures */
indexing_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
physical_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
physical_features2.pNext = &indexing_features;
vkGetPhysicalDeviceFeatures2(context->physical_device, &physical_features2);
bindless_supported = indexing_features.descriptorBindingPartiallyBound && indexing_features.runtimeDescriptorArray;
if (bindless_supported == VK_FALSE) {
LOGF(_("Required features for bindless design not supported\n"));
return VULKAN_ERROR_MISSING_FEATURES_FOR_BINDLESS_DESIGN;
}
/* Get information about supported queue families */
vkGetPhysicalDeviceQueueFamilyProperties(context->physical_device, &queue_families_property_count, NULL);
rse_malloc(queue_family_properties, sizeof(VkQueueFamilyProperties) * queue_families_property_count);
vkGetPhysicalDeviceQueueFamilyProperties(context->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 (uint32_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;
graphics_family_found = true;
LOGI("Setting graphics family idx to %ld", familyIdx);
numberOfQueues = queue_family_properties[familyIdx].queueCount;
device_queues_count++;
}
}
/* Check for family with surface support */
VkBool32 presentSupport = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(context->physical_device, familyIdx, context->surface, &presentSupport);
if (presentSupport) {
presentation_familiy_idx = familyIdx;
presentation_familiy_found = true;
/* If this is the same queue family as in graphics family, don't increase queue count */
if (presentation_familiy_idx != graphics_family_idx) {
device_queues_count++;
}
}
if (graphics_family_found && presentation_familiy_found) {
break;
}
}
}
/* This is not magic number ;P. We want to support exact number of required queues flags */
if (0 > graphics_family_idx) {
LOGF(_("Selected physical device does not support required queue capabilities\n"));
return VULKAN_ERROR_QUEUE_NOT_SUPPORTED;
}
context->queue_family_indices[0] = graphics_family_idx;
rse_malloc(graphics_familiy_queue_priorities, 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;
}
rse_malloc(presentation_familiy_queue_priorities, 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;
}
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 = &physical_features2;
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_count;
device_create_info.ppEnabledExtensionNames = enabled_device_extensions_names;
device_create_info.pEnabledFeatures = NULL;
if (VK_SUCCESS != vkCreateDevice(context->physical_device, &device_create_info, NULL, &context->device)) {
LOGF(_("Failed to create logical device\n"));
return VULKAN_ERROR_DEVICE_CREATION_FAILED;
}
vkGetDeviceQueue(context->device, graphics_family_idx, 0, &context->graphics_queue);
vkGetDeviceQueue(context->device, presentation_familiy_idx, 0, &context->present_queue);
rse_free(presentation_familiy_queue_priorities);
rse_free(graphics_familiy_queue_priorities);
rse_free(queue_family_properties);
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Create a Memory Allocator object
*
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_ALLOCATOR_CREATION_FAILED
*/
static rse_err_t create_memory_allocator(struct graphics_context_t* context)
{
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 = context->physical_device;
allocator_create_info.device = context->device;
allocator_create_info.instance = context->instance;
allocator_create_info.pVulkanFunctions = &vulkan_functions;
if (VK_SUCCESS != vmaCreateAllocator(&allocator_create_info, &context->allocator)) {
LOGF(_("Failed to create Vulkan Memory Allocator\n"));
return VULKAN_ERROR_ALLOCATOR_CREATION_FAILED;
}
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Create a Sync Objects
*
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED
*/
static rse_err_t create_sync_objects(struct graphics_context_t* context)
{
VkSemaphoreCreateInfo semaphore_info = {0};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fenceInfo = {0};
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(context->device, &semaphore_info, NULL, &context->image_available_semaphores[i]) ||
VK_SUCCESS != vkCreateSemaphore(context->device, &semaphore_info, NULL, &context->render_finished_semaphores[i]) ||
VK_SUCCESS != vkCreateFence(context->device, &fenceInfo, NULL, &context->in_flight_fences[i])) {
LOGF(_("Failed to create sync objects.\n"));
return VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED;
}
}
return RSE_ERROR_NO_ERROR;
}
rse_err_t init_vulkan(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(create_instance(&context->instance));
STATUS_CHECK(setup_debug_messenger(context->instance, &context->debug_messenger));
STATUS_CHECK(create_surface(context->instance, context->window_handle, &context->surface));
STATUS_CHECK(pick_physical_device(context->instance, &context->physical_device));
STATUS_CHECK(create_device(context));
STATUS_CHECK(create_memory_allocator(context));
STATUS_CHECK(create_render_pass(context));
STATUS_CHECK(init_commands(context));
STATUS_CHECK(create_buffers(context));
STATUS_CHECK(init_vulkan_images(context));
STATUS_CHECK(create_swapchain_and_framebuffers(context));
return status;
}
rse_err_t run_vulkan(struct graphics_context_t* context)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
STATUS_CHECK(create_sync_objects(context));
init_time();
return status;
}
rse_err_t draw_frame(struct graphics_context_t* context)
{
uint32_t image_index = 0U;
VkResult result = VK_FALSE;
VkSubmitInfo submit_info = {0};
VkSemaphore wait_semaphores[1];
VkPipelineStageFlags wait_stages[1];
VkSemaphore signal_semaphores[1];
VkPresentInfoKHR present_info = {0};
VkSwapchainKHR swap_chains[1];
vkWaitForFences(context->device, 1, &context->in_flight_fences[context->current_frame], VK_TRUE, UINT64_MAX);
update_time();
result = vkAcquireNextImageKHR(context->device, context->swapchain, UINT64_MAX, context->image_available_semaphores[context->current_frame],
VK_NULL_HANDLE, &image_index);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
recreate_swapchain(context);
return RSE_ERROR_NO_ERROR;
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
LOGF(_("Failed to acquire swapchain image\n"));
return VULKAN_ERROR_ACQUIRE_SWAPCHAIN_FAILED;
}
// update_uniform_buffers(context);
/* Only reset the fence if we are submitting work */
vkResetFences(context->device, 1, &context->in_flight_fences[context->current_frame]);
reset_command_buffer(context);
record_command_buffer(context, image_index);
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
wait_semaphores[0] = context->image_available_semaphores[context->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 = &context->command_buffers[context->current_frame];
signal_semaphores[0] = context->render_finished_semaphores[context->current_frame];
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = signal_semaphores;
if (vkQueueSubmit(context->graphics_queue, 1, &submit_info, context->in_flight_fences[context->current_frame]) != VK_SUCCESS) {
LOGF(_("Failed to submit draw command buffer!\n"));
return VULKAN_ERROR_DRAW_FAILED;
}
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = signal_semaphores;
swap_chains[0] = context->swapchain;
present_info.swapchainCount = 1;
present_info.pSwapchains = swap_chains;
present_info.pImageIndices = &image_index;
result = vkQueuePresentKHR(context->present_queue, &present_info);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || context->is_framebuffer_resized) {
context->is_framebuffer_resized = false;
recreate_swapchain(context);
} else if (result != VK_SUCCESS) {
LOGF(_("Failed to submit draw command buffer!\n"));
return VULKAN_ERROR_DRAW_FAILED;
}
context->current_frame = (context->current_frame + 1) % SWAP_BUFFER_COUNT;
return RSE_ERROR_NO_ERROR;
}
void deinit_vulkan(struct graphics_context_t* context)
{
destroy_buffers(context);
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
vkDestroySemaphore(context->device, context->image_available_semaphores[i], NULL);
vkDestroySemaphore(context->device, context->render_finished_semaphores[i], NULL);
vkDestroyFence(context->device, context->in_flight_fences[i], NULL);
}
destroy_textures(context);
destroy_commands(context);
destroy_render_pass(context);
cleanup_swapchain(context);
destroy_pipelines(context);
vmaDestroyAllocator(context->allocator);
vkDestroyDevice(context->device, NULL);
DestroyDebugUtilsMessengerEXT(context->instance, context->debug_messenger, NULL);
vkDestroySurfaceKHR(context->instance, context->surface, NULL);
vkDestroyInstance(context->instance, NULL);
}