Move back to C
I'm insane and as such I have moved to C AGAIN! Without any reason.
This commit is contained in:
1 parent
bf31b96a2e
commit
3d538eaa2c
43 files changed
+2643
-2647
No files matched your search
@@ -7,6 +7,7 @@ project("RedScarfEngine"
|
|||||||
|
|
||||||
# make sure c++ 20 is set
|
# make sure c++ 20 is set
|
||||||
set(CMAKE_CXX_STANDARD 20)# turn on the dynamic depends for ninja
|
set(CMAKE_CXX_STANDARD 20)# turn on the dynamic depends for ninja
|
||||||
|
set(CMAKE_C_STANDARD 23)# turn on the dynamic depends for ninja
|
||||||
# set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
|
# set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
|
||||||
# Enable C++ modules
|
# Enable C++ modules
|
||||||
# set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046")
|
# set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046")
|
||||||
|
|||||||
@@ -44,17 +44,17 @@ ${CMAKE_SOURCE_DIR}
|
|||||||
${glfw3_INCLUDE_DIR}
|
${glfw3_INCLUDE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
message("Vulkan_INCLUDE_DIR: ${Vulkan_INCLUDE_DIR}")
|
|
||||||
target_sources(rse_graphics PRIVATE
|
target_sources(rse_graphics PRIVATE
|
||||||
src/window.cpp
|
src/vma_port.cpp
|
||||||
src/vulkan_base.cpp
|
src/window.c
|
||||||
src/vulkan_pipeline.cpp
|
src/vulkan_base.c
|
||||||
src/vulkan_buffers.cpp
|
src/vulkan_pipeline.c
|
||||||
src/vulkan_descriptors.cpp
|
src/vulkan_buffers.c
|
||||||
src/rse_graphics.cpp
|
src/vulkan_descriptors.c
|
||||||
|
src/rse_graphics.c
|
||||||
src/rse_math.c
|
src/rse_math.c
|
||||||
src/mesh_controller.cpp
|
src/mesh_controller.c
|
||||||
src/vulkan_commons.cpp
|
src/vulkan_commons.c
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(rse_graphics PRIVATE
|
target_link_libraries(rse_graphics PRIVATE
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef RSE_GRAPHICS_H
|
||||||
|
#define RSE_GRAPHICS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void rse_graphics_init(void);
|
||||||
|
uint8_t rse_graphics_run(void);
|
||||||
|
|
||||||
|
#endif /* RSE_GRAPHICS_H */
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#ifndef RSE_GRAPHICS_HPP
|
|
||||||
#define RSE_GRAPHICS_HPP
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
struct VulkanState;
|
|
||||||
namespace rse::graphics
|
|
||||||
{
|
|
||||||
class RseGraphics {
|
|
||||||
public:
|
|
||||||
RseGraphics();
|
|
||||||
~RseGraphics();
|
|
||||||
RseGraphics& operator=(const RseGraphics&) = delete;
|
|
||||||
RseGraphics(const RseGraphics&) = delete;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize graphics module, runs main loop and deinits everything, when interrupted.
|
|
||||||
* This has to been run AFTER all scene objects have been already created.
|
|
||||||
*
|
|
||||||
* @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 */
|
|
||||||
VulkanState* vulkan_state;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace rse::graphics
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -9,15 +9,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LOCALE_VULKAN_H
|
#ifndef RSE_LOCALE_VULKAN_H
|
||||||
#define LOCALE_VULKAN_H
|
#define RSE_LOCALE_VULKAN_H
|
||||||
|
|
||||||
#include "utilities/localization.h"
|
#include "utilities/localization.h"
|
||||||
|
|
||||||
#define vulkanErrorMesssages vulkanErrorMesssages_nolocale[SELECTED_LANGUAGE]
|
#define vulkan_error_messages vulkan_error_messages_nolocale[SELECTED_LANGUAGE]
|
||||||
|
|
||||||
enum Errors
|
enum vulkan_errors_t {
|
||||||
{
|
|
||||||
VULKAN_LAYER_NOT_SUPPORTED,
|
VULKAN_LAYER_NOT_SUPPORTED,
|
||||||
VULKAN_EXTENSION_NOT_SUPPORTED,
|
VULKAN_EXTENSION_NOT_SUPPORTED,
|
||||||
VULKAN_INTANCE_INIT_FAILED,
|
VULKAN_INTANCE_INIT_FAILED,
|
||||||
@@ -45,66 +44,78 @@ enum Errors
|
|||||||
VULKAN_RECORD_COMMAND_BEGIN_FAILED,
|
VULKAN_RECORD_COMMAND_BEGIN_FAILED,
|
||||||
VULKAN_RECORD_COMMAND_BUFFER_FAILED,
|
VULKAN_RECORD_COMMAND_BUFFER_FAILED,
|
||||||
VULKAN_SYNCOBJCTS_CREATION_FAILED,
|
VULKAN_SYNCOBJCTS_CREATION_FAILED,
|
||||||
|
VULKAN_MESH_NO_FREE,
|
||||||
|
VULKAN_MESH_INSTANCE_NO_FREE,
|
||||||
|
VULAKN_DRAW_FAILED,
|
||||||
|
COMMON_MEMORY_ALLOCATION_FAILED,
|
||||||
LAST_MESSAGE
|
LAST_MESSAGE
|
||||||
};
|
};
|
||||||
|
|
||||||
const char vulkanErrorMesssages_nolocale[LAST_LANGUAGE][LAST_MESSAGE][MAX_MESSAGE_LENGHT] = {
|
static const char vulkan_error_messages_nolocale[LAST_LANGUAGE][LAST_MESSAGE][MAX_MESSAGE_LENGHT] = {
|
||||||
{/* EN_US */
|
{/* EN_US */
|
||||||
/* VULKAN_LAYER_NOT_SUPPORTED */
|
/* VULKAN_LAYER_NOT_SUPPORTED */
|
||||||
"Selected layer is not supported: ",
|
"Selected layer is not supported: %s\n",
|
||||||
/* VULKAN_EXTENSION_NOT_SUPPORTED */
|
/* VULKAN_EXTENSION_NOT_SUPPORTED */
|
||||||
"Selected extension is not supported: ",
|
"Selected extension is not supported: %s\n",
|
||||||
/* VULKAN_INTANCE_INIT_FAILED */
|
/* VULKAN_INTANCE_INIT_FAILED */
|
||||||
"Vulkan instance initialization failed",
|
"Vulkan instance initialization failed\n",
|
||||||
/* VULKAN_NO_PHYSICAL_DEVICES */
|
/* VULKAN_NO_PHYSICAL_DEVICES */
|
||||||
"No physical graphical physical devices found in system",
|
"No physical graphical physical devices found in system\n",
|
||||||
/* VULKAN_NO_SUITABLE_PHYSICAL_DEVICES */
|
/* VULKAN_NO_SUITABLE_PHYSICAL_DEVICES */
|
||||||
"No suitable physical device found",
|
"No suitable physical device found\n",
|
||||||
/* VULKAN_QUEUE_NOT_SUPPORTED */
|
/* VULKAN_QUEUE_NOT_SUPPORTED */
|
||||||
"Selected physical device does not support required queue capabilities",
|
"Selected physical device does not support required queue capabilities\n",
|
||||||
/* VULKAN_FAILED_TO_CREATE_DEVICE */
|
/* VULKAN_FAILED_TO_CREATE_DEVICE */
|
||||||
"Failed to create logical device",
|
"Failed to create logical device\n",
|
||||||
/* VULKAN_ALLOCATOR_CREATION_FAILED */
|
/* VULKAN_ALLOCATOR_CREATION_FAILED */
|
||||||
"Failed to create Vulkan Memory Allocator",
|
"Failed to create Vulkan Memory Allocator\n",
|
||||||
/* VULKAN_COMMAND_POOL_CREATION_FAILED */
|
/* VULKAN_COMMAND_POOL_CREATION_FAILED */
|
||||||
"Failed to create command pool",
|
"Failed to create command pool\n",
|
||||||
/* VULKAN_BUFFER_CREATION_FAILED */
|
/* VULKAN_BUFFER_CREATION_FAILED */
|
||||||
"Failed to create a vertex buffer",
|
"Failed to create a vertex buffer\n",
|
||||||
/* VULKAN_VERTEX_BUFFER_MAPPING_FAILED */
|
/* VULKAN_VERTEX_BUFFER_MAPPING_FAILED */
|
||||||
"Failed to map vulkan memory buffer",
|
"Failed to map vulkan memory buffer\n",
|
||||||
/* VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED */
|
/* VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED */
|
||||||
"Failed to allocate command buffers",
|
"Failed to allocate command buffers\n",
|
||||||
/* VULKAN_SURFACE_CREATION_FAILED */
|
/* VULKAN_SURFACE_CREATION_FAILED */
|
||||||
"Failed to create window surface",
|
"Failed to create window surface\n",
|
||||||
/* VULKAN_SWAPCHAIN_CREATION_FAILED */
|
/* VULKAN_SWAPCHAIN_CREATION_FAILED */
|
||||||
"Failed to create swapchain",
|
"Failed to create swapchain\n",
|
||||||
/* VULKAN_SWAPCHAIN_IMVIEW_CREATION_FAILED */
|
/* VULKAN_SWAPCHAIN_IMVIEW_CREATION_FAILED */
|
||||||
"Failed to create swapchain image view",
|
"Failed to create swapchain image view\n",
|
||||||
/* VULKAN_SHADER_UNABLE_TO_OPEN */
|
/* VULKAN_SHADER_UNABLE_TO_OPEN */
|
||||||
"Failed to open a file containing shader code",
|
"Failed to open a file containing shader code\n",
|
||||||
/* VULKAN_SHADER_UNABLE_TO_CREATE_SHADER */
|
/* VULKAN_SHADER_UNABLE_TO_CREATE_SHADER */
|
||||||
"Failed to create a shader",
|
"Failed to create a shader\n",
|
||||||
/* VULKAN_PIPELAYOUT_CREATION_FAILED */
|
/* VULKAN_PIPELAYOUT_CREATION_FAILED */
|
||||||
"Failed to create pipeline layout",
|
"Failed to create pipeline layout\n",
|
||||||
/* VULKAN_RENDERPASS_CREATION_FAILED */
|
/* VULKAN_RENDERPASS_CREATION_FAILED */
|
||||||
"Failed to create render pass",
|
"Failed to create render pass\n",
|
||||||
/* VULKAN_DESCRIPTOR_POOL_CREATION_FAILED */
|
/* VULKAN_DESCRIPTOR_POOL_CREATION_FAILED */
|
||||||
"Failed to create descriptor pool",
|
"Failed to create descriptor pool\n",
|
||||||
/* VULKAN_DESCRIPTOR_SETS_ALLOCATION_FAILED */
|
/* VULKAN_DESCRIPTOR_SETS_ALLOCATION_FAILED */
|
||||||
"Failed to allocate descriptor sets",
|
"Failed to allocate descriptor sets\n",
|
||||||
/* VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED */
|
/* VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED */
|
||||||
"Failed to create descriptor set layout",
|
"Failed to create descriptor set layout\n",
|
||||||
/* VULKAN_GRAPHICSPIPELINE_CREATION_FAILED */
|
/* VULKAN_GRAPHICSPIPELINE_CREATION_FAILED */
|
||||||
"Failed to create graphics pipeline",
|
"Failed to create graphics pipeline\n",
|
||||||
/* VULKAN_FRAMEBUFFERS_CREATION_FAILED */
|
/* VULKAN_FRAMEBUFFERS_CREATION_FAILED */
|
||||||
"Failed to create framebuffer",
|
"Failed to create framebuffer\n",
|
||||||
/* VULKAN_RECORD_COMMAND_BEGIN_FAILED */
|
/* VULKAN_RECORD_COMMAND_BEGIN_FAILED */
|
||||||
"Failed to start recording command buffer",
|
"Failed to start recording command buffer\n",
|
||||||
/* 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.\n",
|
||||||
/* VULKAN_SYNCOBJCTS_CREATION_FAILED */
|
/* VULKAN_SYNCOBJCTS_CREATION_FAILED */
|
||||||
"Failed to create sync objects"
|
"Failed to create sync objects.\n",
|
||||||
|
/* VULKAN_MESH_NO_FREE */
|
||||||
|
"Could not allocate new mesh, reached maximum.\n",
|
||||||
|
/* VULKAN_MESH_INSTANCE_NO_FREE */
|
||||||
|
"Could not allocate new mesh instance\n",
|
||||||
|
/* VULAKN_DRAW_FAILED */
|
||||||
|
"Failed to submit draw command buffer!",
|
||||||
|
/* COMMON_MEMORY_ALLOCATION_FAILED */
|
||||||
|
"Unable to allocate memory"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* LOCALE_VULKAN_H */
|
#endif /* RSE_LOCALE_VULKAN_H */
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @file localeWindow.hxx
|
* @file locale_window.h
|
||||||
* @author Piotr Krygier (everyonencancode@gmail.com)
|
* @author Piotr Krygier (everyonencancode@gmail.com)
|
||||||
* @brief Error messages
|
* @brief Error messages
|
||||||
* @version 0.1
|
* @version 0.1
|
||||||
@@ -9,13 +9,12 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#ifndef LOCALE_WINDOW_H
|
||||||
|
#define LOCALE_WINDOW_H
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include "utilities/localization.h"
|
#include "utilities/localization.h"
|
||||||
|
|
||||||
#define windowErrorMesssages windowErrorMesssages_nolocale[SELECTED_LANGUAGE]
|
#define window_error_messages window_error_messages_nolocale[SELECTED_LANGUAGE]
|
||||||
|
|
||||||
enum Errors
|
enum Errors
|
||||||
{
|
{
|
||||||
@@ -25,13 +24,15 @@ enum Errors
|
|||||||
LAST_MESSAGE
|
LAST_MESSAGE
|
||||||
};
|
};
|
||||||
|
|
||||||
const char windowErrorMesssages_nolocale[LAST_LANGUAGE][LAST_MESSAGE][MAX_MESSAGE_LENGHT] = {
|
static const char window_error_messages_nolocale[LAST_LANGUAGE][LAST_MESSAGE][MAX_MESSAGE_LENGHT] = {
|
||||||
{/* EN_US */
|
{/* EN_US */
|
||||||
/* WINDOW_GLFW_INIT_FAILED */
|
/* WINDOW_GLFW_INIT_FAILED */
|
||||||
"GLFW init failed!",
|
"GLFW init failed!\n",
|
||||||
/* WINDOW_VULKAN_NOT_SUPPORTED */
|
/* WINDOW_VULKAN_NOT_SUPPORTED */
|
||||||
"Unable to load Vulkan",
|
"Unable to load Vulkan\n",
|
||||||
/*WINDOW_NOT_CREATED*/
|
/*WINDOW_NOT_CREATED*/
|
||||||
"Window context creation failed"
|
"Window context creation failed\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif /* LOCALE_WINDOW_H */
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
#include "mesh_controller.h"
|
||||||
|
|
||||||
|
#include "vulkan_buffers.h"
|
||||||
|
#include "locale_vulkan.h"
|
||||||
|
#include "utilities/logger.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MAX_MESH_NUMBER 256
|
||||||
|
#define MAX_INSTANCE_NUMBER 256
|
||||||
|
|
||||||
|
#define OBJECT_FREE 0
|
||||||
|
#define OBJECT_TAKEN 1
|
||||||
|
|
||||||
|
struct rse_mesh_t g_meshes[MAX_MESH_NUMBER] = {OBJECT_FREE};
|
||||||
|
uint16_t g_free_ids[MAX_MESH_NUMBER] = {OBJECT_FREE};
|
||||||
|
|
||||||
|
uint8_t g_instance_free_ids[MAX_MESH_NUMBER][MAX_INSTANCE_NUMBER] = {OBJECT_FREE};
|
||||||
|
|
||||||
|
uint16_t create_mesh(struct rse_vertex_t* vertices,
|
||||||
|
uint16_t* indices,
|
||||||
|
size_t vertices_num,
|
||||||
|
size_t indices_num)
|
||||||
|
{
|
||||||
|
size_t iter = 0U;
|
||||||
|
|
||||||
|
for(iter = 0U; iter < MAX_MESH_NUMBER; ++iter) {
|
||||||
|
if(g_free_ids[iter] == OBJECT_FREE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(iter >= MAX_MESH_NUMBER) {
|
||||||
|
LOGE(vulkan_error_messages[VULKAN_MESH_NO_FREE]);
|
||||||
|
/* TODO: Add error handling */
|
||||||
|
return MAX_MESH_NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free_ids[iter] = OBJECT_TAKEN;
|
||||||
|
|
||||||
|
g_meshes[iter].unique_id = iter;
|
||||||
|
g_meshes[iter].vertices = (struct rse_vertex_t*)malloc(sizeof(struct rse_vertex_t) * vertices_num);
|
||||||
|
g_meshes[iter].indices = (uint16_t*)malloc(sizeof(uint16_t) * indices_num);
|
||||||
|
g_meshes[iter].instances = (struct rse_instance_data_t*)malloc(sizeof(struct rse_instance_data_t) * MAX_INSTANCE_NUMBER);
|
||||||
|
|
||||||
|
if(g_meshes[iter].vertices == NULL ||
|
||||||
|
g_meshes[iter].indices == NULL) {
|
||||||
|
LOGE(vulkan_error_messages[COMMON_MEMORY_ALLOCATION_FAILED]);
|
||||||
|
/* TODO: Add error handling */
|
||||||
|
return MAX_MESH_NUMBER;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
g_meshes[iter].instances_count = 0;
|
||||||
|
g_meshes[iter].indices_count = indices_num;
|
||||||
|
g_meshes[iter].vertices_count = vertices_num;
|
||||||
|
memcpy(g_meshes[iter].vertices, vertices, vertices_num);
|
||||||
|
memcpy(g_meshes[iter].indices, indices, indices_num);
|
||||||
|
|
||||||
|
update_mesh_buffers(vertices, indices, vertices_num, indices_num);
|
||||||
|
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_mesh_instance(uint16_t mesh_id,
|
||||||
|
struct rse_instance_data_t instance_data)
|
||||||
|
{
|
||||||
|
size_t iter = 0U;
|
||||||
|
struct rse_instance_data_t* instances = g_meshes[mesh_id].instances;
|
||||||
|
uint8_t* free_instance_ids = g_instance_free_ids[mesh_id];
|
||||||
|
|
||||||
|
for(iter = 0U; iter < MAX_INSTANCE_NUMBER; ++iter) {
|
||||||
|
if(free_instance_ids[iter] == OBJECT_FREE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(iter >= MAX_INSTANCE_NUMBER) {
|
||||||
|
LOGE(vulkan_error_messages[VULKAN_MESH_NO_FREE]);
|
||||||
|
/* TODO: Add error handling */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_instance_ids[iter] = OBJECT_TAKEN;
|
||||||
|
instances[iter] = instance_data;
|
||||||
|
|
||||||
|
if(iter >= g_meshes[mesh_id].instances_count) {
|
||||||
|
g_meshes[mesh_id].instances_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_mesh_instances(&instance_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rse_instance_data_t* get_instance_data_for_mesh(uint16_t mesh_id)
|
||||||
|
{
|
||||||
|
return g_meshes[mesh_id].instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct rse_vertex_t* get_mesh_vertices(uint16_t mesh_id) {
|
||||||
|
return g_meshes[mesh_id].vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t* get_mesh_indices(uint16_t mesh_id) {
|
||||||
|
return g_meshes[mesh_id].indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_vertices_count(uint16_t mesh_id)
|
||||||
|
{
|
||||||
|
return g_meshes[mesh_id].vertices_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_instances_count(uint16_t mesh_id)
|
||||||
|
{
|
||||||
|
return g_meshes[mesh_id].instances_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_indices_count(uint16_t mesh_id)
|
||||||
|
{
|
||||||
|
return g_meshes[mesh_id].indices_count;
|
||||||
|
}
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
#include "mesh_controller.h"
|
|
||||||
|
|
||||||
#include "vulkan_buffers.hpp"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
std::map<uint16_t, struct Mesh> g_meshes;
|
|
||||||
std::queue<uint16_t> free_ids;
|
|
||||||
|
|
||||||
std::map<uint16_t, std::queue<uint16_t>> instance_free_ids;
|
|
||||||
|
|
||||||
uint16_t create_mesh(std::vector<Vertex> vertices,
|
|
||||||
std::vector<uint16_t> indices)
|
|
||||||
{
|
|
||||||
static uint16_t last_id = 0U;
|
|
||||||
uint16_t assignable_id = 0U;
|
|
||||||
|
|
||||||
if (free_ids.empty()) {
|
|
||||||
assignable_id = last_id++;
|
|
||||||
} else {
|
|
||||||
assignable_id = free_ids.front();
|
|
||||||
free_ids.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
g_meshes.emplace(assignable_id, Mesh (
|
|
||||||
assignable_id,
|
|
||||||
vertices,
|
|
||||||
indices
|
|
||||||
));
|
|
||||||
|
|
||||||
rse::graphics::vulkanbase::update_mesh_buffers(vertices, indices);
|
|
||||||
|
|
||||||
return assignable_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void create_mesh_instance(uint16_t mesh_id,
|
|
||||||
InstanceData instance_data)
|
|
||||||
{
|
|
||||||
uint16_t instance_id = 0U;
|
|
||||||
auto& instances = g_meshes[mesh_id].instances;
|
|
||||||
auto& free_instance_ids = instance_free_ids[mesh_id];
|
|
||||||
|
|
||||||
if (free_instance_ids.empty()) {
|
|
||||||
instance_id = instances.size();
|
|
||||||
} else {
|
|
||||||
instance_id = free_instance_ids.front();
|
|
||||||
free_instance_ids.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
instances[instance_id] = std::make_unique<struct MeshInstance>(
|
|
||||||
instance_id,
|
|
||||||
instance_data
|
|
||||||
);
|
|
||||||
|
|
||||||
rse::graphics::vulkanbase::update_mesh_instances(instance_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Replace with something else. It will take to much time to do this each time we draw frame for all the objects
|
|
||||||
std::vector<InstanceData> get_instance_data_for_mesh(uint16_t mesh_id) {
|
|
||||||
std::vector<InstanceData> data;
|
|
||||||
|
|
||||||
for(const auto& [id, instance] : g_meshes[mesh_id].instances) {
|
|
||||||
data.push_back(instance.get()->instance_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<Vertex> get_mesh_vertices(uint16_t mesh_id) {
|
|
||||||
return g_meshes[mesh_id].vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint16_t> get_mesh_indices(uint16_t mesh_id) {
|
|
||||||
return g_meshes[mesh_id].indices;
|
|
||||||
}
|
|
||||||
@@ -14,38 +14,35 @@
|
|||||||
|
|
||||||
#include "vulkan_commons.h"
|
#include "vulkan_commons.h"
|
||||||
|
|
||||||
#include <map>
|
/**
|
||||||
#include <vector>
|
* @brief Mesh data, with unique id and dynamic arrays holding vertices and indices.
|
||||||
#include <cstdint>
|
* One mesh can have multiple instances.
|
||||||
#include <memory>
|
*
|
||||||
/* Forward declarations */
|
*/
|
||||||
struct MeshInstance;
|
struct rse_mesh_t
|
||||||
|
|
||||||
struct MeshInstance
|
|
||||||
{
|
|
||||||
uint32_t instance_id;
|
|
||||||
InstanceData instance_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Mesh
|
|
||||||
{
|
{
|
||||||
uint32_t unique_id;
|
uint32_t unique_id;
|
||||||
const std::vector<Vertex> vertices;
|
size_t instances_count;
|
||||||
std::vector<uint16_t> indices;
|
size_t indices_count;
|
||||||
std::map<uint16_t, std::unique_ptr<MeshInstance>> instances;
|
size_t vertices_count;
|
||||||
|
struct rse_vertex_t* vertices;
|
||||||
|
uint16_t* indices;
|
||||||
|
struct rse_instance_data_t* instances;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new mesh for provided vertices and indices.
|
* @brief Create a new mesh for provided vertices and indices.
|
||||||
*
|
*
|
||||||
* @param vertices vector of vertices
|
* @param vertices vector of vertices
|
||||||
* @param indices indices for mesh
|
* @param indices indices for mesh
|
||||||
|
* @param vertices_num vertices count
|
||||||
|
* @param indices_num indices count
|
||||||
* @return uint16_t Mesh identifier. Can be useful for getting vertices, indices and instances
|
* @return uint16_t Mesh identifier. Can be useful for getting vertices, indices and instances
|
||||||
*/
|
*/
|
||||||
uint16_t create_mesh(std::vector<Vertex> vertices,
|
uint16_t create_mesh(struct rse_vertex_t* vertices,
|
||||||
std::vector<uint16_t> indices);
|
uint16_t* indices,
|
||||||
|
size_t vertices_num,
|
||||||
|
size_t indices_num);
|
||||||
/**
|
/**
|
||||||
* @brief Create a instance for the selected mesh. Instance can have its own location, rotation and scale
|
* @brief Create a instance for the selected mesh. Instance can have its own location, rotation and scale
|
||||||
*
|
*
|
||||||
@@ -53,11 +50,55 @@ uint16_t create_mesh(std::vector<Vertex> vertices,
|
|||||||
* @param instance_data Transformation info
|
* @param instance_data Transformation info
|
||||||
*/
|
*/
|
||||||
void create_mesh_instance(uint16_t mesh_id,
|
void create_mesh_instance(uint16_t mesh_id,
|
||||||
InstanceData instance_data);
|
struct rse_instance_data_t instance_data);
|
||||||
|
|
||||||
std::vector<Vertex> get_mesh_vertices(uint16_t mesh_id);
|
/**
|
||||||
std::vector<uint16_t> get_mesh_indices(uint16_t mesh_id);
|
* @brief Get the vertex count for mesh with mesh_id ID
|
||||||
std::vector<InstanceData> get_instance_data_for_mesh(uint16_t mesh_id);
|
*
|
||||||
|
* @param mesh_id Mesh identifier
|
||||||
|
* @return size_t Number of vertices
|
||||||
|
*/
|
||||||
|
size_t get_vertices_count(uint16_t mesh_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the index count for mesh with mesh_id ID
|
||||||
|
*
|
||||||
|
* @param mesh_id Mesh identifier
|
||||||
|
* @return size_t Number of indices
|
||||||
|
*/
|
||||||
|
size_t get_indices_count(uint16_t mesh_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the instance count for mesh with mesh_id ID
|
||||||
|
*
|
||||||
|
* @param mesh_id Mesh identifier
|
||||||
|
* @return size_t Number of instances
|
||||||
|
*/
|
||||||
|
size_t get_instances_count(uint16_t mesh_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get pointer to vertices of mesh
|
||||||
|
*
|
||||||
|
* @param mesh_id Mesh identifier
|
||||||
|
* @return struct rse_vertex_t* Vertex data
|
||||||
|
*/
|
||||||
|
struct rse_vertex_t* get_mesh_vertices(uint16_t mesh_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get pointer to indices of mesh
|
||||||
|
*
|
||||||
|
* @param mesh_id Mesh identifier
|
||||||
|
* @return uint16_t* Indices data
|
||||||
|
*/
|
||||||
|
uint16_t* get_mesh_indices(uint16_t mesh_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the instance data for mesh object.
|
||||||
|
*
|
||||||
|
* @param mesh_id Mesh identifier
|
||||||
|
* @return struct rse_instance_data_t* Pointer to instances array
|
||||||
|
*/
|
||||||
|
struct rse_instance_data_t* get_instance_data_for_mesh(uint16_t mesh_id);
|
||||||
|
|
||||||
|
|
||||||
#endif /* RSE_MESH_CONTROLLER_H */
|
#endif /* RSE_MESH_CONTROLLER_H */
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
#include "rse_graphics.h"
|
||||||
|
#include "window.h"
|
||||||
|
#include "vulkan_base.h"
|
||||||
|
|
||||||
|
#include "mesh_controller.h"
|
||||||
|
#include "vulkan_commons.h"
|
||||||
|
|
||||||
|
#include "rse_math.h"
|
||||||
|
|
||||||
|
extern struct rse_vulkan_state_t g_vulkan_state;
|
||||||
|
|
||||||
|
void rse_graphics_init(void)
|
||||||
|
{
|
||||||
|
// g_vulkan_state = malloc(sizeof(struct rse_vulkan_state_t));
|
||||||
|
g_vulkan_state.graphics_queue = VK_NULL_HANDLE;
|
||||||
|
g_vulkan_state.physical_device = VK_NULL_HANDLE;
|
||||||
|
g_vulkan_state.current_frame = 0U;
|
||||||
|
// vulkan_state->queueFamilyIndices = {0U}; /* TODO: Change when more families are needed */
|
||||||
|
// g_vulkan_state->uniformBuffers = {};
|
||||||
|
// g_vulkan_state->pCommandBuffers = {};
|
||||||
|
// g_vulkan_state->descriptorSets = {};
|
||||||
|
// g_vulkan_state->swapChainImageViews = {}; TODO: Allocate later
|
||||||
|
// g_vulkan_state->swapchainExtent = {}; TODO: Allocate later
|
||||||
|
g_vulkan_state.render_pass = VK_NULL_HANDLE;
|
||||||
|
g_vulkan_state.descriptor_set_layout = VK_NULL_HANDLE;
|
||||||
|
// g_vulkan_state->swapchainFramebuffers = {}; TODO: Allocate later
|
||||||
|
g_vulkan_state.graphics_pipeline = VK_NULL_HANDLE;
|
||||||
|
g_vulkan_state.pipeline_layout = VK_NULL_HANDLE;
|
||||||
|
g_vulkan_state.swapchain = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rse_graphics_deinit()
|
||||||
|
{
|
||||||
|
// free(g_vulkan_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t rse_graphics_run(void)
|
||||||
|
{
|
||||||
|
window_init();
|
||||||
|
|
||||||
|
if (0 != init_vulkan())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// FIXME: Temporary array of vertices, for testing purposes
|
||||||
|
struct rse_vertex_t 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}}};
|
||||||
|
struct rse_vertex_t vertices2[] = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}},
|
||||||
|
|
||||||
|
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}},
|
||||||
|
{{0.5f, 0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}},
|
||||||
|
{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 0.0f}}};
|
||||||
|
|
||||||
|
// FIXME: Temporary array of vertices, for testing purposes
|
||||||
|
uint16_t indices[] = {0, 1, 2, 2, 3, 0};
|
||||||
|
|
||||||
|
uint16_t mesh_id = create_mesh(vertices, indices, 24, 6);
|
||||||
|
create_mesh_instance(mesh_id, (struct rse_instance_data_t){
|
||||||
|
{0.0f, 0.0f, 0.0f},
|
||||||
|
{0.0f, 0.0f, rse_math_deg_to_radians(45.0f)},
|
||||||
|
1.0f
|
||||||
|
});
|
||||||
|
|
||||||
|
create_mesh_instance(mesh_id, (struct rse_instance_data_t){
|
||||||
|
{-1.0f, 0.0f, 0.0f},
|
||||||
|
{0.0f, 0.0f, 0.0f},
|
||||||
|
1.0f
|
||||||
|
});
|
||||||
|
|
||||||
|
mesh_id = create_mesh(vertices, indices, 24, 6);
|
||||||
|
create_mesh_instance(mesh_id, (struct rse_instance_data_t){
|
||||||
|
{0.0f, 1.0f, 0.0f},
|
||||||
|
{0.0f, 0.0f, rse_math_deg_to_radians(0.0f)},
|
||||||
|
1.0f
|
||||||
|
});
|
||||||
|
|
||||||
|
window_loop();
|
||||||
|
|
||||||
|
deinit_vulkan();
|
||||||
|
|
||||||
|
window_terminate();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
#include "rse_graphics.hpp"
|
|
||||||
#include "window.hpp"
|
|
||||||
#include "vulkan_base.hpp"
|
|
||||||
|
|
||||||
#include "mesh_controller.h"
|
|
||||||
#include "vulkan_commons.h"
|
|
||||||
|
|
||||||
#include "rse_math.h"
|
|
||||||
namespace rse::graphics
|
|
||||||
{
|
|
||||||
|
|
||||||
RseGraphics::RseGraphics()
|
|
||||||
{
|
|
||||||
vulkan_state = new VulkanState;
|
|
||||||
vulkan_state->graphicsQueue = VK_NULL_HANDLE;
|
|
||||||
vulkan_state->physicalDevice = VK_NULL_HANDLE;
|
|
||||||
vulkan_state->currentFrame = 0U;
|
|
||||||
// vulkan_state->queueFamilyIndices = {0U}; /* TODO: Change when more families are needed */
|
|
||||||
vulkan_state->uniformBuffers = {};
|
|
||||||
vulkan_state->pCommandBuffers = {};
|
|
||||||
vulkan_state->descriptorSets = {};
|
|
||||||
vulkan_state->swapChainImageViews = {};
|
|
||||||
vulkan_state->swapchainExtent = {};
|
|
||||||
vulkan_state->renderPass = VK_NULL_HANDLE;
|
|
||||||
vulkan_state->descriptorSetLayout = VK_NULL_HANDLE;
|
|
||||||
vulkan_state->swapchainFramebuffers = {};
|
|
||||||
vulkan_state->graphicsPipeline = VK_NULL_HANDLE;
|
|
||||||
vulkan_state->pipelineLayout = VK_NULL_HANDLE;
|
|
||||||
vulkan_state->swapchain = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
RseGraphics::~RseGraphics()
|
|
||||||
{
|
|
||||||
delete vulkan_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t RseGraphics::graphics_run(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
window::windowInit();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (0 != vulkanbase::initVulkan())
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// 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}}};
|
|
||||||
const std::vector<Vertex> vertices2 = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}},
|
|
||||||
|
|
||||||
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}},
|
|
||||||
{{0.5f, 0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}},
|
|
||||||
{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 0.0f}}};
|
|
||||||
|
|
||||||
// FIXME: Temporary array of vertices, for testing purposes
|
|
||||||
std::vector<uint16_t> indices = {0, 1, 2, 2, 3, 0};
|
|
||||||
|
|
||||||
uint16_t mesh_id = create_mesh(vertices, indices);
|
|
||||||
create_mesh_instance(mesh_id, InstanceData(
|
|
||||||
{0.0f, 0.0f, 0.0f},
|
|
||||||
{0.0f, 0.0f, rse_math_deg_to_radians(45.0f)},
|
|
||||||
1.0f
|
|
||||||
));
|
|
||||||
|
|
||||||
create_mesh_instance(mesh_id, InstanceData(
|
|
||||||
{-1.0f, 0.0f, 0.0f},
|
|
||||||
{0.0f, 0.0f, 0.0f},
|
|
||||||
1.0f
|
|
||||||
));
|
|
||||||
|
|
||||||
mesh_id = create_mesh(vertices, indices);
|
|
||||||
create_mesh_instance(mesh_id, InstanceData(
|
|
||||||
{0.0f, 1.0f, 0.0f},
|
|
||||||
{0.0f, 0.0f, rse_math_deg_to_radians(0.0f)},
|
|
||||||
1.0f
|
|
||||||
));
|
|
||||||
|
|
||||||
window::windowLoop();
|
|
||||||
|
|
||||||
vulkanbase::deinitVulkan();
|
|
||||||
|
|
||||||
window::windowTerminate();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace rse::graphics
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* @file vma_port.cpp
|
||||||
|
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||||
|
* @brief C port for vma
|
||||||
|
* @version 0.1
|
||||||
|
* @date 2023-08-11
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* VMA allows C linkage, but it must be compiled as a CPP file. This file takes cares of it */
|
||||||
|
#define VMA_IMPLEMENTATION
|
||||||
|
#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"
|
||||||
|
#include "vk_mem_alloc.h"
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
@@ -0,0 +1,658 @@
|
|||||||
|
/**
|
||||||
|
* @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.h"
|
||||||
|
|
||||||
|
#include "vulkan_errors.h"
|
||||||
|
#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"
|
||||||
|
#include "vk_mem_alloc.h"
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
#include "locale_vulkan.h"
|
||||||
|
#include "utilities/logger.h"
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
#include "vulkan_commons.h"
|
||||||
|
#include "vulkan_buffers.h"
|
||||||
|
#include "vulkan_descriptors.h"
|
||||||
|
#include "vulkan_pipeline.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define APPLICATION_NAME "RedScarfEngine PoC"
|
||||||
|
#define ENGINE_NAME "RedScarf Engine"
|
||||||
|
|
||||||
|
/* Define Validation Layers for debug build */
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define ENABLED_EXTENSIONS_COUT 4
|
||||||
|
#define ENABLED_INSTANCE_LAYERS_COUNT 1
|
||||||
|
const char* g_enabled_instance_layers_names[] = {"VK_LAYER_KHRONOS_validation"};
|
||||||
|
#else
|
||||||
|
#define ENABLED_EXTENSIONS_COUT 3
|
||||||
|
#define ENABLED_INSTANCE_LAYERS_COUNT 0
|
||||||
|
const char* g_enabled_instance_layers_names = {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char* g_enabled_instance_extensions_names[ENABLED_EXTENSIONS_COUT] = {};
|
||||||
|
|
||||||
|
#define ENABLED_DEVICE_EXTENSIONS_COUT 1
|
||||||
|
const char* g_enabled_device_extensions_names[] = {
|
||||||
|
"VK_KHR_swapchain",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
VkInstance g_vulkan_instance = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
VkDebugUtilsMessengerEXT g_debug_messenger = VK_NULL_HANDLE;
|
||||||
|
VkQueue g_present_queue;
|
||||||
|
|
||||||
|
VkSemaphore g_image_available_semaphores[SWAP_BUFFER_COUNT] = {};
|
||||||
|
VkSemaphore g_render_finished_semaphores[SWAP_BUFFER_COUNT] = {};
|
||||||
|
VkFence g_in_flight_fences[SWAP_BUFFER_COUNT] = {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
static void setup_debug_messenger();
|
||||||
|
static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||||
|
VkDebugUtilsMessageTypeFlagsEXT message_type,
|
||||||
|
const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
|
||||||
|
void* user_data);
|
||||||
|
|
||||||
|
static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* create_info,
|
||||||
|
const VkAllocationCallbacks* allocator,
|
||||||
|
VkDebugUtilsMessengerEXT* debug_messenger);
|
||||||
|
|
||||||
|
static void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debug_messenger,
|
||||||
|
const VkAllocationCallbacks* allocator);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void set_enabled_extension();
|
||||||
|
static uint8_t create_instance();
|
||||||
|
static uint8_t create_surface();
|
||||||
|
static uint8_t pick_physical_device();
|
||||||
|
static uint8_t create_device();
|
||||||
|
static uint8_t create_memory_allocator();
|
||||||
|
|
||||||
|
static uint8_t create_sync_objects();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
/**
|
||||||
|
* @brief Setup debugging system for Vulkan API
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void setup_debug_messenger()
|
||||||
|
{
|
||||||
|
VkDebugUtilsMessengerCreateInfoEXT create_info;
|
||||||
|
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||||
|
create_info.pNext = NULL;
|
||||||
|
create_info.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;
|
||||||
|
create_info.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;
|
||||||
|
create_info.pfnUserCallback = debug_callback;
|
||||||
|
create_info.pUserData = NULL; // Optional
|
||||||
|
create_info.flags = 0U;
|
||||||
|
|
||||||
|
if (CreateDebugUtilsMessengerEXT(g_vulkan_instance, &create_info, NULL, &g_debug_messenger) != VK_SUCCESS) {
|
||||||
|
fprintf(stderr, "failed to set up debug messenger!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calback for debug messenger
|
||||||
|
*
|
||||||
|
* @param message_severity Message severity
|
||||||
|
* @param message_type Type of a message
|
||||||
|
* @param callback_data Contains all the callback related data in the VkDebugUtilsMessengerCallbackDataEXT structure
|
||||||
|
* @param user_data 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 debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||||
|
VkDebugUtilsMessageTypeFlagsEXT message_type,
|
||||||
|
const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data)
|
||||||
|
{
|
||||||
|
(void)message_severity;
|
||||||
|
(void)message_type;
|
||||||
|
(void)user_data;
|
||||||
|
|
||||||
|
fprintf(stderr, "validation layer: %s\n", callback_data->pMessage);
|
||||||
|
|
||||||
|
return VK_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Proxy for vkCreateDebugUtilsMessengerEXT
|
||||||
|
*
|
||||||
|
* @param instance Vulkan instance object
|
||||||
|
* @param create_info VkDebugUtilsMessengerCreateInfoEXT structure
|
||||||
|
* @param allocator Memory allocator
|
||||||
|
* @param debug_messenger Debug messanger handle
|
||||||
|
* @return VkResult VK_SUCCESS on success
|
||||||
|
*/
|
||||||
|
VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* create_info,
|
||||||
|
const VkAllocationCallbacks* allocator,
|
||||||
|
VkDebugUtilsMessengerEXT* debug_messenger)
|
||||||
|
{
|
||||||
|
PFN_vkCreateDebugUtilsMessengerEXT func = NULL;
|
||||||
|
|
||||||
|
func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
|
||||||
|
if (func != NULL) {
|
||||||
|
return func(instance, create_info, allocator, debug_messenger);
|
||||||
|
} else {
|
||||||
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return VK_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Proxy for vkDestroyDebugUtilsMessengerEXT
|
||||||
|
*
|
||||||
|
* @param instance Vulkan instance object
|
||||||
|
* @param debug_messenger Debug messanger handle
|
||||||
|
* @param allocator Memory allocator
|
||||||
|
*/
|
||||||
|
void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debug_messenger,
|
||||||
|
const VkAllocationCallbacks* allocator)
|
||||||
|
{
|
||||||
|
PFN_vkDestroyDebugUtilsMessengerEXT func =
|
||||||
|
(PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
|
||||||
|
if (func != NULL) {
|
||||||
|
func(instance, debug_messenger, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable instance extensions, based on system and requirements
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void set_enabled_extension()
|
||||||
|
{
|
||||||
|
g_enabled_instance_extensions_names[0] = "VK_KHR_surface";
|
||||||
|
g_enabled_instance_extensions_names[1] = "VK_KHR_device_group_creation";
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
if (getenv("WAYLAND_DISPLAY") != NULL) {
|
||||||
|
g_enabled_instance_extensions_names[2] = "VK_KHR_wayland_surface";
|
||||||
|
} else {
|
||||||
|
g_enabled_instance_extensions_names[2] = "VK_KHR_xcb_surface";
|
||||||
|
}
|
||||||
|
#elif defined(_WIN32) || defined(WIN32)
|
||||||
|
g_enabled_instance_extensions_names.push_back("VK_KHR_win32_surface");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
g_enabled_instance_extensions_names[3] = "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 create_instance()
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
size_t j;
|
||||||
|
uint8_t supported = 0;
|
||||||
|
uint32_t extensionCount = 0;
|
||||||
|
uint32_t layersCount = 0;
|
||||||
|
VkApplicationInfo application_info = {};
|
||||||
|
VkInstanceCreateInfo create_info = {};
|
||||||
|
VkLayerProperties* layer_properties;
|
||||||
|
VkExtensionProperties* extensions_properties;
|
||||||
|
|
||||||
|
vkEnumerateInstanceLayerProperties(&layersCount, NULL);
|
||||||
|
layer_properties = malloc(sizeof(VkLayerProperties) * layersCount);
|
||||||
|
vkEnumerateInstanceLayerProperties(&layersCount, layer_properties);
|
||||||
|
|
||||||
|
vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL);
|
||||||
|
extensions_properties = malloc(sizeof(VkExtensionProperties) * extensionCount);
|
||||||
|
vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensions_properties);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
LOGD("Supported Layers");
|
||||||
|
for (i = 0; i < layersCount; i++) {
|
||||||
|
LOGD("%s", layer_properties[i].layerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGD("Supported Extensions");
|
||||||
|
for (size_t i = 0; i < extensionCount; i++) {
|
||||||
|
LOGD("%s", extensions_properties[i].extensionName);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Check if selected layers are supported*/
|
||||||
|
for (i = 0; i < ENABLED_INSTANCE_LAYERS_COUNT; ++i) {
|
||||||
|
for(j = 0; i < layersCount; ++j) {
|
||||||
|
if(strcmp(layer_properties[j].layerName, g_enabled_instance_layers_names[i]) == 0) {
|
||||||
|
supported = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(supported == 0) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_LAYER_NOT_SUPPORTED], layer_properties[j].layerName);
|
||||||
|
return VULKAN_ERROR_LAYER_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
supported = 0;
|
||||||
|
|
||||||
|
/* Check if selected extensions are supported*/
|
||||||
|
for (i = 0; i < ENABLED_EXTENSIONS_COUT; ++i) {
|
||||||
|
for(j = 0; j < extensionCount; ++j) {
|
||||||
|
if(strcmp(extensions_properties[j].extensionName, g_enabled_instance_extensions_names[i]) == 0) {
|
||||||
|
supported = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(supported == 0) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_EXTENSION_NOT_SUPPORTED], extensions_properties[j].extensionName);
|
||||||
|
return VULKAN_ERROR_EXTENSION_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||||
|
application_info.pNext = NULL;
|
||||||
|
application_info.pApplicationName = APPLICATION_NAME;
|
||||||
|
application_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
|
||||||
|
application_info.pEngineName = ENGINE_NAME;
|
||||||
|
application_info.engineVersion = VK_MAKE_VERSION(0, 1, 0);
|
||||||
|
application_info.apiVersion = VK_API_VERSION_1_3;
|
||||||
|
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||||
|
create_info.pNext = NULL;
|
||||||
|
create_info.flags = 0U;
|
||||||
|
create_info.pApplicationInfo = &application_info;
|
||||||
|
create_info.enabledLayerCount = ENABLED_INSTANCE_LAYERS_COUNT;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
create_info.ppEnabledLayerNames = g_enabled_instance_layers_names;
|
||||||
|
#else
|
||||||
|
create_info.ppEnabledLayerNames = NULL;
|
||||||
|
#endif
|
||||||
|
create_info.enabledExtensionCount = ENABLED_EXTENSIONS_COUT;
|
||||||
|
create_info.ppEnabledExtensionNames = g_enabled_instance_extensions_names;
|
||||||
|
|
||||||
|
VkResult result = vkCreateInstance(&create_info, NULL, &g_vulkan_instance);
|
||||||
|
if (VK_SUCCESS != result) {
|
||||||
|
LOGF(vulkan_error_messages[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 create_surface()
|
||||||
|
{
|
||||||
|
if (VK_SUCCESS != glfwCreateWindowSurface(g_vulkan_instance, get_window_handle(), NULL, &g_vulkan_state.surface)) {
|
||||||
|
LOGF(vulkan_error_messages[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 pick_physical_device()
|
||||||
|
{
|
||||||
|
uint32_t physical_device_count = 0U;
|
||||||
|
VkPhysicalDevice* physical_devices = NULL;
|
||||||
|
|
||||||
|
/* Get number of existing physical devices */
|
||||||
|
vkEnumeratePhysicalDevices(g_vulkan_instance, &physical_device_count, NULL);
|
||||||
|
|
||||||
|
if (0 == physical_device_count) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND]);
|
||||||
|
return VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
physical_devices = malloc(sizeof(VkPhysicalDevice) * physical_device_count);
|
||||||
|
vkEnumeratePhysicalDevices(g_vulkan_instance, &physical_device_count, physical_devices);
|
||||||
|
|
||||||
|
/* Check for suitability */
|
||||||
|
for (size_t physicalDeviceIdx = 0U; physicalDeviceIdx < physical_device_count; ++physicalDeviceIdx) {
|
||||||
|
VkPhysicalDeviceProperties device_properties;
|
||||||
|
VkPhysicalDeviceFeatures device_features;
|
||||||
|
vkGetPhysicalDeviceProperties(physical_devices[physicalDeviceIdx], &device_properties);
|
||||||
|
vkGetPhysicalDeviceFeatures(physical_devices[physicalDeviceIdx], &device_features);
|
||||||
|
|
||||||
|
/* Expect discrete graphics device type */
|
||||||
|
if (device_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU ||
|
||||||
|
device_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) {
|
||||||
|
/* Application MUST have geometry shader */
|
||||||
|
if (device_features.geometryShader) {
|
||||||
|
g_vulkan_state.physical_device = physical_devices[physicalDeviceIdx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VK_NULL_HANDLE == g_vulkan_state.physical_device) {
|
||||||
|
/* No suitable device found */
|
||||||
|
LOGF(vulkan_error_messages[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 create_device()
|
||||||
|
{
|
||||||
|
uint32_t queue_families_property_count = 0U;
|
||||||
|
ssize_t graphics_family_idx = -1;
|
||||||
|
ssize_t presentation_familiy_idx = -1;
|
||||||
|
VkQueueFamilyProperties* queue_family_properties = NULL;
|
||||||
|
VkDeviceCreateInfo device_create_info = {};
|
||||||
|
VkDeviceQueueCreateInfo device_queue_createinfos[2] = {}; /* TODO: Change when we want more queues */
|
||||||
|
float* graphics_familiy_queue_priorities = NULL;
|
||||||
|
float* presentation_familiy_queue_priorities = NULL;
|
||||||
|
|
||||||
|
/* Get information about supported queue families */
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(g_vulkan_state.physical_device, &queue_families_property_count, NULL);
|
||||||
|
queue_family_properties = malloc(sizeof(VkQueueFamilyProperties) * queue_families_property_count);
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(g_vulkan_state.physical_device, &queue_families_property_count,
|
||||||
|
queue_family_properties);
|
||||||
|
|
||||||
|
/* Get queue families that support graphics operations AND have most
|
||||||
|
* queues. */
|
||||||
|
{
|
||||||
|
size_t numberOfQueues = 0;
|
||||||
|
for (size_t familyIdx = 0; familyIdx < queue_families_property_count; ++familyIdx) {
|
||||||
|
LOGI("Checking queue: %ld", familyIdx);
|
||||||
|
if (queue_family_properties[familyIdx].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||||
|
if (queue_family_properties[familyIdx].queueCount > numberOfQueues) {
|
||||||
|
graphics_family_idx = familyIdx;
|
||||||
|
LOGI("Setting graphics family idx to %d", familyIdx);
|
||||||
|
numberOfQueues = queue_family_properties[familyIdx].queueCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for family with surface support */
|
||||||
|
VkBool32 presentSupport = VK_FALSE;
|
||||||
|
vkGetPhysicalDeviceSurfaceSupportKHR(g_vulkan_state.physical_device, familyIdx, g_vulkan_state.surface, &presentSupport);
|
||||||
|
if (presentSupport) {
|
||||||
|
presentation_familiy_idx = familyIdx;
|
||||||
|
LOGI("Setting present family idx to %d", familyIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (graphics_family_idx > 0 && presentation_familiy_idx > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is not magic number ;P. We want to support exact number of required queues flags */
|
||||||
|
if (0 > graphics_family_idx) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_QUEUE_NOT_SUPPORTED]);
|
||||||
|
return VULKAN_ERROR_QUEUE_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_vulkan_state.queue_family_indices[0] = graphics_family_idx; /* TODO: Change when more families are needed */
|
||||||
|
|
||||||
|
graphics_familiy_queue_priorities = malloc(sizeof(float) * queue_family_properties[graphics_family_idx].queueCount);
|
||||||
|
for (size_t i = 0; i < queue_family_properties[graphics_family_idx].queueCount; i++) {
|
||||||
|
graphics_familiy_queue_priorities[i] = 1.0f; /* TODO: When queues have different tasks, change this if needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
presentation_familiy_queue_priorities = malloc(sizeof(float) * queue_family_properties[presentation_familiy_idx].queueCount);
|
||||||
|
for (size_t i = 0; i < queue_family_properties[presentation_familiy_idx].queueCount; i++) {
|
||||||
|
presentation_familiy_queue_priorities[i] =
|
||||||
|
1.0f; /* TODO: When queues have different tasks, change this if needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
device_queue_createinfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
|
device_queue_createinfos[0].pNext = NULL;
|
||||||
|
device_queue_createinfos[0].flags = 0;
|
||||||
|
device_queue_createinfos[0].queueFamilyIndex = graphics_family_idx;
|
||||||
|
device_queue_createinfos[0].queueCount = queue_family_properties[graphics_family_idx].queueCount;
|
||||||
|
device_queue_createinfos[0].pQueuePriorities = graphics_familiy_queue_priorities;
|
||||||
|
|
||||||
|
device_queue_createinfos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
|
device_queue_createinfos[1].pNext = NULL;
|
||||||
|
device_queue_createinfos[1].flags = 0;
|
||||||
|
device_queue_createinfos[1].queueFamilyIndex = presentation_familiy_idx;
|
||||||
|
device_queue_createinfos[1].queueCount = queue_family_properties[presentation_familiy_idx].queueCount;
|
||||||
|
device_queue_createinfos[1].pQueuePriorities = presentation_familiy_queue_priorities;
|
||||||
|
|
||||||
|
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||||
|
device_create_info.pNext = NULL;
|
||||||
|
device_create_info.flags = 0U;
|
||||||
|
device_create_info.queueCreateInfoCount = 2u; /* For now one is enough. TODO: Change when needed. */
|
||||||
|
device_create_info.pQueueCreateInfos = device_queue_createinfos;
|
||||||
|
device_create_info.enabledLayerCount = 0;
|
||||||
|
device_create_info.ppEnabledLayerNames = NULL;
|
||||||
|
device_create_info.enabledExtensionCount = ENABLED_DEVICE_EXTENSIONS_COUT;
|
||||||
|
device_create_info.ppEnabledExtensionNames = g_enabled_device_extensions_names;
|
||||||
|
device_create_info.pEnabledFeatures = NULL; /* TODO: Enable features, when needed */
|
||||||
|
|
||||||
|
if (VK_SUCCESS != vkCreateDevice(g_vulkan_state.physical_device, &device_create_info, NULL, &g_vulkan_state.device)) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_FAILED_TO_CREATE_DEVICE]);
|
||||||
|
return VULKAN_ERROR_DEVICE_CREATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
vkGetDeviceQueue(g_vulkan_state.device, graphics_family_idx, 0, &g_vulkan_state.graphics_queue);
|
||||||
|
vkGetDeviceQueue(g_vulkan_state.device, presentation_familiy_idx, 0, &g_present_queue);
|
||||||
|
|
||||||
|
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 create_memory_allocator()
|
||||||
|
{
|
||||||
|
VmaVulkanFunctions vulkan_functions = {0};
|
||||||
|
vulkan_functions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr;
|
||||||
|
vulkan_functions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr;
|
||||||
|
|
||||||
|
VmaAllocatorCreateInfo allocator_create_info = {0};
|
||||||
|
allocator_create_info.vulkanApiVersion = VK_API_VERSION_1_3;
|
||||||
|
allocator_create_info.physicalDevice = g_vulkan_state.physical_device;
|
||||||
|
allocator_create_info.device = g_vulkan_state.device;
|
||||||
|
allocator_create_info.instance = g_vulkan_instance;
|
||||||
|
allocator_create_info.pVulkanFunctions = &vulkan_functions;
|
||||||
|
|
||||||
|
if (VK_SUCCESS != vmaCreateAllocator(&allocator_create_info, &g_vulkan_state.allocator)) {
|
||||||
|
LOGF(vulkan_error_messages[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 create_sync_objects()
|
||||||
|
{
|
||||||
|
VkSemaphoreCreateInfo semaphore_info = {};
|
||||||
|
semaphore_info.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(g_vulkan_state.device, &semaphore_info, NULL, &g_image_available_semaphores[i]) ||
|
||||||
|
VK_SUCCESS != vkCreateSemaphore(g_vulkan_state.device, &semaphore_info, NULL, &g_render_finished_semaphores[i]) ||
|
||||||
|
VK_SUCCESS != vkCreateFence(g_vulkan_state.device, &fenceInfo, NULL, &g_in_flight_fences[i])) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_SYNCOBJCTS_CREATION_FAILED]);
|
||||||
|
return VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return VULKAN_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t init_vulkan()
|
||||||
|
{
|
||||||
|
uint8_t status = VULKAN_ERROR_NO_ERROR;
|
||||||
|
|
||||||
|
set_enabled_extension();
|
||||||
|
STATUS_CHECK(create_instance());
|
||||||
|
#ifndef NDEBUG
|
||||||
|
setup_debug_messenger();
|
||||||
|
#endif
|
||||||
|
STATUS_CHECK(create_surface());
|
||||||
|
STATUS_CHECK(pick_physical_device());
|
||||||
|
STATUS_CHECK(create_device());
|
||||||
|
STATUS_CHECK(create_memory_allocator());
|
||||||
|
|
||||||
|
STATUS_CHECK(create_pipeline());
|
||||||
|
|
||||||
|
STATUS_CHECK(create_buffers());
|
||||||
|
|
||||||
|
STATUS_CHECK(create_descriptors());
|
||||||
|
|
||||||
|
STATUS_CHECK(create_sync_objects());
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_frame()
|
||||||
|
{
|
||||||
|
vkWaitForFences(g_vulkan_state.device, 1, &g_in_flight_fences[g_vulkan_state.current_frame], VK_TRUE, UINT64_MAX);
|
||||||
|
|
||||||
|
uint32_t image_index = 0U;
|
||||||
|
VkResult result = VK_FALSE;
|
||||||
|
VkSubmitInfo submit_info = {};
|
||||||
|
VkSemaphore wait_semaphores[1]; //TODO: Increase when needed
|
||||||
|
VkPipelineStageFlags wait_stages[1];
|
||||||
|
VkSemaphore signal_semaphores[1];
|
||||||
|
VkPresentInfoKHR present_info = {};
|
||||||
|
VkSwapchainKHR swap_chains[1];
|
||||||
|
|
||||||
|
result = vkAcquireNextImageKHR(g_vulkan_state.device, g_vulkan_state.swapchain, UINT64_MAX, g_image_available_semaphores[g_vulkan_state.current_frame],
|
||||||
|
VK_NULL_HANDLE, &image_index);
|
||||||
|
|
||||||
|
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||||||
|
recreate_swapchain();
|
||||||
|
return;
|
||||||
|
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
|
||||||
|
// throw std::runtime_error("failed to acquire swap chain image!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_uniform_buffer();
|
||||||
|
|
||||||
|
/* Only reset the fence if we are submitting work */
|
||||||
|
vkResetFences(g_vulkan_state.device, 1, &g_in_flight_fences[g_vulkan_state.current_frame]);
|
||||||
|
reset_command_buffer();
|
||||||
|
|
||||||
|
record_command_buffer(image_index);
|
||||||
|
|
||||||
|
|
||||||
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
|
|
||||||
|
wait_semaphores[0] = g_image_available_semaphores[g_vulkan_state.current_frame];
|
||||||
|
wait_stages[0] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||||
|
submit_info.waitSemaphoreCount = 1;
|
||||||
|
submit_info.pWaitSemaphores = wait_semaphores;
|
||||||
|
submit_info.pWaitDstStageMask = wait_stages;
|
||||||
|
|
||||||
|
submit_info.commandBufferCount = 1;
|
||||||
|
submit_info.pCommandBuffers = &g_vulkan_state.command_buffers[g_vulkan_state.current_frame];
|
||||||
|
|
||||||
|
signal_semaphores[0] = g_render_finished_semaphores[g_vulkan_state.current_frame];
|
||||||
|
submit_info.signalSemaphoreCount = 1;
|
||||||
|
submit_info.pSignalSemaphores = signal_semaphores;
|
||||||
|
|
||||||
|
if (vkQueueSubmit(g_vulkan_state.graphics_queue, 1, &submit_info, g_in_flight_fences[g_vulkan_state.current_frame]) != VK_SUCCESS) {
|
||||||
|
LOGF(vulkan_error_messages[VULAKN_DRAW_FAILED]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||||
|
|
||||||
|
present_info.waitSemaphoreCount = 1;
|
||||||
|
present_info.pWaitSemaphores = signal_semaphores;
|
||||||
|
|
||||||
|
swap_chains[0] = g_vulkan_state.swapchain;
|
||||||
|
present_info.swapchainCount = 1;
|
||||||
|
present_info.pSwapchains = swap_chains;
|
||||||
|
|
||||||
|
present_info.pImageIndices = &image_index;
|
||||||
|
|
||||||
|
vkQueuePresentKHR(g_present_queue, &present_info);
|
||||||
|
|
||||||
|
g_vulkan_state.current_frame = (g_vulkan_state.current_frame + 1) % SWAP_BUFFER_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinit_vulkan()
|
||||||
|
{
|
||||||
|
cleanup_swapchain();
|
||||||
|
destroy_descriptors();
|
||||||
|
destroy_buffers();
|
||||||
|
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
|
||||||
|
vkDestroySemaphore(g_vulkan_state.device, g_image_available_semaphores[i], NULL);
|
||||||
|
vkDestroySemaphore(g_vulkan_state.device, g_render_finished_semaphores[i], NULL);
|
||||||
|
vkDestroyFence(g_vulkan_state.device, g_in_flight_fences[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_pipeline();
|
||||||
|
|
||||||
|
vmaDestroyAllocator(g_vulkan_state.allocator);
|
||||||
|
vkDestroyDevice(g_vulkan_state.device, NULL);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
DestroyDebugUtilsMessengerEXT(g_vulkan_instance, g_debug_messenger, NULL);
|
||||||
|
#endif
|
||||||
|
vkDestroySurfaceKHR(g_vulkan_instance, g_vulkan_state.surface, NULL);
|
||||||
|
vkDestroyInstance(g_vulkan_instance, NULL);
|
||||||
|
}
|
||||||
@@ -1,640 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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.h"
|
|
||||||
#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 "vk_mem_alloc.h"
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
|
|
||||||
#include "locale_vulkan.h"
|
|
||||||
#include "utilities/logger.hpp"
|
|
||||||
#include "window.hpp"
|
|
||||||
|
|
||||||
#include "vulkan_commons.h"
|
|
||||||
#include "vulkan_buffers.hpp"
|
|
||||||
#include "vulkan_descriptors.hpp"
|
|
||||||
#include "vulkan_pipeline.hpp"
|
|
||||||
|
|
||||||
namespace rse::graphics::vulkanbase
|
|
||||||
{
|
|
||||||
|
|
||||||
#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();
|
|
||||||
static uint8_t pickPhysicalDevice();
|
|
||||||
static uint8_t createDevice();
|
|
||||||
static uint8_t createMemoryAllocator();
|
|
||||||
|
|
||||||
static uint8_t createSyncObjects();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#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()
|
|
||||||
{
|
|
||||||
if (VK_SUCCESS != glfwCreateWindowSurface(gVulkanInstance, window::getWindowHandle(), nullptr, &g_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()
|
|
||||||
{
|
|
||||||
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) {
|
|
||||||
g_vulkan_state.physicalDevice = pPhysicalDevices[physicalDeviceIdx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VK_NULL_HANDLE == g_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()
|
|
||||||
{
|
|
||||||
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(g_vulkan_state.physicalDevice, &queueFamiliesPropertyCount, nullptr);
|
|
||||||
pQueueFamilyProperties.resize(queueFamiliesPropertyCount);
|
|
||||||
vkGetPhysicalDeviceQueueFamilyProperties(g_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) {
|
|
||||||
LOGI << "Checking queue: " << familyIdx;
|
|
||||||
if (pQueueFamilyProperties[familyIdx].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
|
||||||
if (pQueueFamilyProperties[familyIdx].queueCount > numberOfQueues) {
|
|
||||||
graphicsFamilyIdx = familyIdx;
|
|
||||||
LOGI << "Setting graphics family idx to " << familyIdx;
|
|
||||||
numberOfQueues = pQueueFamilyProperties[familyIdx].queueCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for family with surface support */
|
|
||||||
VkBool32 presentSupport = false;
|
|
||||||
vkGetPhysicalDeviceSurfaceSupportKHR(g_vulkan_state.physicalDevice, familyIdx, g_vulkan_state.surface, &presentSupport);
|
|
||||||
if (presentSupport) {
|
|
||||||
presentationFamiliyIdx = familyIdx;
|
|
||||||
LOGI << "Setting present family idx to " << 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_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(g_vulkan_state.physicalDevice, &deviceCreateInfo, nullptr, &g_vulkan_state.device)) {
|
|
||||||
LOGF << vulkanErrorMesssages[VULKAN_FAILED_TO_CREATE_DEVICE];
|
|
||||||
return VULKAN_ERROR_DEVICE_CREATION_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
vkGetDeviceQueue(g_vulkan_state.device, graphicsFamilyIdx, 0, &g_vulkan_state.graphicsQueue);
|
|
||||||
vkGetDeviceQueue(g_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()
|
|
||||||
{
|
|
||||||
VmaVulkanFunctions vulkanFunctions = {0};
|
|
||||||
vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr;
|
|
||||||
vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr;
|
|
||||||
|
|
||||||
VmaAllocatorCreateInfo allocatorCreateInfo = {0};
|
|
||||||
allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_3;
|
|
||||||
allocatorCreateInfo.physicalDevice = g_vulkan_state.physicalDevice;
|
|
||||||
allocatorCreateInfo.device = g_vulkan_state.device;
|
|
||||||
allocatorCreateInfo.instance = gVulkanInstance;
|
|
||||||
allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;
|
|
||||||
|
|
||||||
if (VK_SUCCESS != vmaCreateAllocator(&allocatorCreateInfo, &g_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()
|
|
||||||
{
|
|
||||||
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(g_vulkan_state.device, &semaphoreInfo, nullptr, &gImageAvailableSemaphores[i]) ||
|
|
||||||
VK_SUCCESS != vkCreateSemaphore(g_vulkan_state.device, &semaphoreInfo, nullptr, &gRenderFinishedSemaphores[i]) ||
|
|
||||||
VK_SUCCESS != vkCreateFence(g_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()
|
|
||||||
{
|
|
||||||
uint8_t status = VULKAN_ERROR_NO_ERROR;
|
|
||||||
|
|
||||||
setEnabledExtension();
|
|
||||||
STATUS_CHECK(createInstance());
|
|
||||||
#ifndef NDEBUG
|
|
||||||
setupDebugMessenger();
|
|
||||||
#endif
|
|
||||||
STATUS_CHECK(createSurface());
|
|
||||||
STATUS_CHECK(pickPhysicalDevice());
|
|
||||||
STATUS_CHECK(createDevice());
|
|
||||||
STATUS_CHECK(createMemoryAllocator());
|
|
||||||
|
|
||||||
STATUS_CHECK(create_pipeline());
|
|
||||||
|
|
||||||
STATUS_CHECK(create_buffers());
|
|
||||||
|
|
||||||
STATUS_CHECK(create_descriptors());
|
|
||||||
|
|
||||||
STATUS_CHECK(createSyncObjects());
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawFrame()
|
|
||||||
{
|
|
||||||
vkWaitForFences(g_vulkan_state.device, 1, &gInFlightFences[g_vulkan_state.currentFrame], VK_TRUE, UINT64_MAX);
|
|
||||||
|
|
||||||
uint32_t imageIndex;
|
|
||||||
VkResult result = vkAcquireNextImageKHR(g_vulkan_state.device, g_vulkan_state.swapchain, UINT64_MAX, gImageAvailableSemaphores[g_vulkan_state.currentFrame],
|
|
||||||
VK_NULL_HANDLE, &imageIndex);
|
|
||||||
|
|
||||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
|
||||||
recreateSwapchain();
|
|
||||||
return;
|
|
||||||
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
|
|
||||||
// throw std::runtime_error("failed to acquire swap chain image!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUniformBuffer();
|
|
||||||
|
|
||||||
/* Only reset the fence if we are submitting work */
|
|
||||||
vkResetFences(g_vulkan_state.device, 1, &gInFlightFences[g_vulkan_state.currentFrame]);
|
|
||||||
reset_command_buffer();
|
|
||||||
|
|
||||||
recordCommandBuffer(imageIndex);
|
|
||||||
|
|
||||||
VkSubmitInfo submitInfo{};
|
|
||||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
||||||
|
|
||||||
VkSemaphore waitSemaphores[] = {gImageAvailableSemaphores[g_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 = &g_vulkan_state.pCommandBuffers[g_vulkan_state.currentFrame];
|
|
||||||
|
|
||||||
VkSemaphore signalSemaphores[] = {gRenderFinishedSemaphores[g_vulkan_state.currentFrame]};
|
|
||||||
submitInfo.signalSemaphoreCount = 1;
|
|
||||||
submitInfo.pSignalSemaphores = signalSemaphores;
|
|
||||||
|
|
||||||
if (vkQueueSubmit(g_vulkan_state.graphicsQueue, 1, &submitInfo, gInFlightFences[g_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[] = {g_vulkan_state.swapchain};
|
|
||||||
presentInfo.swapchainCount = 1;
|
|
||||||
presentInfo.pSwapchains = swapChains;
|
|
||||||
|
|
||||||
presentInfo.pImageIndices = &imageIndex;
|
|
||||||
|
|
||||||
vkQueuePresentKHR(gPresentQueue, &presentInfo);
|
|
||||||
|
|
||||||
g_vulkan_state.currentFrame = (g_vulkan_state.currentFrame + 1) % SWAP_BUFFER_COUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void deinitVulkan()
|
|
||||||
{
|
|
||||||
cleanup_swapchain();
|
|
||||||
destroy_descriptors();
|
|
||||||
destroy_buffers();
|
|
||||||
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
|
|
||||||
vkDestroySemaphore(g_vulkan_state.device, gImageAvailableSemaphores[i], nullptr);
|
|
||||||
vkDestroySemaphore(g_vulkan_state.device, gRenderFinishedSemaphores[i], nullptr);
|
|
||||||
vkDestroyFence(g_vulkan_state.device, gInFlightFences[i], nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy_pipeline();
|
|
||||||
|
|
||||||
vmaDestroyAllocator(g_vulkan_state.allocator);
|
|
||||||
vkDestroyDevice(g_vulkan_state.device, nullptr);
|
|
||||||
#ifndef NDEBUG
|
|
||||||
DestroyDebugUtilsMessengerEXT(gVulkanInstance, gDebugMessenger, nullptr);
|
|
||||||
#endif
|
|
||||||
vkDestroySurfaceKHR(gVulkanInstance, g_vulkan_state.surface, nullptr);
|
|
||||||
vkDestroyInstance(gVulkanInstance, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace rse::graphics::vulkanbase
|
|
||||||
@@ -9,42 +9,35 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef RSE_GRAPHICS_VULKANBASE_HPP
|
#ifndef RSE_GRAPHICS_VULKANBASE_H
|
||||||
#define RSE_GRAPHICS_VULKANBASE_HPP
|
#define RSE_GRAPHICS_VULKANBASE_H
|
||||||
|
|
||||||
#include <cstdint>
|
#include <stdint.h>
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.hpp>
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
#include "vulkan_commons.h"
|
#include "vulkan_commons.h"
|
||||||
|
|
||||||
namespace rse::graphics::vulkanbase
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize Vulkan backend
|
* @brief Initialize Vulkan backend
|
||||||
*
|
*
|
||||||
* @param state
|
* @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();
|
uint8_t init_vulkan();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Deinitialize Vulkan backend
|
* @brief Deinitialize Vulkan backend
|
||||||
*
|
*
|
||||||
* @param state
|
* @param state
|
||||||
*/
|
*/
|
||||||
void deinitVulkan();
|
void deinit_vulkan();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draw frame on the screen.
|
* @brief Draw frame on the screen.
|
||||||
*
|
*
|
||||||
* @param state
|
* @param state
|
||||||
*/
|
*/
|
||||||
void drawFrame();
|
void draw_frame();
|
||||||
|
|
||||||
} /* namespace rse::graphics::vulkanbase */
|
|
||||||
|
|
||||||
#endif /* RSE_GRAPHICS_VULKANBASE_HPP */
|
#endif /* RSE_GRAPHICS_VULKANBASE_HPP */
|
||||||
@@ -0,0 +1,525 @@
|
|||||||
|
#include "vulkan_buffers.h"
|
||||||
|
|
||||||
|
#include "vulkan_errors.h"
|
||||||
|
#include "locale_vulkan.h"
|
||||||
|
#include "utilities/logger.h"
|
||||||
|
#include "mesh_controller.h"
|
||||||
|
#include "rse_math.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MAX_VERTEX_BUFFER_SIZE 33554432 /* 32 MB*/
|
||||||
|
|
||||||
|
#define MAX_VULKAN_BUFFERS_COUNT 1
|
||||||
|
|
||||||
|
// // 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 */
|
||||||
|
struct rse_vulkan_buffer_t g_vertex_buffers[MAX_VULKAN_BUFFERS_COUNT];
|
||||||
|
struct rse_vulkan_buffer_t g_index_buffers[MAX_VULKAN_BUFFERS_COUNT];
|
||||||
|
struct rse_vulkan_buffer_t g_instance_buffers[MAX_VULKAN_BUFFERS_COUNT];
|
||||||
|
|
||||||
|
VkCommandPool g_command_pool = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t create_buffer(const VkDeviceSize size,
|
||||||
|
VkBufferUsageFlags buffer_usage,
|
||||||
|
VmaMemoryUsage memory_usage,
|
||||||
|
const VmaAllocationCreateFlags allocation_flags,
|
||||||
|
struct rse_vulkan_buffer_t* buffer);
|
||||||
|
static uint8_t copy_buffer(VkBuffer src,
|
||||||
|
VkBuffer dst,
|
||||||
|
VkDeviceSize size,
|
||||||
|
VkDeviceSize dest_offset);
|
||||||
|
|
||||||
|
static uint8_t create_command_pools();
|
||||||
|
static uint8_t create_vertex_buffer();
|
||||||
|
static uint8_t create_index_buffer();
|
||||||
|
static uint8_t create_uniform_buffer();
|
||||||
|
static uint8_t allocate_command_buffers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper function. Creates a buffer object
|
||||||
|
*
|
||||||
|
* @param size Size of the target buffer
|
||||||
|
* @param buffer_usage Buffer usage flags
|
||||||
|
* @param memory_usage memory usage flags (mostly VMA_MEMORY_USAGE_AUTO)
|
||||||
|
* @param allocation_flags 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 create_buffer(const VkDeviceSize size,
|
||||||
|
VkBufferUsageFlags buffer_usage,
|
||||||
|
VmaMemoryUsage memory_usage,
|
||||||
|
const VmaAllocationCreateFlags allocation_flags,
|
||||||
|
struct rse_vulkan_buffer_t* buffer)
|
||||||
|
{
|
||||||
|
VkBufferCreateInfo vertex_buffer_info = {};
|
||||||
|
VmaAllocationCreateInfo create_info = {};
|
||||||
|
|
||||||
|
vertex_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||||
|
vertex_buffer_info.pNext = NULL;
|
||||||
|
vertex_buffer_info.flags = 0U;
|
||||||
|
vertex_buffer_info.size = size;
|
||||||
|
vertex_buffer_info.usage = buffer_usage;
|
||||||
|
vertex_buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
vertex_buffer_info.queueFamilyIndexCount = 0U;
|
||||||
|
vertex_buffer_info.pQueueFamilyIndices = NULL;
|
||||||
|
|
||||||
|
/* Disalows random access to memory. TODO: Consider changing to VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT? */
|
||||||
|
create_info.flags = allocation_flags;
|
||||||
|
create_info.usage = memory_usage;
|
||||||
|
create_info.memoryTypeBits = 0U;
|
||||||
|
create_info.requiredFlags = 0U;
|
||||||
|
create_info.preferredFlags = 0U;
|
||||||
|
create_info.pool = VK_NULL_HANDLE;
|
||||||
|
create_info.pUserData = VK_NULL_HANDLE;
|
||||||
|
create_info.priority = 0.0f;
|
||||||
|
|
||||||
|
if (VK_SUCCESS != vmaCreateBuffer(g_vulkan_state.allocator,
|
||||||
|
&vertex_buffer_info,
|
||||||
|
&create_info,
|
||||||
|
&buffer->buffer,
|
||||||
|
&buffer->allocation,
|
||||||
|
&buffer->allocation_info)) {
|
||||||
|
LOGF(vulkan_error_messages[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 copy_buffer(VkBuffer src, VkBuffer dst, VkDeviceSize size, VkDeviceSize dest_offset)
|
||||||
|
{
|
||||||
|
/* Vulkan buffers can only be copied using command buffers */
|
||||||
|
VkResult status = VK_FALSE;
|
||||||
|
VkBufferCopy copy_region = {};
|
||||||
|
VkSubmitInfo submit_info = {};
|
||||||
|
VkCommandBuffer copy_command_buffer = NULL;
|
||||||
|
VkCommandBufferBeginInfo begin_info = {};
|
||||||
|
VkCommandBufferAllocateInfo alloc_info = {};
|
||||||
|
|
||||||
|
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||||
|
alloc_info.pNext = NULL;
|
||||||
|
alloc_info.commandPool = g_command_pool;
|
||||||
|
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||||
|
alloc_info.commandBufferCount = 1;
|
||||||
|
|
||||||
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
|
begin_info.pNext = NULL;
|
||||||
|
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||||
|
begin_info.pInheritanceInfo = NULL;
|
||||||
|
|
||||||
|
copy_region.srcOffset = 0;
|
||||||
|
copy_region.dstOffset = dest_offset;
|
||||||
|
copy_region.size = size;
|
||||||
|
|
||||||
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
|
submit_info.pNext = NULL;
|
||||||
|
submit_info.waitSemaphoreCount = 0;
|
||||||
|
submit_info.pWaitSemaphores = NULL;
|
||||||
|
submit_info.pWaitDstStageMask = NULL;
|
||||||
|
submit_info.commandBufferCount = 1;
|
||||||
|
submit_info.pCommandBuffers = ©_command_buffer;
|
||||||
|
submit_info.signalSemaphoreCount = 0;
|
||||||
|
submit_info.pSignalSemaphores = NULL;
|
||||||
|
|
||||||
|
// FIXME: Add result checks
|
||||||
|
status = vkAllocateCommandBuffers(g_vulkan_state.device, &alloc_info, ©_command_buffer);
|
||||||
|
status = vkBeginCommandBuffer(copy_command_buffer, &begin_info);
|
||||||
|
vkCmdCopyBuffer(copy_command_buffer, src, dst, 1, ©_region);
|
||||||
|
status = vkEndCommandBuffer(copy_command_buffer);
|
||||||
|
status = vkQueueSubmit(g_vulkan_state.graphics_queue, 1, &submit_info, VK_NULL_HANDLE);
|
||||||
|
/* TODO: Use fences to wait instead of idle */
|
||||||
|
status = vkQueueWaitIdle(g_vulkan_state.graphics_queue);
|
||||||
|
vkFreeCommandBuffers(g_vulkan_state.device, g_command_pool, 1, ©_command_buffer);
|
||||||
|
|
||||||
|
if (status != VK_SUCCESS) {
|
||||||
|
return VULKAN_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
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 create_command_pools()
|
||||||
|
{
|
||||||
|
VkCommandPoolCreateInfo create_info;
|
||||||
|
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||||
|
create_info.pNext = NULL;
|
||||||
|
create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||||
|
create_info.queueFamilyIndex = g_vulkan_state.queue_family_indices[0]; /* TODO: Change when more families are needed */
|
||||||
|
|
||||||
|
if (VK_SUCCESS != vkCreateCommandPool(g_vulkan_state.device, &create_info, NULL, &g_command_pool)) {
|
||||||
|
LOGF(vulkan_error_messages[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 create_vertex_buffer()
|
||||||
|
{
|
||||||
|
VkDeviceSize buffer_size;
|
||||||
|
|
||||||
|
buffer_size = MAX_VERTEX_BUFFER_SIZE;
|
||||||
|
|
||||||
|
/* Create Vertex Buffer*/
|
||||||
|
if (VK_SUCCESS != create_buffer(buffer_size,
|
||||||
|
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 */
|
||||||
|
g_vertex_buffers)) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
|
||||||
|
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_vertex_buffers->allocated_size = 0;
|
||||||
|
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 create_index_buffer()
|
||||||
|
{
|
||||||
|
VkDeviceSize buffer_size;
|
||||||
|
|
||||||
|
buffer_size = MAX_VERTEX_BUFFER_SIZE;
|
||||||
|
|
||||||
|
/* Create Index Buffer*/
|
||||||
|
if (VK_SUCCESS != create_buffer(buffer_size, 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 */
|
||||||
|
g_index_buffers)) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
|
||||||
|
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_index_buffers->allocated_size = 0;
|
||||||
|
return VULKAN_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t createInstanceBuffer()
|
||||||
|
{
|
||||||
|
VkDeviceSize buffer_size;
|
||||||
|
|
||||||
|
buffer_size = MAX_VERTEX_BUFFER_SIZE;
|
||||||
|
|
||||||
|
/* Create Instance Buffer*/
|
||||||
|
if (VK_SUCCESS != create_buffer(buffer_size,
|
||||||
|
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 */
|
||||||
|
g_instance_buffers)) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
|
||||||
|
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_instance_buffers->allocated_size = 0;
|
||||||
|
|
||||||
|
return VULKAN_ERROR_NO_ERROR;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a Uniform Buffers
|
||||||
|
*
|
||||||
|
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
|
||||||
|
*/
|
||||||
|
uint8_t create_uniform_buffer()
|
||||||
|
{
|
||||||
|
VkDeviceSize buffer_size = sizeof(struct rse_uniform_buffer_object_t);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < SWAP_BUFFER_COUNT; i++) {
|
||||||
|
create_buffer(buffer_size, 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,
|
||||||
|
&g_vulkan_state.uniform_buffers[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 allocate_command_buffers()
|
||||||
|
{
|
||||||
|
VkCommandBufferAllocateInfo allocate_info;
|
||||||
|
|
||||||
|
/* Allocate memory for command buffers */
|
||||||
|
allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||||
|
allocate_info.pNext = NULL;
|
||||||
|
allocate_info.commandPool = g_command_pool;
|
||||||
|
allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; /* TODO: When needed, add secondary command buffer */
|
||||||
|
allocate_info.commandBufferCount = SWAP_BUFFER_COUNT;
|
||||||
|
|
||||||
|
if (VK_SUCCESS != vkAllocateCommandBuffers(g_vulkan_state.device,
|
||||||
|
&allocate_info,
|
||||||
|
g_vulkan_state.command_buffers)) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED]);
|
||||||
|
return VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED;
|
||||||
|
}
|
||||||
|
return VULKAN_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update uniform buffers
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void update_uniform_buffer()
|
||||||
|
{
|
||||||
|
struct rse_uniform_buffer_object_t ubo = {};
|
||||||
|
struct vec3_t eye = {0};
|
||||||
|
struct vec3_t center = {0};
|
||||||
|
struct vec3_t up = {0};
|
||||||
|
float fovy;
|
||||||
|
float z_near;
|
||||||
|
float z_far;
|
||||||
|
|
||||||
|
eye.z = 5.0f;
|
||||||
|
|
||||||
|
up.y = 1.0f;
|
||||||
|
|
||||||
|
fovy = rse_math_deg_to_radians(45.0f);
|
||||||
|
z_near = 0.1f;
|
||||||
|
z_far = 20.0f;
|
||||||
|
|
||||||
|
ubo.model = rse_math_uniform_mat4();
|
||||||
|
ubo.view = rse_math_look_at(&eye, ¢er, &up);
|
||||||
|
ubo.proj = rse_math_perspective(fovy,
|
||||||
|
g_vulkan_state.swapchain_extent.width / (float)g_vulkan_state.swapchain_extent.height,
|
||||||
|
z_near,
|
||||||
|
z_far);
|
||||||
|
memcpy(g_vulkan_state.uniform_buffers[g_vulkan_state.current_frame].allocation_info.pMappedData, &ubo, sizeof(ubo));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t update_mesh_buffers(const struct rse_vertex_t* vertices,
|
||||||
|
const uint16_t* indices,
|
||||||
|
size_t vertices_num,
|
||||||
|
size_t indices_num)
|
||||||
|
{
|
||||||
|
// void* mapped_data;
|
||||||
|
struct rse_vulkan_buffer_t staging_buffer;
|
||||||
|
size_t vertices_size = sizeof(vertices[0]) * vertices_num;
|
||||||
|
size_t indices_size = sizeof(indices[0]) * indices_num;
|
||||||
|
|
||||||
|
struct rse_vulkan_buffer_t* last_vertex_buffer = &g_vertex_buffers[MAX_VULKAN_BUFFERS_COUNT - 1];
|
||||||
|
struct rse_vulkan_buffer_t* last_index_buffer = &g_index_buffers[MAX_VULKAN_BUFFERS_COUNT - 1];
|
||||||
|
|
||||||
|
/* Creating staging buffer*/
|
||||||
|
if (VK_SUCCESS != create_buffer(MAX_VERTEX_BUFFER_SIZE,
|
||||||
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||||
|
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
|
||||||
|
VMA_ALLOCATION_CREATE_MAPPED_BIT
|
||||||
|
| VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
||||||
|
&staging_buffer)) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
|
||||||
|
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill staging buffer with vertex data */
|
||||||
|
memset(staging_buffer.allocation_info.pMappedData, 0, MAX_VERTEX_BUFFER_SIZE);
|
||||||
|
memcpy(staging_buffer.allocation_info.pMappedData, vertices, vertices_size);
|
||||||
|
copy_buffer(staging_buffer.buffer,
|
||||||
|
last_vertex_buffer->buffer,
|
||||||
|
vertices_size,
|
||||||
|
last_vertex_buffer->allocated_size);
|
||||||
|
|
||||||
|
/* Fill staging buffer with index data */
|
||||||
|
memset(staging_buffer.allocation_info.pMappedData, 0, MAX_VERTEX_BUFFER_SIZE);
|
||||||
|
memcpy(staging_buffer.allocation_info.pMappedData, indices, indices_size);
|
||||||
|
copy_buffer(staging_buffer.buffer,
|
||||||
|
last_index_buffer->buffer,
|
||||||
|
indices_size,
|
||||||
|
last_index_buffer->allocated_size);
|
||||||
|
|
||||||
|
vmaDestroyBuffer(g_vulkan_state.allocator, staging_buffer.buffer, staging_buffer.allocation);
|
||||||
|
|
||||||
|
return VULKAN_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t update_mesh_instances(struct rse_instance_data_t* instance_data)
|
||||||
|
{
|
||||||
|
struct rse_vulkan_buffer_t staging_buffer;
|
||||||
|
|
||||||
|
struct rse_vulkan_buffer_t* last_instance_buffer = &g_instance_buffers[MAX_VULKAN_BUFFERS_COUNT - 1];
|
||||||
|
size_t instance_size = sizeof(struct rse_instance_data_t);
|
||||||
|
|
||||||
|
/* Creating staging buffer*/
|
||||||
|
if (VK_SUCCESS != create_buffer(MAX_VERTEX_BUFFER_SIZE,
|
||||||
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||||
|
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
|
||||||
|
VMA_ALLOCATION_CREATE_MAPPED_BIT
|
||||||
|
| VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
||||||
|
&staging_buffer)) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
|
||||||
|
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(staging_buffer.allocation_info.pMappedData, instance_data, instance_size);
|
||||||
|
copy_buffer(staging_buffer.buffer,
|
||||||
|
last_instance_buffer->buffer,
|
||||||
|
instance_size,
|
||||||
|
last_instance_buffer->allocated_size);
|
||||||
|
|
||||||
|
vmaDestroyBuffer(g_vulkan_state.allocator, staging_buffer.buffer, staging_buffer.allocation);
|
||||||
|
|
||||||
|
last_instance_buffer->allocated_size += instance_size;
|
||||||
|
|
||||||
|
return VULKAN_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Records commands for provided command buffer
|
||||||
|
*
|
||||||
|
* @param command_buffer Command buffer, that will hold commands
|
||||||
|
* @param image_index Image index
|
||||||
|
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
|
||||||
|
* VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED
|
||||||
|
*/
|
||||||
|
uint8_t record_command_buffer(uint32_t image_index)
|
||||||
|
{
|
||||||
|
VkCommandBuffer command_buffer = g_vulkan_state.command_buffers[g_vulkan_state.current_frame];
|
||||||
|
VkCommandBufferBeginInfo begin_info = {};
|
||||||
|
VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
||||||
|
VkRenderPassBeginInfo render_pass_info = {};
|
||||||
|
VkViewport viewport = {};
|
||||||
|
VkRect2D scissor = {};
|
||||||
|
VkDeviceSize offsets[] = {0};
|
||||||
|
size_t i = 0U;
|
||||||
|
|
||||||
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
|
begin_info.flags = 0;
|
||||||
|
begin_info.pInheritanceInfo = NULL;
|
||||||
|
|
||||||
|
if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_RECORD_COMMAND_BEGIN_FAILED]);
|
||||||
|
return VULKAN_ERROR_RECORD_COMMAND_BEGIN_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||||
|
render_pass_info.renderPass = g_vulkan_state.render_pass;
|
||||||
|
render_pass_info.framebuffer = g_vulkan_state.swapchain_framebuffers[image_index];
|
||||||
|
render_pass_info.renderArea.offset.x = 0;
|
||||||
|
render_pass_info.renderArea.offset.y = 0;
|
||||||
|
render_pass_info.renderArea.extent = g_vulkan_state.swapchain_extent;
|
||||||
|
render_pass_info.clearValueCount = 1;
|
||||||
|
render_pass_info.pClearValues = &clear_color;
|
||||||
|
|
||||||
|
vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
|
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_vulkan_state.graphics_pipeline);
|
||||||
|
|
||||||
|
viewport.x = 0.0f;
|
||||||
|
viewport.y = 0.0f;
|
||||||
|
viewport.width = (float)(g_vulkan_state.swapchain_extent.width);
|
||||||
|
viewport.height = (float)(g_vulkan_state.swapchain_extent.height);
|
||||||
|
viewport.minDepth = 0.0f;
|
||||||
|
viewport.maxDepth = 1.0f;
|
||||||
|
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
|
||||||
|
|
||||||
|
scissor.offset.x = 0;
|
||||||
|
scissor.offset.y = 0;
|
||||||
|
scissor.extent = g_vulkan_state.swapchain_extent;
|
||||||
|
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_VULKAN_BUFFERS_COUNT; ++i) {
|
||||||
|
VkBuffer vertex_buffers[] = {g_vertex_buffers[i].buffer};
|
||||||
|
VkBuffer index_buffers = {g_index_buffers[i].buffer};
|
||||||
|
VkBuffer instance_buffers[] = {g_instance_buffers[i].buffer};
|
||||||
|
uint16_t* indices = get_mesh_indices(0); /* TODO: We need to take care of ALL meshes */
|
||||||
|
struct rse_instance_data_t* instances = get_instance_data_for_mesh(0);
|
||||||
|
|
||||||
|
/* TODO: Figure out the offset */
|
||||||
|
vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, offsets);
|
||||||
|
vkCmdBindVertexBuffers(command_buffer, 1, 1, instance_buffers, offsets);
|
||||||
|
vkCmdBindIndexBuffer(command_buffer, index_buffers, 0, VK_INDEX_TYPE_UINT16);
|
||||||
|
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_vulkan_state.pipeline_layout, 0, 1,
|
||||||
|
&g_vulkan_state.descriptor_sets[g_vulkan_state.current_frame], 0, NULL);
|
||||||
|
|
||||||
|
vkCmdDrawIndexed(command_buffer, (uint32_t)(get_indices_count(0)+3), get_instances_count(0), 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vkCmdEndRenderPass(command_buffer);
|
||||||
|
|
||||||
|
if (VK_SUCCESS != vkEndCommandBuffer(command_buffer)) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_RECORD_COMMAND_BUFFER_FAILED]);
|
||||||
|
return VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return VULKAN_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t create_buffers()
|
||||||
|
{
|
||||||
|
uint8_t status = VULKAN_ERROR_NO_ERROR;\
|
||||||
|
|
||||||
|
STATUS_CHECK(create_command_pools());
|
||||||
|
STATUS_CHECK(create_vertex_buffer());
|
||||||
|
STATUS_CHECK(create_index_buffer());
|
||||||
|
STATUS_CHECK(createInstanceBuffer());
|
||||||
|
STATUS_CHECK(create_uniform_buffer());
|
||||||
|
STATUS_CHECK(allocate_command_buffers());
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_command_buffer()
|
||||||
|
{
|
||||||
|
vkResetCommandBuffer(g_vulkan_state.command_buffers[g_vulkan_state.current_frame], /*VkCommandBufferResetFlagBits*/ 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_buffers()
|
||||||
|
{
|
||||||
|
size_t i = 0U;
|
||||||
|
|
||||||
|
for (i = 0; i < SWAP_BUFFER_COUNT; ++i) {
|
||||||
|
vmaDestroyBuffer(g_vulkan_state.allocator,
|
||||||
|
g_vulkan_state.uniform_buffers[i].buffer,
|
||||||
|
g_vulkan_state.uniform_buffers[i].allocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_VULKAN_BUFFERS_COUNT; ++i) {
|
||||||
|
vmaDestroyBuffer(g_vulkan_state.allocator, g_vertex_buffers[i].buffer, g_vertex_buffers[i].allocation);
|
||||||
|
vmaDestroyBuffer(g_vulkan_state.allocator, g_index_buffers[i].buffer, g_index_buffers[i].allocation);
|
||||||
|
vmaDestroyBuffer(g_vulkan_state.allocator, g_instance_buffers[i].buffer, g_instance_buffers[i].allocation);
|
||||||
|
}
|
||||||
|
vkDestroyCommandPool(g_vulkan_state.device, g_command_pool, NULL);
|
||||||
|
}
|
||||||
@@ -1,530 +0,0 @@
|
|||||||
#include "vulkan_buffers.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "vulkan_errors.h"
|
|
||||||
#include "locale_vulkan.h"
|
|
||||||
#include "utilities/logger.hpp"
|
|
||||||
#include "mesh_controller.h"
|
|
||||||
#include "rse_math.h"
|
|
||||||
|
|
||||||
namespace rse::graphics::vulkanbase
|
|
||||||
{
|
|
||||||
|
|
||||||
#define MAX_VERTEX_BUFFER_SIZE 33554432 /* 32 MB*/
|
|
||||||
|
|
||||||
// // 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 */
|
|
||||||
std::vector<Buffer> gVertexBuffers = {};
|
|
||||||
std::vector<Buffer> gIndexBuffers = {};
|
|
||||||
std::vector<Buffer> gInstanceBuffers = {};
|
|
||||||
/* CPU visible buffer. TODO: Do we need this global? */
|
|
||||||
|
|
||||||
VkCommandPool gCommandPool = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
|
|
||||||
static uint8_t createBuffer(const VkDeviceSize size,
|
|
||||||
VkBufferUsageFlags bufferUsage,
|
|
||||||
VmaMemoryUsage memoryUsage,
|
|
||||||
const VmaAllocationCreateFlags allocationFlags,
|
|
||||||
Buffer& buffer);
|
|
||||||
static uint8_t copyBuffer(VkBuffer src,
|
|
||||||
VkBuffer dst,
|
|
||||||
VkDeviceSize size,
|
|
||||||
VkDeviceSize dest_offset = 0);
|
|
||||||
|
|
||||||
static uint8_t createCommandPools();
|
|
||||||
static uint8_t createVertexBuffer();
|
|
||||||
static uint8_t createIndexBuffer();
|
|
||||||
static uint8_t createUniformBuffers();
|
|
||||||
static uint8_t allocateCommandBuffers();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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(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(g_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(VkBuffer src, VkBuffer dst, VkDeviceSize size, VkDeviceSize dest_offset)
|
|
||||||
{
|
|
||||||
/* 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 = dest_offset;
|
|
||||||
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
|
|
||||||
VkResult status;
|
|
||||||
status = vkAllocateCommandBuffers(g_vulkan_state.device, &allocInfo, ©CommandBuffer);
|
|
||||||
status = vkBeginCommandBuffer(copyCommandBuffer, &beginInfo);
|
|
||||||
vkCmdCopyBuffer(copyCommandBuffer, src, dst, 1, ©Region);
|
|
||||||
status = vkEndCommandBuffer(copyCommandBuffer);
|
|
||||||
status = vkQueueSubmit(g_vulkan_state.graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
|
|
||||||
/* TODO: Use fences to wait instead of idle */
|
|
||||||
status = vkQueueWaitIdle(g_vulkan_state.graphicsQueue);
|
|
||||||
vkFreeCommandBuffers(g_vulkan_state.device, gCommandPool, 1, ©CommandBuffer);
|
|
||||||
|
|
||||||
if (status != VK_SUCCESS) {
|
|
||||||
return VULKAN_ERROR_NO_ERROR;
|
|
||||||
}
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
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 = g_vulkan_state.queueFamilyIndices[0]; /* TODO: Change when more families are needed */
|
|
||||||
|
|
||||||
if (VK_SUCCESS != vkCreateCommandPool(g_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()
|
|
||||||
{
|
|
||||||
VkDeviceSize bufferSize;
|
|
||||||
Buffer vertex_buffer;
|
|
||||||
|
|
||||||
bufferSize = MAX_VERTEX_BUFFER_SIZE;
|
|
||||||
|
|
||||||
/* Create Vertex Buffer*/
|
|
||||||
if (VK_SUCCESS != createBuffer(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 */
|
|
||||||
vertex_buffer)) {
|
|
||||||
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
|
|
||||||
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
vertex_buffer.allocated_size = 0;
|
|
||||||
gVertexBuffers.push_back(std::move(vertex_buffer));
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
VkDeviceSize bufferSize;
|
|
||||||
Buffer index_buffer;
|
|
||||||
|
|
||||||
bufferSize = MAX_VERTEX_BUFFER_SIZE;
|
|
||||||
|
|
||||||
/* Create Index Buffer*/
|
|
||||||
if (VK_SUCCESS != createBuffer(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 */
|
|
||||||
index_buffer)) {
|
|
||||||
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
|
|
||||||
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
index_buffer.allocated_size = 0;
|
|
||||||
gIndexBuffers.push_back(std::move(index_buffer));
|
|
||||||
return VULKAN_ERROR_NO_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t createInstanceBuffer()
|
|
||||||
{
|
|
||||||
VkDeviceSize bufferSize;
|
|
||||||
Buffer instance_buffer;
|
|
||||||
|
|
||||||
|
|
||||||
bufferSize = MAX_VERTEX_BUFFER_SIZE;
|
|
||||||
|
|
||||||
/* Create Instance Buffer*/
|
|
||||||
if (VK_SUCCESS != createBuffer(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 */
|
|
||||||
instance_buffer)) {
|
|
||||||
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
|
|
||||||
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
instance_buffer.allocated_size = 0;
|
|
||||||
|
|
||||||
gInstanceBuffers.push_back(std::move(instance_buffer));
|
|
||||||
return VULKAN_ERROR_NO_ERROR;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create a Uniform Buffers
|
|
||||||
*
|
|
||||||
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
|
|
||||||
*/
|
|
||||||
uint8_t createUniformBuffers()
|
|
||||||
{
|
|
||||||
VkDeviceSize bufferSize = sizeof(UniformBufferObject);
|
|
||||||
|
|
||||||
g_vulkan_state.uniformBuffers.resize(SWAP_BUFFER_COUNT);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < SWAP_BUFFER_COUNT; i++) {
|
|
||||||
createBuffer(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,
|
|
||||||
g_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()
|
|
||||||
{
|
|
||||||
VkCommandBufferAllocateInfo allocateInfo;
|
|
||||||
|
|
||||||
/* Allocate memory for command buffers */
|
|
||||||
g_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(g_vulkan_state.device,
|
|
||||||
&allocateInfo,
|
|
||||||
g_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()
|
|
||||||
{
|
|
||||||
struct vec3_t eye = {0};
|
|
||||||
struct vec3_t center = {0};
|
|
||||||
struct vec3_t up = {0};
|
|
||||||
float fovy;
|
|
||||||
float z_near;
|
|
||||||
float z_far;
|
|
||||||
UniformBufferObject ubo{};
|
|
||||||
|
|
||||||
eye.z = 5.0f;
|
|
||||||
|
|
||||||
up.y = 1.0f;
|
|
||||||
|
|
||||||
fovy = rse_math_deg_to_radians(45.0f);
|
|
||||||
z_near = 0.1f;
|
|
||||||
z_far = 20.0f;
|
|
||||||
|
|
||||||
ubo.model = rse_math_uniform_mat4();
|
|
||||||
ubo.view = rse_math_look_at(&eye, ¢er, &up);
|
|
||||||
ubo.proj = rse_math_perspective(fovy,
|
|
||||||
g_vulkan_state.swapchainExtent.width / (float)g_vulkan_state.swapchainExtent.height,
|
|
||||||
z_near,
|
|
||||||
z_far);
|
|
||||||
memcpy(g_vulkan_state.uniformBuffers[g_vulkan_state.currentFrame].allocationInfo.pMappedData, &ubo, sizeof(ubo));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t update_mesh_buffers(const std::vector<Vertex>& vertices,
|
|
||||||
const std::vector<uint16_t>& indices)
|
|
||||||
{
|
|
||||||
// void* mapped_data;
|
|
||||||
Buffer staging_buffer;
|
|
||||||
auto vertices_size = sizeof(vertices[0]) * vertices.size();
|
|
||||||
auto indices_size = sizeof(indices[0]) * indices.size();
|
|
||||||
|
|
||||||
auto last_vertex_buffer = &gVertexBuffers[gVertexBuffers.size() - 1];
|
|
||||||
auto last_index_buffer = &gIndexBuffers[gIndexBuffers.size() - 1];
|
|
||||||
|
|
||||||
/* Creating staging buffer*/
|
|
||||||
if (VK_SUCCESS != createBuffer(MAX_VERTEX_BUFFER_SIZE,
|
|
||||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
||||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
|
|
||||||
VMA_ALLOCATION_CREATE_MAPPED_BIT
|
|
||||||
| VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
|
||||||
staging_buffer)) {
|
|
||||||
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
|
|
||||||
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill staging buffer with vertex data */
|
|
||||||
memset(staging_buffer.allocationInfo.pMappedData, 0, MAX_VERTEX_BUFFER_SIZE);
|
|
||||||
memcpy(staging_buffer.allocationInfo.pMappedData, vertices.data(), vertices_size);
|
|
||||||
copyBuffer(staging_buffer.buffer,
|
|
||||||
last_vertex_buffer->buffer,
|
|
||||||
vertices_size,
|
|
||||||
last_vertex_buffer->allocated_size);
|
|
||||||
|
|
||||||
/* Fill staging buffer with index data */
|
|
||||||
memset(staging_buffer.allocationInfo.pMappedData, 0, MAX_VERTEX_BUFFER_SIZE);
|
|
||||||
memcpy(staging_buffer.allocationInfo.pMappedData, indices.data(), indices_size);
|
|
||||||
copyBuffer(staging_buffer.buffer,
|
|
||||||
last_index_buffer->buffer,
|
|
||||||
indices_size,
|
|
||||||
last_index_buffer->allocated_size);
|
|
||||||
|
|
||||||
vmaDestroyBuffer(g_vulkan_state.allocator, staging_buffer.buffer, staging_buffer.allocation);
|
|
||||||
|
|
||||||
return VULKAN_ERROR_NO_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t update_mesh_instances(InstanceData& instance_data)
|
|
||||||
{
|
|
||||||
Buffer staging_buffer;
|
|
||||||
|
|
||||||
auto last_instance_buffer = &gInstanceBuffers[gInstanceBuffers.size() - 1];
|
|
||||||
auto instance_size = sizeof(instance_data);
|
|
||||||
|
|
||||||
/* Creating staging buffer*/
|
|
||||||
if (VK_SUCCESS != createBuffer(MAX_VERTEX_BUFFER_SIZE,
|
|
||||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
||||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
|
|
||||||
VMA_ALLOCATION_CREATE_MAPPED_BIT
|
|
||||||
| VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
|
||||||
staging_buffer)) {
|
|
||||||
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
|
|
||||||
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(staging_buffer.allocationInfo.pMappedData, &instance_data, instance_size);
|
|
||||||
copyBuffer(staging_buffer.buffer,
|
|
||||||
last_instance_buffer->buffer,
|
|
||||||
instance_size,
|
|
||||||
last_instance_buffer->allocated_size);
|
|
||||||
|
|
||||||
vmaDestroyBuffer(g_vulkan_state.allocator, staging_buffer.buffer, staging_buffer.allocation);
|
|
||||||
|
|
||||||
last_instance_buffer->allocated_size += instance_size;
|
|
||||||
|
|
||||||
return VULKAN_ERROR_NO_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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(uint32_t imageIndex)
|
|
||||||
{
|
|
||||||
VkCommandBuffer commandBuffer = g_vulkan_state.pCommandBuffers[g_vulkan_state.currentFrame];
|
|
||||||
VkCommandBufferBeginInfo beginInfo{};
|
|
||||||
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
|
||||||
VkRenderPassBeginInfo renderPassInfo{};
|
|
||||||
VkViewport viewport{};
|
|
||||||
VkRect2D scissor{};
|
|
||||||
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 = g_vulkan_state.renderPass;
|
|
||||||
renderPassInfo.framebuffer = g_vulkan_state.swapchainFramebuffers[imageIndex];
|
|
||||||
renderPassInfo.renderArea.offset = {0, 0};
|
|
||||||
renderPassInfo.renderArea.extent = g_vulkan_state.swapchainExtent;
|
|
||||||
renderPassInfo.clearValueCount = 1;
|
|
||||||
renderPassInfo.pClearValues = &clearColor;
|
|
||||||
|
|
||||||
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
|
||||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_vulkan_state.graphicsPipeline);
|
|
||||||
|
|
||||||
viewport.x = 0.0f;
|
|
||||||
viewport.y = 0.0f;
|
|
||||||
viewport.width = static_cast<float>(g_vulkan_state.swapchainExtent.width);
|
|
||||||
viewport.height = static_cast<float>(g_vulkan_state.swapchainExtent.height);
|
|
||||||
viewport.minDepth = 0.0f;
|
|
||||||
viewport.maxDepth = 1.0f;
|
|
||||||
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
|
|
||||||
|
|
||||||
scissor.offset = {0, 0};
|
|
||||||
scissor.extent = g_vulkan_state.swapchainExtent;
|
|
||||||
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < gVertexBuffers.size(); ++i) {
|
|
||||||
VkBuffer vertexBuffers[] = {gVertexBuffers[i].buffer};
|
|
||||||
VkBuffer indexBuffer = {gIndexBuffers[i].buffer};
|
|
||||||
VkBuffer instanceBuffers[] = {gInstanceBuffers[i].buffer};
|
|
||||||
auto indices = get_mesh_indices(0);
|
|
||||||
auto instances = get_instance_data_for_mesh(0);
|
|
||||||
|
|
||||||
/* TODO: Figure out the offset */
|
|
||||||
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
|
|
||||||
vkCmdBindVertexBuffers(commandBuffer, 1, 1, instanceBuffers, offsets);
|
|
||||||
vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT16);
|
|
||||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_vulkan_state.pipelineLayout, 0, 1,
|
|
||||||
&g_vulkan_state.descriptorSets[g_vulkan_state.currentFrame], 0, nullptr);
|
|
||||||
|
|
||||||
vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(indices.size()+3), instances.size(), 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()
|
|
||||||
{
|
|
||||||
uint8_t status = VULKAN_ERROR_NO_ERROR;\
|
|
||||||
|
|
||||||
STATUS_CHECK(createCommandPools());
|
|
||||||
STATUS_CHECK(createVertexBuffer());
|
|
||||||
STATUS_CHECK(createIndexBuffer());
|
|
||||||
STATUS_CHECK(createInstanceBuffer());
|
|
||||||
STATUS_CHECK(createUniformBuffers());
|
|
||||||
STATUS_CHECK(allocateCommandBuffers());
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset_command_buffer()
|
|
||||||
{
|
|
||||||
vkResetCommandBuffer(g_vulkan_state.pCommandBuffers[g_vulkan_state.currentFrame], /*VkCommandBufferResetFlagBits*/ 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_buffers()
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
|
|
||||||
vmaDestroyBuffer(g_vulkan_state.allocator,
|
|
||||||
g_vulkan_state.uniformBuffers[i].buffer,
|
|
||||||
g_vulkan_state.uniformBuffers[i].allocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < gVertexBuffers.size(); ++i) {
|
|
||||||
vmaDestroyBuffer(g_vulkan_state.allocator, gVertexBuffers[i].buffer, gVertexBuffers[i].allocation);
|
|
||||||
vmaDestroyBuffer(g_vulkan_state.allocator, gIndexBuffers[i].buffer, gIndexBuffers[i].allocation);
|
|
||||||
vmaDestroyBuffer(g_vulkan_state.allocator, gInstanceBuffers[i].buffer, gInstanceBuffers[i].allocation);
|
|
||||||
}
|
|
||||||
vkDestroyCommandPool(g_vulkan_state.device, gCommandPool, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,11 @@
|
|||||||
#ifndef RSE_VULKAN_BUFFERS_HPP
|
#ifndef RSE_VULKAN_BUFFERS_H
|
||||||
#define RSE_VULKAN_BUFFERS_HPP
|
#define RSE_VULKAN_BUFFERS_H
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
#include <vulkan/vulkan.hpp>
|
|
||||||
|
|
||||||
#include "vk_mem_alloc.h"
|
|
||||||
|
|
||||||
#include "vulkan_commons.h"
|
#include "vulkan_commons.h"
|
||||||
|
#include "vk_mem_alloc.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace rse::graphics::vulkanbase
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create buffers needed by vulkan pipeline
|
* @brief Create buffers needed by vulkan pipeline
|
||||||
@@ -25,12 +20,14 @@ uint8_t create_buffers();
|
|||||||
*
|
*
|
||||||
* @param vulkan_state
|
* @param vulkan_state
|
||||||
*/
|
*/
|
||||||
void updateUniformBuffer();
|
void update_uniform_buffers();
|
||||||
|
|
||||||
uint8_t update_mesh_buffers(const std::vector<Vertex>& vertices,
|
uint8_t update_mesh_buffers(const struct rse_vertex_t* vertices,
|
||||||
const std::vector<uint16_t>& indices);
|
const uint16_t* indices,
|
||||||
|
size_t vertices_num,
|
||||||
|
size_t indices_num);
|
||||||
|
|
||||||
uint8_t update_mesh_instances(InstanceData& instance_data);
|
uint8_t update_mesh_instances(struct rse_instance_data_t* instance_data);
|
||||||
/**
|
/**
|
||||||
* @brief Record commands for given image index
|
* @brief Record commands for given image index
|
||||||
*
|
*
|
||||||
@@ -38,7 +35,7 @@ uint8_t update_mesh_instances(InstanceData& instance_data);
|
|||||||
* @param imageIndex
|
* @param imageIndex
|
||||||
* @return uint8_t
|
* @return uint8_t
|
||||||
*/
|
*/
|
||||||
uint8_t recordCommandBuffer(uint32_t imageIndex);
|
uint8_t record_command_buffer(uint32_t imageIndex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reset command buffer for current frame.
|
* @brief Reset command buffer for current frame.
|
||||||
@@ -54,7 +51,4 @@ void reset_command_buffer();
|
|||||||
*/
|
*/
|
||||||
void destroy_buffers();
|
void destroy_buffers();
|
||||||
|
|
||||||
} // namespace rse::graphics::vulkanbase
|
#endif /* RSE_VULKAN_BUFFERS_H */
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#include "vulkan_commons.h"
|
||||||
|
|
||||||
|
struct rse_vulkan_state_t g_vulkan_state;
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#include "vulkan_commons.h"
|
|
||||||
|
|
||||||
VulkanState g_vulkan_state;
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @file vulkan_global.h
|
* @file vulkan_global.h
|
||||||
* @author Piotr Krygier (piotr.krygier@meshsystems.com)
|
* @author Piotr Krygier (everyonecancode@gmail.com)
|
||||||
* @brief Declaration of a structure holding Vulkan variables needed by different parts of the code
|
* @brief Declaration of a structure holding Vulkan variables needed by different parts of the code
|
||||||
* @version 0.1
|
* @version 0.1
|
||||||
* @date 2023-03-28
|
* @date 2023-03-28
|
||||||
@@ -9,101 +9,74 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef RSE_VULKAN_GLOBAL_HPP
|
#ifndef RSE_VULKAN_GLOBAL_H
|
||||||
#define RSE_VULKAN_GLOBAL_HPP
|
#define RSE_VULKAN_GLOBAL_H
|
||||||
|
|
||||||
#include <vulkan/vulkan.hpp>
|
|
||||||
#include <vk_mem_alloc.h>
|
|
||||||
|
|
||||||
#include "rse_math.h"
|
#include "rse_math.h"
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <vk_mem_alloc.h>
|
||||||
|
|
||||||
|
#include <stdalign.h>
|
||||||
|
|
||||||
#define SWAP_BUFFER_COUNT 2U
|
#define SWAP_BUFFER_COUNT 2U
|
||||||
|
|
||||||
|
extern struct rse_vulkan_state_t g_vulkan_state;
|
||||||
/**
|
/**
|
||||||
* @brief Represents single vertex on the screen
|
* @brief Represents single vertex on the screen
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct Vertex
|
struct rse_vertex_t
|
||||||
{
|
{
|
||||||
struct vec3_t pos;
|
struct vec3_t pos;
|
||||||
struct vec3_t color;
|
struct vec3_t 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;
|
* @brief Wrapper for Vulkan buffer. Holds allocation data;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct Buffer
|
struct rse_vulkan_buffer_t
|
||||||
{
|
{
|
||||||
uint32_t allocated_size;
|
uint32_t allocated_size;
|
||||||
VkBuffer buffer;
|
VkBuffer buffer;
|
||||||
VmaAllocation allocation;
|
VmaAllocation allocation;
|
||||||
VmaAllocationInfo allocationInfo;
|
VmaAllocationInfo allocation_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UniformBufferObject
|
struct rse_uniform_buffer_object_t
|
||||||
{
|
{
|
||||||
alignas(16) mat4_t model;
|
alignas(16) struct mat4_t model;
|
||||||
alignas(16) mat4_t view;
|
alignas(16) struct mat4_t view;
|
||||||
alignas(16) mat4_t proj;
|
alignas(16) struct mat4_t proj;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VulkanState
|
struct rse_vulkan_state_t
|
||||||
{
|
{
|
||||||
VkDevice device;
|
VkDevice device;
|
||||||
VkSurfaceKHR surface;
|
VkSurfaceKHR surface;
|
||||||
VkQueue graphicsQueue;
|
VkQueue graphics_queue;
|
||||||
VmaAllocator allocator;
|
VmaAllocator allocator;
|
||||||
VkPhysicalDevice physicalDevice;
|
VkPhysicalDevice physical_device;
|
||||||
uint32_t currentFrame;
|
uint32_t current_frame;
|
||||||
uint32_t queueFamilyIndices[1];
|
uint32_t queue_family_indices[1];
|
||||||
std::vector<Buffer> uniformBuffers;
|
struct rse_vulkan_buffer_t uniform_buffers[SWAP_BUFFER_COUNT];
|
||||||
std::vector<VkCommandBuffer> pCommandBuffers;
|
VkCommandBuffer command_buffers[SWAP_BUFFER_COUNT];
|
||||||
std::vector<VkDescriptorSet> descriptorSets;
|
VkDescriptorSet descriptor_sets[SWAP_BUFFER_COUNT];
|
||||||
std::vector<VkImageView> swapChainImageViews;
|
VkImageView* swapchain_image_views;
|
||||||
VkExtent2D swapchainExtent;
|
VkExtent2D swapchain_extent;
|
||||||
VkRenderPass renderPass;
|
VkRenderPass render_pass;
|
||||||
VkDescriptorSetLayout descriptorSetLayout;
|
VkDescriptorSetLayout descriptor_set_layout;
|
||||||
std::vector<VkFramebuffer> swapchainFramebuffers;
|
VkFramebuffer* swapchain_framebuffers;
|
||||||
VkPipeline graphicsPipeline;
|
VkPipeline graphics_pipeline;
|
||||||
VkPipelineLayout pipelineLayout;
|
VkPipelineLayout pipeline_layout;
|
||||||
VkSwapchainKHR swapchain;
|
VkSwapchainKHR swapchain;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct rse_instance_data_t {
|
||||||
extern VulkanState g_vulkan_state;
|
|
||||||
|
|
||||||
struct InstanceData {
|
|
||||||
struct vec3_t pos;
|
struct vec3_t pos;
|
||||||
struct vec3_t rot;
|
struct vec3_t rot;
|
||||||
float scale;
|
float scale;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif /* RSE_VULKAN_GLOBAL_H */
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
#include "vulkan_descriptors.h"
|
||||||
|
|
||||||
|
#include "locale_vulkan.h"
|
||||||
|
#include "utilities/logger.h"
|
||||||
|
#include "vulkan_errors.h"
|
||||||
|
#include "vulkan_commons.h"
|
||||||
|
|
||||||
|
#include "vulkan/vulkan.h"
|
||||||
|
|
||||||
|
VkDescriptorPool gDescriptorPool;
|
||||||
|
|
||||||
|
static uint8_t create_descriptor_pool();
|
||||||
|
static uint8_t create_descriptor_sets();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a Descriptor Pool
|
||||||
|
*
|
||||||
|
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
|
||||||
|
* VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED
|
||||||
|
*/
|
||||||
|
uint8_t create_descriptor_pool()
|
||||||
|
{
|
||||||
|
VkDescriptorPoolSize pool_size = {};
|
||||||
|
VkDescriptorPoolCreateInfo pool_info = {};
|
||||||
|
|
||||||
|
pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
|
pool_size.descriptorCount = (uint32_t)(SWAP_BUFFER_COUNT);
|
||||||
|
|
||||||
|
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||||
|
pool_info.poolSizeCount = 1;
|
||||||
|
pool_info.pPoolSizes = &pool_size;
|
||||||
|
|
||||||
|
pool_info.maxSets = (uint32_t)(SWAP_BUFFER_COUNT);
|
||||||
|
if (vkCreateDescriptorPool(g_vulkan_state.device, &pool_info, NULL, &gDescriptorPool) != VK_SUCCESS) {
|
||||||
|
LOGF(vulkan_error_messages[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 create_descriptor_sets()
|
||||||
|
{
|
||||||
|
VkDescriptorSetLayout layouts[SWAP_BUFFER_COUNT] = {
|
||||||
|
g_vulkan_state.descriptor_set_layout,
|
||||||
|
g_vulkan_state.descriptor_set_layout
|
||||||
|
};
|
||||||
|
VkDescriptorSetAllocateInfo allocInfo = {};
|
||||||
|
|
||||||
|
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||||
|
allocInfo.descriptorPool = gDescriptorPool;
|
||||||
|
allocInfo.descriptorSetCount = (uint32_t)(SWAP_BUFFER_COUNT);
|
||||||
|
allocInfo.pSetLayouts = layouts;
|
||||||
|
|
||||||
|
if (vkAllocateDescriptorSets(g_vulkan_state.device, &allocInfo, g_vulkan_state.descriptor_sets) != VK_SUCCESS) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_DESCRIPTOR_SETS_ALLOCATION_FAILED]);
|
||||||
|
return VULKAN_ERROR_DESCRIPTOR_SETS_ALLOCATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < SWAP_BUFFER_COUNT; i++) {
|
||||||
|
VkDescriptorBufferInfo buffer_info = {};
|
||||||
|
VkWriteDescriptorSet descriptor_write = {};
|
||||||
|
|
||||||
|
buffer_info.buffer = g_vulkan_state.uniform_buffers[i].buffer;
|
||||||
|
buffer_info.offset = 0;
|
||||||
|
buffer_info.range = sizeof(struct rse_uniform_buffer_object_t);
|
||||||
|
|
||||||
|
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
descriptor_write.dstSet = g_vulkan_state.descriptor_sets[i];
|
||||||
|
descriptor_write.dstBinding = 0;
|
||||||
|
descriptor_write.dstArrayElement = 0;
|
||||||
|
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
|
descriptor_write.descriptorCount = 1;
|
||||||
|
descriptor_write.pBufferInfo = &buffer_info;
|
||||||
|
|
||||||
|
vkUpdateDescriptorSets(g_vulkan_state.device, 1, &descriptor_write, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return VULKAN_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t create_descriptors()
|
||||||
|
{
|
||||||
|
uint8_t status = VULKAN_ERROR_NO_ERROR;
|
||||||
|
|
||||||
|
STATUS_CHECK(create_descriptor_pool());
|
||||||
|
STATUS_CHECK(create_descriptor_sets());
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_descriptors()
|
||||||
|
{
|
||||||
|
vkDestroyDescriptorPool(g_vulkan_state.device, gDescriptorPool, NULL);
|
||||||
|
vkDestroyDescriptorSetLayout(g_vulkan_state.device, g_vulkan_state.descriptor_set_layout, NULL);
|
||||||
|
}
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
#include "vulkan_descriptors.hpp"
|
|
||||||
|
|
||||||
#include "locale_vulkan.h"
|
|
||||||
#include "utilities/logger.hpp"
|
|
||||||
#include "vulkan_errors.h"
|
|
||||||
|
|
||||||
namespace rse::graphics::vulkanbase
|
|
||||||
{
|
|
||||||
|
|
||||||
VkDescriptorPool gDescriptorPool = {};
|
|
||||||
|
|
||||||
static uint8_t createDescriptorPool();
|
|
||||||
static uint8_t createDescriptorSets();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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()
|
|
||||||
{
|
|
||||||
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(g_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()
|
|
||||||
{
|
|
||||||
std::vector<VkDescriptorSetLayout> layouts(SWAP_BUFFER_COUNT, g_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();
|
|
||||||
|
|
||||||
g_vulkan_state.descriptorSets.resize(SWAP_BUFFER_COUNT);
|
|
||||||
if (vkAllocateDescriptorSets(g_vulkan_state.device, &allocInfo, g_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 = g_vulkan_state.uniformBuffers[i].buffer;
|
|
||||||
bufferInfo.offset = 0;
|
|
||||||
bufferInfo.range = sizeof(UniformBufferObject);
|
|
||||||
|
|
||||||
VkWriteDescriptorSet descriptorWrite{};
|
|
||||||
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
||||||
descriptorWrite.dstSet = g_vulkan_state.descriptorSets[i];
|
|
||||||
descriptorWrite.dstBinding = 0;
|
|
||||||
descriptorWrite.dstArrayElement = 0;
|
|
||||||
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
||||||
descriptorWrite.descriptorCount = 1;
|
|
||||||
descriptorWrite.pBufferInfo = &bufferInfo;
|
|
||||||
|
|
||||||
vkUpdateDescriptorSets(g_vulkan_state.device, 1, &descriptorWrite, 0, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return VULKAN_ERROR_NO_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t create_descriptors()
|
|
||||||
{
|
|
||||||
uint8_t status = VULKAN_ERROR_NO_ERROR;
|
|
||||||
|
|
||||||
STATUS_CHECK(createDescriptorPool());
|
|
||||||
STATUS_CHECK(createDescriptorSets());
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_descriptors()
|
|
||||||
{
|
|
||||||
vkDestroyDescriptorPool(g_vulkan_state.device, gDescriptorPool, nullptr);
|
|
||||||
vkDestroyDescriptorSetLayout(g_vulkan_state.device, g_vulkan_state.descriptorSetLayout, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace rse::graphics::vulkanbase
|
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef RSE_VULKAN_DESCRIPTORS_H
|
||||||
|
#define RSE_VULKAN_DESCRIPTORS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
/**
|
||||||
|
* @brief Create descriptors
|
||||||
|
*
|
||||||
|
* @param vulkan_state
|
||||||
|
* @return uint8_t
|
||||||
|
*/
|
||||||
|
uint8_t create_descriptors();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destroy all descriptors
|
||||||
|
*
|
||||||
|
* @param vulkan_state
|
||||||
|
*/
|
||||||
|
void destroy_descriptors();
|
||||||
|
|
||||||
|
#endif /* RSE_VULKAN_DESCRIPTORS_H */
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#ifndef RSE_VULKAN_DESCRIPTORS_HPP
|
|
||||||
#define RSE_VULKAN_DESCRIPTORS_HPP
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
#include <vulkan/vulkan.hpp>
|
|
||||||
|
|
||||||
#include "vulkan_commons.h"
|
|
||||||
|
|
||||||
namespace rse::graphics::vulkanbase
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create descriptors
|
|
||||||
*
|
|
||||||
* @param vulkan_state
|
|
||||||
* @return uint8_t
|
|
||||||
*/
|
|
||||||
uint8_t create_descriptors();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destroy all descriptors
|
|
||||||
*
|
|
||||||
* @param vulkan_state
|
|
||||||
*/
|
|
||||||
void destroy_descriptors();
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace rse::graphics::vulkanbase
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,748 @@
|
|||||||
|
#include "vulkan_pipeline.h"
|
||||||
|
|
||||||
|
#include "locale_vulkan.h"
|
||||||
|
#include "utilities/file_utils.h"
|
||||||
|
#include "utilities/logger.h"
|
||||||
|
#include "vulkan_errors.h"
|
||||||
|
#include "mesh_controller.h"
|
||||||
|
|
||||||
|
#include "vulkan/vulkan.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define SHADER_MODULES_COUNT 2U
|
||||||
|
|
||||||
|
enum shader_stage_t
|
||||||
|
{
|
||||||
|
VERTEX,
|
||||||
|
FRAGMENTS
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IMAGE_FORMAT VK_FORMAT_B8G8R8A8_SRGB;
|
||||||
|
|
||||||
|
uint32_t g_swapchain_images_count = 0U;
|
||||||
|
|
||||||
|
VkImage* g_swapchain_images = NULL;
|
||||||
|
VkShaderModule g_shader_modules[SHADER_MODULES_COUNT];
|
||||||
|
|
||||||
|
|
||||||
|
const char* g_shader_paths[] = {
|
||||||
|
/* TODO: When changing to release and install, move those files*/
|
||||||
|
"../graphics/shaders/shader.vert.num",
|
||||||
|
"../graphics/shaders/shader.frag.num",
|
||||||
|
};
|
||||||
|
|
||||||
|
VkShaderModule create_shader_module(const char* binary_data, size_t data_size);
|
||||||
|
|
||||||
|
static uint8_t create_swapchain();
|
||||||
|
static uint8_t create_swapchain_image_views();
|
||||||
|
static uint8_t create_render_pass();
|
||||||
|
static uint8_t create_descriptor_set_layout();
|
||||||
|
static uint8_t create_pipeline_layout();
|
||||||
|
static uint8_t create_graphics_pipeline();
|
||||||
|
static uint8_t create_framebuffers();
|
||||||
|
|
||||||
|
static uint8_t fill_shader_stages(VkPipelineShaderStageCreateInfo* shader_stages);
|
||||||
|
static void fill_dynamic_pipeline_state_info(VkPipelineDynamicStateCreateInfo* dynamic_state);
|
||||||
|
static void free_dynamic_pipeline_state_info(VkPipelineDynamicStateCreateInfo* dynamic_state);
|
||||||
|
static void fill_vertex_input_info(VkPipelineVertexInputStateCreateInfo* vertex_input_info);
|
||||||
|
static void free_vertex_input_info(VkPipelineVertexInputStateCreateInfo* vertex_input_info);
|
||||||
|
static void fill_input_assembly_info(VkPipelineInputAssemblyStateCreateInfo* input_assembly);
|
||||||
|
static void fill_viewport_state_info(VkPipelineViewportStateCreateInfo* viewport_state);
|
||||||
|
static void free_viewport_state_info(VkPipelineViewportStateCreateInfo* viewport_state);
|
||||||
|
static void fill_rasterization_info(VkPipelineRasterizationStateCreateInfo* rasterizer);
|
||||||
|
static void fill_multisampling_state_info(VkPipelineMultisampleStateCreateInfo* multisampling);
|
||||||
|
static void fill_color_blend_state_info(VkPipelineColorBlendStateCreateInfo* color_blending);
|
||||||
|
static void free_color_blend_info(VkPipelineColorBlendStateCreateInfo* color_blending);
|
||||||
|
|
||||||
|
|
||||||
|
static VkVertexInputBindingDescription get_binding_description()
|
||||||
|
{
|
||||||
|
VkVertexInputBindingDescription binding_description;
|
||||||
|
|
||||||
|
binding_description.binding = 0;
|
||||||
|
binding_description.stride = sizeof(struct rse_vertex_t);
|
||||||
|
binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||||
|
|
||||||
|
return binding_description;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static VkVertexInputAttributeDescription* get_attribute_descriptions()
|
||||||
|
{
|
||||||
|
static VkVertexInputAttributeDescription attributeDescriptions[2];
|
||||||
|
|
||||||
|
attributeDescriptions[0].binding = 0;
|
||||||
|
attributeDescriptions[0].location = 0; /* inPosition in shader.vert */
|
||||||
|
attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||||
|
attributeDescriptions[0].offset = offsetof(struct rse_vertex_t, pos);
|
||||||
|
|
||||||
|
attributeDescriptions[1].binding = 0;
|
||||||
|
attributeDescriptions[1].location = 1; /* inColor in shader.vert */
|
||||||
|
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||||
|
attributeDescriptions[1].offset = offsetof(struct rse_vertex_t, color);
|
||||||
|
|
||||||
|
return attributeDescriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates shader module
|
||||||
|
*
|
||||||
|
* @param binary_data Shader compiled code
|
||||||
|
* @return std::optional<VkShaderModule> Handle to compiled shader module or nullopt
|
||||||
|
*/
|
||||||
|
VkShaderModule create_shader_module(const char* binary_data, size_t data_size)
|
||||||
|
{
|
||||||
|
VkShaderModule shader_module;
|
||||||
|
VkShaderModuleCreateInfo create_info = {};
|
||||||
|
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||||
|
create_info.pNext = NULL;
|
||||||
|
create_info.flags = 0U;
|
||||||
|
create_info.codeSize = data_size;
|
||||||
|
create_info.pCode = (const uint32_t*)(binary_data);
|
||||||
|
|
||||||
|
if (VK_SUCCESS != vkCreateShaderModule(g_vulkan_state.device, &create_info, NULL, &shader_module)) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_SHADER_UNABLE_TO_CREATE_SHADER]);
|
||||||
|
// TODO: Add error handling
|
||||||
|
return shader_module;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader_module;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a Swapchain
|
||||||
|
*
|
||||||
|
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
|
||||||
|
* VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED
|
||||||
|
*/
|
||||||
|
uint8_t create_swapchain()
|
||||||
|
{
|
||||||
|
VkSwapchainCreateInfoKHR create_info;
|
||||||
|
VkSurfaceCapabilitiesKHR physical_device_surface_capabilities;
|
||||||
|
VkBool32 surfaceSupported;
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_vulkan_state.physical_device,
|
||||||
|
g_vulkan_state.surface,
|
||||||
|
&physical_device_surface_capabilities);
|
||||||
|
|
||||||
|
/* Store extent in global variable, for later use */
|
||||||
|
g_vulkan_state.swapchain_extent = physical_device_surface_capabilities.currentExtent;
|
||||||
|
|
||||||
|
/* Check if device supports surface for presentation */
|
||||||
|
vkGetPhysicalDeviceSurfaceSupportKHR(g_vulkan_state.physical_device,
|
||||||
|
g_vulkan_state.queue_family_indices[0],
|
||||||
|
g_vulkan_state.surface,
|
||||||
|
&surfaceSupported);
|
||||||
|
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||||
|
create_info.pNext = NULL;
|
||||||
|
create_info.flags = 0U;
|
||||||
|
create_info.surface = g_vulkan_state.surface;
|
||||||
|
create_info.minImageCount = SWAP_BUFFER_COUNT + 1;
|
||||||
|
create_info.imageFormat = IMAGE_FORMAT; /* TODO: Check for supported formats */
|
||||||
|
create_info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; /* TODO: Check for supported color space */
|
||||||
|
create_info.imageExtent = g_vulkan_state.swapchain_extent;
|
||||||
|
create_info.imageArrayLayers = 1U; /* For non-stereoscopic-3D applications, this value is 1. */
|
||||||
|
create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; /* TODO: Probably change later? Dunno */
|
||||||
|
create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; /* Using only one queue family, so this is ok */
|
||||||
|
create_info.queueFamilyIndexCount = 1U;
|
||||||
|
create_info.pQueueFamilyIndices = g_vulkan_state.queue_family_indices;
|
||||||
|
create_info.preTransform = physical_device_surface_capabilities.currentTransform;
|
||||||
|
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
|
create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; /* TODO: Choose present mode from VkPresentModeKHR */
|
||||||
|
create_info.clipped = VK_TRUE;
|
||||||
|
create_info.oldSwapchain = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
if (VK_SUCCESS != vkCreateSwapchainKHR(g_vulkan_state.device, &create_info, NULL, &g_vulkan_state.swapchain)) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_SWAPCHAIN_CREATION_FAILED]);
|
||||||
|
return VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Obtain swapchain images */
|
||||||
|
vkGetSwapchainImagesKHR(g_vulkan_state.device, g_vulkan_state.swapchain, &g_swapchain_images_count, NULL);
|
||||||
|
g_swapchain_images = (VkImage*)malloc(sizeof(VkImage) * g_swapchain_images_count);
|
||||||
|
vkGetSwapchainImagesKHR(g_vulkan_state.device, g_vulkan_state.swapchain, &g_swapchain_images_count, g_swapchain_images);
|
||||||
|
|
||||||
|
return VULKAN_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a Swapchain Image Views
|
||||||
|
*
|
||||||
|
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
|
||||||
|
*/
|
||||||
|
uint8_t create_swapchain_image_views()
|
||||||
|
{
|
||||||
|
g_vulkan_state.swapchain_image_views = malloc(sizeof(VkImageView) * g_swapchain_images_count);
|
||||||
|
for (size_t i = 0; i < g_swapchain_images_count; i++) {
|
||||||
|
VkImageViewCreateInfo create_info;
|
||||||
|
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
|
create_info.pNext = NULL;
|
||||||
|
create_info.flags = 0U;
|
||||||
|
create_info.image = g_swapchain_images[i];
|
||||||
|
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; /* 2D Texture */
|
||||||
|
create_info.format = VK_FORMAT_B8G8R8A8_SRGB;
|
||||||
|
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
create_info.subresourceRange.baseMipLevel = 0U;
|
||||||
|
create_info.subresourceRange.levelCount = 1U;
|
||||||
|
create_info.subresourceRange.baseArrayLayer = 0U;
|
||||||
|
create_info.subresourceRange.layerCount = 1U;
|
||||||
|
|
||||||
|
if (VK_SUCCESS != vkCreateImageView(g_vulkan_state.device, &create_info, NULL, &g_vulkan_state.swapchain_image_views[i])) {
|
||||||
|
LOGF(vulkan_error_messages[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 create_render_pass()
|
||||||
|
{
|
||||||
|
VkAttachmentDescription color_attachment = {};
|
||||||
|
VkAttachmentReference color_attachment_ref = {};
|
||||||
|
VkSubpassDescription subpass = {};
|
||||||
|
VkRenderPassCreateInfo renderpass_info = {};
|
||||||
|
|
||||||
|
color_attachment.flags = 0U;
|
||||||
|
color_attachment.format = IMAGE_FORMAT;
|
||||||
|
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||||
|
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||||
|
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||||
|
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||||
|
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||||
|
|
||||||
|
color_attachment_ref.attachment = 0;
|
||||||
|
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||||
|
|
||||||
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||||
|
subpass.colorAttachmentCount = 1;
|
||||||
|
subpass.pColorAttachments = &color_attachment_ref;
|
||||||
|
|
||||||
|
renderpass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||||
|
renderpass_info.attachmentCount = 1;
|
||||||
|
renderpass_info.pAttachments = &color_attachment;
|
||||||
|
renderpass_info.subpassCount = 1;
|
||||||
|
renderpass_info.pSubpasses = &subpass;
|
||||||
|
|
||||||
|
if (VK_SUCCESS != vkCreateRenderPass(g_vulkan_state.device, &renderpass_info, NULL, &g_vulkan_state.render_pass)) {
|
||||||
|
LOGF(vulkan_error_messages[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 create_descriptor_set_layout()
|
||||||
|
{
|
||||||
|
VkDescriptorSetLayoutCreateInfo layout_info = {};
|
||||||
|
VkDescriptorSetLayoutBinding ubo_layout_binding = {};
|
||||||
|
|
||||||
|
ubo_layout_binding.binding = 0;
|
||||||
|
ubo_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
|
ubo_layout_binding.descriptorCount = 1;
|
||||||
|
ubo_layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||||
|
ubo_layout_binding.pImmutableSamplers = NULL;
|
||||||
|
|
||||||
|
layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||||
|
layout_info.pNext = NULL;
|
||||||
|
layout_info.flags = 0U;
|
||||||
|
layout_info.bindingCount = 1U;
|
||||||
|
layout_info.pBindings = &ubo_layout_binding;
|
||||||
|
|
||||||
|
if (vkCreateDescriptorSetLayout(g_vulkan_state.device, &layout_info, NULL, &g_vulkan_state.descriptor_set_layout) != VK_SUCCESS) {
|
||||||
|
LOGF(vulkan_error_messages[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 create_pipeline_layout()
|
||||||
|
{
|
||||||
|
VkPipelineLayoutCreateInfo create_info = {};
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||||
|
create_info.pNext = NULL;
|
||||||
|
create_info.flags = 0U;
|
||||||
|
create_info.setLayoutCount = 1U;
|
||||||
|
create_info.pSetLayouts = &g_vulkan_state.descriptor_set_layout;
|
||||||
|
create_info.pushConstantRangeCount = 0U;
|
||||||
|
create_info.pPushConstantRanges = NULL;
|
||||||
|
|
||||||
|
if (VK_SUCCESS != vkCreatePipelineLayout(g_vulkan_state.device, &create_info, NULL, &g_vulkan_state.pipeline_layout)) {
|
||||||
|
LOGF(vulkan_error_messages[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 create_graphics_pipeline()
|
||||||
|
{
|
||||||
|
uint8_t status = VULKAN_ERROR_NO_ERROR;
|
||||||
|
|
||||||
|
VkPipelineShaderStageCreateInfo shader_stages[SHADER_MODULES_COUNT];
|
||||||
|
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
|
||||||
|
VkPipelineInputAssemblyStateCreateInfo input_assembly = {};
|
||||||
|
VkPipelineViewportStateCreateInfo viewport_state = {};
|
||||||
|
VkPipelineRasterizationStateCreateInfo rasterizer = {};
|
||||||
|
VkPipelineMultisampleStateCreateInfo multisampling = {};
|
||||||
|
VkPipelineColorBlendStateCreateInfo color_blending = {};
|
||||||
|
VkPipelineDynamicStateCreateInfo dynamic_state = {};
|
||||||
|
|
||||||
|
STATUS_CHECK(fill_shader_stages(shader_stages));
|
||||||
|
fill_vertex_input_info(&vertex_input_info);
|
||||||
|
fill_input_assembly_info(&input_assembly);
|
||||||
|
fill_viewport_state_info(&viewport_state);
|
||||||
|
fill_rasterization_info(&rasterizer);
|
||||||
|
fill_multisampling_state_info(&multisampling);
|
||||||
|
fill_color_blend_state_info(&color_blending);
|
||||||
|
fill_dynamic_pipeline_state_info(&dynamic_state);
|
||||||
|
|
||||||
|
VkGraphicsPipelineCreateInfo pipelineInfo = {};
|
||||||
|
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||||
|
pipelineInfo.pNext = NULL;
|
||||||
|
pipelineInfo.flags = 0U;
|
||||||
|
pipelineInfo.stageCount = SHADER_MODULES_COUNT;
|
||||||
|
pipelineInfo.pStages = shader_stages;
|
||||||
|
pipelineInfo.pVertexInputState = &vertex_input_info;
|
||||||
|
pipelineInfo.pInputAssemblyState = &input_assembly;
|
||||||
|
pipelineInfo.pTessellationState = NULL;
|
||||||
|
pipelineInfo.pViewportState = &viewport_state;
|
||||||
|
pipelineInfo.pRasterizationState = &rasterizer;
|
||||||
|
pipelineInfo.pMultisampleState = &multisampling;
|
||||||
|
pipelineInfo.pDepthStencilState = NULL;
|
||||||
|
pipelineInfo.pColorBlendState = &color_blending;
|
||||||
|
pipelineInfo.pDynamicState = &dynamic_state;
|
||||||
|
pipelineInfo.layout = g_vulkan_state.pipeline_layout;
|
||||||
|
pipelineInfo.renderPass = g_vulkan_state.render_pass;
|
||||||
|
pipelineInfo.subpass = 0;
|
||||||
|
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
|
||||||
|
pipelineInfo.basePipelineIndex = -1;
|
||||||
|
|
||||||
|
if (VK_SUCCESS !=
|
||||||
|
vkCreateGraphicsPipelines(g_vulkan_state.device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL, &g_vulkan_state.graphics_pipeline)) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_GRAPHICSPIPELINE_CREATION_FAILED]);
|
||||||
|
status = VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_dynamic_pipeline_state_info(&dynamic_state);
|
||||||
|
free_color_blend_info(&color_blending);
|
||||||
|
free_viewport_state_info(&viewport_state);
|
||||||
|
free_vertex_input_info(&vertex_input_info);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create Frambuffers
|
||||||
|
*
|
||||||
|
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
|
||||||
|
*/
|
||||||
|
uint8_t create_framebuffers()
|
||||||
|
{
|
||||||
|
g_vulkan_state.swapchain_framebuffers = malloc(sizeof(VkFramebuffer) * g_swapchain_images_count);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < g_swapchain_images_count; i++) {
|
||||||
|
VkImageView attachments[] = {g_vulkan_state.swapchain_image_views[i]};
|
||||||
|
|
||||||
|
VkFramebufferCreateInfo framebufferInfo = {};
|
||||||
|
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||||
|
framebufferInfo.renderPass = g_vulkan_state.render_pass;
|
||||||
|
framebufferInfo.attachmentCount = 1;
|
||||||
|
framebufferInfo.pAttachments = attachments;
|
||||||
|
framebufferInfo.width = g_vulkan_state.swapchain_extent.width;
|
||||||
|
framebufferInfo.height = g_vulkan_state.swapchain_extent.height;
|
||||||
|
framebufferInfo.layers = 1;
|
||||||
|
|
||||||
|
if (vkCreateFramebuffer(g_vulkan_state.device, &framebufferInfo, NULL, &g_vulkan_state.swapchain_framebuffers[i]) != VK_SUCCESS) {
|
||||||
|
LOGF(vulkan_error_messages[VULKAN_FRAMEBUFFERS_CREATION_FAILED]);
|
||||||
|
return VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return VULKAN_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Recreate swapchain
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void recreate_swapchain()
|
||||||
|
{
|
||||||
|
cleanup_swapchain();
|
||||||
|
create_swapchain();
|
||||||
|
create_swapchain_image_views();
|
||||||
|
create_framebuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cleanup swapchain
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void cleanup_swapchain()
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
vkDeviceWaitIdle(g_vulkan_state.device);
|
||||||
|
for (i = 0; i < g_swapchain_images_count; i++) {
|
||||||
|
vkDestroyFramebuffer(g_vulkan_state.device, g_vulkan_state.swapchain_framebuffers[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < g_swapchain_images_count; i++) {
|
||||||
|
vkDestroyImageView(g_vulkan_state.device, g_vulkan_state.swapchain_image_views[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
vkDestroySwapchainKHR(g_vulkan_state.device, g_vulkan_state.swapchain, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates shader stages from hardcoded shader paths. Changes input
|
||||||
|
*
|
||||||
|
* @param shader_stages 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
|
||||||
|
*/
|
||||||
|
static uint8_t fill_shader_stages(VkPipelineShaderStageCreateInfo* shader_stages)
|
||||||
|
{
|
||||||
|
char* buffer;
|
||||||
|
size_t buffer_size;
|
||||||
|
VkShaderModule vertex_shader_module;
|
||||||
|
VkShaderModule fragments_shader_module;
|
||||||
|
VkPipelineShaderStageCreateInfo vert_shader_stage_info = {};
|
||||||
|
VkPipelineShaderStageCreateInfo frag_shader_stage_info = {};
|
||||||
|
|
||||||
|
read_file(g_shader_paths[VERTEX], &buffer_size, NULL);
|
||||||
|
buffer = malloc(buffer_size);
|
||||||
|
read_file(g_shader_paths[VERTEX], &buffer_size, buffer);
|
||||||
|
|
||||||
|
vertex_shader_module = create_shader_module(buffer, buffer_size);
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
read_file(g_shader_paths[FRAGMENTS], &buffer_size, NULL);
|
||||||
|
buffer = malloc(buffer_size);
|
||||||
|
read_file(g_shader_paths[FRAGMENTS], &buffer_size, buffer);
|
||||||
|
|
||||||
|
fragments_shader_module = create_shader_module(buffer, buffer_size);
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
//TODO: Shader creation might have failed. Add error handling
|
||||||
|
|
||||||
|
/* Vertex Shader Pipeline Stage */
|
||||||
|
vert_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
vert_shader_stage_info.pNext = NULL;
|
||||||
|
vert_shader_stage_info.flags = 0U;
|
||||||
|
vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||||
|
vert_shader_stage_info.module = vertex_shader_module;
|
||||||
|
vert_shader_stage_info.pName = "main"; /* Entry point function */
|
||||||
|
vert_shader_stage_info.pSpecializationInfo = NULL; /* Specialization in a SPIR-V module */
|
||||||
|
|
||||||
|
/* Fragments Shader Pipeline Stage */
|
||||||
|
frag_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
frag_shader_stage_info.pNext = NULL;
|
||||||
|
frag_shader_stage_info.flags = 0U;
|
||||||
|
frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
frag_shader_stage_info.module = fragments_shader_module;
|
||||||
|
frag_shader_stage_info.pName = "main"; /* Entry point function */
|
||||||
|
frag_shader_stage_info.pSpecializationInfo = NULL; /* Specialization in a SPIR-V module */
|
||||||
|
|
||||||
|
shader_stages[VERTEX] = vert_shader_stage_info;
|
||||||
|
shader_stages[FRAGMENTS] = frag_shader_stage_info;
|
||||||
|
|
||||||
|
memcpy(&g_shader_modules[0], &vertex_shader_module, sizeof(VkShaderModule));
|
||||||
|
memcpy(&g_shader_modules[1], &fragments_shader_module, sizeof(VkShaderModule));
|
||||||
|
|
||||||
|
return VULKAN_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill dynamic pipeline state info
|
||||||
|
*
|
||||||
|
* @param dynamic_state structure to be filled
|
||||||
|
*/
|
||||||
|
void fill_dynamic_pipeline_state_info(VkPipelineDynamicStateCreateInfo* dynamic_state)
|
||||||
|
{
|
||||||
|
VkDynamicState* dynamic_states;
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
dynamic_states = malloc(sizeof(VkDynamicState) * 2);
|
||||||
|
dynamic_states[0] = VK_DYNAMIC_STATE_VIEWPORT; /* VkPipelineViewportStateCreateInfo:pViewport; vkCmdSetViewport() */
|
||||||
|
dynamic_states[1] = VK_DYNAMIC_STATE_SCISSOR; /* VkPipelineViewportStateCreateInfo:pScissors; vkCmdSetScissors() */
|
||||||
|
|
||||||
|
dynamic_state->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||||
|
dynamic_state->pNext = NULL;
|
||||||
|
dynamic_state->flags = 0U;
|
||||||
|
dynamic_state->dynamicStateCount = 2U;
|
||||||
|
|
||||||
|
/* dynamic_state 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 */
|
||||||
|
dynamic_state->pDynamicStates = dynamic_states;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Release allocated dynamic pipeline state info
|
||||||
|
*
|
||||||
|
* @param dynamic_state structure to be freed
|
||||||
|
*/
|
||||||
|
void free_dynamic_pipeline_state_info(VkPipelineDynamicStateCreateInfo* dynamic_state)
|
||||||
|
{
|
||||||
|
free((VkDynamicState*)dynamic_state->pDynamicStates);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill vertex input info
|
||||||
|
*
|
||||||
|
* @param vertex_input_info structure to be filled
|
||||||
|
*/
|
||||||
|
void fill_vertex_input_info(VkPipelineVertexInputStateCreateInfo* vertex_input_info)
|
||||||
|
{
|
||||||
|
VkVertexInputBindingDescription vertex_binding_description = get_binding_description();
|
||||||
|
VkVertexInputAttributeDescription* vertex_attribute_descriptions = get_attribute_descriptions();
|
||||||
|
VkVertexInputBindingDescription* bindingDescriptions = malloc(sizeof(VkVertexInputBindingDescription) * 2);
|
||||||
|
VkVertexInputAttributeDescription* attributeDescriptions = malloc(sizeof(VkVertexInputAttributeDescription) * 5);
|
||||||
|
|
||||||
|
bindingDescriptions[0] = vertex_binding_description;
|
||||||
|
|
||||||
|
bindingDescriptions[1].binding = 1;
|
||||||
|
bindingDescriptions[1].stride = sizeof(struct rse_instance_data_t); //TODO: change
|
||||||
|
bindingDescriptions[1].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;
|
||||||
|
|
||||||
|
memcpy(attributeDescriptions, vertex_attribute_descriptions, sizeof(VkVertexInputAttributeDescription) * 2);
|
||||||
|
|
||||||
|
attributeDescriptions[2].binding = 1;
|
||||||
|
attributeDescriptions[2].location = 2;
|
||||||
|
attributeDescriptions[2].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||||
|
attributeDescriptions[2].offset = offsetof(struct rse_instance_data_t, pos);
|
||||||
|
|
||||||
|
attributeDescriptions[3].binding = 1;
|
||||||
|
attributeDescriptions[3].location = 3;
|
||||||
|
attributeDescriptions[3].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||||
|
attributeDescriptions[3].offset = offsetof(struct rse_instance_data_t, rot);
|
||||||
|
|
||||||
|
attributeDescriptions[4].binding = 1;
|
||||||
|
attributeDescriptions[4].location = 4;
|
||||||
|
attributeDescriptions[4].format = VK_FORMAT_R32_SFLOAT;
|
||||||
|
attributeDescriptions[4].offset = offsetof(struct rse_instance_data_t, scale);
|
||||||
|
// ************************
|
||||||
|
|
||||||
|
vertex_input_info->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||||
|
vertex_input_info->pNext = NULL;
|
||||||
|
vertex_input_info->flags = 0U;
|
||||||
|
|
||||||
|
vertex_input_info->vertexBindingDescriptionCount = 2U;
|
||||||
|
vertex_input_info->pVertexBindingDescriptions = bindingDescriptions;
|
||||||
|
vertex_input_info->vertexAttributeDescriptionCount = 5U;
|
||||||
|
vertex_input_info->pVertexAttributeDescriptions = attributeDescriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free allocated vertex input info
|
||||||
|
*
|
||||||
|
* @param vertex_input_info structure to be freed
|
||||||
|
*/
|
||||||
|
void free_vertex_input_info(VkPipelineVertexInputStateCreateInfo* vertex_input_info)
|
||||||
|
{
|
||||||
|
free((VkVertexInputBindingDescription*)vertex_input_info->pVertexBindingDescriptions);
|
||||||
|
free((VkVertexInputAttributeDescription*)vertex_input_info->pVertexAttributeDescriptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill input assembly info
|
||||||
|
*
|
||||||
|
* @param input_assembly structure to be filled
|
||||||
|
*/
|
||||||
|
void fill_input_assembly_info(VkPipelineInputAssemblyStateCreateInfo* input_assembly)
|
||||||
|
{
|
||||||
|
input_assembly->sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||||
|
input_assembly->pNext = NULL;
|
||||||
|
input_assembly->flags = 0U;
|
||||||
|
input_assembly->topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; // TODO: Change to _STRIP
|
||||||
|
input_assembly->primitiveRestartEnable = VK_FALSE; /* If I want to break lines during _STRIP,
|
||||||
|
* I must set this to VK_TRUE */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill viewport state info
|
||||||
|
*
|
||||||
|
* @param viewport_state structure to be filled
|
||||||
|
*/
|
||||||
|
void fill_viewport_state_info(VkPipelineViewportStateCreateInfo* viewport_state)
|
||||||
|
{
|
||||||
|
VkViewport* viewport = malloc(sizeof(VkViewport));
|
||||||
|
VkRect2D* scissor = malloc(sizeof(VkRect2D));
|
||||||
|
|
||||||
|
viewport->x = 0.0f;
|
||||||
|
viewport->y = 0.0f;
|
||||||
|
viewport->width = (float)(g_vulkan_state.swapchain_extent.width);
|
||||||
|
viewport->height = (float)(g_vulkan_state.swapchain_extent.height);
|
||||||
|
viewport->minDepth = 0.0f;
|
||||||
|
viewport->maxDepth = 1.0f;
|
||||||
|
|
||||||
|
/* Setting Scissors to fill entire window*/
|
||||||
|
scissor->offset.x = 0;
|
||||||
|
scissor->offset.y = 0;
|
||||||
|
scissor->extent = g_vulkan_state.swapchain_extent;
|
||||||
|
|
||||||
|
viewport_state->sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||||
|
viewport_state->viewportCount = 1;
|
||||||
|
viewport_state->pViewports = viewport;
|
||||||
|
viewport_state->scissorCount = 1;
|
||||||
|
viewport_state->pScissors = scissor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free allocated viewport state input info
|
||||||
|
*
|
||||||
|
* @param viewport_state structure to be freed
|
||||||
|
*/
|
||||||
|
void free_viewport_state_info(VkPipelineViewportStateCreateInfo* viewport_state)
|
||||||
|
{
|
||||||
|
free((VkViewport*)viewport_state->pViewports);
|
||||||
|
free((VkRect2D*)viewport_state->pScissors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill rasteriation info
|
||||||
|
*
|
||||||
|
* @param rasterizer
|
||||||
|
*/
|
||||||
|
void fill_rasterization_info(VkPipelineRasterizationStateCreateInfo* rasterizer)
|
||||||
|
{
|
||||||
|
rasterizer->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||||
|
rasterizer->pNext = NULL;
|
||||||
|
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 fill_multisampling_state_info(VkPipelineMultisampleStateCreateInfo* multisampling)
|
||||||
|
{
|
||||||
|
multisampling->sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||||
|
multisampling->pNext = NULL;
|
||||||
|
multisampling->flags = 0U;
|
||||||
|
multisampling->rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
multisampling->sampleShadingEnable = VK_FALSE;
|
||||||
|
multisampling->minSampleShading = 1.0f;
|
||||||
|
multisampling->pSampleMask = NULL;
|
||||||
|
multisampling->alphaToCoverageEnable = VK_FALSE;
|
||||||
|
multisampling->alphaToOneEnable = VK_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill color blend state info
|
||||||
|
*
|
||||||
|
* @param color_blending structure to be filled
|
||||||
|
*/
|
||||||
|
void fill_color_blend_state_info(VkPipelineColorBlendStateCreateInfo* color_blending)
|
||||||
|
{
|
||||||
|
VkPipelineColorBlendAttachmentState* color_blend_attachment = malloc(sizeof(VkPipelineColorBlendAttachmentState));
|
||||||
|
|
||||||
|
color_blend_attachment->blendEnable = VK_FALSE;
|
||||||
|
color_blend_attachment->srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||||
|
color_blend_attachment->dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||||
|
color_blend_attachment->colorBlendOp = VK_BLEND_OP_ADD;
|
||||||
|
color_blend_attachment->srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||||
|
color_blend_attachment->dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||||
|
color_blend_attachment->alphaBlendOp = VK_BLEND_OP_ADD;
|
||||||
|
color_blend_attachment->colorWriteMask =
|
||||||
|
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||||
|
|
||||||
|
color_blending->sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||||
|
color_blending->pNext = NULL;
|
||||||
|
color_blending->flags = 0U;
|
||||||
|
color_blending->logicOpEnable = VK_FALSE;
|
||||||
|
color_blending->logicOp = VK_LOGIC_OP_COPY;
|
||||||
|
color_blending->attachmentCount = 1U;
|
||||||
|
color_blending->pAttachments = color_blend_attachment;
|
||||||
|
color_blending->blendConstants[0] = 0.0f;
|
||||||
|
color_blending->blendConstants[1] = 0.0f;
|
||||||
|
color_blending->blendConstants[2] = 0.0f;
|
||||||
|
color_blending->blendConstants[3] = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free allocated color blend state info
|
||||||
|
*
|
||||||
|
* @param color_blending Structure to be freed
|
||||||
|
*/
|
||||||
|
void free_color_blend_info(VkPipelineColorBlendStateCreateInfo* color_blending)
|
||||||
|
{
|
||||||
|
free((VkPipelineColorBlendAttachmentState*)color_blending->pAttachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t create_pipeline()
|
||||||
|
{
|
||||||
|
uint8_t status = VULKAN_ERROR_NO_ERROR;
|
||||||
|
|
||||||
|
STATUS_CHECK(create_swapchain());
|
||||||
|
STATUS_CHECK(create_swapchain_image_views());
|
||||||
|
STATUS_CHECK(create_render_pass());
|
||||||
|
STATUS_CHECK(create_descriptor_set_layout());
|
||||||
|
STATUS_CHECK(create_pipeline_layout());
|
||||||
|
STATUS_CHECK(create_graphics_pipeline());
|
||||||
|
STATUS_CHECK(create_framebuffers());
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_pipeline()
|
||||||
|
{
|
||||||
|
vkDestroyPipeline(g_vulkan_state.device, g_vulkan_state.graphics_pipeline, NULL);
|
||||||
|
vkDestroyPipelineLayout(g_vulkan_state.device, g_vulkan_state.pipeline_layout, NULL);
|
||||||
|
vkDestroyRenderPass(g_vulkan_state.device, g_vulkan_state.render_pass, NULL);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < SHADER_MODULES_COUNT; i++) {
|
||||||
|
vkDestroyShaderModule(g_vulkan_state.device, g_shader_modules[i], NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,711 +0,0 @@
|
|||||||
#include "vulkan_pipeline.hpp"
|
|
||||||
|
|
||||||
#include "locale_vulkan.h"
|
|
||||||
#include "utilities/file_utils.hpp"
|
|
||||||
#include "utilities/logger.hpp"
|
|
||||||
#include "vulkan_errors.h"
|
|
||||||
#include "mesh_controller.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <map>
|
|
||||||
#include <optional>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace rse::graphics::vulkanbase
|
|
||||||
{
|
|
||||||
|
|
||||||
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(const std::vector<char>& binaryData);
|
|
||||||
|
|
||||||
static uint8_t createSwapchain();
|
|
||||||
static uint8_t createSwapchainImageViews();
|
|
||||||
static uint8_t createRenderPass();
|
|
||||||
static uint8_t createDescriptorSetLayout();
|
|
||||||
static uint8_t createPipelineLayout();
|
|
||||||
static uint8_t createGraphicsPipeline();
|
|
||||||
static uint8_t createFrambuffers();
|
|
||||||
|
|
||||||
static uint8_t fillShaderStages(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(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(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(g_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()
|
|
||||||
{
|
|
||||||
VkSwapchainCreateInfoKHR createInfo;
|
|
||||||
VkSurfaceCapabilitiesKHR physicalDeviceSurfaceCapabilities;
|
|
||||||
VkBool32 surfaceSupported;
|
|
||||||
|
|
||||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_vulkan_state.physicalDevice, g_vulkan_state.surface, &physicalDeviceSurfaceCapabilities);
|
|
||||||
|
|
||||||
/* Store extent in global variable, for later use */
|
|
||||||
g_vulkan_state.swapchainExtent = physicalDeviceSurfaceCapabilities.currentExtent;
|
|
||||||
|
|
||||||
/* Check if device supports surface for presentation */
|
|
||||||
vkGetPhysicalDeviceSurfaceSupportKHR(g_vulkan_state.physicalDevice, g_vulkan_state.queueFamilyIndices[0], g_vulkan_state.surface, &surfaceSupported);
|
|
||||||
|
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
|
||||||
createInfo.pNext = nullptr;
|
|
||||||
createInfo.flags = 0U;
|
|
||||||
createInfo.surface = g_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 = g_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 = g_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(g_vulkan_state.device, &createInfo, nullptr, &g_vulkan_state.swapchain)) {
|
|
||||||
LOGF << vulkanErrorMesssages[VULKAN_SWAPCHAIN_CREATION_FAILED];
|
|
||||||
return VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Obtain swapchain images */
|
|
||||||
vkGetSwapchainImagesKHR(g_vulkan_state.device, g_vulkan_state.swapchain, &gSwapchainImagesCount, nullptr);
|
|
||||||
gSwapChainImages.resize(gSwapchainImagesCount);
|
|
||||||
vkGetSwapchainImagesKHR(g_vulkan_state.device, g_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()
|
|
||||||
{
|
|
||||||
g_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(g_vulkan_state.device, &createInfo, nullptr, &g_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()
|
|
||||||
{
|
|
||||||
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(g_vulkan_state.device, &renderPassInfo, nullptr, &g_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()
|
|
||||||
{
|
|
||||||
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(g_vulkan_state.device, &layoutInfo, nullptr, &g_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()
|
|
||||||
{
|
|
||||||
VkPipelineLayoutCreateInfo createInfo{};
|
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
||||||
createInfo.pNext = nullptr;
|
|
||||||
createInfo.flags = 0U;
|
|
||||||
createInfo.setLayoutCount = 1U;
|
|
||||||
createInfo.pSetLayouts = &g_vulkan_state.descriptorSetLayout;
|
|
||||||
createInfo.pushConstantRangeCount = 0U;
|
|
||||||
createInfo.pPushConstantRanges = nullptr;
|
|
||||||
|
|
||||||
if (VK_SUCCESS != vkCreatePipelineLayout(g_vulkan_state.device, &createInfo, nullptr, &g_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()
|
|
||||||
{
|
|
||||||
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(&shaderStages));
|
|
||||||
fillVertexInputInfo(&vertexInputInfo);
|
|
||||||
fillInputAssemblyInfo(&inputAssembly);
|
|
||||||
fillViewportStateInfo(&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 = g_vulkan_state.pipelineLayout;
|
|
||||||
pipelineInfo.renderPass = g_vulkan_state.renderPass;
|
|
||||||
pipelineInfo.subpass = 0;
|
|
||||||
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
|
|
||||||
pipelineInfo.basePipelineIndex = -1;
|
|
||||||
|
|
||||||
if (VK_SUCCESS !=
|
|
||||||
vkCreateGraphicsPipelines(g_vulkan_state.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &g_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()
|
|
||||||
{
|
|
||||||
g_vulkan_state.swapchainFramebuffers.resize(g_vulkan_state.swapChainImageViews.size());
|
|
||||||
|
|
||||||
for (size_t i = 0; i < g_vulkan_state.swapChainImageViews.size(); i++) {
|
|
||||||
VkImageView attachments[] = {g_vulkan_state.swapChainImageViews[i]};
|
|
||||||
|
|
||||||
VkFramebufferCreateInfo framebufferInfo{};
|
|
||||||
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
|
||||||
framebufferInfo.renderPass = g_vulkan_state.renderPass;
|
|
||||||
framebufferInfo.attachmentCount = 1;
|
|
||||||
framebufferInfo.pAttachments = attachments;
|
|
||||||
framebufferInfo.width = g_vulkan_state.swapchainExtent.width;
|
|
||||||
framebufferInfo.height = g_vulkan_state.swapchainExtent.height;
|
|
||||||
framebufferInfo.layers = 1;
|
|
||||||
|
|
||||||
if (vkCreateFramebuffer(g_vulkan_state.device, &framebufferInfo, nullptr, &g_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()
|
|
||||||
{
|
|
||||||
cleanup_swapchain();
|
|
||||||
createSwapchain();
|
|
||||||
createSwapchainImageViews();
|
|
||||||
createFrambuffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Cleanup swapchain
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void cleanup_swapchain()
|
|
||||||
{
|
|
||||||
vkDeviceWaitIdle(g_vulkan_state.device);
|
|
||||||
for (size_t i = 0; i < g_vulkan_state.swapchainFramebuffers.size(); i++) {
|
|
||||||
vkDestroyFramebuffer(g_vulkan_state.device, g_vulkan_state.swapchainFramebuffers[i], nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < g_vulkan_state.swapChainImageViews.size(); i++) {
|
|
||||||
vkDestroyImageView(g_vulkan_state.device, g_vulkan_state.swapChainImageViews[i], nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
vkDestroySwapchainKHR(g_vulkan_state.device, g_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(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(vertexShaderCode);
|
|
||||||
std::optional<VkShaderModule> fragmentsShaderModule = createShaderModule(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 vertexBindingDescription = Vertex::getBindingDescription();
|
|
||||||
auto vertexAttributeDescriptions = Vertex::getAttributeDescriptions();
|
|
||||||
|
|
||||||
//TODO: move somewhere else
|
|
||||||
auto bindingDescriptions = new VkVertexInputBindingDescription[2];
|
|
||||||
bindingDescriptions[0] = vertexBindingDescription;
|
|
||||||
|
|
||||||
bindingDescriptions[1].binding = 1;
|
|
||||||
bindingDescriptions[1].stride = sizeof(InstanceData); //TODO: change
|
|
||||||
bindingDescriptions[1].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;
|
|
||||||
|
|
||||||
auto attributeDescriptions = new VkVertexInputAttributeDescription[5];
|
|
||||||
std::copy(vertexAttributeDescriptions.begin(), vertexAttributeDescriptions.end(), attributeDescriptions);
|
|
||||||
|
|
||||||
attributeDescriptions[2].binding = 1;
|
|
||||||
attributeDescriptions[2].location = 2;
|
|
||||||
attributeDescriptions[2].format = VK_FORMAT_R32G32B32_SFLOAT;
|
|
||||||
attributeDescriptions[2].offset = offsetof(InstanceData, pos);
|
|
||||||
|
|
||||||
attributeDescriptions[3].binding = 1;
|
|
||||||
attributeDescriptions[3].location = 3;
|
|
||||||
attributeDescriptions[3].format = VK_FORMAT_R32G32B32_SFLOAT;
|
|
||||||
attributeDescriptions[3].offset = offsetof(InstanceData, rot);
|
|
||||||
|
|
||||||
attributeDescriptions[4].binding = 1;
|
|
||||||
attributeDescriptions[4].location = 4;
|
|
||||||
attributeDescriptions[4].format = VK_FORMAT_R32_SFLOAT;
|
|
||||||
attributeDescriptions[4].offset = offsetof(InstanceData, scale);
|
|
||||||
// ************************
|
|
||||||
|
|
||||||
vertexInputInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
|
||||||
vertexInputInfo->pNext = nullptr;
|
|
||||||
vertexInputInfo->flags = 0U;
|
|
||||||
vertexInputInfo->vertexBindingDescriptionCount = 2U;
|
|
||||||
vertexInputInfo->pVertexBindingDescriptions = bindingDescriptions;
|
|
||||||
vertexInputInfo->vertexAttributeDescriptionCount = 5U;
|
|
||||||
vertexInputInfo->pVertexAttributeDescriptions = attributeDescriptions;
|
|
||||||
|
|
||||||
// VkVertexInputAttributeDescription* tmp = new VkVertexInputAttributeDescription[attributeDescriptions.size()];
|
|
||||||
// std::copy(attributeDescriptions.begin(), attributeDescriptions.end(), 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(VkPipelineViewportStateCreateInfo* viewportState)
|
|
||||||
{
|
|
||||||
VkViewport viewport{};
|
|
||||||
viewport.x = 0.0f;
|
|
||||||
viewport.y = 0.0f;
|
|
||||||
viewport.width = static_cast<float>(g_vulkan_state.swapchainExtent.width);
|
|
||||||
viewport.height = static_cast<float>(g_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 = g_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()
|
|
||||||
{
|
|
||||||
uint8_t status = VULKAN_ERROR_NO_ERROR;
|
|
||||||
|
|
||||||
STATUS_CHECK(createSwapchain());
|
|
||||||
STATUS_CHECK(createSwapchainImageViews());
|
|
||||||
STATUS_CHECK(createRenderPass());
|
|
||||||
STATUS_CHECK(createDescriptorSetLayout());
|
|
||||||
STATUS_CHECK(createPipelineLayout());
|
|
||||||
STATUS_CHECK(createGraphicsPipeline());
|
|
||||||
STATUS_CHECK(createFrambuffers());
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_pipeline()
|
|
||||||
{
|
|
||||||
vkDestroyPipeline(g_vulkan_state.device, g_vulkan_state.graphicsPipeline, nullptr);
|
|
||||||
vkDestroyPipelineLayout(g_vulkan_state.device, g_vulkan_state.pipelineLayout, nullptr);
|
|
||||||
vkDestroyRenderPass(g_vulkan_state.device, g_vulkan_state.renderPass, nullptr);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < gShaderModules.size(); i++) {
|
|
||||||
vkDestroyShaderModule(g_vulkan_state.device, gShaderModules[i], nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace rse::graphics::vulkanbase
|
|
||||||
@@ -1,13 +1,9 @@
|
|||||||
#ifndef RSE_VULKAN_PIPELINE_HPP
|
#ifndef RSE_VULKAN_PIPELINE_H
|
||||||
#define RSE_VULKAN_PIPELINE_HPP
|
#define RSE_VULKAN_PIPELINE_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <vulkan/vulkan.hpp>
|
|
||||||
|
|
||||||
#include "vulkan_commons.h"
|
#include "vulkan_commons.h"
|
||||||
|
|
||||||
namespace rse::graphics::vulkanbase
|
#include <stdint.h>
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a graphics pipeline
|
* @brief Create a graphics pipeline
|
||||||
@@ -15,29 +11,27 @@ namespace rse::graphics::vulkanbase
|
|||||||
* @param vulkan_state
|
* @param vulkan_state
|
||||||
* @return uint8_t
|
* @return uint8_t
|
||||||
*/
|
*/
|
||||||
uint8_t create_pipeline();
|
uint8_t create_pipeline(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Cleanup swapchain
|
* @brief Cleanup swapchain
|
||||||
*
|
*
|
||||||
* @param vulkan_state
|
* @param vulkan_state
|
||||||
*/
|
*/
|
||||||
void cleanup_swapchain();
|
void cleanup_swapchain(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Recreates swapchain
|
* @brief Recreates swapchain
|
||||||
*
|
*
|
||||||
* @param vulkan_state
|
* @param vulkan_state
|
||||||
*/
|
*/
|
||||||
void recreateSwapchain();
|
void recreate_swapchain(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destroy all vulkan pipeline objects
|
* @brief Destroy all vulkan pipeline objects
|
||||||
*
|
*
|
||||||
* @param vulkan_state
|
* @param vulkan_state
|
||||||
*/
|
*/
|
||||||
void destroy_pipeline();
|
void destroy_pipeline(void);
|
||||||
|
|
||||||
}
|
#endif /* RSE_VULKAN_PIPELINE_H */
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -9,14 +9,11 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "window.hpp"
|
#include "window.h"
|
||||||
|
|
||||||
#include "locale_window.h"
|
#include "locale_window.h"
|
||||||
#include "utilities/logger.hpp"
|
#include "utilities/logger.h"
|
||||||
#include "vulkan_base.hpp"
|
#include "vulkan_base.h"
|
||||||
|
|
||||||
namespace rse::graphics::window
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Error codes */
|
/* Error codes */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -26,18 +23,18 @@ typedef enum {
|
|||||||
WINDOW_ERROR_WINDOW_NOT_CREATED
|
WINDOW_ERROR_WINDOW_NOT_CREATED
|
||||||
} WINDOW_ERROR;
|
} WINDOW_ERROR;
|
||||||
|
|
||||||
GLFWwindow *gpWindowHandle = NULL;
|
GLFWwindow *g_window_handle = NULL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief GLFW erro callback
|
* @brief GLFW error callback
|
||||||
*
|
*
|
||||||
* @param error Error code
|
* @param error Error code
|
||||||
* @param description Error description
|
* @param description Error description
|
||||||
*/
|
*/
|
||||||
static void errrorCallback(int error, const char* description)
|
static void error_callback(int error, const char* description)
|
||||||
{
|
{
|
||||||
(void)error;
|
(void)error;
|
||||||
LOGE << "Error: " << description;
|
LOGE("Error: %s", description);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,71 +46,70 @@ static void errrorCallback(int error, const char* description)
|
|||||||
* @param action GLFW_PRESS, GLFW_RELEASE or GLFW_REPEAT
|
* @param action GLFW_PRESS, GLFW_RELEASE or GLFW_REPEAT
|
||||||
* @param mods Bit field describing which modifier keys were held down
|
* @param mods Bit field describing which modifier keys were held down
|
||||||
*/
|
*/
|
||||||
static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
|
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
|
||||||
{
|
{
|
||||||
(void)scancode;
|
(void)scancode;
|
||||||
(void)action;
|
(void)action;
|
||||||
(void)mods;
|
(void)mods;
|
||||||
|
|
||||||
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
|
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
|
||||||
glfwSetWindowShouldClose(window, GLFW_TRUE);
|
glfwSetWindowShouldClose(window, GLFW_TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint16_t windowInit()
|
uint16_t window_init()
|
||||||
{
|
{
|
||||||
/* Initiliaze GLFW AP */
|
/* Initiliaze GLFW AP */
|
||||||
if (GLFW_TRUE != glfwInit())
|
if (GLFW_TRUE != glfwInit())
|
||||||
{
|
{
|
||||||
LOGF << windowErrorMesssages[WINDOW_GLFW_INIT_FAILED];
|
LOGF(window_error_messages[WINDOW_GLFW_INIT_FAILED]);
|
||||||
return WINDOW_ERROR_GLFW_INIT_FAILED;
|
return WINDOW_ERROR_GLFW_INIT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Check for Vulkan support */
|
/* Check for Vulkan support */
|
||||||
if (GLFW_FALSE == glfwVulkanSupported()) {
|
if (GLFW_FALSE == glfwVulkanSupported()) {
|
||||||
LOGF << windowErrorMesssages[WINDOW_VULKAN_NOT_SUPPORTED];
|
LOGF(window_error_messages[WINDOW_VULKAN_NOT_SUPPORTED]);
|
||||||
return WINDOW_ERROR_VULKAN_NOT_LOADED;
|
return WINDOW_ERROR_VULKAN_NOT_LOADED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set error callback */
|
/* Set error callback */
|
||||||
glfwSetErrorCallback(errrorCallback);
|
glfwSetErrorCallback(error_callback);
|
||||||
|
|
||||||
/* Create window */
|
/* Create window */
|
||||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
|
|
||||||
gpWindowHandle = glfwCreateWindow(640, 480, "RedScarfEngine", NULL, NULL);
|
g_window_handle = glfwCreateWindow(640, 480, "RedScarfEngine", NULL, NULL);
|
||||||
|
|
||||||
if (NULL == gpWindowHandle) {
|
if (NULL == g_window_handle) {
|
||||||
LOGF << windowErrorMesssages[WINDOW_NOT_CREATED];
|
LOGF(window_error_messages[WINDOW_NOT_CREATED]);
|
||||||
return WINDOW_ERROR_WINDOW_NOT_CREATED;
|
return WINDOW_ERROR_WINDOW_NOT_CREATED;
|
||||||
}
|
}
|
||||||
|
|
||||||
glfwSetKeyCallback(gpWindowHandle, keyCallback);
|
glfwSetKeyCallback(g_window_handle, key_callback);
|
||||||
|
|
||||||
return WINDOW_ERROR_NO_ERROR;
|
return WINDOW_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t windowLoop()
|
uint16_t window_loop()
|
||||||
{
|
{
|
||||||
while (!glfwWindowShouldClose(gpWindowHandle)) {
|
while (!glfwWindowShouldClose(g_window_handle)) {
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
rse::graphics::vulkanbase::drawFrame();
|
draw_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
return WINDOW_ERROR_NO_ERROR;
|
return WINDOW_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void windowTerminate()
|
void window_terminate()
|
||||||
{
|
{
|
||||||
glfwDestroyWindow(gpWindowHandle);
|
glfwDestroyWindow(g_window_handle);
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GLFWwindow* getWindowHandle()
|
GLFWwindow* get_window_handle()
|
||||||
{
|
{
|
||||||
return gpWindowHandle;
|
return g_window_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace rse::graphics::window */
|
|
||||||
@@ -9,17 +9,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef RSE_WINDOW_HPP
|
#ifndef RSE_WINDOW_H
|
||||||
#define RSE_WINDOW_HPP
|
#define RSE_WINDOW_H
|
||||||
|
|
||||||
#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>
|
||||||
|
|
||||||
namespace rse::graphics::window
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize window
|
* @brief Initialize window
|
||||||
*
|
*
|
||||||
@@ -28,7 +25,7 @@ namespace rse::graphics::window
|
|||||||
* WINDOW_WINDOW_NOT_CREATED
|
* WINDOW_WINDOW_NOT_CREATED
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
uint16_t windowInit();
|
uint16_t window_init();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Main window loop
|
* @brief Main window loop
|
||||||
@@ -36,16 +33,14 @@ uint16_t windowInit();
|
|||||||
* @param vulkan_state
|
* @param vulkan_state
|
||||||
* @return uint16_t WINDOW_SUCCESS or error code
|
* @return uint16_t WINDOW_SUCCESS or error code
|
||||||
*/
|
*/
|
||||||
uint16_t windowLoop();
|
uint16_t window_loop();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Closes window and terminates GLFW
|
* @brief Closes window and terminates GLFW
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void windowTerminate();
|
void window_terminate();
|
||||||
|
|
||||||
GLFWwindow* getWindowHandle();
|
GLFWwindow* get_window_handle();
|
||||||
|
|
||||||
} /* namespace rse::graphics::window */
|
#endif /* RSE_WINDOW_H */
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
add_executable(${PROJECT_NAME} src/main.cpp)
|
add_executable(${PROJECT_NAME} src/main.c)
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||||
${CMAKE_SOURCE_DIR})
|
${CMAKE_SOURCE_DIR})
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#include "graphics/rse_graphics.h"
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
rse_graphics_init();
|
||||||
|
rse_graphics_run();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,6 @@ target_include_directories(rse_utilities PRIVATE
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_sources(rse_utilities PRIVATE
|
target_sources(rse_utilities PRIVATE
|
||||||
src/logger.cpp
|
src/file_utils.c
|
||||||
src/file_utils.cpp
|
src/logger.c
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef RSE_FILE_UTILS_H
|
||||||
|
#define RSE_FILE_UTILS_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads file and puts its content into char buffer. If NULL is provided as buffer, than just bytes_count
|
||||||
|
* is set
|
||||||
|
*
|
||||||
|
* @param file_path Path to file
|
||||||
|
* @param bytes_count Returned bytes count
|
||||||
|
* @param buffer Returned filled buffer
|
||||||
|
*/
|
||||||
|
void read_file(const char* file_path, long* bytes_count, char* buffer);
|
||||||
|
|
||||||
|
#endif /* RSE_FILE_UTILS_H */
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#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
|
|
||||||
|
|
||||||
@@ -9,8 +9,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LOCALIZATION_H
|
#ifndef RSE_LOCALIZATION_H
|
||||||
#define LOCALIZATION_H
|
#define RSE_LOCALIZATION_H
|
||||||
|
|
||||||
#define SELECTED_LANGUAGE EN_US
|
#define SELECTED_LANGUAGE EN_US
|
||||||
#define MAX_MESSAGE_LENGHT 128
|
#define MAX_MESSAGE_LENGHT 128
|
||||||
@@ -19,10 +19,10 @@
|
|||||||
* @brief Available languages
|
* @brief Available languages
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
enum Languages_t
|
enum languages_t
|
||||||
{
|
{
|
||||||
EN_US = 0,
|
EN_US = 0,
|
||||||
LAST_LANGUAGE
|
LAST_LANGUAGE
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* LOCALIZATION_H */
|
#endif /* RSE_LOCALIZATION_H */
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* @file log.h
|
||||||
|
* @author Piotr Krygier (everyonencancode@gmail.com)
|
||||||
|
* @brief Logging interfaces
|
||||||
|
* @version 0.1
|
||||||
|
* @date 2022-02-11
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RSE_LOGGER_H
|
||||||
|
#define RSE_LOGGER_H
|
||||||
|
|
||||||
|
#ifndef LOG_LEVEL
|
||||||
|
#define LOG_LEVEL LOGLEVEL_DEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define LOGGER(log_level, ...) _log_log(log_level, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
// #define LOGGER(log_level, ...) printf("[##log_level] %s:%d", __FILE__, __LINE__); printf(__VA_ARGS__)
|
||||||
|
|
||||||
|
#define LOGT(...) LOGGER(LOGLEVEL_TRACE, __VA_ARGS__)
|
||||||
|
#define LOGD(...) LOGGER(LOGLEVEL_DEBUG, __VA_ARGS__)
|
||||||
|
#define LOGI(...) LOGGER(LOGLEVEL_INFO, __VA_ARGS__)
|
||||||
|
#define LOGW(...) LOGGER(LOGLEVEL_WARNING, __VA_ARGS__)
|
||||||
|
#define LOGE(...) LOGGER(LOGLEVEL_ERROR, __VA_ARGS__)
|
||||||
|
#define LOGF(...) LOGGER(LOGLEVEL_FATAL, __VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Log level. Is not an enum class, because level is being compared to enable prints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
LOGLEVEL_TRACE,
|
||||||
|
LOGLEVEL_DEBUG,
|
||||||
|
LOGLEVEL_INFO,
|
||||||
|
LOGLEVEL_WARNING,
|
||||||
|
LOGLEVEL_ERROR,
|
||||||
|
LOGLEVEL_FATAL
|
||||||
|
} LogLevel;
|
||||||
|
|
||||||
|
void _log_log(unsigned int level, const char* file, int line, const char* fmt, ...);
|
||||||
|
|
||||||
|
#endif /* RSE_LOGGER_H */
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file log.hpp
|
|
||||||
* @author Piotr Krygier (everyonencancode@gmail.com)
|
|
||||||
* @brief Logging interfaces
|
|
||||||
* @version 0.1
|
|
||||||
* @date 2022-02-11
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RSE_LOGGER_HPP
|
|
||||||
#define RSE_LOGGER_HPP
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#ifndef LOG_LEVEL
|
|
||||||
#define LOG_LEVEL LOGLEVEL_DEBUG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LOGT rse::utils::log::Logger(rse::utils::log::LOGLEVEL_TRACE)
|
|
||||||
#define LOGD rse::utils::log::Logger(rse::utils::log::LOGLEVEL_DEBUG)
|
|
||||||
#define LOGI rse::utils::log::Logger(rse::utils::log::LOGLEVEL_INFO)
|
|
||||||
#define LOGW rse::utils::log::Logger(rse::utils::log::LOGLEVEL_WARNING)
|
|
||||||
#define LOGE rse::utils::log::Logger(rse::utils::log::LOGLEVEL_ERROR)
|
|
||||||
#define LOGF rse::utils::log::Logger(rse::utils::log::LOGLEVEL_FATAL)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace rse::utils::log
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Log level. Is not an enum class, because level is being compared to enable prints
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
LOGLEVEL_TRACE = 1,
|
|
||||||
LOGLEVEL_DEBUG = 2,
|
|
||||||
LOGLEVEL_INFO = 3,
|
|
||||||
LOGLEVEL_WARNING = 4,
|
|
||||||
LOGLEVEL_ERROR = 5,
|
|
||||||
LOGLEVEL_FATAL = 6
|
|
||||||
} LogLevel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Class used indirectly as a logger.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class Logger {
|
|
||||||
private:
|
|
||||||
bool printMessage;
|
|
||||||
std::stringstream mMessageBuffer;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Construct a new Logger object
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
Logger(LogLevel);
|
|
||||||
~Logger();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief For log printing. I want it to work like log << "Message " << args;
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
Logger& operator<<(T&& message)
|
|
||||||
{
|
|
||||||
if (printMessage)
|
|
||||||
mMessageBuffer << message;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace rse::log
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#include "file_utils.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void read_file(const char* file_name, long* bytes_count, char* buffer)
|
||||||
|
{
|
||||||
|
FILE* file;
|
||||||
|
|
||||||
|
file = fopen(file_name, "rb");
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
*bytes_count = ftell(file);
|
||||||
|
rewind(file);
|
||||||
|
|
||||||
|
if (buffer == NULL) {
|
||||||
|
fclose(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(buffer, *bytes_count, 1, file);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#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
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#include "logger.h"
|
||||||
|
#include "stdarg.h"
|
||||||
|
|
||||||
|
const char* log_levels[] = {
|
||||||
|
"LOGLEVEL_TRACE", "LOGLEVEL_DEBUG", "LOGLEVEL_INFO", "LOGLEVEL_WARNING", "LOGLEVEL_ERROR", "LOGLEVEL_FATAL"
|
||||||
|
};
|
||||||
|
|
||||||
|
void _log_log(unsigned int level, const char* file, int line, const char* fmt, ...)
|
||||||
|
{
|
||||||
|
char buf[256];
|
||||||
|
va_list list;
|
||||||
|
|
||||||
|
va_start(list, fmt);
|
||||||
|
fprintf(stdout, "%s %s:%d ", log_levels[level], file, line);
|
||||||
|
vfprintf(stdout, fmt, list);
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file logger.cpp
|
|
||||||
* @author Piotr Krygier (everyonecancode@gmail.com)
|
|
||||||
* @brief Implementation for the logger
|
|
||||||
* @version 0.1
|
|
||||||
* @date 2022-07-06
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "logger.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace rse::utils::log
|
|
||||||
{
|
|
||||||
|
|
||||||
Logger::Logger(LogLevel level) : printMessage(false)
|
|
||||||
{
|
|
||||||
if (level < LOG_LEVEL)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (level)
|
|
||||||
{
|
|
||||||
case LOGLEVEL_TRACE:
|
|
||||||
mMessageBuffer << "[TRACE] ";
|
|
||||||
break;
|
|
||||||
case LOGLEVEL_DEBUG:
|
|
||||||
mMessageBuffer << "[DEBUG] ";
|
|
||||||
break;
|
|
||||||
case LOGLEVEL_INFO:
|
|
||||||
mMessageBuffer << "[INFO] ";
|
|
||||||
break;
|
|
||||||
case LOGLEVEL_WARNING:
|
|
||||||
mMessageBuffer << "[WARNING] ";
|
|
||||||
break;
|
|
||||||
case LOGLEVEL_ERROR:
|
|
||||||
mMessageBuffer << "[ERROR] ";
|
|
||||||
break;
|
|
||||||
case LOGLEVEL_FATAL:
|
|
||||||
mMessageBuffer << "[FATAL] ";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printMessage = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::~Logger()
|
|
||||||
{
|
|
||||||
std::cout << mMessageBuffer.str() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace rse::log
|
|
||||||
Reference in new issue
Block a user