diff --git a/CMakeLists.txt b/CMakeLists.txt index 35a86e73..900a5ce4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ project("RedScarfEngine" # make sure c++ 20 is set 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) # Enable C++ modules # set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046") diff --git a/graphics/CMakeLists.txt b/graphics/CMakeLists.txt index 3ebd1b29..f8c4b5a8 100644 --- a/graphics/CMakeLists.txt +++ b/graphics/CMakeLists.txt @@ -44,17 +44,17 @@ ${CMAKE_SOURCE_DIR} ${glfw3_INCLUDE_DIR} ) -message("Vulkan_INCLUDE_DIR: ${Vulkan_INCLUDE_DIR}") target_sources(rse_graphics PRIVATE -src/window.cpp -src/vulkan_base.cpp -src/vulkan_pipeline.cpp -src/vulkan_buffers.cpp -src/vulkan_descriptors.cpp -src/rse_graphics.cpp +src/vma_port.cpp +src/window.c +src/vulkan_base.c +src/vulkan_pipeline.c +src/vulkan_buffers.c +src/vulkan_descriptors.c +src/rse_graphics.c src/rse_math.c -src/mesh_controller.cpp -src/vulkan_commons.cpp +src/mesh_controller.c +src/vulkan_commons.c ) target_link_libraries(rse_graphics PRIVATE diff --git a/graphics/rse_graphics.h b/graphics/rse_graphics.h new file mode 100644 index 00000000..64829281 --- /dev/null +++ b/graphics/rse_graphics.h @@ -0,0 +1,9 @@ +#ifndef RSE_GRAPHICS_H +#define RSE_GRAPHICS_H + +#include + +void rse_graphics_init(void); +uint8_t rse_graphics_run(void); + +#endif /* RSE_GRAPHICS_H */ \ No newline at end of file diff --git a/graphics/rse_graphics.hpp b/graphics/rse_graphics.hpp deleted file mode 100644 index 0f74dc20..00000000 --- a/graphics/rse_graphics.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef RSE_GRAPHICS_HPP -#define RSE_GRAPHICS_HPP - -#include - -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 \ No newline at end of file diff --git a/graphics/src/locale_vulkan.h b/graphics/src/locale_vulkan.h index d337ad0a..17fbc97b 100644 --- a/graphics/src/locale_vulkan.h +++ b/graphics/src/locale_vulkan.h @@ -9,15 +9,14 @@ * */ -#ifndef LOCALE_VULKAN_H -#define LOCALE_VULKAN_H +#ifndef RSE_LOCALE_VULKAN_H +#define RSE_LOCALE_VULKAN_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_EXTENSION_NOT_SUPPORTED, VULKAN_INTANCE_INIT_FAILED, @@ -45,66 +44,78 @@ enum Errors VULKAN_RECORD_COMMAND_BEGIN_FAILED, VULKAN_RECORD_COMMAND_BUFFER_FAILED, VULKAN_SYNCOBJCTS_CREATION_FAILED, + VULKAN_MESH_NO_FREE, + VULKAN_MESH_INSTANCE_NO_FREE, + VULAKN_DRAW_FAILED, + COMMON_MEMORY_ALLOCATION_FAILED, 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 */ /* VULKAN_LAYER_NOT_SUPPORTED */ - "Selected layer is not supported: ", + "Selected layer is not supported: %s\n", /* VULKAN_EXTENSION_NOT_SUPPORTED */ - "Selected extension is not supported: ", + "Selected extension is not supported: %s\n", /* VULKAN_INTANCE_INIT_FAILED */ - "Vulkan instance initialization failed", + "Vulkan instance initialization failed\n", /* 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 */ - "No suitable physical device found", + "No suitable physical device found\n", /* 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 */ - "Failed to create logical device", + "Failed to create logical device\n", /* VULKAN_ALLOCATOR_CREATION_FAILED */ - "Failed to create Vulkan Memory Allocator", + "Failed to create Vulkan Memory Allocator\n", /* VULKAN_COMMAND_POOL_CREATION_FAILED */ - "Failed to create command pool", + "Failed to create command pool\n", /* VULKAN_BUFFER_CREATION_FAILED */ - "Failed to create a vertex buffer", + "Failed to create a vertex buffer\n", /* VULKAN_VERTEX_BUFFER_MAPPING_FAILED */ - "Failed to map vulkan memory buffer", + "Failed to map vulkan memory buffer\n", /* VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED */ - "Failed to allocate command buffers", + "Failed to allocate command buffers\n", /* VULKAN_SURFACE_CREATION_FAILED */ - "Failed to create window surface", + "Failed to create window surface\n", /* VULKAN_SWAPCHAIN_CREATION_FAILED */ - "Failed to create swapchain", + "Failed to create swapchain\n", /* VULKAN_SWAPCHAIN_IMVIEW_CREATION_FAILED */ - "Failed to create swapchain image view", + "Failed to create swapchain image view\n", /* 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 */ - "Failed to create a shader", + "Failed to create a shader\n", /* VULKAN_PIPELAYOUT_CREATION_FAILED */ - "Failed to create pipeline layout", + "Failed to create pipeline layout\n", /* VULKAN_RENDERPASS_CREATION_FAILED */ - "Failed to create render pass", + "Failed to create render pass\n", /* VULKAN_DESCRIPTOR_POOL_CREATION_FAILED */ - "Failed to create descriptor pool", + "Failed to create descriptor pool\n", /* VULKAN_DESCRIPTOR_SETS_ALLOCATION_FAILED */ - "Failed to allocate descriptor sets", + "Failed to allocate descriptor sets\n", /* VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED */ - "Failed to create descriptor set layout", + "Failed to create descriptor set layout\n", /* VULKAN_GRAPHICSPIPELINE_CREATION_FAILED */ - "Failed to create graphics pipeline", + "Failed to create graphics pipeline\n", /* VULKAN_FRAMEBUFFERS_CREATION_FAILED */ - "Failed to create framebuffer", + "Failed to create framebuffer\n", /* VULKAN_RECORD_COMMAND_BEGIN_FAILED */ - "Failed to start recording command buffer", + "Failed to start recording command buffer\n", /* 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 */ - "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 */ \ No newline at end of file +#endif /* RSE_LOCALE_VULKAN_H */ \ No newline at end of file diff --git a/graphics/src/locale_window.h b/graphics/src/locale_window.h index 42d919f8..42f2adcd 100644 --- a/graphics/src/locale_window.h +++ b/graphics/src/locale_window.h @@ -1,5 +1,5 @@ /** - * @file localeWindow.hxx + * @file locale_window.h * @author Piotr Krygier (everyonencancode@gmail.com) * @brief Error messages * @version 0.1 @@ -9,13 +9,12 @@ * */ -#pragma once - -#include +#ifndef LOCALE_WINDOW_H +#define LOCALE_WINDOW_H #include "utilities/localization.h" -#define windowErrorMesssages windowErrorMesssages_nolocale[SELECTED_LANGUAGE] +#define window_error_messages window_error_messages_nolocale[SELECTED_LANGUAGE] enum Errors { @@ -25,13 +24,15 @@ enum Errors 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 */ /* WINDOW_GLFW_INIT_FAILED */ - "GLFW init failed!", + "GLFW init failed!\n", /* WINDOW_VULKAN_NOT_SUPPORTED */ - "Unable to load Vulkan", + "Unable to load Vulkan\n", /*WINDOW_NOT_CREATED*/ - "Window context creation failed" + "Window context creation failed\n" } -}; \ No newline at end of file +}; + +#endif /* LOCALE_WINDOW_H */ \ No newline at end of file diff --git a/graphics/src/mesh_controller.c b/graphics/src/mesh_controller.c new file mode 100644 index 00000000..70c124fc --- /dev/null +++ b/graphics/src/mesh_controller.c @@ -0,0 +1,122 @@ +#include "mesh_controller.h" + +#include "vulkan_buffers.h" +#include "locale_vulkan.h" +#include "utilities/logger.h" + +#include +#include + +#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; +} diff --git a/graphics/src/mesh_controller.cpp b/graphics/src/mesh_controller.cpp deleted file mode 100644 index deae2e49..00000000 --- a/graphics/src/mesh_controller.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "mesh_controller.h" - -#include "vulkan_buffers.hpp" - -#include -#include - -std::map g_meshes; -std::queue free_ids; - -std::map> instance_free_ids; - -uint16_t create_mesh(std::vector vertices, - std::vector 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( - 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 get_instance_data_for_mesh(uint16_t mesh_id) { - std::vector data; - - for(const auto& [id, instance] : g_meshes[mesh_id].instances) { - data.push_back(instance.get()->instance_data); - } - - return data; -} - - -std::vector get_mesh_vertices(uint16_t mesh_id) { - return g_meshes[mesh_id].vertices; -} - -std::vector get_mesh_indices(uint16_t mesh_id) { - return g_meshes[mesh_id].indices; -} \ No newline at end of file diff --git a/graphics/src/mesh_controller.h b/graphics/src/mesh_controller.h index 07d0d102..c16b0eea 100644 --- a/graphics/src/mesh_controller.h +++ b/graphics/src/mesh_controller.h @@ -14,38 +14,35 @@ #include "vulkan_commons.h" -#include -#include -#include -#include -/* Forward declarations */ -struct MeshInstance; - -struct MeshInstance -{ - uint32_t instance_id; - InstanceData instance_data; -}; - -struct Mesh +/** + * @brief Mesh data, with unique id and dynamic arrays holding vertices and indices. + * One mesh can have multiple instances. + * + */ +struct rse_mesh_t { uint32_t unique_id; - const std::vector vertices; - std::vector indices; - std::map> instances; + size_t instances_count; + size_t indices_count; + 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. * * @param vertices vector of vertices * @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 */ -uint16_t create_mesh(std::vector vertices, - std::vector indices); - +uint16_t create_mesh(struct rse_vertex_t* vertices, + 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 * @@ -53,11 +50,55 @@ uint16_t create_mesh(std::vector vertices, * @param instance_data Transformation info */ void create_mesh_instance(uint16_t mesh_id, - InstanceData instance_data); + struct rse_instance_data_t instance_data); -std::vector get_mesh_vertices(uint16_t mesh_id); -std::vector get_mesh_indices(uint16_t mesh_id); -std::vector get_instance_data_for_mesh(uint16_t mesh_id); +/** + * @brief Get the vertex count for mesh with mesh_id 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 */ \ No newline at end of file diff --git a/graphics/src/rse_graphics.c b/graphics/src/rse_graphics.c new file mode 100644 index 00000000..8a8a156d --- /dev/null +++ b/graphics/src/rse_graphics.c @@ -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; +} diff --git a/graphics/src/rse_graphics.cpp b/graphics/src/rse_graphics.cpp deleted file mode 100644 index f166893b..00000000 --- a/graphics/src/rse_graphics.cpp +++ /dev/null @@ -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 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 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 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 diff --git a/graphics/src/vma_port.cpp b/graphics/src/vma_port.cpp new file mode 100644 index 00000000..620bbff8 --- /dev/null +++ b/graphics/src/vma_port.cpp @@ -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 \ No newline at end of file diff --git a/graphics/src/vulkan_base.c b/graphics/src/vulkan_base.c new file mode 100644 index 00000000..66a60a4e --- /dev/null +++ b/graphics/src/vulkan_base.c @@ -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 + +#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 +#include +#include + +#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); +} \ No newline at end of file diff --git a/graphics/src/vulkan_base.cpp b/graphics/src/vulkan_base.cpp deleted file mode 100644 index 36356994..00000000 --- a/graphics/src/vulkan_base.cpp +++ /dev/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 - -#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 gEnabledInstanceLayersNames = {"VK_LAYER_KHRONOS_validation"}; -#else -std::vector gEnabledInstanceLayersNames = {}; -#endif - -std::vector gEnabledInstanceExtensionsNames = {}; - -const std::vector gEnabledDeviceExtensionsNames = { - "VK_KHR_swapchain", -}; - - -VkInstance gVulkanInstance = VK_NULL_HANDLE; - -VkDebugUtilsMessengerEXT gDebugMessenger = VK_NULL_HANDLE; -VkQueue gPresentQueue; - -std::vector gImageAvailableSemaphores = {}; -std::vector gRenderFinishedSemaphores = {}; -std::vector 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 layerProperties; - std::vector 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 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 pQueueFamilyProperties; - VkDeviceCreateInfo deviceCreateInfo; - VkDeviceQueueCreateInfo deviceQueueCreateInfos[2]; /* TODO: Change when we want more queues */ - std::vector pGraphicsFamiliyQueuePriorities; - std::vector 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 \ No newline at end of file diff --git a/graphics/src/vulkan_base.hpp b/graphics/src/vulkan_base.h similarity index 66% rename from graphics/src/vulkan_base.hpp rename to graphics/src/vulkan_base.h index 997b4671..7665ebbf 100644 --- a/graphics/src/vulkan_base.hpp +++ b/graphics/src/vulkan_base.h @@ -9,42 +9,35 @@ * */ -#ifndef RSE_GRAPHICS_VULKANBASE_HPP -#define RSE_GRAPHICS_VULKANBASE_HPP +#ifndef RSE_GRAPHICS_VULKANBASE_H +#define RSE_GRAPHICS_VULKANBASE_H -#include -#include +#include -#include +#include #include "vulkan_commons.h" -namespace rse::graphics::vulkanbase -{ - - /** * @brief Initialize Vulkan backend * * @param state * @return uint8_t 0 on success. Status code on failure */ -uint8_t initVulkan(); +uint8_t init_vulkan(); /** * @brief Deinitialize Vulkan backend * * @param state */ -void deinitVulkan(); +void deinit_vulkan(); /** * @brief Draw frame on the screen. * * @param state */ -void drawFrame(); - -} /* namespace rse::graphics::vulkanbase */ +void draw_frame(); #endif /* RSE_GRAPHICS_VULKANBASE_HPP */ \ No newline at end of file diff --git a/graphics/src/vulkan_buffers.c b/graphics/src/vulkan_buffers.c new file mode 100644 index 00000000..3307bb48 --- /dev/null +++ b/graphics/src/vulkan_buffers.c @@ -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 +#include + +#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 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 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); +} \ No newline at end of file diff --git a/graphics/src/vulkan_buffers.cpp b/graphics/src/vulkan_buffers.cpp deleted file mode 100644 index 94d8155d..00000000 --- a/graphics/src/vulkan_buffers.cpp +++ /dev/null @@ -1,530 +0,0 @@ -#include "vulkan_buffers.hpp" - - -#include - -#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 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 indices = {0, 1, 2, 2, 3, 0}; - -/* GPU visible buffer */ -std::vector gVertexBuffers = {}; -std::vector gIndexBuffers = {}; -std::vector 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& vertices, - const std::vector& 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(g_vulkan_state.swapchainExtent.width); - viewport.height = static_cast(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(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); -} - -} \ No newline at end of file diff --git a/graphics/src/vulkan_buffers.hpp b/graphics/src/vulkan_buffers.h similarity index 55% rename from graphics/src/vulkan_buffers.hpp rename to graphics/src/vulkan_buffers.h index 27732190..9e9b741e 100644 --- a/graphics/src/vulkan_buffers.hpp +++ b/graphics/src/vulkan_buffers.h @@ -1,16 +1,11 @@ -#ifndef RSE_VULKAN_BUFFERS_HPP -#define RSE_VULKAN_BUFFERS_HPP - -#include -#include -#include - -#include "vk_mem_alloc.h" +#ifndef RSE_VULKAN_BUFFERS_H +#define RSE_VULKAN_BUFFERS_H #include "vulkan_commons.h" +#include "vk_mem_alloc.h" + +#include -namespace rse::graphics::vulkanbase -{ /** * @brief Create buffers needed by vulkan pipeline @@ -25,12 +20,14 @@ uint8_t create_buffers(); * * @param vulkan_state */ -void updateUniformBuffer(); +void update_uniform_buffers(); -uint8_t update_mesh_buffers(const std::vector& vertices, - const std::vector& indices); +uint8_t update_mesh_buffers(const struct rse_vertex_t* vertices, + 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 * @@ -38,7 +35,7 @@ uint8_t update_mesh_instances(InstanceData& instance_data); * @param imageIndex * @return uint8_t */ -uint8_t recordCommandBuffer(uint32_t imageIndex); +uint8_t record_command_buffer(uint32_t imageIndex); /** * @brief Reset command buffer for current frame. @@ -54,7 +51,4 @@ void reset_command_buffer(); */ void destroy_buffers(); -} // namespace rse::graphics::vulkanbase - - -#endif \ No newline at end of file +#endif /* RSE_VULKAN_BUFFERS_H */ \ No newline at end of file diff --git a/graphics/src/vulkan_commons.c b/graphics/src/vulkan_commons.c new file mode 100644 index 00000000..5fee13a9 --- /dev/null +++ b/graphics/src/vulkan_commons.c @@ -0,0 +1,3 @@ +#include "vulkan_commons.h" + +struct rse_vulkan_state_t g_vulkan_state; \ No newline at end of file diff --git a/graphics/src/vulkan_commons.cpp b/graphics/src/vulkan_commons.cpp deleted file mode 100644 index 7a55ba98..00000000 --- a/graphics/src/vulkan_commons.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "vulkan_commons.h" - -VulkanState g_vulkan_state; \ No newline at end of file diff --git a/graphics/src/vulkan_commons.h b/graphics/src/vulkan_commons.h index a22af076..c44acab1 100644 --- a/graphics/src/vulkan_commons.h +++ b/graphics/src/vulkan_commons.h @@ -1,6 +1,6 @@ /** * @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 * @version 0.1 * @date 2023-03-28 @@ -9,101 +9,74 @@ * */ -#ifndef RSE_VULKAN_GLOBAL_HPP -#define RSE_VULKAN_GLOBAL_HPP - -#include -#include +#ifndef RSE_VULKAN_GLOBAL_H +#define RSE_VULKAN_GLOBAL_H #include "rse_math.h" +#include +#include + +#include + #define SWAP_BUFFER_COUNT 2U +extern struct rse_vulkan_state_t g_vulkan_state; /** * @brief Represents single vertex on the screen * */ -struct Vertex +struct rse_vertex_t { struct vec3_t pos; 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 getAttributeDescriptions() { - std::array attributeDescriptions{}; - - attributeDescriptions[0].binding = 0; - attributeDescriptions[0].location = 0; /* inPosition in shader.vert */ - attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; - attributeDescriptions[0].offset = offsetof(Vertex, pos); - - attributeDescriptions[1].binding = 0; - attributeDescriptions[1].location = 1; /* inColor in shader.vert */ - attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; - attributeDescriptions[1].offset = offsetof(Vertex, color); - - return attributeDescriptions; - } - }; /** * @brief Wrapper for Vulkan buffer. Holds allocation data; * */ -struct Buffer +struct rse_vulkan_buffer_t { uint32_t allocated_size; VkBuffer buffer; VmaAllocation allocation; - VmaAllocationInfo allocationInfo; + VmaAllocationInfo allocation_info; }; -struct UniformBufferObject +struct rse_uniform_buffer_object_t { - alignas(16) mat4_t model; - alignas(16) mat4_t view; - alignas(16) mat4_t proj; + alignas(16) struct mat4_t model; + alignas(16) struct mat4_t view; + alignas(16) struct mat4_t proj; }; -struct VulkanState +struct rse_vulkan_state_t { VkDevice device; VkSurfaceKHR surface; - VkQueue graphicsQueue; + VkQueue graphics_queue; VmaAllocator allocator; - VkPhysicalDevice physicalDevice; - uint32_t currentFrame; - uint32_t queueFamilyIndices[1]; - std::vector uniformBuffers; - std::vector pCommandBuffers; - std::vector descriptorSets; - std::vector swapChainImageViews; - VkExtent2D swapchainExtent; - VkRenderPass renderPass; - VkDescriptorSetLayout descriptorSetLayout; - std::vector swapchainFramebuffers; - VkPipeline graphicsPipeline; - VkPipelineLayout pipelineLayout; + VkPhysicalDevice physical_device; + uint32_t current_frame; + uint32_t queue_family_indices[1]; + struct rse_vulkan_buffer_t uniform_buffers[SWAP_BUFFER_COUNT]; + VkCommandBuffer command_buffers[SWAP_BUFFER_COUNT]; + VkDescriptorSet descriptor_sets[SWAP_BUFFER_COUNT]; + VkImageView* swapchain_image_views; + VkExtent2D swapchain_extent; + VkRenderPass render_pass; + VkDescriptorSetLayout descriptor_set_layout; + VkFramebuffer* swapchain_framebuffers; + VkPipeline graphics_pipeline; + VkPipelineLayout pipeline_layout; VkSwapchainKHR swapchain; }; - -extern VulkanState g_vulkan_state; - -struct InstanceData { +struct rse_instance_data_t { struct vec3_t pos; struct vec3_t rot; float scale; }; -#endif +#endif /* RSE_VULKAN_GLOBAL_H */ diff --git a/graphics/src/vulkan_descriptors.c b/graphics/src/vulkan_descriptors.c new file mode 100644 index 00000000..40b53f43 --- /dev/null +++ b/graphics/src/vulkan_descriptors.c @@ -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); +} \ No newline at end of file diff --git a/graphics/src/vulkan_descriptors.cpp b/graphics/src/vulkan_descriptors.cpp deleted file mode 100644 index aa9ef63c..00000000 --- a/graphics/src/vulkan_descriptors.cpp +++ /dev/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(SWAP_BUFFER_COUNT); - - VkDescriptorPoolCreateInfo poolInfo{}; - poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.poolSizeCount = 1; - poolInfo.pPoolSizes = &poolSize; - - poolInfo.maxSets = static_cast(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 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(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 - diff --git a/graphics/src/vulkan_descriptors.h b/graphics/src/vulkan_descriptors.h new file mode 100644 index 00000000..d80ccf95 --- /dev/null +++ b/graphics/src/vulkan_descriptors.h @@ -0,0 +1,20 @@ +#ifndef RSE_VULKAN_DESCRIPTORS_H +#define RSE_VULKAN_DESCRIPTORS_H + +#include +/** + * @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 */ \ No newline at end of file diff --git a/graphics/src/vulkan_descriptors.hpp b/graphics/src/vulkan_descriptors.hpp deleted file mode 100644 index 2ee89a13..00000000 --- a/graphics/src/vulkan_descriptors.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef RSE_VULKAN_DESCRIPTORS_HPP -#define RSE_VULKAN_DESCRIPTORS_HPP - -#include -#include -#include - -#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 \ No newline at end of file diff --git a/graphics/src/vulkan_pipeline.c b/graphics/src/vulkan_pipeline.c new file mode 100644 index 00000000..34c12953 --- /dev/null +++ b/graphics/src/vulkan_pipeline.c @@ -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 +#include +#include + +#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 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); + } +} \ No newline at end of file diff --git a/graphics/src/vulkan_pipeline.cpp b/graphics/src/vulkan_pipeline.cpp deleted file mode 100644 index be56e2a3..00000000 --- a/graphics/src/vulkan_pipeline.cpp +++ /dev/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 -#include -#include -#include - -namespace rse::graphics::vulkanbase -{ - -enum class ShaderStage -{ - VERTEX, - FRAGMENTS -}; - -constexpr VkFormat IMAGE_FORMAT = VK_FORMAT_B8G8R8A8_SRGB; - -uint32_t gSwapchainImagesCount = 0U; - -std::vector gSwapChainImages; -std::vector gShaderModules; - - -const std::map 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 createShaderModule(const std::vector& 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* 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 Handle to compiled shader module or nullopt - */ -std::optional createShaderModule(const std::vector& 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(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 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* 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 vertexShaderModule = createShaderModule(vertexShaderCode); - std::optional 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 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(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(g_vulkan_state.swapchainExtent.width); - viewport.height = static_cast(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 \ No newline at end of file diff --git a/graphics/src/vulkan_pipeline.hpp b/graphics/src/vulkan_pipeline.h similarity index 55% rename from graphics/src/vulkan_pipeline.hpp rename to graphics/src/vulkan_pipeline.h index 762c97e2..395438be 100644 --- a/graphics/src/vulkan_pipeline.hpp +++ b/graphics/src/vulkan_pipeline.h @@ -1,13 +1,9 @@ -#ifndef RSE_VULKAN_PIPELINE_HPP -#define RSE_VULKAN_PIPELINE_HPP - -#include -#include +#ifndef RSE_VULKAN_PIPELINE_H +#define RSE_VULKAN_PIPELINE_H #include "vulkan_commons.h" -namespace rse::graphics::vulkanbase -{ +#include /** * @brief Create a graphics pipeline @@ -15,29 +11,27 @@ namespace rse::graphics::vulkanbase * @param vulkan_state * @return uint8_t */ -uint8_t create_pipeline(); +uint8_t create_pipeline(void); /** * @brief Cleanup swapchain * * @param vulkan_state */ -void cleanup_swapchain(); +void cleanup_swapchain(void); /** * @brief Recreates swapchain * * @param vulkan_state */ -void recreateSwapchain(); +void recreate_swapchain(void); /** * @brief Destroy all vulkan pipeline objects * * @param vulkan_state */ -void destroy_pipeline(); +void destroy_pipeline(void); -} - -#endif +#endif /* RSE_VULKAN_PIPELINE_H */ diff --git a/graphics/src/window.cpp b/graphics/src/window.c similarity index 59% rename from graphics/src/window.cpp rename to graphics/src/window.c index dc9aa040..04fc5e14 100644 --- a/graphics/src/window.cpp +++ b/graphics/src/window.c @@ -9,14 +9,11 @@ * */ -#include "window.hpp" +#include "window.h" #include "locale_window.h" -#include "utilities/logger.hpp" -#include "vulkan_base.hpp" - -namespace rse::graphics::window -{ +#include "utilities/logger.h" +#include "vulkan_base.h" /* Error codes */ typedef enum { @@ -26,18 +23,18 @@ typedef enum { WINDOW_ERROR_WINDOW_NOT_CREATED } WINDOW_ERROR; -GLFWwindow *gpWindowHandle = NULL; +GLFWwindow *g_window_handle = NULL; /** - * @brief GLFW erro callback + * @brief GLFW error callback * * @param error Error code * @param description Error description */ -static void errrorCallback(int error, const char* description) +static void error_callback(int error, const char* description) { (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 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)action; (void)mods; + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { glfwSetWindowShouldClose(window, GLFW_TRUE); } } -uint16_t windowInit() +uint16_t window_init() { /* Initiliaze GLFW AP */ if (GLFW_TRUE != glfwInit()) { - LOGF << windowErrorMesssages[WINDOW_GLFW_INIT_FAILED]; + LOGF(window_error_messages[WINDOW_GLFW_INIT_FAILED]); return WINDOW_ERROR_GLFW_INIT_FAILED; } /* Check for Vulkan support */ if (GLFW_FALSE == glfwVulkanSupported()) { - LOGF << windowErrorMesssages[WINDOW_VULKAN_NOT_SUPPORTED]; + LOGF(window_error_messages[WINDOW_VULKAN_NOT_SUPPORTED]); return WINDOW_ERROR_VULKAN_NOT_LOADED; } /* Set error callback */ - glfwSetErrorCallback(errrorCallback); + glfwSetErrorCallback(error_callback); /* Create window */ 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) { - LOGF << windowErrorMesssages[WINDOW_NOT_CREATED]; + if (NULL == g_window_handle) { + LOGF(window_error_messages[WINDOW_NOT_CREATED]); return WINDOW_ERROR_WINDOW_NOT_CREATED; } - glfwSetKeyCallback(gpWindowHandle, keyCallback); + glfwSetKeyCallback(g_window_handle, key_callback); return WINDOW_ERROR_NO_ERROR; } -uint16_t windowLoop() +uint16_t window_loop() { - while (!glfwWindowShouldClose(gpWindowHandle)) { + while (!glfwWindowShouldClose(g_window_handle)) { glfwPollEvents(); - rse::graphics::vulkanbase::drawFrame(); + draw_frame(); } return WINDOW_ERROR_NO_ERROR; } -void windowTerminate() +void window_terminate() { - glfwDestroyWindow(gpWindowHandle); + glfwDestroyWindow(g_window_handle); glfwTerminate(); } -GLFWwindow* getWindowHandle() +GLFWwindow* get_window_handle() { - return gpWindowHandle; -} - -} /* namespace rse::graphics::window */ \ No newline at end of file + return g_window_handle; +} \ No newline at end of file diff --git a/graphics/src/window.hpp b/graphics/src/window.h similarity index 74% rename from graphics/src/window.hpp rename to graphics/src/window.h index f595a411..bd69de87 100644 --- a/graphics/src/window.hpp +++ b/graphics/src/window.h @@ -9,17 +9,14 @@ * */ -#ifndef RSE_WINDOW_HPP -#define RSE_WINDOW_HPP +#ifndef RSE_WINDOW_H +#define RSE_WINDOW_H #include /* Vulkan header MUST be included before glfw */ #include -namespace rse::graphics::window -{ - /** * @brief Initialize window * @@ -28,7 +25,7 @@ namespace rse::graphics::window * WINDOW_WINDOW_NOT_CREATED * */ -uint16_t windowInit(); +uint16_t window_init(); /** * @brief Main window loop @@ -36,16 +33,14 @@ uint16_t windowInit(); * @param vulkan_state * @return uint16_t WINDOW_SUCCESS or error code */ -uint16_t windowLoop(); +uint16_t window_loop(); /** * @brief Closes window and terminates GLFW * */ -void windowTerminate(); +void window_terminate(); -GLFWwindow* getWindowHandle(); +GLFWwindow* get_window_handle(); -} /* namespace rse::graphics::window */ - -#endif \ No newline at end of file +#endif /* RSE_WINDOW_H */ \ No newline at end of file diff --git a/red_scarf_engine/CMakeLists.txt b/red_scarf_engine/CMakeLists.txt index cfc6f2ad..60c2f691 100644 --- a/red_scarf_engine/CMakeLists.txt +++ b/red_scarf_engine/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable(${PROJECT_NAME} src/main.cpp) +add_executable(${PROJECT_NAME} src/main.c) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/red_scarf_engine/src/main.c b/red_scarf_engine/src/main.c new file mode 100644 index 00000000..2cc6c999 --- /dev/null +++ b/red_scarf_engine/src/main.c @@ -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; +} diff --git a/red_scarf_engine/src/main.cpp b/red_scarf_engine/src/main.cpp deleted file mode 100644 index d63eaa5a..00000000 --- a/red_scarf_engine/src/main.cpp +++ /dev/null @@ -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; -} diff --git a/utilities/CMakeLists.txt b/utilities/CMakeLists.txt index 1875e1b2..61d48fe4 100644 --- a/utilities/CMakeLists.txt +++ b/utilities/CMakeLists.txt @@ -5,6 +5,6 @@ target_include_directories(rse_utilities PRIVATE ) target_sources(rse_utilities PRIVATE - src/logger.cpp - src/file_utils.cpp + src/file_utils.c + src/logger.c ) diff --git a/utilities/file_utils.h b/utilities/file_utils.h new file mode 100644 index 00000000..38d971b4 --- /dev/null +++ b/utilities/file_utils.h @@ -0,0 +1,17 @@ +#ifndef RSE_FILE_UTILS_H +#define RSE_FILE_UTILS_H + +#include + +/** + * @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 */ + diff --git a/utilities/file_utils.hpp b/utilities/file_utils.hpp deleted file mode 100644 index c5df0112..00000000 --- a/utilities/file_utils.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef RSE_FILE_UTILS_HPP -#define RSE_FILE_UTILS_HPP - -#include -#include - -namespace rse::utils::file -{ - -std::vector read_file(const std::string& fileName); - -} - -#endif - diff --git a/utilities/localization.h b/utilities/localization.h index eee731f7..742dca4c 100644 --- a/utilities/localization.h +++ b/utilities/localization.h @@ -9,8 +9,8 @@ * */ -#ifndef LOCALIZATION_H -#define LOCALIZATION_H +#ifndef RSE_LOCALIZATION_H +#define RSE_LOCALIZATION_H #define SELECTED_LANGUAGE EN_US #define MAX_MESSAGE_LENGHT 128 @@ -19,10 +19,10 @@ * @brief Available languages * */ -enum Languages_t +enum languages_t { EN_US = 0, LAST_LANGUAGE }; -#endif /* LOCALIZATION_H */ \ No newline at end of file +#endif /* RSE_LOCALIZATION_H */ \ No newline at end of file diff --git a/utilities/logger.h b/utilities/logger.h new file mode 100644 index 00000000..7aa0220a --- /dev/null +++ b/utilities/logger.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 + + +#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 */ \ No newline at end of file diff --git a/utilities/logger.hpp b/utilities/logger.hpp deleted file mode 100644 index 4ff4257f..00000000 --- a/utilities/logger.hpp +++ /dev/null @@ -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 - -#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 - Logger& operator<<(T&& message) - { - if (printMessage) - mMessageBuffer << message; - return *this; - } -}; - -} // namespace rse::log - -#endif \ No newline at end of file diff --git a/utilities/src/file_utils.c b/utilities/src/file_utils.c new file mode 100644 index 00000000..1fe346e0 --- /dev/null +++ b/utilities/src/file_utils.c @@ -0,0 +1,22 @@ +#include "file_utils.h" + +#include +#include + +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); +} diff --git a/utilities/src/file_utils.cpp b/utilities/src/file_utils.cpp deleted file mode 100644 index 189cc4b9..00000000 --- a/utilities/src/file_utils.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "file_utils.hpp" - -#include - -namespace rse::utils::file -{ - -/** - * @brief Reads file and puts its content into char buffer - * - * @param fileName Path to file - * @return std::vector Binary buffer data - */ -std::vector 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(file.tellg()); - std::vector buffer(fileSize); - - file.seekg(0); - file.read(buffer.data(), fileSize); - - file.close(); - - return buffer; -} - -} // namespace rse::utils::file \ No newline at end of file diff --git a/utilities/src/logger.c b/utilities/src/logger.c new file mode 100644 index 00000000..0bc1ffe4 --- /dev/null +++ b/utilities/src/logger.c @@ -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); +} \ No newline at end of file diff --git a/utilities/src/logger.cpp b/utilities/src/logger.cpp deleted file mode 100644 index 81db6369..00000000 --- a/utilities/src/logger.cpp +++ /dev/null @@ -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 - -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