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
+2346
-1994
No files matched your search
+5
-3
@@ -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_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
|
||||
|
||||
add_subdirectory(RedScarfEngine)
|
||||
add_subdirectory(Utilities)
|
||||
add_subdirectory(Graphics)
|
||||
|
||||
add_subdirectory(red_scarf_engine)
|
||||
add_subdirectory(utilities)
|
||||
add_subdirectory(graphics)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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;
|
||||
}
|
||||
@@ -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(glfw3 REQUIRED)
|
||||
find_package(glm REQUIRED)
|
||||
@@ -7,9 +5,11 @@ find_package(glm REQUIRED)
|
||||
find_package(Vulkan COMPONENTS glslc)
|
||||
find_program(glslc_executable NAMES glslc HINTS Vulkan::glslc)
|
||||
|
||||
cmake_policy(SET CMP0116 NEW)
|
||||
function(compile_shader)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 arg "" "ENV;FORMAT" "SOURCES")
|
||||
foreach(source ${arg_SOURCES})
|
||||
message(${source}.${arg_FORMAT})
|
||||
add_custom_command(
|
||||
OUTPUT ${source}.${arg_FORMAT}
|
||||
DEPENDS ${source}
|
||||
@@ -23,29 +23,39 @@ function(compile_shader)
|
||||
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
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/third_party/vma/include
|
||||
${CMAKE_SOURCE_DIR}/Utilities/include
|
||||
${PROJECT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${Vulkan_INCLUDE_DIR}
|
||||
${glfw3_INCLUDE_DIR}
|
||||
${glm_INCLUDE_DIR})
|
||||
${glm_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
|
||||
target_sources(rse_graphics PRIVATE
|
||||
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
|
||||
rse_utilities
|
||||
${Vulkan_LIBRARIES}
|
||||
glfw
|
||||
glm)
|
||||
|
||||
compile_shader(rse_graphics
|
||||
ENV opengl
|
||||
FORMAT num
|
||||
SOURCES
|
||||
shaders/shader.vert
|
||||
shaders/shader.frag
|
||||
glm
|
||||
)
|
||||
@@ -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 <vector>
|
||||
|
||||
#include "localization.hpp"
|
||||
|
||||
#include "utilities/localization.hpp"
|
||||
|
||||
namespace rse::locale::vulkan
|
||||
{
|
||||
@@ -48,12 +47,13 @@ enum Errors
|
||||
VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED,
|
||||
VULKAN_GRAPHICSPIPELINE_CREATION_FAILED,
|
||||
VULKAN_FRAMEBUFFERS_CREATION_FAILED,
|
||||
VULKAN_RECORD_COMMAND_BEGIN_FAILED,
|
||||
VULKAN_RECORD_COMMAND_BUFFER_FAILED,
|
||||
VULKAN_SYNCOBJCTS_CREATION_FAILED,
|
||||
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 */
|
||||
/* VULKAN_LAYER_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",
|
||||
/* VULKAN_FRAMEBUFFERS_CREATION_FAILED */
|
||||
"Failed to create framebuffer",
|
||||
/* VULKAN_RECORD_COMMAND_BEGIN_FAILED */
|
||||
"Failed to start recording command buffer",
|
||||
/* VULKAN_RECORD_COMMAND_BUFFER_FAILED */
|
||||
"Failed to record command buffer. Validate your commands.",
|
||||
/* VULKAN_SYNCOBJCTS_CREATION_FAILED */
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "localization.hpp"
|
||||
#include "utilities/localization.hpp"
|
||||
|
||||
|
||||
namespace rse::locale::window
|
||||
@@ -29,7 +29,7 @@ enum Errors
|
||||
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 */
|
||||
/* WINDOW_GLFW_INIT_FAILED */
|
||||
"GLFW init failed!"
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "vulkan_commons.hpp"
|
||||
|
||||
namespace rse::graphics::vulkanbase
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize Vulkan backend
|
||||
*
|
||||
* @param state
|
||||
* @return uint8_t 0 on success. Status code on failure
|
||||
*/
|
||||
|
||||
|
||||
uint8_t initVulkan();
|
||||
uint8_t initVulkan(VulkanState& state);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize Vulkan backend
|
||||
*
|
||||
* @param state
|
||||
*/
|
||||
void deinitVulkan();
|
||||
void deinitVulkan(VulkanState& state);
|
||||
|
||||
/**
|
||||
* @brief Draw frame on the screen.
|
||||
*
|
||||
* @param state
|
||||
*/
|
||||
void drawFrame();
|
||||
void drawFrame(VulkanState& state);
|
||||
|
||||
} /* namespace rse::graphics::vulkanbase */
|
||||
|
||||
@@ -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 = ©CommandBuffer;
|
||||
submitInfo.signalSemaphoreCount = 0;
|
||||
submitInfo.pSignalSemaphores = nullptr;
|
||||
|
||||
// FIXME: At result checks
|
||||
vkAllocateCommandBuffers(vulkan_state.device, &allocInfo, ©CommandBuffer);
|
||||
vkBeginCommandBuffer(copyCommandBuffer, &beginInfo);
|
||||
vkCmdCopyBuffer(copyCommandBuffer, src, dst, 1, ©Region);
|
||||
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, ©CommandBuffer);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 "localeWindow.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "vulkanBase.hpp"
|
||||
#include "locale_window.hpp"
|
||||
#include "utilities/logger.hpp"
|
||||
#include "vulkan_base.hpp"
|
||||
|
||||
namespace rse::graphics::window
|
||||
{
|
||||
@@ -96,11 +96,11 @@ uint16_t windowInit()
|
||||
return WINDOW_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
uint16_t windowLoop()
|
||||
uint16_t windowLoop(rse::graphics::vulkanbase::VulkanState& vulkan_state)
|
||||
{
|
||||
while (!glfwWindowShouldClose(gpWindowHandle)) {
|
||||
glfwPollEvents();
|
||||
rse::graphics::vulkanbase::drawFrame();
|
||||
rse::graphics::vulkanbase::drawFrame(vulkan_state);
|
||||
}
|
||||
|
||||
return WINDOW_ERROR_NO_ERROR;
|
||||
@@ -9,12 +9,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef RSE_WINDOW_HPP
|
||||
#define RSE_WINDOW_HPP
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
/* Vulkan header MUST be included before glfw */
|
||||
#include <GLFW/glfw3.h>
|
||||
#include "vulkan_commons.hpp"
|
||||
|
||||
namespace rse::graphics::window
|
||||
{
|
||||
@@ -32,9 +34,10 @@ uint16_t windowInit();
|
||||
/**
|
||||
* @brief Main window loop
|
||||
*
|
||||
* @param vulkan_state
|
||||
* @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
|
||||
@@ -45,3 +48,5 @@ void windowTerminate();
|
||||
GLFWwindow* getWindowHandle();
|
||||
|
||||
} /* namespace rse::graphics::window */
|
||||
|
||||
#endif
|
||||
@@ -1,8 +1,7 @@
|
||||
add_executable(${PROJECT_NAME} src/main.cpp)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/Utilities/include
|
||||
${CMAKE_SOURCE_DIR}/Graphics/include)
|
||||
${CMAKE_SOURCE_DIR})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
rse_graphics)
|
||||
@@ -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.
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
#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>
|
||||
|
||||
@@ -17,16 +18,16 @@
|
||||
#define LOG_LEVEL LOGLEVEL_DEBUG
|
||||
#endif
|
||||
|
||||
#define LOGT rse::log::Logger(rse::log::LOGLEVEL_TRACE)
|
||||
#define LOGD rse::log::Logger(rse::log::LOGLEVEL_DEBUG)
|
||||
#define LOGI rse::log::Logger(rse::log::LOGLEVEL_INFO)
|
||||
#define LOGW rse::log::Logger(rse::log::LOGLEVEL_WARNING)
|
||||
#define LOGE rse::log::Logger(rse::log::LOGLEVEL_ERROR)
|
||||
#define LOGF rse::log::Logger(rse::log::LOGLEVEL_FATAL)
|
||||
#define LOGT rse::utils::log::Logger(rse::utils::log::LOGLEVEL_TRACE)
|
||||
#define LOGD rse::utils::log::Logger(rse::utils::log::LOGLEVEL_DEBUG)
|
||||
#define LOGI rse::utils::log::Logger(rse::utils::log::LOGLEVEL_INFO)
|
||||
#define LOGW rse::utils::log::Logger(rse::utils::log::LOGLEVEL_WARNING)
|
||||
#define LOGE rse::utils::log::Logger(rse::utils::log::LOGLEVEL_ERROR)
|
||||
#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
|
||||
|
||||
#endif
|
||||
@@ -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>
|
||||
|
||||
namespace rse::log
|
||||
namespace rse::utils::log
|
||||
{
|
||||
|
||||
Logger::Logger(LogLevel level) : printMessage(false)
|
||||
Reference in new issue
Block a user