Files
RedScarfEngine/graphics/src/vulkan_base.cpp
T
Piotr Krygier 6a09350902 Split vulkan_base into smaller units
Closes #30. This is a basic refactoring of the graphics module, so
everything won't be in single file.
2023-03-29 09:41:04 +02:00

638 lines
24 KiB
C++

/**
* @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 <GLFW/glfw3.h>
#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<const char*> gEnabledInstanceLayersNames = {"VK_LAYER_KHRONOS_validation"};
#else
std::vector<const char*> gEnabledInstanceLayersNames = {};
#endif
std::vector<const char*> gEnabledInstanceExtensionsNames = {};
const std::vector<const char*> gEnabledDeviceExtensionsNames = {
"VK_KHR_swapchain",
};
VkInstance gVulkanInstance = VK_NULL_HANDLE;
VkDebugUtilsMessengerEXT gDebugMessenger = VK_NULL_HANDLE;
VkQueue gPresentQueue;
std::vector<VkSemaphore> gImageAvailableSemaphores = {};
std::vector<VkSemaphore> gRenderFinishedSemaphores = {};
std::vector<VkFence> 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<VkLayerProperties> layerProperties;
std::vector<VkExtensionProperties> 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<VkPhysicalDevice> 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<VkQueueFamilyProperties> pQueueFamilyProperties;
VkDeviceCreateInfo deviceCreateInfo;
VkDeviceQueueCreateInfo deviceQueueCreateInfos[2]; /* TODO: Change when we want more queues */
std::vector<float> pGraphicsFamiliyQueuePriorities;
std::vector<float> 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