diff --git a/CMakeLists.txt b/CMakeLists.txt index eeb953e3..fa468781 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,9 @@ set(CMAKE_CXX_STANDARD 20)# turn on the dynamic depends for ninja set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") -add_subdirectory(RedScarfEngine) -add_subdirectory(Utilities) -add_subdirectory(Graphics) \ No newline at end of file + +add_subdirectory(red_scarf_engine) +add_subdirectory(utilities) +add_subdirectory(graphics) \ No newline at end of file diff --git a/Graphics/src/vulkanBase.cpp b/Graphics/src/vulkanBase.cpp deleted file mode 100644 index 957be17e..00000000 --- a/Graphics/src/vulkanBase.cpp +++ /dev/null @@ -1,1916 +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 "vulkanBase.hpp" - -#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 -#pragma GCC diagnostic pop -#include - -#include "localeVulkan.hpp" -#include "logger.hpp" -#include "window.hpp" - -#define GLM_FORCE_RADIANS -#include -#include - -#include -#include -#include -#include -#include - -namespace rse::graphics::vulkanbase -{ - -using namespace locale::vulkan; - - - -/* Macro for checking status of function execution in initVulkan. Created to avoid writing boilerplate code */ -#define STATUS_CHECK(FUNC) \ - status = FUNC; \ - if (VULKAN_ERROR_NO_ERROR != status) { \ - return status; \ - } - -#define APPLICATION_NAME "RedScarfEngine PoC" -#define ENGINE_NAME "RedScarf Engine" -#define SWAP_BUFFER_COUNT 2U -#define IMAGE_FORMAT VK_FORMAT_B8G8R8A8_SRGB - -enum VULKAN_ERROR -{ - VULKAN_ERROR_NO_ERROR = 0x0U, - VULKAN_ERROR_LAYER_NOT_SUPPORTED, - VULKAN_ERROR_EXTENSION_NOT_SUPPORTED, - VULKAN_ERROR_INSTANCE_INIT_FAILED, - VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND, - VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND, - VULKAN_ERROR_QUEUE_NOT_SUPPORTED, - VULKAN_ERROR_DEVICE_CREATION_FAILED, - VULKAN_ERROR_ALLOCATOR_CREATION_FAILED, - VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED, - VULKAN_ERROR_BUFFER_CREATION_FAILED, - VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED, - VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED, - VULKAN_ERROR_SURFACE_CREATION_FAILED, - VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED, - VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED, - VULKAN_ERROR_SHADER_FILE_OPEN_FAILED, - VULKAN_ERROR_SHADER_CREATION_FAILED, - VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED, - VULKAN_ERROR_RENDERPASS_CREATION_FAILED, - VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED, - VULKAN_ERROR_DESCRIPTOR_SETS_ALLOCATION_FAILED, - VULKAN_ERROR_DESCRIPTORSETLAYOUT_CREATION_FAILED, - VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED, - VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED, - VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED, - VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED, -}; - -enum class ShaderStage -{ - VERTEX, - FRAGMENTS -}; - -/** - * @brief Represents single vertex on the screen - * - */ -struct Vertex -{ - glm::vec2 pos; - glm::vec3 color; - - static VkVertexInputBindingDescription getBindingDescription() { - VkVertexInputBindingDescription bindingDescription; - - bindingDescription.binding = 0; - bindingDescription.stride = sizeof(Vertex); - bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - return bindingDescription; - } - - static std::array getAttributeDescriptions() { - std::array attributeDescriptions{}; - - attributeDescriptions[0].binding = 0; - attributeDescriptions[0].location = 0; /* inPosition in shader.vert */ - attributeDescriptions[0].format = VK_FORMAT_R32G32_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; - } - -}; - -struct UniformBufferObject { - alignas(16) glm::mat4 model; - alignas(16) glm::mat4 view; - alignas(16) glm::mat4 proj; -}; - -/** - * @brief Wrapper for Vulan buffer. Holds allocation data; - * - */ -struct Buffer -{ - VkBuffer buffer; - VmaAllocation allocation; - VmaAllocationInfo allocationInfo; -}; - -// FIXME: Temporary array of vertices, for testing purposes -const std::vector vertices = { - {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}}, - {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}}, - {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}, - {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}} -}; - -// FIXME: Temporary array of vertices, for testing purposes -std::vector indices = { - 0, 1, 2, 2, 3, 0 -}; - -/* 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", -}; - -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", - } -}; - -uint32_t gQueueFamilyIndices[1] = {0U}; /* TODO: Change when more families are needed */ -uint32_t gSwapchainImagesCount = 0U; -uint32_t gCurrentFrame = 0U; - -VkInstance gVulkanInstance = VK_NULL_HANDLE; -VkSurfaceKHR gSurface = VK_NULL_HANDLE; -VkDebugUtilsMessengerEXT gDebugMessenger = VK_NULL_HANDLE; -VkPhysicalDevice gPhysicalDevice = VK_NULL_HANDLE; -VkDevice gDevice = VK_NULL_HANDLE; -VkQueue gGraphicsQueue; -VkQueue gPresentQueue; -VmaAllocator gAllocator = VK_NULL_HANDLE; -VkCommandPool gCommandPool = VK_NULL_HANDLE; -std::vector gpCommandBuffers; -VkSwapchainKHR gSwapchain = VK_NULL_HANDLE; -std::vector gSwapChainImages; -std::vector gSwapChainImageViews; -VkExtent2D gSwapchainExtent; -std::vector gShaderModules; -VkRenderPass gRenderPass = VK_NULL_HANDLE; -VkPipelineLayout gPipelineLayout = VK_NULL_HANDLE; -VkDescriptorSetLayout gDescriptorSetLayout = VK_NULL_HANDLE; -VkPipeline gGraphicsPipeline = VK_NULL_HANDLE; -VkDescriptorPool gDescriptorPool = {}; -std::vector gDescriptorSets = {}; -std::vector gSwapchainFramebuffers = {}; -std::vector gImageAvailableSemaphores = {}; -std::vector gRenderFinishedSemaphores = {}; -std::vector gInFlightFences = {}; - -/* GPU visible buffer */ -Buffer gVertexBuffer = {}; -/* CPU visible buffer. TODO: Do we need this global? */ -Buffer gVertexStagingBuffer = {}; -Buffer gIndexBuffer = {}; -Buffer gIndexStagingBuffer = {}; -std::vector gUniformBuffers = {}; - -#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 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); -static std::vector readFile(const std::string& fileName); -static std::optional createShaderModule(const std::vector& binaryData); -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); - - -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 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 createCommandPools(); -static uint8_t createVertexBuffer(); -static uint8_t createIndexBuffer(); -static uint8_t createUniformBuffers(); -static uint8_t createDescriptorPool(); -static uint8_t createDescriptorSets(); -static uint8_t allocateCommandBuffers(); -static uint8_t createSyncObjects(); - -static void updateUniformBuffer(uint32_t currentImage); -static void recreateSwapchain(); -static void cleanupSwapchain(); - - -static uint8_t recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex); - -#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 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(gAllocator, - &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) -{ - /* Vulkan buffers can only be copied using command buffers */ - VkBufferCopy copyRegion; - VkSubmitInfo submitInfo; - VkCommandBuffer copyCommandBuffer; - VkCommandBufferBeginInfo beginInfo; - VkCommandBufferAllocateInfo allocInfo; - - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.pNext = nullptr; - allocInfo.commandPool = gCommandPool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = 1; - - - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.pNext = nullptr; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - beginInfo.pInheritanceInfo = nullptr; - - copyRegion.srcOffset = 0; - copyRegion.dstOffset = 0; - copyRegion.size = size; - - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.pNext = nullptr; - submitInfo.waitSemaphoreCount = 0; - submitInfo.pWaitSemaphores = nullptr; - submitInfo.pWaitDstStageMask = nullptr; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = ©CommandBuffer; - submitInfo.signalSemaphoreCount = 0; - submitInfo.pSignalSemaphores = nullptr; - - // FIXME: At result checks - vkAllocateCommandBuffers(gDevice, &allocInfo, ©CommandBuffer); - vkBeginCommandBuffer(copyCommandBuffer, &beginInfo); - vkCmdCopyBuffer(copyCommandBuffer, src, dst, 1, ©Region); - vkEndCommandBuffer(copyCommandBuffer); - vkQueueSubmit(gGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); - /* TODO: Use fences to wait instead of idle */ - vkQueueWaitIdle(gGraphicsQueue); - vkFreeCommandBuffers(gDevice, gCommandPool, 1, ©CommandBuffer); - - return VULKAN_ERROR_NO_ERROR; -} - -/** - * @brief Reads file and puts its content into char buffer - * - * @param fileName Path to file - * @return std::vector Binary buffer data - */ -std::vector readFile(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; -} - -/** - * @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(gDevice, &createInfo, nullptr, &shaderModule)) - { - LOGF << vulkanErrorMesssages[VULKAN_SHADER_UNABLE_TO_CREATE_SHADER]; - return std::nullopt; - } - - return shaderModule; -} - -/** - * @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 = readFile(gShaderPaths.at(ShaderStage::VERTEX)); - auto fragmentsShaderCode = readFile(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 bindingDescription = Vertex::getBindingDescription(); - auto attributeDescriptions = Vertex::getAttributeDescriptions(); - - - vertexInputInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo->pNext = nullptr; - vertexInputInfo->flags = 0U; - vertexInputInfo->vertexBindingDescriptionCount = 1U; - vertexInputInfo->pVertexBindingDescriptions = new VkVertexInputBindingDescription(bindingDescription); - vertexInputInfo->vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); - - VkVertexInputAttributeDescription* tmp = new VkVertexInputAttributeDescription[attributeDescriptions.size()]; - std::copy(attributeDescriptions.begin(), attributeDescriptions.end(), tmp); - vertexInputInfo->pVertexAttributeDescriptions = tmp; -} - -/** - * @brief Free allocated vertex input info - * - * @param vertexInputInfo structure to be freed - */ -void freeVertexInputInfo(VkPipelineVertexInputStateCreateInfo* vertexInputInfo) -{ - delete vertexInputInfo->pVertexBindingDescriptions; - delete[] vertexInputInfo->pVertexAttributeDescriptions; -} - -/** - * @brief Fill input assembly info - * - * @param inputAssembly structure to be filled - */ -void fillInputAssemblyInfo(VkPipelineInputAssemblyStateCreateInfo* inputAssembly) -{ - inputAssembly->sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly->pNext = nullptr; - inputAssembly->flags = 0U; - inputAssembly->topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; //TODO: Change to _STRIP - inputAssembly->primitiveRestartEnable = VK_FALSE; /* If I want to break lines during _STRIP, - * I must set this to VK_TRUE */ - } - -/** - * @brief Fill viewport state info - * - * @param viewportState structure to be filled - */ -void fillViewportStateInfo(VkPipelineViewportStateCreateInfo* viewportState) -{ - VkViewport viewport{}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = static_cast(gSwapchainExtent.width); - viewport.height = static_cast(gSwapchainExtent.height); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - /* Setting Scissors to fill entire window*/ - VkRect2D scissor{}; - scissor.offset = {0, 0}; - scissor.extent = gSwapchainExtent; - - 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; -} - -/** - * @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, &gSurface)) { - 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) { - gPhysicalDevice = pPhysicalDevices[physicalDeviceIdx]; - } - } - } - - if (VK_NULL_HANDLE == gPhysicalDevice) { - /* 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(gPhysicalDevice, &queueFamiliesPropertyCount, nullptr); - pQueueFamilyProperties.resize(queueFamiliesPropertyCount); - vkGetPhysicalDeviceQueueFamilyProperties(gPhysicalDevice, &queueFamiliesPropertyCount, pQueueFamilyProperties.data()); - - /* Get queue families that support graphics operations AND have most - * queues. */ - { - size_t numberOfQueues = 0; - for (size_t familyIdx = 0; familyIdx < queueFamiliesPropertyCount; ++familyIdx) { - if (pQueueFamilyProperties[familyIdx].queueFlags & VK_QUEUE_GRAPHICS_BIT) { - if (pQueueFamilyProperties[familyIdx].queueCount > numberOfQueues) { - graphicsFamilyIdx = familyIdx; - numberOfQueues = pQueueFamilyProperties[familyIdx].queueCount; - } - } - /* Check for family with surface support */ - VkBool32 presentSupport = false; - vkGetPhysicalDeviceSurfaceSupportKHR(gPhysicalDevice, familyIdx, gSurface, &presentSupport); - if (presentSupport) - { - presentationFamiliyIdx = familyIdx; - } - - if (graphicsFamilyIdx > 0 && presentationFamiliyIdx > 0) - { - break; - } - } - } - - /* This is not magic number ;P. We want to support exact number of required queues flags */ - if (0 > graphicsFamilyIdx) { - LOGF << vulkanErrorMesssages[VULKAN_QUEUE_NOT_SUPPORTED]; - return VULKAN_ERROR_QUEUE_NOT_SUPPORTED; - } - - gQueueFamilyIndices[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(gPhysicalDevice, &deviceCreateInfo, nullptr, &gDevice)) { - LOGF << vulkanErrorMesssages[VULKAN_FAILED_TO_CREATE_DEVICE]; - return VULKAN_ERROR_DEVICE_CREATION_FAILED; - } - - vkGetDeviceQueue(gDevice, graphicsFamilyIdx, 0, &gGraphicsQueue); - vkGetDeviceQueue(gDevice, 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 = gPhysicalDevice; - allocatorCreateInfo.device = gDevice; - allocatorCreateInfo.instance = gVulkanInstance; - allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; - - if (VK_SUCCESS != vmaCreateAllocator(&allocatorCreateInfo, &gAllocator)) { - LOGF << vulkanErrorMesssages[VULKAN_ERROR_ALLOCATOR_CREATION_FAILED]; - return VULKAN_ERROR_ALLOCATOR_CREATION_FAILED; - } - - return VULKAN_ERROR_NO_ERROR; -} - -/** - * @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(gPhysicalDevice, gSurface, &physicalDeviceSurfaceCapabilities); - - /* Store extent in global variable, for later use */ - gSwapchainExtent = physicalDeviceSurfaceCapabilities.currentExtent; - - /* Check if device supports surface for presentation */ - vkGetPhysicalDeviceSurfaceSupportKHR(gPhysicalDevice, gQueueFamilyIndices[0], gSurface, &surfaceSupported); - - - - createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - createInfo.pNext = nullptr; - createInfo.flags = 0U; - createInfo.surface = gSurface; - 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 = gSwapchainExtent; - 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 = gQueueFamilyIndices; - 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(gDevice, &createInfo, nullptr, &gSwapchain)) { - LOGF << vulkanErrorMesssages[VULKAN_SWAPCHAIN_CREATION_FAILED]; - return VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED; - } - - /* Obtain swapchain images */ - vkGetSwapchainImagesKHR(gDevice, gSwapchain, &gSwapchainImagesCount, nullptr); - gSwapChainImages.resize(gSwapchainImagesCount); - vkGetSwapchainImagesKHR(gDevice, gSwapchain, &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() -{ - gSwapChainImageViews.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(gDevice, &createInfo, nullptr, &gSwapChainImageViews[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(gDevice, &renderPassInfo, nullptr, &gRenderPass)) { - 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(gDevice, &layoutInfo, nullptr, &gDescriptorSetLayout) != 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 = &gDescriptorSetLayout; - createInfo.pushConstantRangeCount = 0U; - createInfo.pPushConstantRanges = nullptr; - - - if(VK_SUCCESS != vkCreatePipelineLayout(gDevice, &createInfo, nullptr, &gPipelineLayout)) - { - 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 = gPipelineLayout; - pipelineInfo.renderPass = gRenderPass; - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - pipelineInfo.basePipelineIndex = -1; - - if (VK_SUCCESS != vkCreateGraphicsPipelines(gDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &gGraphicsPipeline)) - { - 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() -{ - gSwapchainFramebuffers.resize(gSwapChainImageViews.size()); - - for (size_t i = 0; i < gSwapChainImageViews.size(); i++) { - VkImageView attachments[] = { - gSwapChainImageViews[i] - }; - - VkFramebufferCreateInfo framebufferInfo{}; - framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.renderPass = gRenderPass; - framebufferInfo.attachmentCount = 1; - framebufferInfo.pAttachments = attachments; - framebufferInfo.width = gSwapchainExtent.width; - framebufferInfo.height = gSwapchainExtent.height; - framebufferInfo.layers = 1; - - if (vkCreateFramebuffer(gDevice, &framebufferInfo, nullptr, &gSwapchainFramebuffers[i]) != VK_SUCCESS) { - LOGF << vulkanErrorMesssages[VULKAN_FRAMEBUFFERS_CREATION_FAILED]; - return VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED; - } - } - - 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 = gQueueFamilyIndices[0]; /* TODO: Change when more families are needed */ - - if (VK_SUCCESS != vkCreateCommandPool(gDevice, &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() -{ - void* mappedData; - VkDeviceSize bufferSize; - - bufferSize = sizeof(vertices[0]) * vertices.size(); - - /* Creating staging buffer*/ - if (VK_SUCCESS != createBuffer(bufferSize, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VMA_MEMORY_USAGE_AUTO_PREFER_HOST, - VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, - gVertexStagingBuffer)) { - LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; - return VULKAN_ERROR_BUFFER_CREATION_FAILED; - } - - /* Create Vertex Buffer*/ - if (VK_SUCCESS != createBuffer(bufferSize, - VK_BUFFER_USAGE_TRANSFER_DST_BIT - | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, - 0, /* Will not be mapped with vmaMapMemory */ - gVertexBuffer)) { - LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; - return VULKAN_ERROR_BUFFER_CREATION_FAILED; - } - - /* Fill staging buffer */ - if (VK_SUCCESS != vmaMapMemory(gAllocator, gVertexStagingBuffer.allocation, &mappedData)) - { - LOGF << vulkanErrorMesssages[VULKAN_VERTEX_BUFFER_MAPPING_FAILED]; - return VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED; - } - memcpy(mappedData, vertices.data(), sizeof(Vertex) * vertices.size()); - vmaUnmapMemory(gAllocator, gVertexStagingBuffer.allocation); - - copyBuffer(gVertexStagingBuffer.buffer, gVertexBuffer.buffer, bufferSize); - - return VULKAN_ERROR_NO_ERROR; -} - -/** - * @brief Create an index buffer, connected with vertex buffer - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - * VULKAN_ERROR_BUFFER_CREATION_FAILED - */ -uint8_t createIndexBuffer() -{ - void* mappedData; - VkDeviceSize bufferSize; - - bufferSize = sizeof(indices[0]) * indices.size(); - - /* Creating staging buffer*/ - if (VK_SUCCESS != createBuffer(bufferSize, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VMA_MEMORY_USAGE_AUTO_PREFER_HOST, - VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, - gIndexStagingBuffer)) { - LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; - return VULKAN_ERROR_BUFFER_CREATION_FAILED; - } - - /* Create Vertex Buffer*/ - if (VK_SUCCESS != createBuffer(bufferSize, - VK_BUFFER_USAGE_TRANSFER_DST_BIT - | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, - VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, - 0, /* Will not be mapped with vmaMapMemory */ - gIndexBuffer)) { - LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; - return VULKAN_ERROR_BUFFER_CREATION_FAILED; - } - - /* Fill staging buffer */ - if (VK_SUCCESS != vmaMapMemory(gAllocator, gIndexStagingBuffer.allocation, &mappedData)) - { - LOGF << vulkanErrorMesssages[VULKAN_VERTEX_BUFFER_MAPPING_FAILED]; - return VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED; - } - memcpy(mappedData, indices.data(), static_cast(bufferSize)); - vmaUnmapMemory(gAllocator, gIndexStagingBuffer.allocation); - - copyBuffer(gIndexStagingBuffer.buffer, gIndexBuffer.buffer, bufferSize); - - return VULKAN_ERROR_NO_ERROR; -} - -/** - * @brief Create a Uniform Buffers - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - */ -uint8_t createUniformBuffers() -{ - VkDeviceSize bufferSize = sizeof(UniformBufferObject); - - gUniformBuffers.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, - gUniformBuffers[i]); - } - - return VULKAN_ERROR_NO_ERROR; -} - -/** - * @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(gDevice, &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, gDescriptorSetLayout); - 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(); - - gDescriptorSets.resize(SWAP_BUFFER_COUNT); - if (vkAllocateDescriptorSets(gDevice, &allocInfo, gDescriptorSets.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 = gUniformBuffers[i].buffer; - bufferInfo.offset = 0; - bufferInfo.range = sizeof(UniformBufferObject); - - VkWriteDescriptorSet descriptorWrite{}; - descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite.dstSet = gDescriptorSets[i]; - descriptorWrite.dstBinding = 0; - descriptorWrite.dstArrayElement = 0; - descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorWrite.descriptorCount = 1; - descriptorWrite.pBufferInfo = &bufferInfo; - - vkUpdateDescriptorSets(gDevice, 1, &descriptorWrite, 0, nullptr); - } - - 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 */ - gpCommandBuffers.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(gDevice, &allocateInfo, gpCommandBuffers.data())) { - LOGF << vulkanErrorMesssages[VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED]; - return VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_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(gDevice, &semaphoreInfo, nullptr, &gImageAvailableSemaphores[i]) || - VK_SUCCESS != vkCreateSemaphore(gDevice, &semaphoreInfo, nullptr, &gRenderFinishedSemaphores[i]) || - VK_SUCCESS != vkCreateFence(gDevice, &fenceInfo, nullptr, &gInFlightFences[i])) { - LOGF << vulkanErrorMesssages[VULKAN_SYNCOBJCTS_CREATION_FAILED]; - return VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED; - } - } - - return VULKAN_ERROR_NO_ERROR; -} - -/** - * @brief Update uniform buffers - * - * @param currentImage Current image index - */ -void updateUniformBuffer(uint32_t currentImage) -{ - // static auto startTime = std::chrono::high_resolution_clock::now(); - - // auto currentTime = std::chrono::high_resolution_clock::now(); - // float time = std::chrono::duration(currentTime - startTime).count(); - - UniformBufferObject ubo{}; - ubo.model = glm::mat4(1.0f); - // ubo.model = glm::rotate(glm::mat4(1.0f), glm::radians(0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); - ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); - ubo.proj = glm::perspective(glm::radians(45.0f), gSwapchainExtent.width / (float) gSwapchainExtent.height, 0.1f, 10.0f); - ubo.proj[1][1] *= -1; /* GLM was developed for OpenGL, where Y coordinate of the clip is inverted */ - memcpy(gUniformBuffers[currentImage].allocationInfo.pMappedData, &ubo, sizeof(ubo)); -} - -/** - * @brief Recreate swapchain - * - */ -void recreateSwapchain() -{ - cleanupSwapchain(); - createSwapchain(); - createSwapchainImageViews(); - createFrambuffers(); -} - -/** - * @brief Cleanup swapchain - * - */ -void cleanupSwapchain() -{ - vkDeviceWaitIdle(gDevice); - for (size_t i = 0; i < gSwapchainFramebuffers.size(); i++) { - vkDestroyFramebuffer(gDevice, gSwapchainFramebuffers[i], nullptr); - } - - for (size_t i = 0; i < gSwapChainImageViews.size(); i++) { - vkDestroyImageView(gDevice, gSwapChainImageViews[i], nullptr); - } - - vkDestroySwapchainKHR(gDevice, gSwapchain, nullptr); -} - -/** - * @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(VkCommandBuffer commandBuffer, uint32_t imageIndex) -{ - VkCommandBufferBeginInfo beginInfo{}; - VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; - VkRenderPassBeginInfo renderPassInfo{}; - VkViewport viewport{}; - VkRect2D scissor{}; - VkBuffer vertexBuffers[] = {gVertexBuffer.buffer}; - VkDeviceSize offsets[] = {0}; - - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = 0; // Optional - beginInfo.pInheritanceInfo = nullptr; // Optional - - if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { - throw std::runtime_error("failed to begin recording command buffer!"); - } - - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = gRenderPass; - renderPassInfo.framebuffer = gSwapchainFramebuffers[imageIndex]; - renderPassInfo.renderArea.offset = {0, 0}; - renderPassInfo.renderArea.extent = gSwapchainExtent; - renderPassInfo.clearValueCount = 1; - renderPassInfo.pClearValues = &clearColor; - - vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, gGraphicsPipeline); - - - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = static_cast(gSwapchainExtent.width); - viewport.height = static_cast(gSwapchainExtent.height); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - vkCmdSetViewport(commandBuffer, 0, 1, &viewport); - - scissor.offset = {0, 0}; - scissor.extent = gSwapchainExtent; - vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - - vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); - vkCmdBindIndexBuffer(commandBuffer, gIndexBuffer.buffer, 0, VK_INDEX_TYPE_UINT16); - vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, gPipelineLayout, 0, 1, &gDescriptorSets[gCurrentFrame], 0, nullptr); - - vkCmdDrawIndexed(commandBuffer, static_cast(indices.size()), 1, 0, 0, 0); - - vkCmdEndRenderPass(commandBuffer); - - if (VK_SUCCESS != vkEndCommandBuffer(commandBuffer)) { - LOGF << vulkanErrorMesssages[VULKAN_RECORD_COMMAND_BUFFER_FAILED]; - return VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED; - } - - return VULKAN_ERROR_NO_ERROR; -} - -uint8_t 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(createSwapchain()); - STATUS_CHECK(createSwapchainImageViews()); - STATUS_CHECK(createRenderPass()); - STATUS_CHECK(createDescriptorSetLayout()); - STATUS_CHECK(createPipelineLayout()); - STATUS_CHECK(createGraphicsPipeline()); - STATUS_CHECK(createFrambuffers()); - STATUS_CHECK(createCommandPools()); - STATUS_CHECK(createVertexBuffer()); - STATUS_CHECK(createIndexBuffer()); - STATUS_CHECK(createUniformBuffers()); - STATUS_CHECK(createDescriptorPool()); - STATUS_CHECK(createDescriptorSets()); - STATUS_CHECK(allocateCommandBuffers()); - STATUS_CHECK(createSyncObjects()); - return status; -} - -void drawFrame() -{ - vkWaitForFences(gDevice, 1, &gInFlightFences[gCurrentFrame], VK_TRUE, UINT64_MAX); - - - uint32_t imageIndex; - VkResult result = vkAcquireNextImageKHR(gDevice, gSwapchain, UINT64_MAX, gImageAvailableSemaphores[gCurrentFrame], 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(gCurrentFrame); - - /* Only reset the fence if we are submitting work */ - vkResetFences(gDevice, 1, &gInFlightFences[gCurrentFrame]); - - vkResetCommandBuffer(gpCommandBuffers[gCurrentFrame], /*VkCommandBufferResetFlagBits*/ 0); - recordCommandBuffer(gpCommandBuffers[gCurrentFrame], imageIndex); - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - - VkSemaphore waitSemaphores[] = {gImageAvailableSemaphores[gCurrentFrame]}; - VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = waitSemaphores; - submitInfo.pWaitDstStageMask = waitStages; - - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &gpCommandBuffers[gCurrentFrame]; - - VkSemaphore signalSemaphores[] = {gRenderFinishedSemaphores[gCurrentFrame]}; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = signalSemaphores; - - if (vkQueueSubmit(gGraphicsQueue, 1, &submitInfo, gInFlightFences[gCurrentFrame]) != 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[] = {gSwapchain}; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = swapChains; - - presentInfo.pImageIndices = &imageIndex; - - vkQueuePresentKHR(gPresentQueue, &presentInfo); - - gCurrentFrame = (gCurrentFrame + 1) % SWAP_BUFFER_COUNT; -} - -void deinitVulkan() -{ - cleanupSwapchain(); - for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) { - vmaDestroyBuffer(gAllocator, gUniformBuffers[i].buffer, gUniformBuffers[i].allocation); - } - - vkDestroyDescriptorPool(gDevice, gDescriptorPool, nullptr); - vkDestroyDescriptorSetLayout(gDevice, gDescriptorSetLayout, nullptr); - vmaDestroyBuffer(gAllocator, gVertexBuffer.buffer, gVertexBuffer.allocation); - vmaDestroyBuffer(gAllocator, gVertexStagingBuffer.buffer, gVertexStagingBuffer.allocation); - vmaDestroyBuffer(gAllocator, gIndexBuffer.buffer, gIndexBuffer.allocation); - vmaDestroyBuffer(gAllocator, gIndexStagingBuffer.buffer, gIndexStagingBuffer.allocation); - for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) { - vkDestroySemaphore(gDevice, gImageAvailableSemaphores[i], nullptr); - vkDestroySemaphore(gDevice, gRenderFinishedSemaphores[i], nullptr); - vkDestroyFence(gDevice, gInFlightFences[i], nullptr); - } - - vkDestroyPipeline(gDevice, gGraphicsPipeline, nullptr); - vkDestroyPipelineLayout(gDevice, gPipelineLayout, nullptr); - vkDestroyRenderPass(gDevice, gRenderPass, nullptr); - - for (size_t i = 0; i < gShaderModules.size(); i++) { - vkDestroyShaderModule(gDevice, gShaderModules[i], nullptr); - } - - vkDestroyCommandPool(gDevice, gCommandPool, nullptr); - vmaDestroyAllocator(gAllocator); - vkDestroyDevice(gDevice, nullptr); -#ifndef NDEBUG - DestroyDebugUtilsMessengerEXT(gVulkanInstance, gDebugMessenger, nullptr); -#endif - vkDestroySurfaceKHR(gVulkanInstance, gSurface, nullptr); - vkDestroyInstance(gVulkanInstance, nullptr); -} - -} // namespace rse::graphics::vulkanbase \ No newline at end of file diff --git a/RedScarfEngine/src/main.cpp b/RedScarfEngine/src/main.cpp deleted file mode 100644 index a7d65aec..00000000 --- a/RedScarfEngine/src/main.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "logger.hpp" -#include "window.hpp" -#include "vulkanBase.hpp" - -int main(int argc, char** argv) -{ - (void)argc; - (void)argv; - - rse::graphics::window::windowInit(); - - if (0 != rse::graphics::vulkanbase::initVulkan()) - return -1; - - rse::graphics::window::windowLoop(); - - rse::graphics::vulkanbase::deinitVulkan(); - - rse::graphics::window::windowTerminate(); - - return 0; -} diff --git a/Utilities/CMakeLists.txt b/Utilities/CMakeLists.txt deleted file mode 100644 index 6e3ec4ee..00000000 --- a/Utilities/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -add_library(rse_utilities) - -target_include_directories(rse_utilities PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/include - ) - -target_sources(rse_utilities PRIVATE - src/logger.cpp) diff --git a/Graphics/CMakeLists.txt b/graphics/CMakeLists.txt similarity index 50% rename from Graphics/CMakeLists.txt rename to graphics/CMakeLists.txt index 3109ec6c..08adcb8f 100644 --- a/Graphics/CMakeLists.txt +++ b/graphics/CMakeLists.txt @@ -1,5 +1,3 @@ -add_library(rse_graphics) - find_package(Vulkan REQUIRED) find_package(glfw3 REQUIRED) find_package(glm REQUIRED) @@ -7,45 +5,57 @@ find_package(glm REQUIRED) find_package(Vulkan COMPONENTS glslc) find_program(glslc_executable NAMES glslc HINTS Vulkan::glslc) +cmake_policy(SET CMP0116 NEW) function(compile_shader) cmake_parse_arguments(PARSE_ARGV 1 arg "" "ENV;FORMAT" "SOURCES") foreach(source ${arg_SOURCES}) + message(${source}.${arg_FORMAT}) add_custom_command( - OUTPUT ${source}.${arg_FORMAT} - DEPENDS ${source} - DEPFILE ${source}.d - COMMAND + OUTPUT ${source}.${arg_FORMAT} + DEPENDS ${source} + DEPFILE ${source}.d + COMMAND ${glslc_executable} -o ${source}.${arg_FORMAT} - ${CMAKE_CURRENT_SOURCE_DIR}/${source} - ) + ${CMAKE_CURRENT_SOURCE_DIR}/${source} + ) endforeach() endfunction() + +compile_shader(rse_graphics +ENV opengl +FORMAT num +SOURCES +shaders/shader.vert +shaders/shader.frag +) + +add_library(rse_graphics SHARED shaders/shader.vert.num shaders/shader.frag.num) target_include_directories(rse_graphics PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_SOURCE_DIR}/third_party/vma/include - ${CMAKE_SOURCE_DIR}/Utilities/include +${PROJECT_SOURCE_DIR} +${CMAKE_CURRENT_SOURCE_DIR} +${CMAKE_CURRENT_SOURCE_DIR} +${CMAKE_SOURCE_DIR} ${Vulkan_INCLUDE_DIR} ${glfw3_INCLUDE_DIR} - ${glm_INCLUDE_DIR}) + ${glm_INCLUDE_DIR} +) target_sources(rse_graphics PRIVATE src/window.cpp - src/vulkanBase.cpp) + src/vulkan_base.cpp + src/vulkan_pipeline.cpp + src/vulkan_buffers.cpp + src/vulkan_descriptors.cpp + src/rse_graphics.cpp +) target_link_libraries(rse_graphics PRIVATE rse_utilities ${Vulkan_LIBRARIES} glfw - glm) - -compile_shader(rse_graphics - ENV opengl - FORMAT num - SOURCES - shaders/shader.vert - shaders/shader.frag + glm ) diff --git a/graphics/rse_graphics.hpp b/graphics/rse_graphics.hpp new file mode 100644 index 00000000..621749bb --- /dev/null +++ b/graphics/rse_graphics.hpp @@ -0,0 +1,31 @@ +#ifndef RSE_GRAPHICS_HPP +#define RSE_GRAPHICS_HPP + +#include +#include "src/vulkan_commons.hpp" + +namespace rse::graphics +{ +class RseGraphics { + public: + RseGraphics(); + RseGraphics& operator=(const RseGraphics&) = delete; + RseGraphics(const RseGraphics&) = delete; + + /** + * @brief Initialize graphics module, runs main loop and deinits everything, when interrupted + * + * @return uint8_t 0 on success. + */ + uint8_t graphics_run(void); + + private: + /* Hold vulkan state. It is modified by function initializing and communicating with Vulkan API */ + vulkanbase::VulkanState vulkan_state; +}; + + +} // namespace rse::graphics + + +#endif \ No newline at end of file diff --git a/Graphics/shaders/shader.frag b/graphics/shaders/shader.frag similarity index 100% rename from Graphics/shaders/shader.frag rename to graphics/shaders/shader.frag diff --git a/Graphics/shaders/shader.vert b/graphics/shaders/shader.vert similarity index 100% rename from Graphics/shaders/shader.vert rename to graphics/shaders/shader.vert diff --git a/Graphics/src/localeVulkan.hpp b/graphics/src/locale_vulkan.hpp similarity index 92% rename from Graphics/src/localeVulkan.hpp rename to graphics/src/locale_vulkan.hpp index cd43f16a..a45e6ea6 100644 --- a/Graphics/src/localeVulkan.hpp +++ b/graphics/src/locale_vulkan.hpp @@ -14,8 +14,7 @@ #include #include -#include "localization.hpp" - +#include "utilities/localization.hpp" namespace rse::locale::vulkan { @@ -48,12 +47,13 @@ enum Errors VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED, VULKAN_GRAPHICSPIPELINE_CREATION_FAILED, VULKAN_FRAMEBUFFERS_CREATION_FAILED, + VULKAN_RECORD_COMMAND_BEGIN_FAILED, VULKAN_RECORD_COMMAND_BUFFER_FAILED, VULKAN_SYNCOBJCTS_CREATION_FAILED, LAST_MESSAGE }; -std::array, LAST_LANGUAGE> vulkanErrorMesssages_nolocale = { +inline std::array, rse::utils::locale::LAST_LANGUAGE> vulkanErrorMesssages_nolocale = { {/* EN_US */ /* VULKAN_LAYER_NOT_SUPPORTED */ "Selected layer is not supported: ", @@ -103,6 +103,8 @@ std::array, LAST_LANGUAGE> vulkanErrorMess "Failed to create graphics pipeline", /* VULKAN_FRAMEBUFFERS_CREATION_FAILED */ "Failed to create framebuffer", + /* VULKAN_RECORD_COMMAND_BEGIN_FAILED */ + "Failed to start recording command buffer", /* VULKAN_RECORD_COMMAND_BUFFER_FAILED */ "Failed to record command buffer. Validate your commands.", /* VULKAN_SYNCOBJCTS_CREATION_FAILED */ diff --git a/Graphics/src/localeWindow.hpp b/graphics/src/locale_window.hpp similarity index 81% rename from Graphics/src/localeWindow.hpp rename to graphics/src/locale_window.hpp index 030b1d8e..5f3c0dff 100644 --- a/Graphics/src/localeWindow.hpp +++ b/graphics/src/locale_window.hpp @@ -13,7 +13,7 @@ #include -#include "localization.hpp" +#include "utilities/localization.hpp" namespace rse::locale::window @@ -29,7 +29,7 @@ enum Errors LAST_MESSAGE }; -std::array, LAST_MESSAGE> windowErrorMesssages_nolocale = { +std::array, LAST_MESSAGE> windowErrorMesssages_nolocale = { {/* EN_US */ /* WINDOW_GLFW_INIT_FAILED */ "GLFW init failed!" diff --git a/graphics/src/rse_graphics.cpp b/graphics/src/rse_graphics.cpp new file mode 100644 index 00000000..06a5fca9 --- /dev/null +++ b/graphics/src/rse_graphics.cpp @@ -0,0 +1,47 @@ +#include "rse_graphics.hpp" +#include "window.hpp" +#include "vulkan_base.hpp" + +namespace rse::graphics +{ + +RseGraphics::RseGraphics() +{ + vulkan_state = { + .graphicsQueue = VK_NULL_HANDLE, + .physicalDevice = VK_NULL_HANDLE, + .currentFrame = 0U, + .queueFamilyIndices = {0U}, /* TODO: Change when more families are needed */ + .uniformBuffers = {}, + .pCommandBuffers = {}, + .descriptorSets = {}, + .swapChainImageViews = {}, + .swapchainExtent = {}, + .renderPass = VK_NULL_HANDLE, + .descriptorSetLayout = VK_NULL_HANDLE, + .swapchainFramebuffers = {}, + .graphicsPipeline = VK_NULL_HANDLE, + .pipelineLayout = VK_NULL_HANDLE, + .swapchain = VK_NULL_HANDLE, + }; +} + +uint8_t RseGraphics::graphics_run(void) +{ + + window::windowInit(); + + if (0 != vulkanbase::initVulkan(vulkan_state)) + return -1; + + window::windowLoop(vulkan_state); + + vulkanbase::deinitVulkan(vulkan_state); + + window::windowTerminate(); + + return 0; +} + + +} // namespace rse::graphics diff --git a/graphics/src/vulkan_base.cpp b/graphics/src/vulkan_base.cpp new file mode 100644 index 00000000..8f21a227 --- /dev/null +++ b/graphics/src/vulkan_base.cpp @@ -0,0 +1,638 @@ +/** + * @file vulkanBase.cpp + * @author Piotr Krygier (everyonecancode@gmail.com) + * @brief Base configuration for Vulkan API + * @version 0.1 + * @date 2022-05-13 + * + * @copyright Copyright (c) 2022 + * + */ + +#include "vulkan_base.hpp" + +#include "vulkan_errors.hpp" +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wpedantic" +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#pragma GCC diagnostic ignored "-Wswitch" +#pragma GCC diagnostic ignored "-Wparentheses" +#define VMA_IMPLEMENTATION +#include "third_party/vma/vk_mem_alloc.h" +#pragma GCC diagnostic pop + +#include "locale_vulkan.hpp" +#include "utilities/logger.hpp" +#include "window.hpp" + +#include "vulkan_commons.hpp" +#include "vulkan_buffers.hpp" +#include "vulkan_descriptors.hpp" +#include "vulkan_pipeline.hpp" + +namespace rse::graphics::vulkanbase +{ + +using namespace locale::vulkan; + +#define APPLICATION_NAME "RedScarfEngine PoC" +#define ENGINE_NAME "RedScarf Engine" + + + +/* Define Validation Layers for debug build */ +#ifndef NDEBUG +std::vector 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(VulkanState& vulkan_state); +static uint8_t pickPhysicalDevice(VulkanState& vulkan_state); +static uint8_t createDevice(VulkanState& vulkan_state); +static uint8_t createMemoryAllocator(VulkanState& vulkan_state); + +static uint8_t createSyncObjects(VulkanState& vulkan_state); + + + +#ifndef NDEBUG +/** + * @brief Setup debugging system for Vulkan API + * + */ +void setupDebugMessenger() +{ + VkDebugUtilsMessengerCreateInfoEXT createInfo; + + createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + createInfo.pNext = nullptr; + createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + createInfo.pfnUserCallback = debugCallback; + createInfo.pUserData = nullptr; // Optional + createInfo.flags = 0U; + + if (CreateDebugUtilsMessengerEXT(gVulkanInstance, &createInfo, nullptr, &gDebugMessenger) != VK_SUCCESS) { + fprintf(stderr, "failed to set up debug messenger!"); + } +} + +/** + * @brief Calback for debug messenger + * + * @param messageSeverity Message severity + * @param messageType Type of a message + * @param pCallbackData Contains all the callback related data in the VkDebugUtilsMessengerCallbackDataEXT structure + * @param pUserData Data provided by the user + * @return VkBool32 Should always return VK_FALSE. The VK_TRUE value is reserved for use in layer development + */ +VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) +{ + (void)messageSeverity; + (void)messageType; + (void)pUserData; + + fprintf(stderr, "validation layer: %s\n", pCallbackData->pMessage); + + return VK_FALSE; +} + +/** + * @brief Proxy for vkCreateDebugUtilsMessengerEXT + * + * @param instance Vulkan instance object + * @param pCreateInfo VkDebugUtilsMessengerCreateInfoEXT structure + * @param pAllocator Memory allocator + * @param pDebugMessenger Debug messanger handle + * @return VkResult VK_SUCCESS on success + */ +VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugUtilsMessengerEXT* pDebugMessenger) +{ + PFN_vkCreateDebugUtilsMessengerEXT func = nullptr; + + func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); + if (func != nullptr) { + return func(instance, pCreateInfo, pAllocator, pDebugMessenger); + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + return VK_SUCCESS; +} + +/** + * @brief Proxy for vkDestroyDebugUtilsMessengerEXT + * + * @param instance Vulkan instance object + * @param debugMessenger Debug messanger handle + * @param pAllocator Memory allocator + */ +void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, + const VkAllocationCallbacks* pAllocator) +{ + PFN_vkDestroyDebugUtilsMessengerEXT func = + (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); + if (func != nullptr) { + func(instance, debugMessenger, pAllocator); + } +} + +#endif + +/** + * @brief Enable instance extensions, based on system and requirements + * + */ +void setEnabledExtension() +{ + gEnabledInstanceExtensionsNames.push_back("VK_KHR_surface"); + gEnabledInstanceExtensionsNames.push_back("VK_KHR_device_group_creation"); + +#ifdef __linux__ + if (getenv("WAYLAND_DISPLAY") != NULL) { + gEnabledInstanceExtensionsNames.push_back("VK_KHR_wayland_surface"); + } else { + gEnabledInstanceExtensionsNames.push_back("VK_KHR_xcb_surface"); + } +#elif defined(_WIN32) || defined(WIN32) + gEnabledInstanceExtensionsNames.push_back("VK_KHR_win32_surface"); +#endif + +#ifndef NDEBUG + gEnabledInstanceExtensionsNames.push_back("VK_EXT_debug_utils"); +#endif +}; + +/** + * @brief Create the Vulkan Instance object + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_INSTANCE_INIT_FAILED + */ +uint8_t createInstance() +{ + uint32_t extensionCount = 0; + uint32_t layersCount = 0; + VkApplicationInfo applicationInfo; + VkInstanceCreateInfo createInfo; + std::vector 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(VulkanState& vulkan_state) +{ + if (VK_SUCCESS != glfwCreateWindowSurface(gVulkanInstance, window::getWindowHandle(), nullptr, &vulkan_state.surface)) { + LOGF << vulkanErrorMesssages[VULKAN_SURFACE_CREATION_FAILED]; + return VULKAN_ERROR_SURFACE_CREATION_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Choose physical device. Used later for vkDevice + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND + * VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND + */ +uint8_t pickPhysicalDevice(VulkanState& vulkan_state) +{ + uint32_t physicalDeviceCount = 0U; + std::vector pPhysicalDevices; + + /* Get number of existing physical devices */ + vkEnumeratePhysicalDevices(gVulkanInstance, &physicalDeviceCount, nullptr); + + if (0 == physicalDeviceCount) { + LOGF << vulkanErrorMesssages[VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND]; + return VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND; + } + + pPhysicalDevices.resize(physicalDeviceCount); + vkEnumeratePhysicalDevices(gVulkanInstance, &physicalDeviceCount, pPhysicalDevices.data()); + + /* Check for suitability */ + for (size_t physicalDeviceIdx = 0U; physicalDeviceIdx < physicalDeviceCount; ++physicalDeviceIdx) { + VkPhysicalDeviceProperties deviceProperties; + VkPhysicalDeviceFeatures deviceFeatures; + vkGetPhysicalDeviceProperties(pPhysicalDevices[physicalDeviceIdx], &deviceProperties); + vkGetPhysicalDeviceFeatures(pPhysicalDevices[physicalDeviceIdx], &deviceFeatures); + + /* Expect discrete graphics device type */ + if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || + deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) { + /* Application MUST have geometry shader */ + if (deviceFeatures.geometryShader) { + vulkan_state.physicalDevice = pPhysicalDevices[physicalDeviceIdx]; + } + } + } + + if (VK_NULL_HANDLE == vulkan_state.physicalDevice) { + /* No suitable device found */ + LOGF << vulkanErrorMesssages[VULKAN_NO_SUITABLE_PHYSICAL_DEVICES]; + return VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND; + } + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Create vkDevice object + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_QUEUE_NOT_SUPPORTED + * VULKAN_ERROR_DEVICE_CREATION_FAILED + */ +uint8_t createDevice(VulkanState& vulkan_state) +{ + uint32_t queueFamiliesPropertyCount = 0U; + ssize_t graphicsFamilyIdx = -1; + ssize_t presentationFamiliyIdx = -1; + std::vector 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(vulkan_state.physicalDevice, &queueFamiliesPropertyCount, nullptr); + pQueueFamilyProperties.resize(queueFamiliesPropertyCount); + vkGetPhysicalDeviceQueueFamilyProperties(vulkan_state.physicalDevice, &queueFamiliesPropertyCount, + pQueueFamilyProperties.data()); + + /* Get queue families that support graphics operations AND have most + * queues. */ + { + size_t numberOfQueues = 0; + for (size_t familyIdx = 0; familyIdx < queueFamiliesPropertyCount; ++familyIdx) { + if (pQueueFamilyProperties[familyIdx].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + if (pQueueFamilyProperties[familyIdx].queueCount > numberOfQueues) { + graphicsFamilyIdx = familyIdx; + numberOfQueues = pQueueFamilyProperties[familyIdx].queueCount; + } + } + /* Check for family with surface support */ + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(vulkan_state.physicalDevice, familyIdx, vulkan_state.surface, &presentSupport); + if (presentSupport) { + presentationFamiliyIdx = familyIdx; + } + + if (graphicsFamilyIdx > 0 && presentationFamiliyIdx > 0) { + break; + } + } + } + + /* This is not magic number ;P. We want to support exact number of required queues flags */ + if (0 > graphicsFamilyIdx) { + LOGF << vulkanErrorMesssages[VULKAN_QUEUE_NOT_SUPPORTED]; + return VULKAN_ERROR_QUEUE_NOT_SUPPORTED; + } + + vulkan_state.queueFamilyIndices[0] = graphicsFamilyIdx; /* TODO: Change when more families are needed */ + + pGraphicsFamiliyQueuePriorities.resize(pQueueFamilyProperties[graphicsFamilyIdx].queueCount); + for (size_t i = 0; i < pQueueFamilyProperties[graphicsFamilyIdx].queueCount; i++) { + pGraphicsFamiliyQueuePriorities[i] = 1.0f; /* TODO: When queues have different tasks, change this if needed */ + } + + pPresentationFamiliyQueuePriorities.resize(pQueueFamilyProperties[presentationFamiliyIdx].queueCount); + for (size_t i = 0; i < pQueueFamilyProperties[presentationFamiliyIdx].queueCount; i++) { + pPresentationFamiliyQueuePriorities[i] = + 1.0f; /* TODO: When queues have different tasks, change this if needed */ + } + + deviceQueueCreateInfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCreateInfos[0].pNext = nullptr; + deviceQueueCreateInfos[0].flags = 0; + deviceQueueCreateInfos[0].queueFamilyIndex = graphicsFamilyIdx; + deviceQueueCreateInfos[0].queueCount = pQueueFamilyProperties[graphicsFamilyIdx].queueCount; + deviceQueueCreateInfos[0].pQueuePriorities = pGraphicsFamiliyQueuePriorities.data(); + + deviceQueueCreateInfos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCreateInfos[1].pNext = nullptr; + deviceQueueCreateInfos[1].flags = 0; + deviceQueueCreateInfos[1].queueFamilyIndex = presentationFamiliyIdx; + deviceQueueCreateInfos[1].queueCount = pQueueFamilyProperties[presentationFamiliyIdx].queueCount; + deviceQueueCreateInfos[1].pQueuePriorities = pPresentationFamiliyQueuePriorities.data(); + + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.pNext = nullptr; + deviceCreateInfo.flags = 0U; + deviceCreateInfo.queueCreateInfoCount = 2u; /* For now one is enough. TODO: Change when needed. */ + deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfos; + deviceCreateInfo.enabledLayerCount = 0; + deviceCreateInfo.ppEnabledLayerNames = nullptr; + deviceCreateInfo.enabledExtensionCount = gEnabledDeviceExtensionsNames.size(); + deviceCreateInfo.ppEnabledExtensionNames = gEnabledDeviceExtensionsNames.data(); + deviceCreateInfo.pEnabledFeatures = nullptr; /* TODO: Enable features, when needed */ + + if (VK_SUCCESS != vkCreateDevice(vulkan_state.physicalDevice, &deviceCreateInfo, nullptr, &vulkan_state.device)) { + LOGF << vulkanErrorMesssages[VULKAN_FAILED_TO_CREATE_DEVICE]; + return VULKAN_ERROR_DEVICE_CREATION_FAILED; + } + + vkGetDeviceQueue(vulkan_state.device, graphicsFamilyIdx, 0, &vulkan_state.graphicsQueue); + vkGetDeviceQueue(vulkan_state.device, presentationFamiliyIdx, 0, &gPresentQueue); + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Create a Memory Allocator object + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_ALLOCATOR_CREATION_FAILED + */ +uint8_t createMemoryAllocator(VulkanState& vulkan_state) +{ + VmaVulkanFunctions vulkanFunctions = {0}; + vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; + vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; + + VmaAllocatorCreateInfo allocatorCreateInfo = {0}; + allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_3; + allocatorCreateInfo.physicalDevice = vulkan_state.physicalDevice; + allocatorCreateInfo.device = vulkan_state.device; + allocatorCreateInfo.instance = gVulkanInstance; + allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; + + if (VK_SUCCESS != vmaCreateAllocator(&allocatorCreateInfo, &vulkan_state.allocator)) { + LOGF << vulkanErrorMesssages[VULKAN_ERROR_ALLOCATOR_CREATION_FAILED]; + return VULKAN_ERROR_ALLOCATOR_CREATION_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} + + +/** + * @brief Create a Sync Objects + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED + */ +uint8_t createSyncObjects(VulkanState& vulkan_state) +{ + gImageAvailableSemaphores.resize(SWAP_BUFFER_COUNT); + gRenderFinishedSemaphores.resize(SWAP_BUFFER_COUNT); + gInFlightFences.resize(SWAP_BUFFER_COUNT); + + VkSemaphoreCreateInfo semaphoreInfo{}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) { + if (VK_SUCCESS != vkCreateSemaphore(vulkan_state.device, &semaphoreInfo, nullptr, &gImageAvailableSemaphores[i]) || + VK_SUCCESS != vkCreateSemaphore(vulkan_state.device, &semaphoreInfo, nullptr, &gRenderFinishedSemaphores[i]) || + VK_SUCCESS != vkCreateFence(vulkan_state.device, &fenceInfo, nullptr, &gInFlightFences[i])) { + LOGF << vulkanErrorMesssages[VULKAN_SYNCOBJCTS_CREATION_FAILED]; + return VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED; + } + } + + return VULKAN_ERROR_NO_ERROR; +} + + +uint8_t initVulkan(VulkanState& vulkan_state) +{ + uint8_t status = VULKAN_ERROR_NO_ERROR; + + setEnabledExtension(); + STATUS_CHECK(createInstance()); +#ifndef NDEBUG + setupDebugMessenger(); +#endif + STATUS_CHECK(createSurface(vulkan_state)); + STATUS_CHECK(pickPhysicalDevice(vulkan_state)); + STATUS_CHECK(createDevice(vulkan_state)); + STATUS_CHECK(createMemoryAllocator(vulkan_state)); + + STATUS_CHECK(create_pipeline(vulkan_state)); + + STATUS_CHECK(create_buffers(vulkan_state)); + + STATUS_CHECK(create_descriptors(vulkan_state)); + + STATUS_CHECK(createSyncObjects(vulkan_state)); + return status; +} + +void drawFrame(VulkanState& vulkan_state) +{ + vkWaitForFences(vulkan_state.device, 1, &gInFlightFences[vulkan_state.currentFrame], VK_TRUE, UINT64_MAX); + + uint32_t imageIndex; + VkResult result = vkAcquireNextImageKHR(vulkan_state.device, vulkan_state.swapchain, UINT64_MAX, gImageAvailableSemaphores[vulkan_state.currentFrame], + VK_NULL_HANDLE, &imageIndex); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { + recreateSwapchain(vulkan_state); + return; + } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + // throw std::runtime_error("failed to acquire swap chain image!"); + return; + } + + updateUniformBuffer(vulkan_state); + + /* Only reset the fence if we are submitting work */ + vkResetFences(vulkan_state.device, 1, &gInFlightFences[vulkan_state.currentFrame]); + reset_command_buffer(vulkan_state); + + recordCommandBuffer(vulkan_state, imageIndex); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = {gImageAvailableSemaphores[vulkan_state.currentFrame]}; + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &vulkan_state.pCommandBuffers[vulkan_state.currentFrame]; + + VkSemaphore signalSemaphores[] = {gRenderFinishedSemaphores[vulkan_state.currentFrame]}; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + if (vkQueueSubmit(vulkan_state.graphicsQueue, 1, &submitInfo, gInFlightFences[vulkan_state.currentFrame]) != VK_SUCCESS) { + throw std::runtime_error("failed to submit draw command buffer!"); + } + + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapChains[] = {vulkan_state.swapchain}; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + + presentInfo.pImageIndices = &imageIndex; + + vkQueuePresentKHR(gPresentQueue, &presentInfo); + + vulkan_state.currentFrame = (vulkan_state.currentFrame + 1) % SWAP_BUFFER_COUNT; +} + +void deinitVulkan(VulkanState& vulkan_state) +{ + cleanup_swapchain(vulkan_state); + destroy_descriptors(vulkan_state); + destroy_buffers(vulkan_state); + for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) { + vkDestroySemaphore(vulkan_state.device, gImageAvailableSemaphores[i], nullptr); + vkDestroySemaphore(vulkan_state.device, gRenderFinishedSemaphores[i], nullptr); + vkDestroyFence(vulkan_state.device, gInFlightFences[i], nullptr); + } + + destroy_pipeline(vulkan_state); + + vmaDestroyAllocator(vulkan_state.allocator); + vkDestroyDevice(vulkan_state.device, nullptr); +#ifndef NDEBUG + DestroyDebugUtilsMessengerEXT(gVulkanInstance, gDebugMessenger, nullptr); +#endif + vkDestroySurfaceKHR(gVulkanInstance, vulkan_state.surface, nullptr); + vkDestroyInstance(gVulkanInstance, nullptr); +} + +} // namespace rse::graphics::vulkanbase \ No newline at end of file diff --git a/Graphics/include/vulkanBase.hpp b/graphics/src/vulkan_base.hpp similarity index 73% rename from Graphics/include/vulkanBase.hpp rename to graphics/src/vulkan_base.hpp index f35447c6..d629649d 100644 --- a/Graphics/include/vulkanBase.hpp +++ b/graphics/src/vulkan_base.hpp @@ -13,30 +13,37 @@ #define RSE_GRAPHICS_VULKANBASE_HPP #include +#include + +#include + +#include "vulkan_commons.hpp" namespace rse::graphics::vulkanbase { + /** * @brief Initialize Vulkan backend * + * @param state * @return uint8_t 0 on success. Status code on failure */ - - -uint8_t initVulkan(); +uint8_t initVulkan(VulkanState& state); /** * @brief Deinitialize Vulkan backend * + * @param state */ -void deinitVulkan(); +void deinitVulkan(VulkanState& state); /** * @brief Draw frame on the screen. * + * @param state */ -void drawFrame(); +void drawFrame(VulkanState& state); } /* namespace rse::graphics::vulkanbase */ diff --git a/graphics/src/vulkan_buffers.cpp b/graphics/src/vulkan_buffers.cpp new file mode 100644 index 00000000..309c19dc --- /dev/null +++ b/graphics/src/vulkan_buffers.cpp @@ -0,0 +1,419 @@ +#include "vulkan_buffers.hpp" + +#include "vulkan_errors.hpp" + +#include "locale_vulkan.hpp" +#include "utilities/logger.hpp" + +#include + +#define GLM_FORCE_RADIANS +#include + + +namespace rse::graphics::vulkanbase +{ + + +using namespace locale::vulkan; + +// 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 */ +Buffer gVertexBuffer = {}; +/* CPU visible buffer. TODO: Do we need this global? */ +Buffer gVertexStagingBuffer = {}; +Buffer gIndexBuffer = {}; +Buffer gIndexStagingBuffer = {}; + +VkCommandPool gCommandPool = VK_NULL_HANDLE; + + +static uint8_t createBuffer(VulkanState& vulkan_state, const VkDeviceSize size, VkBufferUsageFlags bufferUsage, VmaMemoryUsage memoryUsage, + const VmaAllocationCreateFlags allocationFlags, Buffer& buffer); +static uint8_t copyBuffer(VulkanState& vulkan_state, VkBuffer src, VkBuffer dst, VkDeviceSize size); + + +static uint8_t createCommandPools(VulkanState& vulkan_state); +static uint8_t createVertexBuffer(VulkanState& vulkan_state); +static uint8_t createIndexBuffer(VulkanState& vulkan_state); +static uint8_t createUniformBuffers(VulkanState& vulkan_state); +static uint8_t allocateCommandBuffers(VulkanState& vulkan_state); + +/** + * @brief Helper function. Creates a buffer object + * + * @param size Size of the target buffer + * @param bufferUsage Buffer usage flags + * @param memoryUsage memory usage flags (mostly VMA_MEMORY_USAGE_AUTO) + * @param allocationFlags VMA allocation flags + * @param buffer Buffer, that will be allocated + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED + */ +uint8_t createBuffer(VulkanState& vulkan_state, const VkDeviceSize size, VkBufferUsageFlags bufferUsage, + VmaMemoryUsage memoryUsage, const VmaAllocationCreateFlags allocationFlags, Buffer& buffer) +{ + VkBufferCreateInfo vertexBufferInfo; + VmaAllocationCreateInfo createInfo; + + vertexBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + vertexBufferInfo.pNext = nullptr; + vertexBufferInfo.flags = 0U; + vertexBufferInfo.size = size; + vertexBufferInfo.usage = bufferUsage; + vertexBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + vertexBufferInfo.queueFamilyIndexCount = 0U; + vertexBufferInfo.pQueueFamilyIndices = nullptr; + + /* Disalows random access to memory. TODO: Consider changing to VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT? */ + createInfo.flags = allocationFlags; + createInfo.usage = memoryUsage; + createInfo.memoryTypeBits = 0U; + createInfo.requiredFlags = 0U; + createInfo.preferredFlags = 0U; + createInfo.pool = VK_NULL_HANDLE; + createInfo.pUserData = VK_NULL_HANDLE; + createInfo.priority = 0.0f; + + if (VK_SUCCESS != vmaCreateBuffer(vulkan_state.allocator, &vertexBufferInfo, &createInfo, &buffer.buffer, &buffer.allocation, + &buffer.allocationInfo)) { + LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; + return VULKAN_ERROR_BUFFER_CREATION_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Copy one buffer's data to another + * + * @param src Source buffer + * @param dst Destination buffer + * @param size Size of buffer to copy + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + */ +uint8_t copyBuffer(VulkanState& vulkan_state, VkBuffer src, VkBuffer dst, VkDeviceSize size) +{ + /* Vulkan buffers can only be copied using command buffers */ + VkBufferCopy copyRegion; + VkSubmitInfo submitInfo; + VkCommandBuffer copyCommandBuffer; + VkCommandBufferBeginInfo beginInfo; + VkCommandBufferAllocateInfo allocInfo; + + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.pNext = nullptr; + allocInfo.commandPool = gCommandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = 1; + + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.pNext = nullptr; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + beginInfo.pInheritanceInfo = nullptr; + + copyRegion.srcOffset = 0; + copyRegion.dstOffset = 0; + copyRegion.size = size; + + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.pNext = nullptr; + submitInfo.waitSemaphoreCount = 0; + submitInfo.pWaitSemaphores = nullptr; + submitInfo.pWaitDstStageMask = nullptr; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = ©CommandBuffer; + submitInfo.signalSemaphoreCount = 0; + submitInfo.pSignalSemaphores = nullptr; + + // FIXME: At result checks + vkAllocateCommandBuffers(vulkan_state.device, &allocInfo, ©CommandBuffer); + vkBeginCommandBuffer(copyCommandBuffer, &beginInfo); + vkCmdCopyBuffer(copyCommandBuffer, src, dst, 1, ©Region); + vkEndCommandBuffer(copyCommandBuffer); + vkQueueSubmit(vulkan_state.graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + /* TODO: Use fences to wait instead of idle */ + vkQueueWaitIdle(vulkan_state.graphicsQueue); + vkFreeCommandBuffers(vulkan_state.device, gCommandPool, 1, ©CommandBuffer); + + return VULKAN_ERROR_NO_ERROR; +} + + + +/** + * @brief Create Command Pools + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED + */ +uint8_t createCommandPools(VulkanState& vulkan_state) +{ + VkCommandPoolCreateInfo createInfo; + + createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + createInfo.queueFamilyIndex = vulkan_state.queueFamilyIndices[0]; /* TODO: Change when more families are needed */ + + if (VK_SUCCESS != vkCreateCommandPool(vulkan_state.device, &createInfo, nullptr, &gCommandPool)) { + LOGF << vulkanErrorMesssages[VULKAN_COMMAND_POOL_CREATION_FAILED]; + return VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} + + +/** + * @brief Create a buffer holding all vertex data + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_BUFFER_CREATION_FAILED + * VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED + */ +uint8_t createVertexBuffer(VulkanState& vulkan_state) +{ + void* mappedData; + VkDeviceSize bufferSize; + + bufferSize = sizeof(vertices[0]) * vertices.size(); + + /* Creating staging buffer*/ + if (VK_SUCCESS != createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST, + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, gVertexStagingBuffer)) { + LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; + return VULKAN_ERROR_BUFFER_CREATION_FAILED; + } + + /* Create Vertex Buffer*/ + if (VK_SUCCESS != createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0, /* Will not be mapped with vmaMapMemory */ + gVertexBuffer)) { + LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; + return VULKAN_ERROR_BUFFER_CREATION_FAILED; + } + + /* Fill staging buffer */ + if (VK_SUCCESS != vmaMapMemory(vulkan_state.allocator, gVertexStagingBuffer.allocation, &mappedData)) { + LOGF << vulkanErrorMesssages[VULKAN_VERTEX_BUFFER_MAPPING_FAILED]; + return VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED; + } + memcpy(mappedData, vertices.data(), sizeof(Vertex) * vertices.size()); + vmaUnmapMemory(vulkan_state.allocator, gVertexStagingBuffer.allocation); + + copyBuffer(vulkan_state, gVertexStagingBuffer.buffer, gVertexBuffer.buffer, bufferSize); + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Create an index buffer, connected with vertex buffer + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_BUFFER_CREATION_FAILED + */ +uint8_t createIndexBuffer(VulkanState& vulkan_state) +{ + void* mappedData; + VkDeviceSize bufferSize; + + bufferSize = sizeof(indices[0]) * indices.size(); + + /* Creating staging buffer*/ + if (VK_SUCCESS != createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST, + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, gIndexStagingBuffer)) { + LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; + return VULKAN_ERROR_BUFFER_CREATION_FAILED; + } + + /* Create Vertex Buffer*/ + if (VK_SUCCESS != createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0, /* Will not be mapped with vmaMapMemory */ + gIndexBuffer)) { + LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; + return VULKAN_ERROR_BUFFER_CREATION_FAILED; + } + + /* Fill staging buffer */ + if (VK_SUCCESS != vmaMapMemory(vulkan_state.allocator, gIndexStagingBuffer.allocation, &mappedData)) { + LOGF << vulkanErrorMesssages[VULKAN_VERTEX_BUFFER_MAPPING_FAILED]; + return VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED; + } + memcpy(mappedData, indices.data(), static_cast(bufferSize)); + vmaUnmapMemory(vulkan_state.allocator, gIndexStagingBuffer.allocation); + + copyBuffer(vulkan_state, gIndexStagingBuffer.buffer, gIndexBuffer.buffer, bufferSize); + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Create a Uniform Buffers + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + */ +uint8_t createUniformBuffers(VulkanState& vulkan_state) +{ + VkDeviceSize bufferSize = sizeof(UniformBufferObject); + + vulkan_state.uniformBuffers.resize(SWAP_BUFFER_COUNT); + + for (size_t i = 0; i < SWAP_BUFFER_COUNT; i++) { + createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST, + VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, + vulkan_state.uniformBuffers[i]); + } + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Allocate Command Buffers + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED + */ +uint8_t allocateCommandBuffers(VulkanState& vulkan_state) +{ + VkCommandBufferAllocateInfo allocateInfo; + + /* Allocate memory for command buffers */ + vulkan_state.pCommandBuffers.resize(SWAP_BUFFER_COUNT); + allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocateInfo.pNext = nullptr; + allocateInfo.commandPool = gCommandPool; + allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; /* TODO: When needed, add secondary command buffer */ + allocateInfo.commandBufferCount = SWAP_BUFFER_COUNT; + + if (VK_SUCCESS != vkAllocateCommandBuffers(vulkan_state.device, &allocateInfo, vulkan_state.pCommandBuffers.data())) { + LOGF << vulkanErrorMesssages[VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED]; + return VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED; + } + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Update uniform buffers + * + * @param currentImage Current image index + */ +void updateUniformBuffer(VulkanState& vulkan_state) +{ + UniformBufferObject ubo{}; + ubo.model = glm::mat4(1.0f); + ubo.view = glm::lookAt(glm::vec3(0.0f, 0.0f, 5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + ubo.proj = + glm::perspective(glm::radians(45.0f), vulkan_state.swapchainExtent.width / (float)vulkan_state.swapchainExtent.height, 0.1f, 20.0f); + ubo.proj[1][1] *= -1; /* GLM was developed for OpenGL, where Y coordinate of the clip is inverted */ + memcpy(vulkan_state.uniformBuffers[vulkan_state.currentFrame].allocationInfo.pMappedData, &ubo, sizeof(ubo)); +} + +/** + * @brief Records commands for provided command buffer + * + * @param commandBuffer Command buffer, that will hold commands + * @param imageIndex Image index + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED + */ +uint8_t recordCommandBuffer(VulkanState& vulkan_state, uint32_t imageIndex) +{ + VkCommandBuffer commandBuffer = vulkan_state.pCommandBuffers[vulkan_state.currentFrame]; + VkCommandBufferBeginInfo beginInfo{}; + VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + VkRenderPassBeginInfo renderPassInfo{}; + VkViewport viewport{}; + VkRect2D scissor{}; + VkBuffer vertexBuffers[] = {gVertexBuffer.buffer}; + VkDeviceSize offsets[] = {0}; + + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; + beginInfo.pInheritanceInfo = nullptr; + + if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { + LOGF << vulkanErrorMesssages[VULKAN_RECORD_COMMAND_BEGIN_FAILED]; + return VULKAN_ERROR_RECORD_COMMAND_BEGIN_FAILED; + } + + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = vulkan_state.renderPass; + renderPassInfo.framebuffer = vulkan_state.swapchainFramebuffers[imageIndex]; + renderPassInfo.renderArea.offset = {0, 0}; + renderPassInfo.renderArea.extent = vulkan_state.swapchainExtent; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_state.graphicsPipeline); + + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(vulkan_state.swapchainExtent.width); + viewport.height = static_cast(vulkan_state.swapchainExtent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + scissor.offset = {0, 0}; + scissor.extent = vulkan_state.swapchainExtent; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + vkCmdBindIndexBuffer(commandBuffer, gIndexBuffer.buffer, 0, VK_INDEX_TYPE_UINT16); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_state.pipelineLayout, 0, 1, + &vulkan_state.descriptorSets[vulkan_state.currentFrame], 0, nullptr); + + vkCmdDrawIndexed(commandBuffer, static_cast(indices.size()), 1, 0, 0, 0); + + vkCmdEndRenderPass(commandBuffer); + + if (VK_SUCCESS != vkEndCommandBuffer(commandBuffer)) { + LOGF << vulkanErrorMesssages[VULKAN_RECORD_COMMAND_BUFFER_FAILED]; + return VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} + +uint8_t create_buffers(VulkanState& vulkan_state) +{ + uint8_t status = VULKAN_ERROR_NO_ERROR;\ + + STATUS_CHECK(createCommandPools(vulkan_state)); + STATUS_CHECK(createVertexBuffer(vulkan_state)); + STATUS_CHECK(createIndexBuffer(vulkan_state)); + STATUS_CHECK(createUniformBuffers(vulkan_state)); + STATUS_CHECK(allocateCommandBuffers(vulkan_state)); + + return status; +} + +void reset_command_buffer(VulkanState& vulkan_state) +{ + vkResetCommandBuffer(vulkan_state.pCommandBuffers[vulkan_state.currentFrame], /*VkCommandBufferResetFlagBits*/ 0); +} + +void destroy_buffers(VulkanState& vulkan_state) +{ + for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) { + vmaDestroyBuffer(vulkan_state.allocator, vulkan_state.uniformBuffers[i].buffer, vulkan_state.uniformBuffers[i].allocation); + } + + vmaDestroyBuffer(vulkan_state.allocator, gVertexBuffer.buffer, gVertexBuffer.allocation); + vmaDestroyBuffer(vulkan_state.allocator, gVertexStagingBuffer.buffer, gVertexStagingBuffer.allocation); + vmaDestroyBuffer(vulkan_state.allocator, gIndexBuffer.buffer, gIndexBuffer.allocation); + vmaDestroyBuffer(vulkan_state.allocator, gIndexStagingBuffer.buffer, gIndexStagingBuffer.allocation); + vkDestroyCommandPool(vulkan_state.device, gCommandPool, nullptr); +} + +} \ No newline at end of file diff --git a/graphics/src/vulkan_buffers.hpp b/graphics/src/vulkan_buffers.hpp new file mode 100644 index 00000000..033a7177 --- /dev/null +++ b/graphics/src/vulkan_buffers.hpp @@ -0,0 +1,56 @@ +#ifndef RSE_VULKAN_BUFFERS_HPP +#define RSE_VULKAN_BUFFERS_HPP + +#include +#include +#include + +#include "third_party/vma/vk_mem_alloc.h" + +#include "vulkan_commons.hpp" + +namespace rse::graphics::vulkanbase +{ + +/** + * @brief Create buffers needed by vulkan pipeline + * + * @param vulkan_state + * @return uint8_t + */ +uint8_t create_buffers(VulkanState& vulkan_state); + +/** + * @brief Update uniform buffers + * + * @param vulkan_state + */ +void updateUniformBuffer(VulkanState& vulkan_state); + +/** + * @brief Record commands for given image index + * + * @param vulkan_state + * @param imageIndex + * @return uint8_t + */ +uint8_t recordCommandBuffer(VulkanState& vulkan_state, uint32_t imageIndex); + +/** + * @brief Reset command buffer for current frame. + * + * @param vulkan_state + */ +void reset_command_buffer(VulkanState& vulkan_state); + +/** + * @brief Destroy ALL previously allocated buffers + * + * @param vulkan_state + */ +void destroy_buffers(VulkanState& vulkan_state); + +} // namespace rse::graphics::vulkanbase + + +#endif \ No newline at end of file diff --git a/graphics/src/vulkan_commons.hpp b/graphics/src/vulkan_commons.hpp new file mode 100644 index 00000000..7ee49e51 --- /dev/null +++ b/graphics/src/vulkan_commons.hpp @@ -0,0 +1,104 @@ +/** + * @file vulkan_global.hpp + * @author Piotr Krygier (piotr.krygier@meshsystems.com) + * @brief Declaration of a structure holding Vulkan variables needed by different parts of the code + * @version 0.1 + * @date 2023-03-28 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef RSE_VULKAN_GLOBAL_HPP +#define RSE_VULKAN_GLOBAL_HPP + +#include +#include + +#include "third_party/vma/vk_mem_alloc.h" + +#define SWAP_BUFFER_COUNT 2U + +namespace rse::graphics::vulkanbase +{ + +/** + * @brief Represents single vertex on the screen + * + */ +struct Vertex +{ + glm::vec3 pos; + glm::vec3 color; + + static VkVertexInputBindingDescription getBindingDescription() { + VkVertexInputBindingDescription bindingDescription; + + bindingDescription.binding = 0; + bindingDescription.stride = sizeof(Vertex); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + return bindingDescription; + } + + static std::array 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 +{ + VkBuffer buffer; + VmaAllocation allocation; + VmaAllocationInfo allocationInfo; +}; + +struct UniformBufferObject +{ + alignas(16) glm::mat4 model; + alignas(16) glm::mat4 view; + alignas(16) glm::mat4 proj; +}; + +struct VulkanState +{ + VkDevice device; + VkSurfaceKHR surface; + VkQueue graphicsQueue; + VmaAllocator allocator; + VkPhysicalDevice physicalDevice; + uint32_t currentFrame; + uint32_t queueFamilyIndices[1]; + std::vector uniformBuffers; + std::vector pCommandBuffers; + std::vector descriptorSets; + std::vector swapChainImageViews; + VkExtent2D swapchainExtent; + VkRenderPass renderPass; + VkDescriptorSetLayout descriptorSetLayout; + std::vector swapchainFramebuffers; + VkPipeline graphicsPipeline; + VkPipelineLayout pipelineLayout; + VkSwapchainKHR swapchain; +}; + +} // namespace rse::graphics::vulkanbase + +#endif diff --git a/graphics/src/vulkan_descriptors.cpp b/graphics/src/vulkan_descriptors.cpp new file mode 100644 index 00000000..d35efe8d --- /dev/null +++ b/graphics/src/vulkan_descriptors.cpp @@ -0,0 +1,101 @@ +#include "vulkan_descriptors.hpp" + +#include "locale_vulkan.hpp" +#include "utilities/logger.hpp" +#include "vulkan_errors.hpp" + +namespace rse::graphics::vulkanbase +{ + +using namespace locale::vulkan; + +VkDescriptorPool gDescriptorPool = {}; + +static uint8_t createDescriptorPool(VulkanState& vulkan_state); +static uint8_t createDescriptorSets(VulkanState& vulkan_state); + +/** + * @brief Create a Descriptor Pool + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED + */ +uint8_t createDescriptorPool(VulkanState& vulkan_state) +{ + VkDescriptorPoolSize poolSize{}; + poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSize.descriptorCount = static_cast(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(vulkan_state.device, &poolInfo, nullptr, &gDescriptorPool) != VK_SUCCESS) { + LOGF << vulkanErrorMesssages[VULKAN_DESCRIPTOR_POOL_CREATION_FAILED]; + return VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Create a Descriptor Sets + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_DESCRIPTOR_SETS_ALLOCATION_FAILED */ +uint8_t createDescriptorSets(VulkanState& vulkan_state) +{ + std::vector layouts(SWAP_BUFFER_COUNT, vulkan_state.descriptorSetLayout); + VkDescriptorSetAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = gDescriptorPool; + allocInfo.descriptorSetCount = static_cast(SWAP_BUFFER_COUNT); + allocInfo.pSetLayouts = layouts.data(); + + vulkan_state.descriptorSets.resize(SWAP_BUFFER_COUNT); + if (vkAllocateDescriptorSets(vulkan_state.device, &allocInfo, vulkan_state.descriptorSets.data()) != VK_SUCCESS) { + LOGF << vulkanErrorMesssages[VULKAN_DESCRIPTOR_SETS_ALLOCATION_FAILED]; + return VULKAN_ERROR_DESCRIPTOR_SETS_ALLOCATION_FAILED; + } + + for (size_t i = 0; i < SWAP_BUFFER_COUNT; i++) { + VkDescriptorBufferInfo bufferInfo{}; + bufferInfo.buffer = vulkan_state.uniformBuffers[i].buffer; + bufferInfo.offset = 0; + bufferInfo.range = sizeof(UniformBufferObject); + + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = vulkan_state.descriptorSets[i]; + descriptorWrite.dstBinding = 0; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &bufferInfo; + + vkUpdateDescriptorSets(vulkan_state.device, 1, &descriptorWrite, 0, nullptr); + } + + return VULKAN_ERROR_NO_ERROR; +} + +uint8_t create_descriptors(VulkanState& vulkan_state) +{ + uint8_t status = VULKAN_ERROR_NO_ERROR; + + STATUS_CHECK(createDescriptorPool(vulkan_state)); + STATUS_CHECK(createDescriptorSets(vulkan_state)); + + return status; +} + +void destroy_descriptors(VulkanState& vulkan_state) +{ + vkDestroyDescriptorPool(vulkan_state.device, gDescriptorPool, nullptr); + vkDestroyDescriptorSetLayout(vulkan_state.device, vulkan_state.descriptorSetLayout, nullptr); +} + +} // namespace rse::graphics::vulkanbase + diff --git a/graphics/src/vulkan_descriptors.hpp b/graphics/src/vulkan_descriptors.hpp new file mode 100644 index 00000000..64d456fd --- /dev/null +++ b/graphics/src/vulkan_descriptors.hpp @@ -0,0 +1,32 @@ +#ifndef RSE_VULKAN_DESCRIPTORS_HPP +#define RSE_VULKAN_DESCRIPTORS_HPP + +#include +#include +#include + +#include "vulkan_commons.hpp" + +namespace rse::graphics::vulkanbase +{ + +/** + * @brief Create descriptors + * + * @param vulkan_state + * @return uint8_t + */ +uint8_t create_descriptors(VulkanState& vulkan_state); + +/** + * @brief Destroy all descriptors + * + * @param vulkan_state + */ +void destroy_descriptors(VulkanState& vulkan_state); + + +} // namespace rse::graphics::vulkanbase + + +#endif \ No newline at end of file diff --git a/graphics/src/vulkan_errors.hpp b/graphics/src/vulkan_errors.hpp new file mode 100644 index 00000000..d0f98099 --- /dev/null +++ b/graphics/src/vulkan_errors.hpp @@ -0,0 +1,43 @@ +#ifndef VULKAN_ERRORS_HPP +#define VULKAN_ERRORS_HPP + +/* Macro for checking status of function execution in initVulkan. Created to avoid writing boilerplate code */ +#define STATUS_CHECK(FUNC) \ + status = FUNC; \ + if (VULKAN_ERROR_NO_ERROR != status) { \ + return status; \ + } + +enum VULKAN_ERROR +{ + VULKAN_ERROR_NO_ERROR = 0x0U, + VULKAN_ERROR_LAYER_NOT_SUPPORTED, + VULKAN_ERROR_EXTENSION_NOT_SUPPORTED, + VULKAN_ERROR_INSTANCE_INIT_FAILED, + VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND, + VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND, + VULKAN_ERROR_QUEUE_NOT_SUPPORTED, + VULKAN_ERROR_DEVICE_CREATION_FAILED, + VULKAN_ERROR_ALLOCATOR_CREATION_FAILED, + VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED, + VULKAN_ERROR_BUFFER_CREATION_FAILED, + VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED, + VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED, + VULKAN_ERROR_SURFACE_CREATION_FAILED, + VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED, + VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED, + VULKAN_ERROR_SHADER_FILE_OPEN_FAILED, + VULKAN_ERROR_SHADER_CREATION_FAILED, + VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED, + VULKAN_ERROR_RENDERPASS_CREATION_FAILED, + VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED, + VULKAN_ERROR_DESCRIPTOR_SETS_ALLOCATION_FAILED, + VULKAN_ERROR_DESCRIPTORSETLAYOUT_CREATION_FAILED, + VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED, + VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED, + VULKAN_ERROR_RECORD_COMMAND_BEGIN_FAILED, + VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED, + VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED, +}; + +#endif \ No newline at end of file diff --git a/graphics/src/vulkan_pipeline.cpp b/graphics/src/vulkan_pipeline.cpp new file mode 100644 index 00000000..433f0c06 --- /dev/null +++ b/graphics/src/vulkan_pipeline.cpp @@ -0,0 +1,685 @@ +#include "vulkan_pipeline.hpp" + +#include "locale_vulkan.hpp" +#include "utilities/file_utils.hpp" +#include "utilities/logger.hpp" +#include "vulkan_errors.hpp" + +#include +#include +#include +#include + +namespace rse::graphics::vulkanbase +{ + +using namespace locale::vulkan; + +enum class ShaderStage +{ + VERTEX, + FRAGMENTS +}; + +constexpr VkFormat IMAGE_FORMAT = VK_FORMAT_B8G8R8A8_SRGB; + +uint32_t gSwapchainImagesCount = 0U; + +std::vector 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(VulkanState& state, const std::vector& binaryData); + +static uint8_t createSwapchain(VulkanState& vulkan_state); +static uint8_t createSwapchainImageViews(VulkanState& vulkan_state); +static uint8_t createRenderPass(VulkanState& vulkan_state); +static uint8_t createDescriptorSetLayout(VulkanState& vulkan_state); +static uint8_t createPipelineLayout(VulkanState& vulkan_state); +static uint8_t createGraphicsPipeline(VulkanState& vulkan_state); +static uint8_t createFrambuffers(VulkanState& vulkan_state); + +static uint8_t fillShaderStages(VulkanState& vulkan_state, std::vector* shaderStages); +static void fillDynamicPipelineStateInfo(VkPipelineDynamicStateCreateInfo* dynamicState); +static void freeDynamicPipelineStateInfo(VkPipelineDynamicStateCreateInfo* dynamicState); +static void fillVertexInputInfo(VkPipelineVertexInputStateCreateInfo* vertexInputInfo); +static void freeVertexInputInfo(VkPipelineVertexInputStateCreateInfo* vertexInputInfo); +static void fillInputAssemblyInfo(VkPipelineInputAssemblyStateCreateInfo* inputAssembly); +static void fillViewportStateInfo(VulkanState& vulkan_state, VkPipelineViewportStateCreateInfo* viewportState); +static void freeViewportStateInfo(VkPipelineViewportStateCreateInfo* viewportState); +static void fillRasterizationInfo(VkPipelineRasterizationStateCreateInfo* rasterizer); +static void fillMultisamplingStateInfo(VkPipelineMultisampleStateCreateInfo* multisampling); +static void fillColorBlendStateInfo(VkPipelineColorBlendStateCreateInfo* colorBlending); +static void freeColorBlendInfo(VkPipelineColorBlendStateCreateInfo* colorBlending); + +/** + * @brief Creates shader module + * + * @param binaryData Shader compiled code + * @return std::optional Handle to compiled shader module or nullopt + */ +std::optional createShaderModule(VulkanState& vulkan_state, 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(vulkan_state.device, &createInfo, nullptr, &shaderModule)) { + LOGF << vulkanErrorMesssages[VULKAN_SHADER_UNABLE_TO_CREATE_SHADER]; + return std::nullopt; + } + + return shaderModule; +} + +/** + * @brief Create a Swapchain + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED + */ +uint8_t createSwapchain(VulkanState& vulkan_state) +{ + VkSwapchainCreateInfoKHR createInfo; + VkSurfaceCapabilitiesKHR physicalDeviceSurfaceCapabilities; + VkBool32 surfaceSupported; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkan_state.physicalDevice, vulkan_state.surface, &physicalDeviceSurfaceCapabilities); + + /* Store extent in global variable, for later use */ + vulkan_state.swapchainExtent = physicalDeviceSurfaceCapabilities.currentExtent; + + /* Check if device supports surface for presentation */ + vkGetPhysicalDeviceSurfaceSupportKHR(vulkan_state.physicalDevice, vulkan_state.queueFamilyIndices[0], vulkan_state.surface, &surfaceSupported); + + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.pNext = nullptr; + createInfo.flags = 0U; + createInfo.surface = vulkan_state.surface; + createInfo.minImageCount = SWAP_BUFFER_COUNT + 1; + createInfo.imageFormat = IMAGE_FORMAT; /* TODO: Check for supported formats */ + createInfo.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; /* TODO: Check for supported color space */ + createInfo.imageExtent = vulkan_state.swapchainExtent; + createInfo.imageArrayLayers = 1U; /* For non-stereoscopic-3D applications, this value is 1. */ + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; /* TODO: Probably change later? Dunno */ + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; /* Using only one queue family, so this is ok */ + createInfo.queueFamilyIndexCount = 1U; + createInfo.pQueueFamilyIndices = vulkan_state.queueFamilyIndices; + createInfo.preTransform = physicalDeviceSurfaceCapabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; /* TODO: Choose present mode from VkPresentModeKHR */ + createInfo.clipped = VK_TRUE; + createInfo.oldSwapchain = VK_NULL_HANDLE; + + if (VK_SUCCESS != vkCreateSwapchainKHR(vulkan_state.device, &createInfo, nullptr, &vulkan_state.swapchain)) { + LOGF << vulkanErrorMesssages[VULKAN_SWAPCHAIN_CREATION_FAILED]; + return VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED; + } + + /* Obtain swapchain images */ + vkGetSwapchainImagesKHR(vulkan_state.device, vulkan_state.swapchain, &gSwapchainImagesCount, nullptr); + gSwapChainImages.resize(gSwapchainImagesCount); + vkGetSwapchainImagesKHR(vulkan_state.device, vulkan_state.swapchain, &gSwapchainImagesCount, gSwapChainImages.data()); + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Create a Swapchain Image Views + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + */ +uint8_t createSwapchainImageViews(VulkanState& vulkan_state) +{ + vulkan_state.swapChainImageViews.resize(gSwapchainImagesCount); + for (size_t i = 0; i < gSwapchainImagesCount; i++) { + VkImageViewCreateInfo createInfo; + + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.flags = 0U; + createInfo.image = gSwapChainImages[i]; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; /* 2D Texture */ + createInfo.format = VK_FORMAT_B8G8R8A8_SRGB; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0U; + createInfo.subresourceRange.levelCount = 1U; + createInfo.subresourceRange.baseArrayLayer = 0U; + createInfo.subresourceRange.layerCount = 1U; + + if (VK_SUCCESS != vkCreateImageView(vulkan_state.device, &createInfo, nullptr, &vulkan_state.swapChainImageViews[i])) { + LOGF << vulkanErrorMesssages[VULKAN_SWAPCHAIN_IMVIEW_CREATION_FAILED]; + return VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED; + } + } + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Create a Render Pass + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_RENDERPASS_CREATION_FAILED + */ +uint8_t createRenderPass(VulkanState& vulkan_state) +{ + VkAttachmentDescription colorAttachment{}; + colorAttachment.flags = 0U; + colorAttachment.format = IMAGE_FORMAT; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef{}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass{}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkRenderPassCreateInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + + if (VK_SUCCESS != vkCreateRenderPass(vulkan_state.device, &renderPassInfo, nullptr, &vulkan_state.renderPass)) { + LOGF << vulkanErrorMesssages[VULKAN_RENDERPASS_CREATION_FAILED]; + return VULKAN_ERROR_RENDERPASS_CREATION_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Create a Descriptor Set Layout + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + */ +uint8_t createDescriptorSetLayout(VulkanState& vulkan_state) +{ + VkDescriptorSetLayoutCreateInfo layoutInfo; + VkDescriptorSetLayoutBinding uboLayoutBinding; + + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + uboLayoutBinding.pImmutableSamplers = nullptr; + + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.pNext = nullptr; + layoutInfo.flags = 0U; + layoutInfo.bindingCount = 1U; + layoutInfo.pBindings = &uboLayoutBinding; + + if (vkCreateDescriptorSetLayout(vulkan_state.device, &layoutInfo, nullptr, &vulkan_state.descriptorSetLayout) != VK_SUCCESS) { + LOGF << vulkanErrorMesssages[VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED]; + return VULKAN_ERROR_DESCRIPTORSETLAYOUT_CREATION_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} +/** + * @brief Create a Pipeline Layout + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED + */ +uint8_t createPipelineLayout(VulkanState& vulkan_state) +{ + VkPipelineLayoutCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.flags = 0U; + createInfo.setLayoutCount = 1U; + createInfo.pSetLayouts = &vulkan_state.descriptorSetLayout; + createInfo.pushConstantRangeCount = 0U; + createInfo.pPushConstantRanges = nullptr; + + if (VK_SUCCESS != vkCreatePipelineLayout(vulkan_state.device, &createInfo, nullptr, &vulkan_state.pipelineLayout)) { + LOGF << vulkanErrorMesssages[VULKAN_PIPELAYOUT_CREATION_FAILED]; + return VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Create Graphics Pipeline + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED + */ +uint8_t createGraphicsPipeline(VulkanState& vulkan_state) +{ + uint8_t status = VULKAN_ERROR_NO_ERROR; + + std::vector shaderStages{}; + VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + VkPipelineViewportStateCreateInfo viewportState{}; + VkPipelineRasterizationStateCreateInfo rasterizer{}; + VkPipelineMultisampleStateCreateInfo multisampling{}; + VkPipelineColorBlendStateCreateInfo colorBlending{}; + VkPipelineDynamicStateCreateInfo dynamicState{}; + + STATUS_CHECK(fillShaderStages(vulkan_state, &shaderStages)); + fillVertexInputInfo(&vertexInputInfo); + fillInputAssemblyInfo(&inputAssembly); + fillViewportStateInfo(vulkan_state, &viewportState); + fillRasterizationInfo(&rasterizer); + fillMultisamplingStateInfo(&multisampling); + fillColorBlendStateInfo(&colorBlending); + fillDynamicPipelineStateInfo(&dynamicState); + + VkGraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.pNext = nullptr; + pipelineInfo.flags = 0U; + pipelineInfo.stageCount = shaderStages.size(); + pipelineInfo.pStages = shaderStages.data(); + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pTessellationState = nullptr; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = nullptr; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicState; + pipelineInfo.layout = vulkan_state.pipelineLayout; + pipelineInfo.renderPass = vulkan_state.renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + if (VK_SUCCESS != + vkCreateGraphicsPipelines(vulkan_state.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &vulkan_state.graphicsPipeline)) { + LOGF << vulkanErrorMesssages[VULKAN_GRAPHICSPIPELINE_CREATION_FAILED]; + status = VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED; + } + + freeDynamicPipelineStateInfo(&dynamicState); + freeColorBlendInfo(&colorBlending); + freeViewportStateInfo(&viewportState); + freeVertexInputInfo(&vertexInputInfo); + + return status; +} + +/** + * @brief Create Frambuffers + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + */ +uint8_t createFrambuffers(VulkanState& vulkan_state) +{ + vulkan_state.swapchainFramebuffers.resize(vulkan_state.swapChainImageViews.size()); + + for (size_t i = 0; i < vulkan_state.swapChainImageViews.size(); i++) { + VkImageView attachments[] = {vulkan_state.swapChainImageViews[i]}; + + VkFramebufferCreateInfo framebufferInfo{}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = vulkan_state.renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = vulkan_state.swapchainExtent.width; + framebufferInfo.height = vulkan_state.swapchainExtent.height; + framebufferInfo.layers = 1; + + if (vkCreateFramebuffer(vulkan_state.device, &framebufferInfo, nullptr, &vulkan_state.swapchainFramebuffers[i]) != VK_SUCCESS) { + LOGF << vulkanErrorMesssages[VULKAN_FRAMEBUFFERS_CREATION_FAILED]; + return VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED; + } + } + + return VULKAN_ERROR_NO_ERROR; +} + + +/** + * @brief Recreate swapchain + * + */ +void recreateSwapchain(VulkanState& vulkan_state) +{ + cleanup_swapchain(vulkan_state); + createSwapchain(vulkan_state); + createSwapchainImageViews(vulkan_state); + createFrambuffers(vulkan_state); +} + +/** + * @brief Cleanup swapchain + * + */ +void cleanup_swapchain(VulkanState& vulkan_state) +{ + vkDeviceWaitIdle(vulkan_state.device); + for (size_t i = 0; i < vulkan_state.swapchainFramebuffers.size(); i++) { + vkDestroyFramebuffer(vulkan_state.device, vulkan_state.swapchainFramebuffers[i], nullptr); + } + + for (size_t i = 0; i < vulkan_state.swapChainImageViews.size(); i++) { + vkDestroyImageView(vulkan_state.device, vulkan_state.swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(vulkan_state.device, vulkan_state.swapchain, nullptr); +} + +/** + * @brief Creates shader stages from hardcoded shader paths. Changes input + * + * @param shaderStages Empty vector, that will be filled with data + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_SHADER_FILE_OPEN_FAILED + * VULKAN_ERROR_SHADER_CREATION_FAILED + */ +uint8_t fillShaderStages(VulkanState& vulkan_state, std::vector* 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(vulkan_state, vertexShaderCode); + std::optional fragmentsShaderModule = createShaderModule(vulkan_state, fragmentsShaderCode); + + if (!vertexShaderModule.has_value() || !fragmentsShaderModule.has_value()) { + return VULKAN_ERROR_SHADER_CREATION_FAILED; + } + + /* Vertex Shader Pipeline Stage */ + VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.pNext = nullptr; + vertShaderStageInfo.flags = 0U; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertexShaderModule.value(); + vertShaderStageInfo.pName = "main"; /* Entry point function */ + vertShaderStageInfo.pSpecializationInfo = nullptr; /* Specialization in a SPIR-V module */ + + /* Fragments Shader Pipeline Stage */ + VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.pNext = nullptr; + fragShaderStageInfo.flags = 0U; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragmentsShaderModule.value(); + fragShaderStageInfo.pName = "main"; /* Entry point function */ + fragShaderStageInfo.pSpecializationInfo = nullptr; /* Specialization in a SPIR-V module */ + + shaderStages->push_back(vertShaderStageInfo); + shaderStages->push_back(fragShaderStageInfo); + + gShaderModules.push_back(std::move(vertexShaderModule.value())); + gShaderModules.push_back(std::move(fragmentsShaderModule.value())); + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Fill dynamic pipeline state info + * + * @param dynamicState structure to be filled + */ +void fillDynamicPipelineStateInfo(VkPipelineDynamicStateCreateInfo* dynamicState) +{ + /* + * Select pipeline stages that will be set dynamically. Those that are selected are ignored in + * pipeline create structures. I'll list ignored members of structs, that will be ignored due to the + * settings and also functions, that will need to be called to set specific state. + */ + std::vector 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 bindingDescription = Vertex::getBindingDescription(); + auto attributeDescriptions = Vertex::getAttributeDescriptions(); + + vertexInputInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo->pNext = nullptr; + vertexInputInfo->flags = 0U; + vertexInputInfo->vertexBindingDescriptionCount = 1U; + vertexInputInfo->pVertexBindingDescriptions = new VkVertexInputBindingDescription(bindingDescription); + vertexInputInfo->vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); + + VkVertexInputAttributeDescription* tmp = new VkVertexInputAttributeDescription[attributeDescriptions.size()]; + std::copy(attributeDescriptions.begin(), attributeDescriptions.end(), tmp); + vertexInputInfo->pVertexAttributeDescriptions = tmp; +} + +/** + * @brief Free allocated vertex input info + * + * @param vertexInputInfo structure to be freed + */ +void freeVertexInputInfo(VkPipelineVertexInputStateCreateInfo* vertexInputInfo) +{ + delete vertexInputInfo->pVertexBindingDescriptions; + delete[] vertexInputInfo->pVertexAttributeDescriptions; +} + +/** + * @brief Fill input assembly info + * + * @param inputAssembly structure to be filled + */ +void fillInputAssemblyInfo(VkPipelineInputAssemblyStateCreateInfo* inputAssembly) +{ + inputAssembly->sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly->pNext = nullptr; + inputAssembly->flags = 0U; + inputAssembly->topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; // TODO: Change to _STRIP + inputAssembly->primitiveRestartEnable = VK_FALSE; /* If I want to break lines during _STRIP, + * I must set this to VK_TRUE */ +} + +/** + * @brief Fill viewport state info + * + * @param viewportState structure to be filled + */ +void fillViewportStateInfo(VulkanState& vulkan_state, VkPipelineViewportStateCreateInfo* viewportState) +{ + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(vulkan_state.swapchainExtent.width); + viewport.height = static_cast(vulkan_state.swapchainExtent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + /* Setting Scissors to fill entire window*/ + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = vulkan_state.swapchainExtent; + + viewportState->sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState->viewportCount = 1; + viewportState->pViewports = new VkViewport(viewport); + viewportState->scissorCount = 1; + viewportState->pScissors = new VkRect2D(scissor); +} + +/** + * @brief Free allocated viewport state input info + * + * @param viewportState structure to be freed + */ +void freeViewportStateInfo(VkPipelineViewportStateCreateInfo* viewportState) +{ + delete viewportState->pViewports; + delete viewportState->pScissors; +} + +/** + * @brief Fill rasteriation info + * + * @param rasterizer + */ +void fillRasterizationInfo(VkPipelineRasterizationStateCreateInfo* rasterizer) +{ + rasterizer->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer->pNext = nullptr; + rasterizer->flags = 0U; + rasterizer->depthClampEnable = VK_FALSE; /* VK_TRUE to play with shadow maps */ + rasterizer->rasterizerDiscardEnable = VK_FALSE; /* Disable/Enable output to framebuffer*/ + rasterizer->polygonMode = VK_POLYGON_MODE_FILL; + rasterizer->cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer->frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer->depthBiasEnable = VK_FALSE; + rasterizer->depthBiasConstantFactor = 0.0f; + rasterizer->depthBiasClamp = 0.0f; + rasterizer->depthBiasSlopeFactor = 0.0f; + rasterizer->lineWidth = 1.0f; +} + +/** + * @brief Fill multisampling state info + * + * @param multisampling structure to be filled + */ +void fillMultisamplingStateInfo(VkPipelineMultisampleStateCreateInfo* multisampling) +{ + multisampling->sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling->pNext = nullptr; + multisampling->flags = 0U; + multisampling->rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling->sampleShadingEnable = VK_FALSE; + multisampling->minSampleShading = 1.0f; + multisampling->pSampleMask = nullptr; + multisampling->alphaToCoverageEnable = VK_FALSE; + multisampling->alphaToOneEnable = VK_FALSE; +} + +/** + * @brief Fill color blend state info + * + * @param colorBlending structure to be filled + */ +void fillColorBlendStateInfo(VkPipelineColorBlendStateCreateInfo* colorBlending) +{ + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.blendEnable = VK_FALSE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + colorBlending->sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending->pNext = nullptr; + colorBlending->flags = 0U; + colorBlending->logicOpEnable = VK_FALSE; + colorBlending->logicOp = VK_LOGIC_OP_COPY; + colorBlending->attachmentCount = 1U; + colorBlending->pAttachments = new VkPipelineColorBlendAttachmentState(colorBlendAttachment); + colorBlending->blendConstants[0] = 0.0f; + colorBlending->blendConstants[1] = 0.0f; + colorBlending->blendConstants[2] = 0.0f; + colorBlending->blendConstants[3] = 0.0f; +} + +/** + * @brief Free allocated color blend state info + * + * @param colorBlending Structure to be freed + */ +void freeColorBlendInfo(VkPipelineColorBlendStateCreateInfo* colorBlending) +{ + delete colorBlending->pAttachments; +} + +uint8_t create_pipeline(VulkanState& vulkan_state) +{ + uint8_t status = VULKAN_ERROR_NO_ERROR; + + STATUS_CHECK(createSwapchain(vulkan_state)); + STATUS_CHECK(createSwapchainImageViews(vulkan_state)); + STATUS_CHECK(createRenderPass(vulkan_state)); + STATUS_CHECK(createDescriptorSetLayout(vulkan_state)); + STATUS_CHECK(createPipelineLayout(vulkan_state)); + STATUS_CHECK(createGraphicsPipeline(vulkan_state)); + STATUS_CHECK(createFrambuffers(vulkan_state)); + + return status; +} + +void destroy_pipeline(VulkanState& vulkan_state) +{ + vkDestroyPipeline(vulkan_state.device, vulkan_state.graphicsPipeline, nullptr); + vkDestroyPipelineLayout(vulkan_state.device, vulkan_state.pipelineLayout, nullptr); + vkDestroyRenderPass(vulkan_state.device, vulkan_state.renderPass, nullptr); + + for (size_t i = 0; i < gShaderModules.size(); i++) { + vkDestroyShaderModule(vulkan_state.device, gShaderModules[i], nullptr); + } +} + +} // namespace rse::graphics::vulkanbase \ No newline at end of file diff --git a/graphics/src/vulkan_pipeline.hpp b/graphics/src/vulkan_pipeline.hpp new file mode 100644 index 00000000..3fa28821 --- /dev/null +++ b/graphics/src/vulkan_pipeline.hpp @@ -0,0 +1,43 @@ +#ifndef RSE_VULKAN_PIPELINE_HPP +#define RSE_VULKAN_PIPELINE_HPP + +#include +#include + +#include "vulkan_commons.hpp" + +namespace rse::graphics::vulkanbase +{ + +/** + * @brief Create a graphics pipeline + * + * @param vulkan_state + * @return uint8_t + */ +uint8_t create_pipeline(VulkanState& vulkan_state); + +/** + * @brief Cleanup swapchain + * + * @param vulkan_state + */ +void cleanup_swapchain(VulkanState& vulkan_state); + +/** + * @brief Recreates swapchain + * + * @param vulkan_state + */ +void recreateSwapchain(VulkanState& vulkan_state); + +/** + * @brief Destroy all vulkan pipeline objects + * + * @param vulkan_state + */ +void destroy_pipeline(VulkanState& vulkan_state); + +} + +#endif diff --git a/Graphics/src/window.cpp b/graphics/src/window.cpp similarity index 91% rename from Graphics/src/window.cpp rename to graphics/src/window.cpp index 54067932..6b517be6 100644 --- a/Graphics/src/window.cpp +++ b/graphics/src/window.cpp @@ -11,9 +11,9 @@ #include "window.hpp" -#include "localeWindow.hpp" -#include "logger.hpp" -#include "vulkanBase.hpp" +#include "locale_window.hpp" +#include "utilities/logger.hpp" +#include "vulkan_base.hpp" namespace rse::graphics::window { @@ -96,11 +96,11 @@ uint16_t windowInit() return WINDOW_ERROR_NO_ERROR; } -uint16_t windowLoop() +uint16_t windowLoop(rse::graphics::vulkanbase::VulkanState& vulkan_state) { while (!glfwWindowShouldClose(gpWindowHandle)) { glfwPollEvents(); - rse::graphics::vulkanbase::drawFrame(); + rse::graphics::vulkanbase::drawFrame(vulkan_state); } return WINDOW_ERROR_NO_ERROR; diff --git a/Graphics/include/window.hpp b/graphics/src/window.hpp similarity index 77% rename from Graphics/include/window.hpp rename to graphics/src/window.hpp index e22f9f9a..c3dafe8f 100644 --- a/Graphics/include/window.hpp +++ b/graphics/src/window.hpp @@ -9,12 +9,14 @@ * */ -#pragma once +#ifndef RSE_WINDOW_HPP +#define RSE_WINDOW_HPP #include /* Vulkan header MUST be included before glfw */ #include +#include "vulkan_commons.hpp" namespace rse::graphics::window { @@ -32,9 +34,10 @@ uint16_t windowInit(); /** * @brief Main window loop * + * @param vulkan_state * @return uint16_t WINDOW_SUCCESS or error code */ -uint16_t windowLoop(); +uint16_t windowLoop(rse::graphics::vulkanbase::VulkanState& vulkan_state); /** * @brief Closes window and terminates GLFW @@ -44,4 +47,6 @@ void windowTerminate(); GLFWwindow* getWindowHandle(); -} /* namespace rse::graphics::window */ \ No newline at end of file +} /* namespace rse::graphics::window */ + +#endif \ No newline at end of file diff --git a/RedScarfEngine/CMakeLists.txt b/red_scarf_engine/CMakeLists.txt similarity index 65% rename from RedScarfEngine/CMakeLists.txt rename to red_scarf_engine/CMakeLists.txt index f19c1321..cfc6f2ad 100644 --- a/RedScarfEngine/CMakeLists.txt +++ b/red_scarf_engine/CMakeLists.txt @@ -1,8 +1,7 @@ add_executable(${PROJECT_NAME} src/main.cpp) target_include_directories(${PROJECT_NAME} PRIVATE - ${CMAKE_SOURCE_DIR}/Utilities/include - ${CMAKE_SOURCE_DIR}/Graphics/include) + ${CMAKE_SOURCE_DIR}) target_link_libraries(${PROJECT_NAME} PRIVATE rse_graphics) \ No newline at end of file diff --git a/red_scarf_engine/src/main.cpp b/red_scarf_engine/src/main.cpp new file mode 100644 index 00000000..d63eaa5a --- /dev/null +++ b/red_scarf_engine/src/main.cpp @@ -0,0 +1,13 @@ +#include "graphics/rse_graphics.hpp" + +int main(int argc, char** argv) +{ + (void)argc; + (void)argv; + + rse::graphics::RseGraphics graphics_pipeline; + + graphics_pipeline.graphics_run(); + + return 0; +} diff --git a/third_party/vma/include/vk_mem_alloc.h b/third_party/vma/vk_mem_alloc.h similarity index 100% rename from third_party/vma/include/vk_mem_alloc.h rename to third_party/vma/vk_mem_alloc.h diff --git a/utilities/CMakeLists.txt b/utilities/CMakeLists.txt new file mode 100644 index 00000000..1875e1b2 --- /dev/null +++ b/utilities/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(rse_utilities SHARED) + +target_include_directories(rse_utilities PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ) + +target_sources(rse_utilities PRIVATE + src/logger.cpp + src/file_utils.cpp + ) diff --git a/utilities/file_utils.hpp b/utilities/file_utils.hpp new file mode 100644 index 00000000..c5df0112 --- /dev/null +++ b/utilities/file_utils.hpp @@ -0,0 +1,15 @@ +#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/include/localization.hpp b/utilities/localization.hpp similarity index 80% rename from Utilities/include/localization.hpp rename to utilities/localization.hpp index 1d638cb3..943fa60a 100644 --- a/Utilities/include/localization.hpp +++ b/utilities/localization.hpp @@ -10,9 +10,9 @@ */ #pragma once -#define SELECTED_LANGUAGE rse::locale::EN_US +#define SELECTED_LANGUAGE rse::utils::locale::EN_US -namespace rse::locale +namespace rse::utils::locale { /** diff --git a/Utilities/include/logger.hpp b/utilities/logger.hpp similarity index 67% rename from Utilities/include/logger.hpp rename to utilities/logger.hpp index b623e243..4ff4257f 100644 --- a/Utilities/include/logger.hpp +++ b/utilities/logger.hpp @@ -9,7 +9,8 @@ * */ -#pragma once +#ifndef RSE_LOGGER_HPP +#define RSE_LOGGER_HPP #include @@ -17,16 +18,16 @@ #define LOG_LEVEL LOGLEVEL_DEBUG #endif -#define LOGT rse::log::Logger(rse::log::LOGLEVEL_TRACE) -#define LOGD rse::log::Logger(rse::log::LOGLEVEL_DEBUG) -#define LOGI rse::log::Logger(rse::log::LOGLEVEL_INFO) -#define LOGW rse::log::Logger(rse::log::LOGLEVEL_WARNING) -#define LOGE rse::log::Logger(rse::log::LOGLEVEL_ERROR) -#define LOGF rse::log::Logger(rse::log::LOGLEVEL_FATAL) +#define LOGT rse::utils::log::Logger(rse::utils::log::LOGLEVEL_TRACE) +#define LOGD rse::utils::log::Logger(rse::utils::log::LOGLEVEL_DEBUG) +#define LOGI rse::utils::log::Logger(rse::utils::log::LOGLEVEL_INFO) +#define LOGW rse::utils::log::Logger(rse::utils::log::LOGLEVEL_WARNING) +#define LOGE rse::utils::log::Logger(rse::utils::log::LOGLEVEL_ERROR) +#define LOGF rse::utils::log::Logger(rse::utils::log::LOGLEVEL_FATAL) -namespace rse::log +namespace rse::utils::log { /** @@ -73,4 +74,6 @@ class Logger { } }; -} // namespace rse::log \ No newline at end of file +} // namespace rse::log + +#endif \ No newline at end of file diff --git a/utilities/src/file_utils.cpp b/utilities/src/file_utils.cpp new file mode 100644 index 00000000..189cc4b9 --- /dev/null +++ b/utilities/src/file_utils.cpp @@ -0,0 +1,33 @@ +#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.cpp b/utilities/src/logger.cpp similarity index 97% rename from Utilities/src/logger.cpp rename to utilities/src/logger.cpp index 8322a928..81db6369 100644 --- a/Utilities/src/logger.cpp +++ b/utilities/src/logger.cpp @@ -13,7 +13,7 @@ #include -namespace rse::log +namespace rse::utils::log { Logger::Logger(LogLevel level) : printMessage(false)