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:
Piotr Krygier committed 2023-03-29 09:41:04 +02:00
1 parent 649e7f76fe
commit 6a09350902
32 files changed
+2346 -1994

No files matched your search

+5 -3
View File
@@ -27,7 +27,9 @@ set(CMAKE_CXX_STANDARD 20)# turn on the dynamic depends for ninja
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
add_subdirectory(RedScarfEngine)
add_subdirectory(Utilities) add_subdirectory(red_scarf_engine)
add_subdirectory(Graphics) add_subdirectory(utilities)
add_subdirectory(graphics)
File diff suppressed because it is too large. Load diff
-22
View File
@@ -1,22 +0,0 @@
#include "logger.hpp"
#include "window.hpp"
#include "vulkanBase.hpp"
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
rse::graphics::window::windowInit();
if (0 != rse::graphics::vulkanbase::initVulkan())
return -1;
rse::graphics::window::windowLoop();
rse::graphics::vulkanbase::deinitVulkan();
rse::graphics::window::windowTerminate();
return 0;
}
-8
View File
@@ -1,8 +0,0 @@
add_library(rse_utilities)
target_include_directories(rse_utilities PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_sources(rse_utilities PRIVATE
src/logger.cpp)
@@ -1,5 +1,3 @@
add_library(rse_graphics)
find_package(Vulkan REQUIRED) find_package(Vulkan REQUIRED)
find_package(glfw3 REQUIRED) find_package(glfw3 REQUIRED)
find_package(glm REQUIRED) find_package(glm REQUIRED)
@@ -7,9 +5,11 @@ find_package(glm REQUIRED)
find_package(Vulkan COMPONENTS glslc) find_package(Vulkan COMPONENTS glslc)
find_program(glslc_executable NAMES glslc HINTS Vulkan::glslc) find_program(glslc_executable NAMES glslc HINTS Vulkan::glslc)
cmake_policy(SET CMP0116 NEW)
function(compile_shader) function(compile_shader)
cmake_parse_arguments(PARSE_ARGV 1 arg "" "ENV;FORMAT" "SOURCES") cmake_parse_arguments(PARSE_ARGV 1 arg "" "ENV;FORMAT" "SOURCES")
foreach(source ${arg_SOURCES}) foreach(source ${arg_SOURCES})
message(${source}.${arg_FORMAT})
add_custom_command( add_custom_command(
OUTPUT ${source}.${arg_FORMAT} OUTPUT ${source}.${arg_FORMAT}
DEPENDS ${source} DEPENDS ${source}
@@ -23,29 +23,39 @@ function(compile_shader)
endfunction() endfunction()
compile_shader(rse_graphics
ENV opengl
FORMAT num
SOURCES
shaders/shader.vert
shaders/shader.frag
)
add_library(rse_graphics SHARED shaders/shader.vert.num shaders/shader.frag.num)
target_include_directories(rse_graphics PRIVATE target_include_directories(rse_graphics PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/third_party/vma/include ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/Utilities/include ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}
${Vulkan_INCLUDE_DIR} ${Vulkan_INCLUDE_DIR}
${glfw3_INCLUDE_DIR} ${glfw3_INCLUDE_DIR}
${glm_INCLUDE_DIR}) ${glm_INCLUDE_DIR}
)
target_sources(rse_graphics PRIVATE target_sources(rse_graphics PRIVATE
src/window.cpp src/window.cpp
src/vulkanBase.cpp) src/vulkan_base.cpp
src/vulkan_pipeline.cpp
src/vulkan_buffers.cpp
src/vulkan_descriptors.cpp
src/rse_graphics.cpp
)
target_link_libraries(rse_graphics PRIVATE target_link_libraries(rse_graphics PRIVATE
rse_utilities rse_utilities
${Vulkan_LIBRARIES} ${Vulkan_LIBRARIES}
glfw glfw
glm) glm
compile_shader(rse_graphics
ENV opengl
FORMAT num
SOURCES
shaders/shader.vert
shaders/shader.frag
) )
+31
View File
@@ -0,0 +1,31 @@
#ifndef RSE_GRAPHICS_HPP
#define RSE_GRAPHICS_HPP
#include <cstdint>
#include "src/vulkan_commons.hpp"
namespace rse::graphics
{
class RseGraphics {
public:
RseGraphics();
RseGraphics& operator=(const RseGraphics&) = delete;
RseGraphics(const RseGraphics&) = delete;
/**
* @brief Initialize graphics module, runs main loop and deinits everything, when interrupted
*
* @return uint8_t 0 on success.
*/
uint8_t graphics_run(void);
private:
/* Hold vulkan state. It is modified by function initializing and communicating with Vulkan API */
vulkanbase::VulkanState vulkan_state;
};
} // namespace rse::graphics
#endif
File renamed without changes.
File renamed without changes.
@@ -14,8 +14,7 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include "localization.hpp" #include "utilities/localization.hpp"
namespace rse::locale::vulkan namespace rse::locale::vulkan
{ {
@@ -48,12 +47,13 @@ enum Errors
VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED, VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED,
VULKAN_GRAPHICSPIPELINE_CREATION_FAILED, VULKAN_GRAPHICSPIPELINE_CREATION_FAILED,
VULKAN_FRAMEBUFFERS_CREATION_FAILED, VULKAN_FRAMEBUFFERS_CREATION_FAILED,
VULKAN_RECORD_COMMAND_BEGIN_FAILED,
VULKAN_RECORD_COMMAND_BUFFER_FAILED, VULKAN_RECORD_COMMAND_BUFFER_FAILED,
VULKAN_SYNCOBJCTS_CREATION_FAILED, VULKAN_SYNCOBJCTS_CREATION_FAILED,
LAST_MESSAGE LAST_MESSAGE
}; };
std::array<std::array<const char*, LAST_MESSAGE>, LAST_LANGUAGE> vulkanErrorMesssages_nolocale = { inline std::array<std::array<const char*, LAST_MESSAGE>, rse::utils::locale::LAST_LANGUAGE> vulkanErrorMesssages_nolocale = {
{/* EN_US */ {/* EN_US */
/* VULKAN_LAYER_NOT_SUPPORTED */ /* VULKAN_LAYER_NOT_SUPPORTED */
"Selected layer is not supported: ", "Selected layer is not supported: ",
@@ -103,6 +103,8 @@ std::array<std::array<const char*, LAST_MESSAGE>, LAST_LANGUAGE> vulkanErrorMess
"Failed to create graphics pipeline", "Failed to create graphics pipeline",
/* VULKAN_FRAMEBUFFERS_CREATION_FAILED */ /* VULKAN_FRAMEBUFFERS_CREATION_FAILED */
"Failed to create framebuffer", "Failed to create framebuffer",
/* VULKAN_RECORD_COMMAND_BEGIN_FAILED */
"Failed to start recording command buffer",
/* VULKAN_RECORD_COMMAND_BUFFER_FAILED */ /* VULKAN_RECORD_COMMAND_BUFFER_FAILED */
"Failed to record command buffer. Validate your commands.", "Failed to record command buffer. Validate your commands.",
/* VULKAN_SYNCOBJCTS_CREATION_FAILED */ /* VULKAN_SYNCOBJCTS_CREATION_FAILED */
@@ -13,7 +13,7 @@
#include <array> #include <array>
#include "localization.hpp" #include "utilities/localization.hpp"
namespace rse::locale::window namespace rse::locale::window
@@ -29,7 +29,7 @@ enum Errors
LAST_MESSAGE LAST_MESSAGE
}; };
std::array<std::array<const char*, LAST_LANGUAGE>, LAST_MESSAGE> windowErrorMesssages_nolocale = { std::array<std::array<const char*, rse::utils::locale::LAST_LANGUAGE>, LAST_MESSAGE> windowErrorMesssages_nolocale = {
{/* EN_US */ {/* EN_US */
/* WINDOW_GLFW_INIT_FAILED */ /* WINDOW_GLFW_INIT_FAILED */
"GLFW init failed!" "GLFW init failed!"
+47
View File
@@ -0,0 +1,47 @@
#include "rse_graphics.hpp"
#include "window.hpp"
#include "vulkan_base.hpp"
namespace rse::graphics
{
RseGraphics::RseGraphics()
{
vulkan_state = {
.graphicsQueue = VK_NULL_HANDLE,
.physicalDevice = VK_NULL_HANDLE,
.currentFrame = 0U,
.queueFamilyIndices = {0U}, /* TODO: Change when more families are needed */
.uniformBuffers = {},
.pCommandBuffers = {},
.descriptorSets = {},
.swapChainImageViews = {},
.swapchainExtent = {},
.renderPass = VK_NULL_HANDLE,
.descriptorSetLayout = VK_NULL_HANDLE,
.swapchainFramebuffers = {},
.graphicsPipeline = VK_NULL_HANDLE,
.pipelineLayout = VK_NULL_HANDLE,
.swapchain = VK_NULL_HANDLE,
};
}
uint8_t RseGraphics::graphics_run(void)
{
window::windowInit();
if (0 != vulkanbase::initVulkan(vulkan_state))
return -1;
window::windowLoop(vulkan_state);
vulkanbase::deinitVulkan(vulkan_state);
window::windowTerminate();
return 0;
}
} // namespace rse::graphics
+638
View File
@@ -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
@@ -13,30 +13,37 @@
#define RSE_GRAPHICS_VULKANBASE_HPP #define RSE_GRAPHICS_VULKANBASE_HPP
#include <cstdint> #include <cstdint>
#include <cstddef>
#include <vulkan/vulkan.hpp>
#include "vulkan_commons.hpp"
namespace rse::graphics::vulkanbase namespace rse::graphics::vulkanbase
{ {
/** /**
* @brief Initialize Vulkan backend * @brief Initialize Vulkan backend
* *
* @param state
* @return uint8_t 0 on success. Status code on failure * @return uint8_t 0 on success. Status code on failure
*/ */
uint8_t initVulkan(VulkanState& state);
uint8_t initVulkan();
/** /**
* @brief Deinitialize Vulkan backend * @brief Deinitialize Vulkan backend
* *
* @param state
*/ */
void deinitVulkan(); void deinitVulkan(VulkanState& state);
/** /**
* @brief Draw frame on the screen. * @brief Draw frame on the screen.
* *
* @param state
*/ */
void drawFrame(); void drawFrame(VulkanState& state);
} /* namespace rse::graphics::vulkanbase */ } /* namespace rse::graphics::vulkanbase */
+419
View File
@@ -0,0 +1,419 @@
#include "vulkan_buffers.hpp"
#include "vulkan_errors.hpp"
#include "locale_vulkan.hpp"
#include "utilities/logger.hpp"
#include <cstdint>
#define GLM_FORCE_RADIANS
#include <glm/gtc/matrix_transform.hpp>
namespace rse::graphics::vulkanbase
{
using namespace locale::vulkan;
// FIXME: Temporary array of vertices, for testing purposes
const std::vector<Vertex> vertices = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}},
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 1.0f}},
{{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}},
{{-0.5f, 0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}}};
// FIXME: Temporary array of vertices, for testing purposes
std::vector<uint16_t> indices = {0, 1, 2, 2, 3, 0};
/* GPU visible buffer */
Buffer gVertexBuffer = {};
/* CPU visible buffer. TODO: Do we need this global? */
Buffer gVertexStagingBuffer = {};
Buffer gIndexBuffer = {};
Buffer gIndexStagingBuffer = {};
VkCommandPool gCommandPool = VK_NULL_HANDLE;
static uint8_t createBuffer(VulkanState& vulkan_state, const VkDeviceSize size, VkBufferUsageFlags bufferUsage, VmaMemoryUsage memoryUsage,
const VmaAllocationCreateFlags allocationFlags, Buffer& buffer);
static uint8_t copyBuffer(VulkanState& vulkan_state, VkBuffer src, VkBuffer dst, VkDeviceSize size);
static uint8_t createCommandPools(VulkanState& vulkan_state);
static uint8_t createVertexBuffer(VulkanState& vulkan_state);
static uint8_t createIndexBuffer(VulkanState& vulkan_state);
static uint8_t createUniformBuffers(VulkanState& vulkan_state);
static uint8_t allocateCommandBuffers(VulkanState& vulkan_state);
/**
* @brief Helper function. Creates a buffer object
*
* @param size Size of the target buffer
* @param bufferUsage Buffer usage flags
* @param memoryUsage memory usage flags (mostly VMA_MEMORY_USAGE_AUTO)
* @param allocationFlags VMA allocation flags
* @param buffer Buffer, that will be allocated
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED
*/
uint8_t createBuffer(VulkanState& vulkan_state, const VkDeviceSize size, VkBufferUsageFlags bufferUsage,
VmaMemoryUsage memoryUsage, const VmaAllocationCreateFlags allocationFlags, Buffer& buffer)
{
VkBufferCreateInfo vertexBufferInfo;
VmaAllocationCreateInfo createInfo;
vertexBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
vertexBufferInfo.pNext = nullptr;
vertexBufferInfo.flags = 0U;
vertexBufferInfo.size = size;
vertexBufferInfo.usage = bufferUsage;
vertexBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vertexBufferInfo.queueFamilyIndexCount = 0U;
vertexBufferInfo.pQueueFamilyIndices = nullptr;
/* Disalows random access to memory. TODO: Consider changing to VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT? */
createInfo.flags = allocationFlags;
createInfo.usage = memoryUsage;
createInfo.memoryTypeBits = 0U;
createInfo.requiredFlags = 0U;
createInfo.preferredFlags = 0U;
createInfo.pool = VK_NULL_HANDLE;
createInfo.pUserData = VK_NULL_HANDLE;
createInfo.priority = 0.0f;
if (VK_SUCCESS != vmaCreateBuffer(vulkan_state.allocator, &vertexBufferInfo, &createInfo, &buffer.buffer, &buffer.allocation,
&buffer.allocationInfo)) {
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Copy one buffer's data to another
*
* @param src Source buffer
* @param dst Destination buffer
* @param size Size of buffer to copy
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
*/
uint8_t copyBuffer(VulkanState& vulkan_state, VkBuffer src, VkBuffer dst, VkDeviceSize size)
{
/* Vulkan buffers can only be copied using command buffers */
VkBufferCopy copyRegion;
VkSubmitInfo submitInfo;
VkCommandBuffer copyCommandBuffer;
VkCommandBufferBeginInfo beginInfo;
VkCommandBufferAllocateInfo allocInfo;
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.pNext = nullptr;
allocInfo.commandPool = gCommandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.pNext = nullptr;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
beginInfo.pInheritanceInfo = nullptr;
copyRegion.srcOffset = 0;
copyRegion.dstOffset = 0;
copyRegion.size = size;
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pNext = nullptr;
submitInfo.waitSemaphoreCount = 0;
submitInfo.pWaitSemaphores = nullptr;
submitInfo.pWaitDstStageMask = nullptr;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &copyCommandBuffer;
submitInfo.signalSemaphoreCount = 0;
submitInfo.pSignalSemaphores = nullptr;
// FIXME: At result checks
vkAllocateCommandBuffers(vulkan_state.device, &allocInfo, &copyCommandBuffer);
vkBeginCommandBuffer(copyCommandBuffer, &beginInfo);
vkCmdCopyBuffer(copyCommandBuffer, src, dst, 1, &copyRegion);
vkEndCommandBuffer(copyCommandBuffer);
vkQueueSubmit(vulkan_state.graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
/* TODO: Use fences to wait instead of idle */
vkQueueWaitIdle(vulkan_state.graphicsQueue);
vkFreeCommandBuffers(vulkan_state.device, gCommandPool, 1, &copyCommandBuffer);
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create Command Pools
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED
*/
uint8_t createCommandPools(VulkanState& vulkan_state)
{
VkCommandPoolCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
createInfo.queueFamilyIndex = vulkan_state.queueFamilyIndices[0]; /* TODO: Change when more families are needed */
if (VK_SUCCESS != vkCreateCommandPool(vulkan_state.device, &createInfo, nullptr, &gCommandPool)) {
LOGF << vulkanErrorMesssages[VULKAN_COMMAND_POOL_CREATION_FAILED];
return VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED;
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create a buffer holding all vertex data
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_BUFFER_CREATION_FAILED
* VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED
*/
uint8_t createVertexBuffer(VulkanState& vulkan_state)
{
void* mappedData;
VkDeviceSize bufferSize;
bufferSize = sizeof(vertices[0]) * vertices.size();
/* Creating staging buffer*/
if (VK_SUCCESS != createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, gVertexStagingBuffer)) {
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
/* Create Vertex Buffer*/
if (VK_SUCCESS != createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0, /* Will not be mapped with vmaMapMemory */
gVertexBuffer)) {
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
/* Fill staging buffer */
if (VK_SUCCESS != vmaMapMemory(vulkan_state.allocator, gVertexStagingBuffer.allocation, &mappedData)) {
LOGF << vulkanErrorMesssages[VULKAN_VERTEX_BUFFER_MAPPING_FAILED];
return VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED;
}
memcpy(mappedData, vertices.data(), sizeof(Vertex) * vertices.size());
vmaUnmapMemory(vulkan_state.allocator, gVertexStagingBuffer.allocation);
copyBuffer(vulkan_state, gVertexStagingBuffer.buffer, gVertexBuffer.buffer, bufferSize);
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create an index buffer, connected with vertex buffer
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_BUFFER_CREATION_FAILED
*/
uint8_t createIndexBuffer(VulkanState& vulkan_state)
{
void* mappedData;
VkDeviceSize bufferSize;
bufferSize = sizeof(indices[0]) * indices.size();
/* Creating staging buffer*/
if (VK_SUCCESS != createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, gIndexStagingBuffer)) {
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
/* Create Vertex Buffer*/
if (VK_SUCCESS != createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0, /* Will not be mapped with vmaMapMemory */
gIndexBuffer)) {
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
/* Fill staging buffer */
if (VK_SUCCESS != vmaMapMemory(vulkan_state.allocator, gIndexStagingBuffer.allocation, &mappedData)) {
LOGF << vulkanErrorMesssages[VULKAN_VERTEX_BUFFER_MAPPING_FAILED];
return VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED;
}
memcpy(mappedData, indices.data(), static_cast<size_t>(bufferSize));
vmaUnmapMemory(vulkan_state.allocator, gIndexStagingBuffer.allocation);
copyBuffer(vulkan_state, gIndexStagingBuffer.buffer, gIndexBuffer.buffer, bufferSize);
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create a Uniform Buffers
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
*/
uint8_t createUniformBuffers(VulkanState& vulkan_state)
{
VkDeviceSize bufferSize = sizeof(UniformBufferObject);
vulkan_state.uniformBuffers.resize(SWAP_BUFFER_COUNT);
for (size_t i = 0; i < SWAP_BUFFER_COUNT; i++) {
createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
vulkan_state.uniformBuffers[i]);
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Allocate Command Buffers
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED
*/
uint8_t allocateCommandBuffers(VulkanState& vulkan_state)
{
VkCommandBufferAllocateInfo allocateInfo;
/* Allocate memory for command buffers */
vulkan_state.pCommandBuffers.resize(SWAP_BUFFER_COUNT);
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocateInfo.pNext = nullptr;
allocateInfo.commandPool = gCommandPool;
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; /* TODO: When needed, add secondary command buffer */
allocateInfo.commandBufferCount = SWAP_BUFFER_COUNT;
if (VK_SUCCESS != vkAllocateCommandBuffers(vulkan_state.device, &allocateInfo, vulkan_state.pCommandBuffers.data())) {
LOGF << vulkanErrorMesssages[VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED];
return VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED;
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Update uniform buffers
*
* @param currentImage Current image index
*/
void updateUniformBuffer(VulkanState& vulkan_state)
{
UniformBufferObject ubo{};
ubo.model = glm::mat4(1.0f);
ubo.view = glm::lookAt(glm::vec3(0.0f, 0.0f, 5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
ubo.proj =
glm::perspective(glm::radians(45.0f), vulkan_state.swapchainExtent.width / (float)vulkan_state.swapchainExtent.height, 0.1f, 20.0f);
ubo.proj[1][1] *= -1; /* GLM was developed for OpenGL, where Y coordinate of the clip is inverted */
memcpy(vulkan_state.uniformBuffers[vulkan_state.currentFrame].allocationInfo.pMappedData, &ubo, sizeof(ubo));
}
/**
* @brief Records commands for provided command buffer
*
* @param commandBuffer Command buffer, that will hold commands
* @param imageIndex Image index
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED
*/
uint8_t recordCommandBuffer(VulkanState& vulkan_state, uint32_t imageIndex)
{
VkCommandBuffer commandBuffer = vulkan_state.pCommandBuffers[vulkan_state.currentFrame];
VkCommandBufferBeginInfo beginInfo{};
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
VkRenderPassBeginInfo renderPassInfo{};
VkViewport viewport{};
VkRect2D scissor{};
VkBuffer vertexBuffers[] = {gVertexBuffer.buffer};
VkDeviceSize offsets[] = {0};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0;
beginInfo.pInheritanceInfo = nullptr;
if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
LOGF << vulkanErrorMesssages[VULKAN_RECORD_COMMAND_BEGIN_FAILED];
return VULKAN_ERROR_RECORD_COMMAND_BEGIN_FAILED;
}
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = vulkan_state.renderPass;
renderPassInfo.framebuffer = vulkan_state.swapchainFramebuffers[imageIndex];
renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = vulkan_state.swapchainExtent;
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_state.graphicsPipeline);
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(vulkan_state.swapchainExtent.width);
viewport.height = static_cast<float>(vulkan_state.swapchainExtent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
scissor.offset = {0, 0};
scissor.extent = vulkan_state.swapchainExtent;
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(commandBuffer, gIndexBuffer.buffer, 0, VK_INDEX_TYPE_UINT16);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_state.pipelineLayout, 0, 1,
&vulkan_state.descriptorSets[vulkan_state.currentFrame], 0, nullptr);
vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(indices.size()), 1, 0, 0, 0);
vkCmdEndRenderPass(commandBuffer);
if (VK_SUCCESS != vkEndCommandBuffer(commandBuffer)) {
LOGF << vulkanErrorMesssages[VULKAN_RECORD_COMMAND_BUFFER_FAILED];
return VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED;
}
return VULKAN_ERROR_NO_ERROR;
}
uint8_t create_buffers(VulkanState& vulkan_state)
{
uint8_t status = VULKAN_ERROR_NO_ERROR;\
STATUS_CHECK(createCommandPools(vulkan_state));
STATUS_CHECK(createVertexBuffer(vulkan_state));
STATUS_CHECK(createIndexBuffer(vulkan_state));
STATUS_CHECK(createUniformBuffers(vulkan_state));
STATUS_CHECK(allocateCommandBuffers(vulkan_state));
return status;
}
void reset_command_buffer(VulkanState& vulkan_state)
{
vkResetCommandBuffer(vulkan_state.pCommandBuffers[vulkan_state.currentFrame], /*VkCommandBufferResetFlagBits*/ 0);
}
void destroy_buffers(VulkanState& vulkan_state)
{
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
vmaDestroyBuffer(vulkan_state.allocator, vulkan_state.uniformBuffers[i].buffer, vulkan_state.uniformBuffers[i].allocation);
}
vmaDestroyBuffer(vulkan_state.allocator, gVertexBuffer.buffer, gVertexBuffer.allocation);
vmaDestroyBuffer(vulkan_state.allocator, gVertexStagingBuffer.buffer, gVertexStagingBuffer.allocation);
vmaDestroyBuffer(vulkan_state.allocator, gIndexBuffer.buffer, gIndexBuffer.allocation);
vmaDestroyBuffer(vulkan_state.allocator, gIndexStagingBuffer.buffer, gIndexStagingBuffer.allocation);
vkDestroyCommandPool(vulkan_state.device, gCommandPool, nullptr);
}
}
+56
View File
@@ -0,0 +1,56 @@
#ifndef RSE_VULKAN_BUFFERS_HPP
#define RSE_VULKAN_BUFFERS_HPP
#include <cstdint>
#include <vector>
#include <vulkan/vulkan.hpp>
#include "third_party/vma/vk_mem_alloc.h"
#include "vulkan_commons.hpp"
namespace rse::graphics::vulkanbase
{
/**
* @brief Create buffers needed by vulkan pipeline
*
* @param vulkan_state
* @return uint8_t
*/
uint8_t create_buffers(VulkanState& vulkan_state);
/**
* @brief Update uniform buffers
*
* @param vulkan_state
*/
void updateUniformBuffer(VulkanState& vulkan_state);
/**
* @brief Record commands for given image index
*
* @param vulkan_state
* @param imageIndex
* @return uint8_t
*/
uint8_t recordCommandBuffer(VulkanState& vulkan_state, uint32_t imageIndex);
/**
* @brief Reset command buffer for current frame.
*
* @param vulkan_state
*/
void reset_command_buffer(VulkanState& vulkan_state);
/**
* @brief Destroy ALL previously allocated buffers
*
* @param vulkan_state
*/
void destroy_buffers(VulkanState& vulkan_state);
} // namespace rse::graphics::vulkanbase
#endif
+104
View File
@@ -0,0 +1,104 @@
/**
* @file vulkan_global.hpp
* @author Piotr Krygier (piotr.krygier@meshsystems.com)
* @brief Declaration of a structure holding Vulkan variables needed by different parts of the code
* @version 0.1
* @date 2023-03-28
*
* @copyright Copyright (c) 2023
*
*/
#ifndef RSE_VULKAN_GLOBAL_HPP
#define RSE_VULKAN_GLOBAL_HPP
#include <vulkan/vulkan.hpp>
#include <glm/glm.hpp>
#include "third_party/vma/vk_mem_alloc.h"
#define SWAP_BUFFER_COUNT 2U
namespace rse::graphics::vulkanbase
{
/**
* @brief Represents single vertex on the screen
*
*/
struct Vertex
{
glm::vec3 pos;
glm::vec3 color;
static VkVertexInputBindingDescription getBindingDescription() {
VkVertexInputBindingDescription bindingDescription;
bindingDescription.binding = 0;
bindingDescription.stride = sizeof(Vertex);
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
return bindingDescription;
}
static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescriptions() {
std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions{};
attributeDescriptions[0].binding = 0;
attributeDescriptions[0].location = 0; /* inPosition in shader.vert */
attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[0].offset = offsetof(Vertex, pos);
attributeDescriptions[1].binding = 0;
attributeDescriptions[1].location = 1; /* inColor in shader.vert */
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[1].offset = offsetof(Vertex, color);
return attributeDescriptions;
}
};
/**
* @brief Wrapper for Vulkan buffer. Holds allocation data;
*
*/
struct Buffer
{
VkBuffer buffer;
VmaAllocation allocation;
VmaAllocationInfo allocationInfo;
};
struct UniformBufferObject
{
alignas(16) glm::mat4 model;
alignas(16) glm::mat4 view;
alignas(16) glm::mat4 proj;
};
struct VulkanState
{
VkDevice device;
VkSurfaceKHR surface;
VkQueue graphicsQueue;
VmaAllocator allocator;
VkPhysicalDevice physicalDevice;
uint32_t currentFrame;
uint32_t queueFamilyIndices[1];
std::vector<Buffer> uniformBuffers;
std::vector<VkCommandBuffer> pCommandBuffers;
std::vector<VkDescriptorSet> descriptorSets;
std::vector<VkImageView> swapChainImageViews;
VkExtent2D swapchainExtent;
VkRenderPass renderPass;
VkDescriptorSetLayout descriptorSetLayout;
std::vector<VkFramebuffer> swapchainFramebuffers;
VkPipeline graphicsPipeline;
VkPipelineLayout pipelineLayout;
VkSwapchainKHR swapchain;
};
} // namespace rse::graphics::vulkanbase
#endif
+101
View File
@@ -0,0 +1,101 @@
#include "vulkan_descriptors.hpp"
#include "locale_vulkan.hpp"
#include "utilities/logger.hpp"
#include "vulkan_errors.hpp"
namespace rse::graphics::vulkanbase
{
using namespace locale::vulkan;
VkDescriptorPool gDescriptorPool = {};
static uint8_t createDescriptorPool(VulkanState& vulkan_state);
static uint8_t createDescriptorSets(VulkanState& vulkan_state);
/**
* @brief Create a Descriptor Pool
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED
*/
uint8_t createDescriptorPool(VulkanState& vulkan_state)
{
VkDescriptorPoolSize poolSize{};
poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSize.descriptorCount = static_cast<uint32_t>(SWAP_BUFFER_COUNT);
VkDescriptorPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = 1;
poolInfo.pPoolSizes = &poolSize;
poolInfo.maxSets = static_cast<uint32_t>(SWAP_BUFFER_COUNT);
if (vkCreateDescriptorPool(vulkan_state.device, &poolInfo, nullptr, &gDescriptorPool) != VK_SUCCESS) {
LOGF << vulkanErrorMesssages[VULKAN_DESCRIPTOR_POOL_CREATION_FAILED];
return VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED;
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create a Descriptor Sets
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_DESCRIPTOR_SETS_ALLOCATION_FAILED */
uint8_t createDescriptorSets(VulkanState& vulkan_state)
{
std::vector<VkDescriptorSetLayout> layouts(SWAP_BUFFER_COUNT, vulkan_state.descriptorSetLayout);
VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = gDescriptorPool;
allocInfo.descriptorSetCount = static_cast<uint32_t>(SWAP_BUFFER_COUNT);
allocInfo.pSetLayouts = layouts.data();
vulkan_state.descriptorSets.resize(SWAP_BUFFER_COUNT);
if (vkAllocateDescriptorSets(vulkan_state.device, &allocInfo, vulkan_state.descriptorSets.data()) != VK_SUCCESS) {
LOGF << vulkanErrorMesssages[VULKAN_DESCRIPTOR_SETS_ALLOCATION_FAILED];
return VULKAN_ERROR_DESCRIPTOR_SETS_ALLOCATION_FAILED;
}
for (size_t i = 0; i < SWAP_BUFFER_COUNT; i++) {
VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = vulkan_state.uniformBuffers[i].buffer;
bufferInfo.offset = 0;
bufferInfo.range = sizeof(UniformBufferObject);
VkWriteDescriptorSet descriptorWrite{};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.dstSet = vulkan_state.descriptorSets[i];
descriptorWrite.dstBinding = 0;
descriptorWrite.dstArrayElement = 0;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrite.descriptorCount = 1;
descriptorWrite.pBufferInfo = &bufferInfo;
vkUpdateDescriptorSets(vulkan_state.device, 1, &descriptorWrite, 0, nullptr);
}
return VULKAN_ERROR_NO_ERROR;
}
uint8_t create_descriptors(VulkanState& vulkan_state)
{
uint8_t status = VULKAN_ERROR_NO_ERROR;
STATUS_CHECK(createDescriptorPool(vulkan_state));
STATUS_CHECK(createDescriptorSets(vulkan_state));
return status;
}
void destroy_descriptors(VulkanState& vulkan_state)
{
vkDestroyDescriptorPool(vulkan_state.device, gDescriptorPool, nullptr);
vkDestroyDescriptorSetLayout(vulkan_state.device, vulkan_state.descriptorSetLayout, nullptr);
}
} // namespace rse::graphics::vulkanbase
+32
View File
@@ -0,0 +1,32 @@
#ifndef RSE_VULKAN_DESCRIPTORS_HPP
#define RSE_VULKAN_DESCRIPTORS_HPP
#include <cstdint>
#include <vector>
#include <vulkan/vulkan.hpp>
#include "vulkan_commons.hpp"
namespace rse::graphics::vulkanbase
{
/**
* @brief Create descriptors
*
* @param vulkan_state
* @return uint8_t
*/
uint8_t create_descriptors(VulkanState& vulkan_state);
/**
* @brief Destroy all descriptors
*
* @param vulkan_state
*/
void destroy_descriptors(VulkanState& vulkan_state);
} // namespace rse::graphics::vulkanbase
#endif
+43
View File
@@ -0,0 +1,43 @@
#ifndef VULKAN_ERRORS_HPP
#define VULKAN_ERRORS_HPP
/* Macro for checking status of function execution in initVulkan. Created to avoid writing boilerplate code */
#define STATUS_CHECK(FUNC) \
status = FUNC; \
if (VULKAN_ERROR_NO_ERROR != status) { \
return status; \
}
enum VULKAN_ERROR
{
VULKAN_ERROR_NO_ERROR = 0x0U,
VULKAN_ERROR_LAYER_NOT_SUPPORTED,
VULKAN_ERROR_EXTENSION_NOT_SUPPORTED,
VULKAN_ERROR_INSTANCE_INIT_FAILED,
VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND,
VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND,
VULKAN_ERROR_QUEUE_NOT_SUPPORTED,
VULKAN_ERROR_DEVICE_CREATION_FAILED,
VULKAN_ERROR_ALLOCATOR_CREATION_FAILED,
VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED,
VULKAN_ERROR_BUFFER_CREATION_FAILED,
VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED,
VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED,
VULKAN_ERROR_SURFACE_CREATION_FAILED,
VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED,
VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED,
VULKAN_ERROR_SHADER_FILE_OPEN_FAILED,
VULKAN_ERROR_SHADER_CREATION_FAILED,
VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED,
VULKAN_ERROR_RENDERPASS_CREATION_FAILED,
VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED,
VULKAN_ERROR_DESCRIPTOR_SETS_ALLOCATION_FAILED,
VULKAN_ERROR_DESCRIPTORSETLAYOUT_CREATION_FAILED,
VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED,
VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED,
VULKAN_ERROR_RECORD_COMMAND_BEGIN_FAILED,
VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED,
VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED,
};
#endif
+685
View File
@@ -0,0 +1,685 @@
#include "vulkan_pipeline.hpp"
#include "locale_vulkan.hpp"
#include "utilities/file_utils.hpp"
#include "utilities/logger.hpp"
#include "vulkan_errors.hpp"
#include <cstdint>
#include <map>
#include <optional>
#include <vector>
namespace rse::graphics::vulkanbase
{
using namespace locale::vulkan;
enum class ShaderStage
{
VERTEX,
FRAGMENTS
};
constexpr VkFormat IMAGE_FORMAT = VK_FORMAT_B8G8R8A8_SRGB;
uint32_t gSwapchainImagesCount = 0U;
std::vector<VkImage> gSwapChainImages;
std::vector<VkShaderModule> gShaderModules;
const std::map<ShaderStage, std::string> gShaderPaths =
{/* TODO: When changing to release and install, move those files*/
{
ShaderStage::VERTEX,
"../graphics/shaders/shader.vert.num",
},
{
ShaderStage::FRAGMENTS,
"../graphics/shaders/shader.frag.num",
}};
static std::optional<VkShaderModule> createShaderModule(VulkanState& state, const std::vector<char>& binaryData);
static uint8_t createSwapchain(VulkanState& vulkan_state);
static uint8_t createSwapchainImageViews(VulkanState& vulkan_state);
static uint8_t createRenderPass(VulkanState& vulkan_state);
static uint8_t createDescriptorSetLayout(VulkanState& vulkan_state);
static uint8_t createPipelineLayout(VulkanState& vulkan_state);
static uint8_t createGraphicsPipeline(VulkanState& vulkan_state);
static uint8_t createFrambuffers(VulkanState& vulkan_state);
static uint8_t fillShaderStages(VulkanState& vulkan_state, std::vector<VkPipelineShaderStageCreateInfo>* shaderStages);
static void fillDynamicPipelineStateInfo(VkPipelineDynamicStateCreateInfo* dynamicState);
static void freeDynamicPipelineStateInfo(VkPipelineDynamicStateCreateInfo* dynamicState);
static void fillVertexInputInfo(VkPipelineVertexInputStateCreateInfo* vertexInputInfo);
static void freeVertexInputInfo(VkPipelineVertexInputStateCreateInfo* vertexInputInfo);
static void fillInputAssemblyInfo(VkPipelineInputAssemblyStateCreateInfo* inputAssembly);
static void fillViewportStateInfo(VulkanState& vulkan_state, VkPipelineViewportStateCreateInfo* viewportState);
static void freeViewportStateInfo(VkPipelineViewportStateCreateInfo* viewportState);
static void fillRasterizationInfo(VkPipelineRasterizationStateCreateInfo* rasterizer);
static void fillMultisamplingStateInfo(VkPipelineMultisampleStateCreateInfo* multisampling);
static void fillColorBlendStateInfo(VkPipelineColorBlendStateCreateInfo* colorBlending);
static void freeColorBlendInfo(VkPipelineColorBlendStateCreateInfo* colorBlending);
/**
* @brief Creates shader module
*
* @param binaryData Shader compiled code
* @return std::optional<VkShaderModule> Handle to compiled shader module or nullopt
*/
std::optional<VkShaderModule> createShaderModule(VulkanState& vulkan_state, const std::vector<char>& binaryData)
{
VkShaderModule shaderModule;
VkShaderModuleCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0U;
createInfo.codeSize = binaryData.size();
createInfo.pCode = reinterpret_cast<const uint32_t*>(binaryData.data());
if (VK_SUCCESS != vkCreateShaderModule(vulkan_state.device, &createInfo, nullptr, &shaderModule)) {
LOGF << vulkanErrorMesssages[VULKAN_SHADER_UNABLE_TO_CREATE_SHADER];
return std::nullopt;
}
return shaderModule;
}
/**
* @brief Create a Swapchain
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED
*/
uint8_t createSwapchain(VulkanState& vulkan_state)
{
VkSwapchainCreateInfoKHR createInfo;
VkSurfaceCapabilitiesKHR physicalDeviceSurfaceCapabilities;
VkBool32 surfaceSupported;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkan_state.physicalDevice, vulkan_state.surface, &physicalDeviceSurfaceCapabilities);
/* Store extent in global variable, for later use */
vulkan_state.swapchainExtent = physicalDeviceSurfaceCapabilities.currentExtent;
/* Check if device supports surface for presentation */
vkGetPhysicalDeviceSurfaceSupportKHR(vulkan_state.physicalDevice, vulkan_state.queueFamilyIndices[0], vulkan_state.surface, &surfaceSupported);
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.pNext = nullptr;
createInfo.flags = 0U;
createInfo.surface = vulkan_state.surface;
createInfo.minImageCount = SWAP_BUFFER_COUNT + 1;
createInfo.imageFormat = IMAGE_FORMAT; /* TODO: Check for supported formats */
createInfo.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; /* TODO: Check for supported color space */
createInfo.imageExtent = vulkan_state.swapchainExtent;
createInfo.imageArrayLayers = 1U; /* For non-stereoscopic-3D applications, this value is 1. */
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; /* TODO: Probably change later? Dunno */
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; /* Using only one queue family, so this is ok */
createInfo.queueFamilyIndexCount = 1U;
createInfo.pQueueFamilyIndices = vulkan_state.queueFamilyIndices;
createInfo.preTransform = physicalDeviceSurfaceCapabilities.currentTransform;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; /* TODO: Choose present mode from VkPresentModeKHR */
createInfo.clipped = VK_TRUE;
createInfo.oldSwapchain = VK_NULL_HANDLE;
if (VK_SUCCESS != vkCreateSwapchainKHR(vulkan_state.device, &createInfo, nullptr, &vulkan_state.swapchain)) {
LOGF << vulkanErrorMesssages[VULKAN_SWAPCHAIN_CREATION_FAILED];
return VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED;
}
/* Obtain swapchain images */
vkGetSwapchainImagesKHR(vulkan_state.device, vulkan_state.swapchain, &gSwapchainImagesCount, nullptr);
gSwapChainImages.resize(gSwapchainImagesCount);
vkGetSwapchainImagesKHR(vulkan_state.device, vulkan_state.swapchain, &gSwapchainImagesCount, gSwapChainImages.data());
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create a Swapchain Image Views
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
*/
uint8_t createSwapchainImageViews(VulkanState& vulkan_state)
{
vulkan_state.swapChainImageViews.resize(gSwapchainImagesCount);
for (size_t i = 0; i < gSwapchainImagesCount; i++) {
VkImageViewCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0U;
createInfo.image = gSwapChainImages[i];
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; /* 2D Texture */
createInfo.format = VK_FORMAT_B8G8R8A8_SRGB;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0U;
createInfo.subresourceRange.levelCount = 1U;
createInfo.subresourceRange.baseArrayLayer = 0U;
createInfo.subresourceRange.layerCount = 1U;
if (VK_SUCCESS != vkCreateImageView(vulkan_state.device, &createInfo, nullptr, &vulkan_state.swapChainImageViews[i])) {
LOGF << vulkanErrorMesssages[VULKAN_SWAPCHAIN_IMVIEW_CREATION_FAILED];
return VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED;
}
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create a Render Pass
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_RENDERPASS_CREATION_FAILED
*/
uint8_t createRenderPass(VulkanState& vulkan_state)
{
VkAttachmentDescription colorAttachment{};
colorAttachment.flags = 0U;
colorAttachment.format = IMAGE_FORMAT;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef{};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
VkRenderPassCreateInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
if (VK_SUCCESS != vkCreateRenderPass(vulkan_state.device, &renderPassInfo, nullptr, &vulkan_state.renderPass)) {
LOGF << vulkanErrorMesssages[VULKAN_RENDERPASS_CREATION_FAILED];
return VULKAN_ERROR_RENDERPASS_CREATION_FAILED;
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create a Descriptor Set Layout
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
*/
uint8_t createDescriptorSetLayout(VulkanState& vulkan_state)
{
VkDescriptorSetLayoutCreateInfo layoutInfo;
VkDescriptorSetLayoutBinding uboLayoutBinding;
uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
uboLayoutBinding.pImmutableSamplers = nullptr;
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.pNext = nullptr;
layoutInfo.flags = 0U;
layoutInfo.bindingCount = 1U;
layoutInfo.pBindings = &uboLayoutBinding;
if (vkCreateDescriptorSetLayout(vulkan_state.device, &layoutInfo, nullptr, &vulkan_state.descriptorSetLayout) != VK_SUCCESS) {
LOGF << vulkanErrorMesssages[VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED];
return VULKAN_ERROR_DESCRIPTORSETLAYOUT_CREATION_FAILED;
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create a Pipeline Layout
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED
*/
uint8_t createPipelineLayout(VulkanState& vulkan_state)
{
VkPipelineLayoutCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0U;
createInfo.setLayoutCount = 1U;
createInfo.pSetLayouts = &vulkan_state.descriptorSetLayout;
createInfo.pushConstantRangeCount = 0U;
createInfo.pPushConstantRanges = nullptr;
if (VK_SUCCESS != vkCreatePipelineLayout(vulkan_state.device, &createInfo, nullptr, &vulkan_state.pipelineLayout)) {
LOGF << vulkanErrorMesssages[VULKAN_PIPELAYOUT_CREATION_FAILED];
return VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED;
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create Graphics Pipeline
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED
*/
uint8_t createGraphicsPipeline(VulkanState& vulkan_state)
{
uint8_t status = VULKAN_ERROR_NO_ERROR;
std::vector<VkPipelineShaderStageCreateInfo> shaderStages{};
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
VkPipelineViewportStateCreateInfo viewportState{};
VkPipelineRasterizationStateCreateInfo rasterizer{};
VkPipelineMultisampleStateCreateInfo multisampling{};
VkPipelineColorBlendStateCreateInfo colorBlending{};
VkPipelineDynamicStateCreateInfo dynamicState{};
STATUS_CHECK(fillShaderStages(vulkan_state, &shaderStages));
fillVertexInputInfo(&vertexInputInfo);
fillInputAssemblyInfo(&inputAssembly);
fillViewportStateInfo(vulkan_state, &viewportState);
fillRasterizationInfo(&rasterizer);
fillMultisamplingStateInfo(&multisampling);
fillColorBlendStateInfo(&colorBlending);
fillDynamicPipelineStateInfo(&dynamicState);
VkGraphicsPipelineCreateInfo pipelineInfo{};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.pNext = nullptr;
pipelineInfo.flags = 0U;
pipelineInfo.stageCount = shaderStages.size();
pipelineInfo.pStages = shaderStages.data();
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pTessellationState = nullptr;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pDepthStencilState = nullptr;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.layout = vulkan_state.pipelineLayout;
pipelineInfo.renderPass = vulkan_state.renderPass;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
pipelineInfo.basePipelineIndex = -1;
if (VK_SUCCESS !=
vkCreateGraphicsPipelines(vulkan_state.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &vulkan_state.graphicsPipeline)) {
LOGF << vulkanErrorMesssages[VULKAN_GRAPHICSPIPELINE_CREATION_FAILED];
status = VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED;
}
freeDynamicPipelineStateInfo(&dynamicState);
freeColorBlendInfo(&colorBlending);
freeViewportStateInfo(&viewportState);
freeVertexInputInfo(&vertexInputInfo);
return status;
}
/**
* @brief Create Frambuffers
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
*/
uint8_t createFrambuffers(VulkanState& vulkan_state)
{
vulkan_state.swapchainFramebuffers.resize(vulkan_state.swapChainImageViews.size());
for (size_t i = 0; i < vulkan_state.swapChainImageViews.size(); i++) {
VkImageView attachments[] = {vulkan_state.swapChainImageViews[i]};
VkFramebufferCreateInfo framebufferInfo{};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = vulkan_state.renderPass;
framebufferInfo.attachmentCount = 1;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = vulkan_state.swapchainExtent.width;
framebufferInfo.height = vulkan_state.swapchainExtent.height;
framebufferInfo.layers = 1;
if (vkCreateFramebuffer(vulkan_state.device, &framebufferInfo, nullptr, &vulkan_state.swapchainFramebuffers[i]) != VK_SUCCESS) {
LOGF << vulkanErrorMesssages[VULKAN_FRAMEBUFFERS_CREATION_FAILED];
return VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED;
}
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Recreate swapchain
*
*/
void recreateSwapchain(VulkanState& vulkan_state)
{
cleanup_swapchain(vulkan_state);
createSwapchain(vulkan_state);
createSwapchainImageViews(vulkan_state);
createFrambuffers(vulkan_state);
}
/**
* @brief Cleanup swapchain
*
*/
void cleanup_swapchain(VulkanState& vulkan_state)
{
vkDeviceWaitIdle(vulkan_state.device);
for (size_t i = 0; i < vulkan_state.swapchainFramebuffers.size(); i++) {
vkDestroyFramebuffer(vulkan_state.device, vulkan_state.swapchainFramebuffers[i], nullptr);
}
for (size_t i = 0; i < vulkan_state.swapChainImageViews.size(); i++) {
vkDestroyImageView(vulkan_state.device, vulkan_state.swapChainImageViews[i], nullptr);
}
vkDestroySwapchainKHR(vulkan_state.device, vulkan_state.swapchain, nullptr);
}
/**
* @brief Creates shader stages from hardcoded shader paths. Changes input
*
* @param shaderStages Empty vector, that will be filled with data
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_SHADER_FILE_OPEN_FAILED
* VULKAN_ERROR_SHADER_CREATION_FAILED
*/
uint8_t fillShaderStages(VulkanState& vulkan_state, std::vector<VkPipelineShaderStageCreateInfo>* shaderStages)
{
auto vertexShaderCode = rse::utils::file::read_file(gShaderPaths.at(ShaderStage::VERTEX));
auto fragmentsShaderCode = rse::utils::file::read_file(gShaderPaths.at(ShaderStage::FRAGMENTS));
if (0 == vertexShaderCode.size() || 0 == fragmentsShaderCode.size()) {
LOGF << vulkanErrorMesssages[VULKAN_SHADER_UNABLE_TO_OPEN];
return VULKAN_ERROR_SHADER_FILE_OPEN_FAILED;
}
std::optional<VkShaderModule> vertexShaderModule = createShaderModule(vulkan_state, vertexShaderCode);
std::optional<VkShaderModule> fragmentsShaderModule = createShaderModule(vulkan_state, fragmentsShaderCode);
if (!vertexShaderModule.has_value() || !fragmentsShaderModule.has_value()) {
return VULKAN_ERROR_SHADER_CREATION_FAILED;
}
/* Vertex Shader Pipeline Stage */
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.pNext = nullptr;
vertShaderStageInfo.flags = 0U;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = vertexShaderModule.value();
vertShaderStageInfo.pName = "main"; /* Entry point function */
vertShaderStageInfo.pSpecializationInfo = nullptr; /* Specialization in a SPIR-V module */
/* Fragments Shader Pipeline Stage */
VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.pNext = nullptr;
fragShaderStageInfo.flags = 0U;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fragmentsShaderModule.value();
fragShaderStageInfo.pName = "main"; /* Entry point function */
fragShaderStageInfo.pSpecializationInfo = nullptr; /* Specialization in a SPIR-V module */
shaderStages->push_back(vertShaderStageInfo);
shaderStages->push_back(fragShaderStageInfo);
gShaderModules.push_back(std::move(vertexShaderModule.value()));
gShaderModules.push_back(std::move(fragmentsShaderModule.value()));
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Fill dynamic pipeline state info
*
* @param dynamicState structure to be filled
*/
void fillDynamicPipelineStateInfo(VkPipelineDynamicStateCreateInfo* dynamicState)
{
/*
* Select pipeline stages that will be set dynamically. Those that are selected are ignored in
* pipeline create structures. I'll list ignored members of structs, that will be ignored due to the
* settings and also functions, that will need to be called to set specific state.
*/
std::vector<VkDynamicState> dynamicStates{
VK_DYNAMIC_STATE_VIEWPORT, /* VkPipelineViewportStateCreateInfo:pViewport; vkCmdSetViewport() */
VK_DYNAMIC_STATE_SCISSOR /* VkPipelineViewportStateCreateInfo:pScissors; vkCmdSetScissors() */
};
dynamicState->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState->pNext = nullptr;
dynamicState->flags = 0U;
dynamicState->dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
/* dynamicState is constructed localy. To avoid creation of VkPipelineDynamicStateCreateInfo structure in
* main pipeline construction function, we have to do a little memory trickery here and allocate memory for
* dynamic state pointer manually. We must also free this memory later */
VkDynamicState* tmp = new VkDynamicState[dynamicStates.size()];
std::copy(dynamicStates.begin(), dynamicStates.end(), tmp);
dynamicState->pDynamicStates = tmp;
}
/**
* @brief Release allocated dynamic pipeline state info
*
* @param dynamicState structure to be freed
*/
void freeDynamicPipelineStateInfo(VkPipelineDynamicStateCreateInfo* dynamicState)
{
delete[] dynamicState->pDynamicStates;
}
/**
* @brief Fill vertex input info
*
* @param vertexInputInfo structure to be filled
*/
void fillVertexInputInfo(VkPipelineVertexInputStateCreateInfo* vertexInputInfo)
{
auto bindingDescription = Vertex::getBindingDescription();
auto attributeDescriptions = Vertex::getAttributeDescriptions();
vertexInputInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo->pNext = nullptr;
vertexInputInfo->flags = 0U;
vertexInputInfo->vertexBindingDescriptionCount = 1U;
vertexInputInfo->pVertexBindingDescriptions = new VkVertexInputBindingDescription(bindingDescription);
vertexInputInfo->vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
VkVertexInputAttributeDescription* tmp = new VkVertexInputAttributeDescription[attributeDescriptions.size()];
std::copy(attributeDescriptions.begin(), attributeDescriptions.end(), tmp);
vertexInputInfo->pVertexAttributeDescriptions = tmp;
}
/**
* @brief Free allocated vertex input info
*
* @param vertexInputInfo structure to be freed
*/
void freeVertexInputInfo(VkPipelineVertexInputStateCreateInfo* vertexInputInfo)
{
delete vertexInputInfo->pVertexBindingDescriptions;
delete[] vertexInputInfo->pVertexAttributeDescriptions;
}
/**
* @brief Fill input assembly info
*
* @param inputAssembly structure to be filled
*/
void fillInputAssemblyInfo(VkPipelineInputAssemblyStateCreateInfo* inputAssembly)
{
inputAssembly->sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly->pNext = nullptr;
inputAssembly->flags = 0U;
inputAssembly->topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; // TODO: Change to _STRIP
inputAssembly->primitiveRestartEnable = VK_FALSE; /* If I want to break lines during _STRIP,
* I must set this to VK_TRUE */
}
/**
* @brief Fill viewport state info
*
* @param viewportState structure to be filled
*/
void fillViewportStateInfo(VulkanState& vulkan_state, VkPipelineViewportStateCreateInfo* viewportState)
{
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(vulkan_state.swapchainExtent.width);
viewport.height = static_cast<float>(vulkan_state.swapchainExtent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
/* Setting Scissors to fill entire window*/
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = vulkan_state.swapchainExtent;
viewportState->sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState->viewportCount = 1;
viewportState->pViewports = new VkViewport(viewport);
viewportState->scissorCount = 1;
viewportState->pScissors = new VkRect2D(scissor);
}
/**
* @brief Free allocated viewport state input info
*
* @param viewportState structure to be freed
*/
void freeViewportStateInfo(VkPipelineViewportStateCreateInfo* viewportState)
{
delete viewportState->pViewports;
delete viewportState->pScissors;
}
/**
* @brief Fill rasteriation info
*
* @param rasterizer
*/
void fillRasterizationInfo(VkPipelineRasterizationStateCreateInfo* rasterizer)
{
rasterizer->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer->pNext = nullptr;
rasterizer->flags = 0U;
rasterizer->depthClampEnable = VK_FALSE; /* VK_TRUE to play with shadow maps */
rasterizer->rasterizerDiscardEnable = VK_FALSE; /* Disable/Enable output to framebuffer*/
rasterizer->polygonMode = VK_POLYGON_MODE_FILL;
rasterizer->cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer->frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizer->depthBiasEnable = VK_FALSE;
rasterizer->depthBiasConstantFactor = 0.0f;
rasterizer->depthBiasClamp = 0.0f;
rasterizer->depthBiasSlopeFactor = 0.0f;
rasterizer->lineWidth = 1.0f;
}
/**
* @brief Fill multisampling state info
*
* @param multisampling structure to be filled
*/
void fillMultisamplingStateInfo(VkPipelineMultisampleStateCreateInfo* multisampling)
{
multisampling->sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling->pNext = nullptr;
multisampling->flags = 0U;
multisampling->rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisampling->sampleShadingEnable = VK_FALSE;
multisampling->minSampleShading = 1.0f;
multisampling->pSampleMask = nullptr;
multisampling->alphaToCoverageEnable = VK_FALSE;
multisampling->alphaToOneEnable = VK_FALSE;
}
/**
* @brief Fill color blend state info
*
* @param colorBlending structure to be filled
*/
void fillColorBlendStateInfo(VkPipelineColorBlendStateCreateInfo* colorBlending)
{
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.blendEnable = VK_FALSE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlending->sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending->pNext = nullptr;
colorBlending->flags = 0U;
colorBlending->logicOpEnable = VK_FALSE;
colorBlending->logicOp = VK_LOGIC_OP_COPY;
colorBlending->attachmentCount = 1U;
colorBlending->pAttachments = new VkPipelineColorBlendAttachmentState(colorBlendAttachment);
colorBlending->blendConstants[0] = 0.0f;
colorBlending->blendConstants[1] = 0.0f;
colorBlending->blendConstants[2] = 0.0f;
colorBlending->blendConstants[3] = 0.0f;
}
/**
* @brief Free allocated color blend state info
*
* @param colorBlending Structure to be freed
*/
void freeColorBlendInfo(VkPipelineColorBlendStateCreateInfo* colorBlending)
{
delete colorBlending->pAttachments;
}
uint8_t create_pipeline(VulkanState& vulkan_state)
{
uint8_t status = VULKAN_ERROR_NO_ERROR;
STATUS_CHECK(createSwapchain(vulkan_state));
STATUS_CHECK(createSwapchainImageViews(vulkan_state));
STATUS_CHECK(createRenderPass(vulkan_state));
STATUS_CHECK(createDescriptorSetLayout(vulkan_state));
STATUS_CHECK(createPipelineLayout(vulkan_state));
STATUS_CHECK(createGraphicsPipeline(vulkan_state));
STATUS_CHECK(createFrambuffers(vulkan_state));
return status;
}
void destroy_pipeline(VulkanState& vulkan_state)
{
vkDestroyPipeline(vulkan_state.device, vulkan_state.graphicsPipeline, nullptr);
vkDestroyPipelineLayout(vulkan_state.device, vulkan_state.pipelineLayout, nullptr);
vkDestroyRenderPass(vulkan_state.device, vulkan_state.renderPass, nullptr);
for (size_t i = 0; i < gShaderModules.size(); i++) {
vkDestroyShaderModule(vulkan_state.device, gShaderModules[i], nullptr);
}
}
} // namespace rse::graphics::vulkanbase
+43
View File
@@ -0,0 +1,43 @@
#ifndef RSE_VULKAN_PIPELINE_HPP
#define RSE_VULKAN_PIPELINE_HPP
#include <vector>
#include <vulkan/vulkan.hpp>
#include "vulkan_commons.hpp"
namespace rse::graphics::vulkanbase
{
/**
* @brief Create a graphics pipeline
*
* @param vulkan_state
* @return uint8_t
*/
uint8_t create_pipeline(VulkanState& vulkan_state);
/**
* @brief Cleanup swapchain
*
* @param vulkan_state
*/
void cleanup_swapchain(VulkanState& vulkan_state);
/**
* @brief Recreates swapchain
*
* @param vulkan_state
*/
void recreateSwapchain(VulkanState& vulkan_state);
/**
* @brief Destroy all vulkan pipeline objects
*
* @param vulkan_state
*/
void destroy_pipeline(VulkanState& vulkan_state);
}
#endif
@@ -11,9 +11,9 @@
#include "window.hpp" #include "window.hpp"
#include "localeWindow.hpp" #include "locale_window.hpp"
#include "logger.hpp" #include "utilities/logger.hpp"
#include "vulkanBase.hpp" #include "vulkan_base.hpp"
namespace rse::graphics::window namespace rse::graphics::window
{ {
@@ -96,11 +96,11 @@ uint16_t windowInit()
return WINDOW_ERROR_NO_ERROR; return WINDOW_ERROR_NO_ERROR;
} }
uint16_t windowLoop() uint16_t windowLoop(rse::graphics::vulkanbase::VulkanState& vulkan_state)
{ {
while (!glfwWindowShouldClose(gpWindowHandle)) { while (!glfwWindowShouldClose(gpWindowHandle)) {
glfwPollEvents(); glfwPollEvents();
rse::graphics::vulkanbase::drawFrame(); rse::graphics::vulkanbase::drawFrame(vulkan_state);
} }
return WINDOW_ERROR_NO_ERROR; return WINDOW_ERROR_NO_ERROR;
@@ -9,12 +9,14 @@
* *
*/ */
#pragma once #ifndef RSE_WINDOW_HPP
#define RSE_WINDOW_HPP
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
/* Vulkan header MUST be included before glfw */ /* Vulkan header MUST be included before glfw */
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include "vulkan_commons.hpp"
namespace rse::graphics::window namespace rse::graphics::window
{ {
@@ -32,9 +34,10 @@ uint16_t windowInit();
/** /**
* @brief Main window loop * @brief Main window loop
* *
* @param vulkan_state
* @return uint16_t WINDOW_SUCCESS or error code * @return uint16_t WINDOW_SUCCESS or error code
*/ */
uint16_t windowLoop(); uint16_t windowLoop(rse::graphics::vulkanbase::VulkanState& vulkan_state);
/** /**
* @brief Closes window and terminates GLFW * @brief Closes window and terminates GLFW
@@ -45,3 +48,5 @@ void windowTerminate();
GLFWwindow* getWindowHandle(); GLFWwindow* getWindowHandle();
} /* namespace rse::graphics::window */ } /* namespace rse::graphics::window */
#endif
@@ -1,8 +1,7 @@
add_executable(${PROJECT_NAME} src/main.cpp) add_executable(${PROJECT_NAME} src/main.cpp)
target_include_directories(${PROJECT_NAME} PRIVATE target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/Utilities/include ${CMAKE_SOURCE_DIR})
${CMAKE_SOURCE_DIR}/Graphics/include)
target_link_libraries(${PROJECT_NAME} PRIVATE target_link_libraries(${PROJECT_NAME} PRIVATE
rse_graphics) rse_graphics)
+13
View File
@@ -0,0 +1,13 @@
#include "graphics/rse_graphics.hpp"
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
rse::graphics::RseGraphics graphics_pipeline;
graphics_pipeline.graphics_run();
return 0;
}
File renamed without changes.
+10
View File
@@ -0,0 +1,10 @@
add_library(rse_utilities SHARED)
target_include_directories(rse_utilities PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_sources(rse_utilities PRIVATE
src/logger.cpp
src/file_utils.cpp
)
+15
View File
@@ -0,0 +1,15 @@
#ifndef RSE_FILE_UTILS_HPP
#define RSE_FILE_UTILS_HPP
#include <vector>
#include <string>
namespace rse::utils::file
{
std::vector<char> read_file(const std::string& fileName);
}
#endif
@@ -10,9 +10,9 @@
*/ */
#pragma once #pragma once
#define SELECTED_LANGUAGE rse::locale::EN_US #define SELECTED_LANGUAGE rse::utils::locale::EN_US
namespace rse::locale namespace rse::utils::locale
{ {
/** /**
@@ -9,7 +9,8 @@
* *
*/ */
#pragma once #ifndef RSE_LOGGER_HPP
#define RSE_LOGGER_HPP
#include <sstream> #include <sstream>
@@ -17,16 +18,16 @@
#define LOG_LEVEL LOGLEVEL_DEBUG #define LOG_LEVEL LOGLEVEL_DEBUG
#endif #endif
#define LOGT rse::log::Logger(rse::log::LOGLEVEL_TRACE) #define LOGT rse::utils::log::Logger(rse::utils::log::LOGLEVEL_TRACE)
#define LOGD rse::log::Logger(rse::log::LOGLEVEL_DEBUG) #define LOGD rse::utils::log::Logger(rse::utils::log::LOGLEVEL_DEBUG)
#define LOGI rse::log::Logger(rse::log::LOGLEVEL_INFO) #define LOGI rse::utils::log::Logger(rse::utils::log::LOGLEVEL_INFO)
#define LOGW rse::log::Logger(rse::log::LOGLEVEL_WARNING) #define LOGW rse::utils::log::Logger(rse::utils::log::LOGLEVEL_WARNING)
#define LOGE rse::log::Logger(rse::log::LOGLEVEL_ERROR) #define LOGE rse::utils::log::Logger(rse::utils::log::LOGLEVEL_ERROR)
#define LOGF rse::log::Logger(rse::log::LOGLEVEL_FATAL) #define LOGF rse::utils::log::Logger(rse::utils::log::LOGLEVEL_FATAL)
namespace rse::log namespace rse::utils::log
{ {
/** /**
@@ -74,3 +75,5 @@ class Logger {
}; };
} // namespace rse::log } // namespace rse::log
#endif
+33
View File
@@ -0,0 +1,33 @@
#include "file_utils.hpp"
#include <fstream>
namespace rse::utils::file
{
/**
* @brief Reads file and puts its content into char buffer
*
* @param fileName Path to file
* @return std::vector<char> Binary buffer data
*/
std::vector<char> read_file(const std::string& fileName)
{
std::ifstream file{fileName, std::ios::ate | std::ios::binary};
if (!file.is_open()) {
return {};
}
/* Get file size and create data buffer */
size_t fileSize = static_cast<size_t>(file.tellg());
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
return buffer;
}
} // namespace rse::utils::file
@@ -13,7 +13,7 @@
#include <iostream> #include <iostream>
namespace rse::log namespace rse::utils::log
{ {
Logger::Logger(LogLevel level) : printMessage(false) Logger::Logger(LogLevel level) : printMessage(false)