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.
This commit is contained in:
1 parent
649e7f76fe
commit
6a09350902
32 files changed
+2354
-2002
No files matched your search
@@ -0,0 +1,638 @@
|
||||
/**
|
||||
* @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
|
||||
Reference in new issue
Block a user