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_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
|
||||||
|
|
||||||
add_subdirectory(RedScarfEngine)
|
|
||||||
add_subdirectory(Utilities)
|
add_subdirectory(red_scarf_engine)
|
||||||
add_subdirectory(Graphics)
|
add_subdirectory(utilities)
|
||||||
|
add_subdirectory(graphics)
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -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(Vulkan REQUIRED)
|
||||||
find_package(glfw3 REQUIRED)
|
find_package(glfw3 REQUIRED)
|
||||||
find_package(glm REQUIRED)
|
find_package(glm REQUIRED)
|
||||||
@@ -7,9 +5,11 @@ find_package(glm REQUIRED)
|
|||||||
find_package(Vulkan COMPONENTS glslc)
|
find_package(Vulkan COMPONENTS glslc)
|
||||||
find_program(glslc_executable NAMES glslc HINTS Vulkan::glslc)
|
find_program(glslc_executable NAMES glslc HINTS Vulkan::glslc)
|
||||||
|
|
||||||
|
cmake_policy(SET CMP0116 NEW)
|
||||||
function(compile_shader)
|
function(compile_shader)
|
||||||
cmake_parse_arguments(PARSE_ARGV 1 arg "" "ENV;FORMAT" "SOURCES")
|
cmake_parse_arguments(PARSE_ARGV 1 arg "" "ENV;FORMAT" "SOURCES")
|
||||||
foreach(source ${arg_SOURCES})
|
foreach(source ${arg_SOURCES})
|
||||||
|
message(${source}.${arg_FORMAT})
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${source}.${arg_FORMAT}
|
OUTPUT ${source}.${arg_FORMAT}
|
||||||
DEPENDS ${source}
|
DEPENDS ${source}
|
||||||
@@ -23,29 +23,39 @@ function(compile_shader)
|
|||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
compile_shader(rse_graphics
|
||||||
|
ENV opengl
|
||||||
|
FORMAT num
|
||||||
|
SOURCES
|
||||||
|
shaders/shader.vert
|
||||||
|
shaders/shader.frag
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(rse_graphics SHARED shaders/shader.vert.num shaders/shader.frag.num)
|
||||||
target_include_directories(rse_graphics PRIVATE
|
target_include_directories(rse_graphics PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
${PROJECT_SOURCE_DIR}
|
||||||
${CMAKE_SOURCE_DIR}/third_party/vma/include
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
${CMAKE_SOURCE_DIR}/Utilities/include
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${CMAKE_SOURCE_DIR}
|
||||||
${Vulkan_INCLUDE_DIR}
|
${Vulkan_INCLUDE_DIR}
|
||||||
${glfw3_INCLUDE_DIR}
|
${glfw3_INCLUDE_DIR}
|
||||||
${glm_INCLUDE_DIR})
|
${glm_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
target_sources(rse_graphics PRIVATE
|
target_sources(rse_graphics PRIVATE
|
||||||
src/window.cpp
|
src/window.cpp
|
||||||
src/vulkanBase.cpp)
|
src/vulkan_base.cpp
|
||||||
|
src/vulkan_pipeline.cpp
|
||||||
|
src/vulkan_buffers.cpp
|
||||||
|
src/vulkan_descriptors.cpp
|
||||||
|
src/rse_graphics.cpp
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(rse_graphics PRIVATE
|
target_link_libraries(rse_graphics PRIVATE
|
||||||
rse_utilities
|
rse_utilities
|
||||||
${Vulkan_LIBRARIES}
|
${Vulkan_LIBRARIES}
|
||||||
glfw
|
glfw
|
||||||
glm)
|
glm
|
||||||
|
|
||||||
compile_shader(rse_graphics
|
|
||||||
ENV opengl
|
|
||||||
FORMAT num
|
|
||||||
SOURCES
|
|
||||||
shaders/shader.vert
|
|
||||||
shaders/shader.frag
|
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef RSE_GRAPHICS_HPP
|
||||||
|
#define RSE_GRAPHICS_HPP
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "src/vulkan_commons.hpp"
|
||||||
|
|
||||||
|
namespace rse::graphics
|
||||||
|
{
|
||||||
|
class RseGraphics {
|
||||||
|
public:
|
||||||
|
RseGraphics();
|
||||||
|
RseGraphics& operator=(const RseGraphics&) = delete;
|
||||||
|
RseGraphics(const RseGraphics&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize graphics module, runs main loop and deinits everything, when interrupted
|
||||||
|
*
|
||||||
|
* @return uint8_t 0 on success.
|
||||||
|
*/
|
||||||
|
uint8_t graphics_run(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* Hold vulkan state. It is modified by function initializing and communicating with Vulkan API */
|
||||||
|
vulkanbase::VulkanState vulkan_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace rse::graphics
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
File renamed without changes.
File renamed without changes.
@@ -14,8 +14,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "localization.hpp"
|
#include "utilities/localization.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace rse::locale::vulkan
|
namespace rse::locale::vulkan
|
||||||
{
|
{
|
||||||
@@ -48,12 +47,13 @@ enum Errors
|
|||||||
VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED,
|
VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED,
|
||||||
VULKAN_GRAPHICSPIPELINE_CREATION_FAILED,
|
VULKAN_GRAPHICSPIPELINE_CREATION_FAILED,
|
||||||
VULKAN_FRAMEBUFFERS_CREATION_FAILED,
|
VULKAN_FRAMEBUFFERS_CREATION_FAILED,
|
||||||
|
VULKAN_RECORD_COMMAND_BEGIN_FAILED,
|
||||||
VULKAN_RECORD_COMMAND_BUFFER_FAILED,
|
VULKAN_RECORD_COMMAND_BUFFER_FAILED,
|
||||||
VULKAN_SYNCOBJCTS_CREATION_FAILED,
|
VULKAN_SYNCOBJCTS_CREATION_FAILED,
|
||||||
LAST_MESSAGE
|
LAST_MESSAGE
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array<std::array<const char*, LAST_MESSAGE>, LAST_LANGUAGE> vulkanErrorMesssages_nolocale = {
|
inline std::array<std::array<const char*, LAST_MESSAGE>, rse::utils::locale::LAST_LANGUAGE> vulkanErrorMesssages_nolocale = {
|
||||||
{/* EN_US */
|
{/* EN_US */
|
||||||
/* VULKAN_LAYER_NOT_SUPPORTED */
|
/* VULKAN_LAYER_NOT_SUPPORTED */
|
||||||
"Selected layer is not supported: ",
|
"Selected layer is not supported: ",
|
||||||
@@ -103,6 +103,8 @@ std::array<std::array<const char*, LAST_MESSAGE>, LAST_LANGUAGE> vulkanErrorMess
|
|||||||
"Failed to create graphics pipeline",
|
"Failed to create graphics pipeline",
|
||||||
/* VULKAN_FRAMEBUFFERS_CREATION_FAILED */
|
/* VULKAN_FRAMEBUFFERS_CREATION_FAILED */
|
||||||
"Failed to create framebuffer",
|
"Failed to create framebuffer",
|
||||||
|
/* VULKAN_RECORD_COMMAND_BEGIN_FAILED */
|
||||||
|
"Failed to start recording command buffer",
|
||||||
/* VULKAN_RECORD_COMMAND_BUFFER_FAILED */
|
/* VULKAN_RECORD_COMMAND_BUFFER_FAILED */
|
||||||
"Failed to record command buffer. Validate your commands.",
|
"Failed to record command buffer. Validate your commands.",
|
||||||
/* VULKAN_SYNCOBJCTS_CREATION_FAILED */
|
/* VULKAN_SYNCOBJCTS_CREATION_FAILED */
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "localization.hpp"
|
#include "utilities/localization.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace rse::locale::window
|
namespace rse::locale::window
|
||||||
@@ -29,7 +29,7 @@ enum Errors
|
|||||||
LAST_MESSAGE
|
LAST_MESSAGE
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array<std::array<const char*, LAST_LANGUAGE>, LAST_MESSAGE> windowErrorMesssages_nolocale = {
|
std::array<std::array<const char*, rse::utils::locale::LAST_LANGUAGE>, LAST_MESSAGE> windowErrorMesssages_nolocale = {
|
||||||
{/* EN_US */
|
{/* EN_US */
|
||||||
/* WINDOW_GLFW_INIT_FAILED */
|
/* WINDOW_GLFW_INIT_FAILED */
|
||||||
"GLFW init failed!"
|
"GLFW init failed!"
|
||||||
@@ -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
|
#define RSE_GRAPHICS_VULKANBASE_HPP
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
|
||||||
|
#include "vulkan_commons.hpp"
|
||||||
|
|
||||||
namespace rse::graphics::vulkanbase
|
namespace rse::graphics::vulkanbase
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize Vulkan backend
|
* @brief Initialize Vulkan backend
|
||||||
*
|
*
|
||||||
|
* @param state
|
||||||
* @return uint8_t 0 on success. Status code on failure
|
* @return uint8_t 0 on success. Status code on failure
|
||||||
*/
|
*/
|
||||||
|
uint8_t initVulkan(VulkanState& state);
|
||||||
|
|
||||||
uint8_t initVulkan();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Deinitialize Vulkan backend
|
* @brief Deinitialize Vulkan backend
|
||||||
*
|
*
|
||||||
|
* @param state
|
||||||
*/
|
*/
|
||||||
void deinitVulkan();
|
void deinitVulkan(VulkanState& state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draw frame on the screen.
|
* @brief Draw frame on the screen.
|
||||||
*
|
*
|
||||||
|
* @param state
|
||||||
*/
|
*/
|
||||||
void drawFrame();
|
void drawFrame(VulkanState& state);
|
||||||
|
|
||||||
} /* namespace rse::graphics::vulkanbase */
|
} /* namespace rse::graphics::vulkanbase */
|
||||||
|
|
||||||
@@ -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 "window.hpp"
|
||||||
|
|
||||||
#include "localeWindow.hpp"
|
#include "locale_window.hpp"
|
||||||
#include "logger.hpp"
|
#include "utilities/logger.hpp"
|
||||||
#include "vulkanBase.hpp"
|
#include "vulkan_base.hpp"
|
||||||
|
|
||||||
namespace rse::graphics::window
|
namespace rse::graphics::window
|
||||||
{
|
{
|
||||||
@@ -96,11 +96,11 @@ uint16_t windowInit()
|
|||||||
return WINDOW_ERROR_NO_ERROR;
|
return WINDOW_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t windowLoop()
|
uint16_t windowLoop(rse::graphics::vulkanbase::VulkanState& vulkan_state)
|
||||||
{
|
{
|
||||||
while (!glfwWindowShouldClose(gpWindowHandle)) {
|
while (!glfwWindowShouldClose(gpWindowHandle)) {
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
rse::graphics::vulkanbase::drawFrame();
|
rse::graphics::vulkanbase::drawFrame(vulkan_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
return WINDOW_ERROR_NO_ERROR;
|
return WINDOW_ERROR_NO_ERROR;
|
||||||
@@ -9,12 +9,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#ifndef RSE_WINDOW_HPP
|
||||||
|
#define RSE_WINDOW_HPP
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
/* Vulkan header MUST be included before glfw */
|
/* Vulkan header MUST be included before glfw */
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
#include "vulkan_commons.hpp"
|
||||||
|
|
||||||
namespace rse::graphics::window
|
namespace rse::graphics::window
|
||||||
{
|
{
|
||||||
@@ -32,9 +34,10 @@ uint16_t windowInit();
|
|||||||
/**
|
/**
|
||||||
* @brief Main window loop
|
* @brief Main window loop
|
||||||
*
|
*
|
||||||
|
* @param vulkan_state
|
||||||
* @return uint16_t WINDOW_SUCCESS or error code
|
* @return uint16_t WINDOW_SUCCESS or error code
|
||||||
*/
|
*/
|
||||||
uint16_t windowLoop();
|
uint16_t windowLoop(rse::graphics::vulkanbase::VulkanState& vulkan_state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Closes window and terminates GLFW
|
* @brief Closes window and terminates GLFW
|
||||||
@@ -45,3 +48,5 @@ void windowTerminate();
|
|||||||
GLFWwindow* getWindowHandle();
|
GLFWwindow* getWindowHandle();
|
||||||
|
|
||||||
} /* namespace rse::graphics::window */
|
} /* namespace rse::graphics::window */
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
add_executable(${PROJECT_NAME} src/main.cpp)
|
add_executable(${PROJECT_NAME} src/main.cpp)
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||||
${CMAKE_SOURCE_DIR}/Utilities/include
|
${CMAKE_SOURCE_DIR})
|
||||||
${CMAKE_SOURCE_DIR}/Graphics/include)
|
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
rse_graphics)
|
rse_graphics)
|
||||||
@@ -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
|
#pragma once
|
||||||
#define SELECTED_LANGUAGE rse::locale::EN_US
|
#define SELECTED_LANGUAGE rse::utils::locale::EN_US
|
||||||
|
|
||||||
namespace rse::locale
|
namespace rse::utils::locale
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#ifndef RSE_LOGGER_HPP
|
||||||
|
#define RSE_LOGGER_HPP
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@@ -17,16 +18,16 @@
|
|||||||
#define LOG_LEVEL LOGLEVEL_DEBUG
|
#define LOG_LEVEL LOGLEVEL_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LOGT rse::log::Logger(rse::log::LOGLEVEL_TRACE)
|
#define LOGT rse::utils::log::Logger(rse::utils::log::LOGLEVEL_TRACE)
|
||||||
#define LOGD rse::log::Logger(rse::log::LOGLEVEL_DEBUG)
|
#define LOGD rse::utils::log::Logger(rse::utils::log::LOGLEVEL_DEBUG)
|
||||||
#define LOGI rse::log::Logger(rse::log::LOGLEVEL_INFO)
|
#define LOGI rse::utils::log::Logger(rse::utils::log::LOGLEVEL_INFO)
|
||||||
#define LOGW rse::log::Logger(rse::log::LOGLEVEL_WARNING)
|
#define LOGW rse::utils::log::Logger(rse::utils::log::LOGLEVEL_WARNING)
|
||||||
#define LOGE rse::log::Logger(rse::log::LOGLEVEL_ERROR)
|
#define LOGE rse::utils::log::Logger(rse::utils::log::LOGLEVEL_ERROR)
|
||||||
#define LOGF rse::log::Logger(rse::log::LOGLEVEL_FATAL)
|
#define LOGF rse::utils::log::Logger(rse::utils::log::LOGLEVEL_FATAL)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace rse::log
|
namespace rse::utils::log
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,3 +75,5 @@ class Logger {
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rse::log
|
} // namespace rse::log
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#include "file_utils.hpp"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace rse::utils::file
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads file and puts its content into char buffer
|
||||||
|
*
|
||||||
|
* @param fileName Path to file
|
||||||
|
* @return std::vector<char> Binary buffer data
|
||||||
|
*/
|
||||||
|
std::vector<char> read_file(const std::string& fileName)
|
||||||
|
{
|
||||||
|
std::ifstream file{fileName, std::ios::ate | std::ios::binary};
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get file size and create data buffer */
|
||||||
|
size_t fileSize = static_cast<size_t>(file.tellg());
|
||||||
|
std::vector<char> buffer(fileSize);
|
||||||
|
|
||||||
|
file.seekg(0);
|
||||||
|
file.read(buffer.data(), fileSize);
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace rse::utils::file
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace rse::log
|
namespace rse::utils::log
|
||||||
{
|
{
|
||||||
|
|
||||||
Logger::Logger(LogLevel level) : printMessage(false)
|
Logger::Logger(LogLevel level) : printMessage(false)
|
||||||
Reference in new issue
Block a user