/** * @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.hpp" #include "vulkan_errors.hpp" #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" #define VMA_IMPLEMENTATION #include "third_party/vma/vk_mem_alloc.h" #pragma GCC diagnostic pop #include "locale_vulkan.hpp" #include "utilities/logger.hpp" #include "window.hpp" #include "vulkan_commons.hpp" #include "vulkan_buffers.hpp" #include "vulkan_descriptors.hpp" #include "vulkan_pipeline.hpp" namespace rse::graphics::vulkanbase { using namespace locale::vulkan; #define APPLICATION_NAME "RedScarfEngine PoC" #define ENGINE_NAME "RedScarf Engine" /* Define Validation Layers for debug build */ #ifndef NDEBUG std::vector gEnabledInstanceLayersNames = {"VK_LAYER_KHRONOS_validation"}; #else std::vector gEnabledInstanceLayersNames = {}; #endif std::vector gEnabledInstanceExtensionsNames = {}; const std::vector gEnabledDeviceExtensionsNames = { "VK_KHR_swapchain", }; VkInstance gVulkanInstance = VK_NULL_HANDLE; VkDebugUtilsMessengerEXT gDebugMessenger = VK_NULL_HANDLE; VkQueue gPresentQueue; std::vector gImageAvailableSemaphores = {}; std::vector gRenderFinishedSemaphores = {}; std::vector gInFlightFences = {}; #ifndef NDEBUG static void setupDebugMessenger(); static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger); static void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator); #endif static void setEnabledExtension(); static uint8_t createInstance(); static uint8_t createSurface(VulkanState& vulkan_state); static uint8_t pickPhysicalDevice(VulkanState& vulkan_state); static uint8_t createDevice(VulkanState& vulkan_state); static uint8_t createMemoryAllocator(VulkanState& vulkan_state); static uint8_t createSyncObjects(VulkanState& vulkan_state); #ifndef NDEBUG /** * @brief Setup debugging system for Vulkan API * */ void setupDebugMessenger() { VkDebugUtilsMessengerCreateInfoEXT createInfo; createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; createInfo.pNext = nullptr; createInfo.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; createInfo.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; createInfo.pfnUserCallback = debugCallback; createInfo.pUserData = nullptr; // Optional createInfo.flags = 0U; if (CreateDebugUtilsMessengerEXT(gVulkanInstance, &createInfo, nullptr, &gDebugMessenger) != VK_SUCCESS) { fprintf(stderr, "failed to set up debug messenger!"); } } /** * @brief Calback for debug messenger * * @param messageSeverity Message severity * @param messageType Type of a message * @param pCallbackData Contains all the callback related data in the VkDebugUtilsMessengerCallbackDataEXT structure * @param pUserData 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 debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { (void)messageSeverity; (void)messageType; (void)pUserData; fprintf(stderr, "validation layer: %s\n", pCallbackData->pMessage); return VK_FALSE; } /** * @brief Proxy for vkCreateDebugUtilsMessengerEXT * * @param instance Vulkan instance object * @param pCreateInfo VkDebugUtilsMessengerCreateInfoEXT structure * @param pAllocator Memory allocator * @param pDebugMessenger Debug messanger handle * @return VkResult VK_SUCCESS on success */ VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) { PFN_vkCreateDebugUtilsMessengerEXT func = nullptr; func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); if (func != nullptr) { return func(instance, pCreateInfo, pAllocator, pDebugMessenger); } else { return VK_ERROR_EXTENSION_NOT_PRESENT; } return VK_SUCCESS; } /** * @brief Proxy for vkDestroyDebugUtilsMessengerEXT * * @param instance Vulkan instance object * @param debugMessenger Debug messanger handle * @param pAllocator Memory allocator */ void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) { PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); if (func != nullptr) { func(instance, debugMessenger, pAllocator); } } #endif /** * @brief Enable instance extensions, based on system and requirements * */ void setEnabledExtension() { gEnabledInstanceExtensionsNames.push_back("VK_KHR_surface"); gEnabledInstanceExtensionsNames.push_back("VK_KHR_device_group_creation"); #ifdef __linux__ if (getenv("WAYLAND_DISPLAY") != NULL) { gEnabledInstanceExtensionsNames.push_back("VK_KHR_wayland_surface"); } else { gEnabledInstanceExtensionsNames.push_back("VK_KHR_xcb_surface"); } #elif defined(_WIN32) || defined(WIN32) gEnabledInstanceExtensionsNames.push_back("VK_KHR_win32_surface"); #endif #ifndef NDEBUG gEnabledInstanceExtensionsNames.push_back("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 createInstance() { uint32_t extensionCount = 0; uint32_t layersCount = 0; VkApplicationInfo applicationInfo; VkInstanceCreateInfo createInfo; std::vector layerProperties; std::vector extensionsProperties; vkEnumerateInstanceLayerProperties(&layersCount, nullptr); layerProperties.resize(layersCount); vkEnumerateInstanceLayerProperties(&layersCount, layerProperties.data()); vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); extensionsProperties.resize(extensionCount); vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensionsProperties.data()); #ifndef NDEBUG LOGD << "Supported Layers"; for (size_t i = 0; i < layersCount; i++) { LOGD << layerProperties[i].layerName; } LOGD << "Supported Extensions"; for (size_t i = 0; i < extensionCount; i++) { LOGD << extensionsProperties[i].extensionName; } #endif /* Check if selected layers are supported*/ for (auto& layer : gEnabledInstanceLayersNames) { if (std::find_if(layerProperties.begin(), layerProperties.end(), [&layer](const VkLayerProperties& lp) { return strcmp(lp.layerName, layer) == 0; }) == std::end(layerProperties)) { LOGF << vulkanErrorMesssages[VULKAN_LAYER_NOT_SUPPORTED] << layer; return VULKAN_ERROR_LAYER_NOT_SUPPORTED; } } /* Check if selected extensions are supported*/ for (auto& extension : gEnabledInstanceExtensionsNames) { if (std::find_if(extensionsProperties.begin(), extensionsProperties.end(), [&extension](const VkExtensionProperties& ep) { return strcmp(ep.extensionName, extension) == 0; }) == std::end(extensionsProperties)) { LOGF << vulkanErrorMesssages[VULKAN_EXTENSION_NOT_SUPPORTED] << extension; return VULKAN_ERROR_EXTENSION_NOT_SUPPORTED; } } applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; applicationInfo.pNext = nullptr; applicationInfo.pApplicationName = APPLICATION_NAME; applicationInfo.applicationVersion = VK_MAKE_VERSION(0, 1, 0); applicationInfo.pEngineName = ENGINE_NAME; applicationInfo.engineVersion = VK_MAKE_VERSION(0, 1, 0); applicationInfo.apiVersion = VK_API_VERSION_1_3; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = 0U; createInfo.pApplicationInfo = &applicationInfo; createInfo.enabledLayerCount = gEnabledInstanceLayersNames.size(); #ifndef NDEBUG createInfo.ppEnabledLayerNames = gEnabledInstanceLayersNames.data(); #else createInfo.ppEnabledLayerNames = nullptr; #endif createInfo.enabledExtensionCount = gEnabledInstanceExtensionsNames.size(); createInfo.ppEnabledExtensionNames = gEnabledInstanceExtensionsNames.data(); VkResult result = vkCreateInstance(&createInfo, nullptr, &gVulkanInstance); if (VK_SUCCESS != result) { LOGF << vulkanErrorMesssages[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 createSurface(VulkanState& vulkan_state) { if (VK_SUCCESS != glfwCreateWindowSurface(gVulkanInstance, window::getWindowHandle(), nullptr, &vulkan_state.surface)) { LOGF << vulkanErrorMesssages[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 pickPhysicalDevice(VulkanState& vulkan_state) { uint32_t physicalDeviceCount = 0U; std::vector pPhysicalDevices; /* Get number of existing physical devices */ vkEnumeratePhysicalDevices(gVulkanInstance, &physicalDeviceCount, nullptr); if (0 == physicalDeviceCount) { LOGF << vulkanErrorMesssages[VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND]; return VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND; } pPhysicalDevices.resize(physicalDeviceCount); vkEnumeratePhysicalDevices(gVulkanInstance, &physicalDeviceCount, pPhysicalDevices.data()); /* Check for suitability */ for (size_t physicalDeviceIdx = 0U; physicalDeviceIdx < physicalDeviceCount; ++physicalDeviceIdx) { VkPhysicalDeviceProperties deviceProperties; VkPhysicalDeviceFeatures deviceFeatures; vkGetPhysicalDeviceProperties(pPhysicalDevices[physicalDeviceIdx], &deviceProperties); vkGetPhysicalDeviceFeatures(pPhysicalDevices[physicalDeviceIdx], &deviceFeatures); /* Expect discrete graphics device type */ if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) { /* Application MUST have geometry shader */ if (deviceFeatures.geometryShader) { vulkan_state.physicalDevice = pPhysicalDevices[physicalDeviceIdx]; } } } if (VK_NULL_HANDLE == vulkan_state.physicalDevice) { /* No suitable device found */ LOGF << vulkanErrorMesssages[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 createDevice(VulkanState& vulkan_state) { uint32_t queueFamiliesPropertyCount = 0U; ssize_t graphicsFamilyIdx = -1; ssize_t presentationFamiliyIdx = -1; std::vector pQueueFamilyProperties; VkDeviceCreateInfo deviceCreateInfo; VkDeviceQueueCreateInfo deviceQueueCreateInfos[2]; /* TODO: Change when we want more queues */ std::vector pGraphicsFamiliyQueuePriorities; std::vector pPresentationFamiliyQueuePriorities; /* Get information about supported queue families */ vkGetPhysicalDeviceQueueFamilyProperties(vulkan_state.physicalDevice, &queueFamiliesPropertyCount, nullptr); pQueueFamilyProperties.resize(queueFamiliesPropertyCount); vkGetPhysicalDeviceQueueFamilyProperties(vulkan_state.physicalDevice, &queueFamiliesPropertyCount, pQueueFamilyProperties.data()); /* Get queue families that support graphics operations AND have most * queues. */ { size_t numberOfQueues = 0; for (size_t familyIdx = 0; familyIdx < queueFamiliesPropertyCount; ++familyIdx) { if (pQueueFamilyProperties[familyIdx].queueFlags & VK_QUEUE_GRAPHICS_BIT) { if (pQueueFamilyProperties[familyIdx].queueCount > numberOfQueues) { graphicsFamilyIdx = familyIdx; numberOfQueues = pQueueFamilyProperties[familyIdx].queueCount; } } /* Check for family with surface support */ VkBool32 presentSupport = false; vkGetPhysicalDeviceSurfaceSupportKHR(vulkan_state.physicalDevice, familyIdx, vulkan_state.surface, &presentSupport); if (presentSupport) { presentationFamiliyIdx = familyIdx; } if (graphicsFamilyIdx > 0 && presentationFamiliyIdx > 0) { break; } } } /* This is not magic number ;P. We want to support exact number of required queues flags */ if (0 > graphicsFamilyIdx) { LOGF << vulkanErrorMesssages[VULKAN_QUEUE_NOT_SUPPORTED]; return VULKAN_ERROR_QUEUE_NOT_SUPPORTED; } vulkan_state.queueFamilyIndices[0] = graphicsFamilyIdx; /* TODO: Change when more families are needed */ pGraphicsFamiliyQueuePriorities.resize(pQueueFamilyProperties[graphicsFamilyIdx].queueCount); for (size_t i = 0; i < pQueueFamilyProperties[graphicsFamilyIdx].queueCount; i++) { pGraphicsFamiliyQueuePriorities[i] = 1.0f; /* TODO: When queues have different tasks, change this if needed */ } pPresentationFamiliyQueuePriorities.resize(pQueueFamilyProperties[presentationFamiliyIdx].queueCount); for (size_t i = 0; i < pQueueFamilyProperties[presentationFamiliyIdx].queueCount; i++) { pPresentationFamiliyQueuePriorities[i] = 1.0f; /* TODO: When queues have different tasks, change this if needed */ } deviceQueueCreateInfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; deviceQueueCreateInfos[0].pNext = nullptr; deviceQueueCreateInfos[0].flags = 0; deviceQueueCreateInfos[0].queueFamilyIndex = graphicsFamilyIdx; deviceQueueCreateInfos[0].queueCount = pQueueFamilyProperties[graphicsFamilyIdx].queueCount; deviceQueueCreateInfos[0].pQueuePriorities = pGraphicsFamiliyQueuePriorities.data(); deviceQueueCreateInfos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; deviceQueueCreateInfos[1].pNext = nullptr; deviceQueueCreateInfos[1].flags = 0; deviceQueueCreateInfos[1].queueFamilyIndex = presentationFamiliyIdx; deviceQueueCreateInfos[1].queueCount = pQueueFamilyProperties[presentationFamiliyIdx].queueCount; deviceQueueCreateInfos[1].pQueuePriorities = pPresentationFamiliyQueuePriorities.data(); deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceCreateInfo.pNext = nullptr; deviceCreateInfo.flags = 0U; deviceCreateInfo.queueCreateInfoCount = 2u; /* For now one is enough. TODO: Change when needed. */ deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfos; deviceCreateInfo.enabledLayerCount = 0; deviceCreateInfo.ppEnabledLayerNames = nullptr; deviceCreateInfo.enabledExtensionCount = gEnabledDeviceExtensionsNames.size(); deviceCreateInfo.ppEnabledExtensionNames = gEnabledDeviceExtensionsNames.data(); deviceCreateInfo.pEnabledFeatures = nullptr; /* TODO: Enable features, when needed */ if (VK_SUCCESS != vkCreateDevice(vulkan_state.physicalDevice, &deviceCreateInfo, nullptr, &vulkan_state.device)) { LOGF << vulkanErrorMesssages[VULKAN_FAILED_TO_CREATE_DEVICE]; return VULKAN_ERROR_DEVICE_CREATION_FAILED; } vkGetDeviceQueue(vulkan_state.device, graphicsFamilyIdx, 0, &vulkan_state.graphicsQueue); vkGetDeviceQueue(vulkan_state.device, presentationFamiliyIdx, 0, &gPresentQueue); 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 createMemoryAllocator(VulkanState& vulkan_state) { VmaVulkanFunctions vulkanFunctions = {0}; vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; VmaAllocatorCreateInfo allocatorCreateInfo = {0}; allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_3; allocatorCreateInfo.physicalDevice = vulkan_state.physicalDevice; allocatorCreateInfo.device = vulkan_state.device; allocatorCreateInfo.instance = gVulkanInstance; allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; if (VK_SUCCESS != vmaCreateAllocator(&allocatorCreateInfo, &vulkan_state.allocator)) { LOGF << vulkanErrorMesssages[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 createSyncObjects(VulkanState& vulkan_state) { gImageAvailableSemaphores.resize(SWAP_BUFFER_COUNT); gRenderFinishedSemaphores.resize(SWAP_BUFFER_COUNT); gInFlightFences.resize(SWAP_BUFFER_COUNT); VkSemaphoreCreateInfo semaphoreInfo{}; semaphoreInfo.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(vulkan_state.device, &semaphoreInfo, nullptr, &gImageAvailableSemaphores[i]) || VK_SUCCESS != vkCreateSemaphore(vulkan_state.device, &semaphoreInfo, nullptr, &gRenderFinishedSemaphores[i]) || VK_SUCCESS != vkCreateFence(vulkan_state.device, &fenceInfo, nullptr, &gInFlightFences[i])) { LOGF << vulkanErrorMesssages[VULKAN_SYNCOBJCTS_CREATION_FAILED]; return VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED; } } return VULKAN_ERROR_NO_ERROR; } uint8_t initVulkan(VulkanState& vulkan_state) { uint8_t status = VULKAN_ERROR_NO_ERROR; setEnabledExtension(); STATUS_CHECK(createInstance()); #ifndef NDEBUG setupDebugMessenger(); #endif STATUS_CHECK(createSurface(vulkan_state)); STATUS_CHECK(pickPhysicalDevice(vulkan_state)); STATUS_CHECK(createDevice(vulkan_state)); STATUS_CHECK(createMemoryAllocator(vulkan_state)); STATUS_CHECK(create_pipeline(vulkan_state)); STATUS_CHECK(create_buffers(vulkan_state)); STATUS_CHECK(create_descriptors(vulkan_state)); STATUS_CHECK(createSyncObjects(vulkan_state)); return status; } void drawFrame(VulkanState& vulkan_state) { vkWaitForFences(vulkan_state.device, 1, &gInFlightFences[vulkan_state.currentFrame], VK_TRUE, UINT64_MAX); uint32_t imageIndex; VkResult result = vkAcquireNextImageKHR(vulkan_state.device, vulkan_state.swapchain, UINT64_MAX, gImageAvailableSemaphores[vulkan_state.currentFrame], VK_NULL_HANDLE, &imageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { recreateSwapchain(vulkan_state); return; } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { // throw std::runtime_error("failed to acquire swap chain image!"); return; } updateUniformBuffer(vulkan_state); /* Only reset the fence if we are submitting work */ vkResetFences(vulkan_state.device, 1, &gInFlightFences[vulkan_state.currentFrame]); reset_command_buffer(vulkan_state); recordCommandBuffer(vulkan_state, imageIndex); VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; VkSemaphore waitSemaphores[] = {gImageAvailableSemaphores[vulkan_state.currentFrame]}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &vulkan_state.pCommandBuffers[vulkan_state.currentFrame]; VkSemaphore signalSemaphores[] = {gRenderFinishedSemaphores[vulkan_state.currentFrame]}; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; if (vkQueueSubmit(vulkan_state.graphicsQueue, 1, &submitInfo, gInFlightFences[vulkan_state.currentFrame]) != VK_SUCCESS) { throw std::runtime_error("failed to submit draw command buffer!"); } VkPresentInfoKHR presentInfo{}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = signalSemaphores; VkSwapchainKHR swapChains[] = {vulkan_state.swapchain}; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = swapChains; presentInfo.pImageIndices = &imageIndex; vkQueuePresentKHR(gPresentQueue, &presentInfo); vulkan_state.currentFrame = (vulkan_state.currentFrame + 1) % SWAP_BUFFER_COUNT; } void deinitVulkan(VulkanState& vulkan_state) { cleanup_swapchain(vulkan_state); destroy_descriptors(vulkan_state); destroy_buffers(vulkan_state); for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) { vkDestroySemaphore(vulkan_state.device, gImageAvailableSemaphores[i], nullptr); vkDestroySemaphore(vulkan_state.device, gRenderFinishedSemaphores[i], nullptr); vkDestroyFence(vulkan_state.device, gInFlightFences[i], nullptr); } destroy_pipeline(vulkan_state); vmaDestroyAllocator(vulkan_state.allocator); vkDestroyDevice(vulkan_state.device, nullptr); #ifndef NDEBUG DestroyDebugUtilsMessengerEXT(gVulkanInstance, gDebugMessenger, nullptr); #endif vkDestroySurfaceKHR(gVulkanInstance, vulkan_state.surface, nullptr); vkDestroyInstance(gVulkanInstance, nullptr); } } // namespace rse::graphics::vulkanbase