From 0c45da5cc4e5043b0e83a6c141520f3c1c9ba236 Mon Sep 17 00:00:00 2001 From: Piotr Krygier Date: Thu, 9 Mar 2023 14:19:07 +0100 Subject: [PATCH] Cleanup code --- src/vulkanBase.cpp | 1764 +++++++++++++++++++++++--------------------- 1 file changed, 938 insertions(+), 826 deletions(-) diff --git a/src/vulkanBase.cpp b/src/vulkanBase.cpp index e528c82f..75717954 100644 --- a/src/vulkanBase.cpp +++ b/src/vulkanBase.cpp @@ -46,6 +46,7 @@ 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; \ @@ -164,21 +165,12 @@ std::vector indices = { /* Define Validation Layers for debug build */ #ifndef NDEBUG -const std::vector gEnabledInstanceLayersNames = {"VK_LAYER_KHRONOS_validation"}; +std::vector gEnabledInstanceLayersNames = {"VK_LAYER_KHRONOS_validation"}; #else std::vector gEnabledInstanceLayersNames = {}; #endif -const std::vector gEnabledInstanceExtensionsNames = { - "VK_KHR_surface", -#ifdef __linux__ - "VK_KHR_xcb_surface", -#endif - "VK_KHR_device_group_creation", -#ifndef NDEBUG - "VK_EXT_debug_utils", -#endif -}; +std::vector gEnabledInstanceExtensionsNames = {}; const std::vector gEnabledDeviceExtensionsNames = { "VK_KHR_swapchain", @@ -198,6 +190,7 @@ const std::map gShaderPaths = { 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; @@ -223,6 +216,7 @@ 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? */ @@ -231,6 +225,167 @@ 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 * @@ -242,232 +397,7 @@ std::vector gUniformBuffers = {}; * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED */ -static uint8_t createBuffer(const VkDeviceSize size, VkBufferUsageFlags bufferUsage, - VmaMemoryUsage memoryUsage, const VmaAllocationCreateFlags allocationFlags, - Buffer& buffer); - -/** - * @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: - */ -static uint8_t copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size); - -/** - * @brief Create the Vulkan Instance object - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - * VULKAN_ERROR_INSTANCE_INIT_FAILED - */ -static uint8_t createInstance(); - -/** - * @brief Create a Surface, connection between Vulkan and actual window - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - */ -static uint8_t createSurface(); - -/** - * @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 - */ -static uint8_t pickPhysicalDevice(); - -/** - * @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 - */ -static uint8_t createDevice(); - -#ifndef NDEBUG -/** - * @brief Setup debugging system for Vulkan API - * - */ -static void setupDebugMessenger(); - -/** - * @brief Create a Memory Allocator object - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - * VULKAN_ERROR_ALLOCATOR_CREATION_FAILED - */ -static uint8_t createMemoryAllocator(); - -/** - * @brief Create Command Pools - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - * VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED - */ -static uint8_t createCommandPools(); - -/** - * @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 - */ -static uint8_t createVertexBuffer(); - -/** - * @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 - */ -static uint8_t createIndexBuffer(); - -/** - * @brief Create a Uniform Buffers - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - */ -static uint8_t createUniformBuffers(); - -/** - * @brief Allocate Command Buffers - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - * VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED - */ -static uint8_t allocateCommandBuffers(); - -/** - * @brief Create a Swapchain - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - * VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED - */ -static uint8_t createSwapchain(); - -/** - * @brief Create a Swapchain Image Views - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - */ -static uint8_t createSwapchainImageViews(); - -/** - * @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 - */ -static uint8_t fillShaderStages(std::vector* shaderStages); - -/** - * @brief Reads file and puts its content into char buffer - * - * @param fileName Path to file - * @return std::vector Binary buffer data - */ -static std::vector readFile(const std::string& fileName); - -/** - * @brief Creates shader module - * - * @param binaryData Shader compiled code - * @return std::optional Handle to compiled shader module or nullopt - */ -static std::optional createShaderModule(const std::vector& binaryData); - -/** - * @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 - */ -static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData); - -/** - * @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 - */ -static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDebugUtilsMessengerEXT* pDebugMessenger); - -/** - * @brief Proxy for vkDestroyDebugUtilsMessengerEXT - * - * @param instance Vulkan instance object - * @param debugMessenger Debug messanger handle - * @param pAllocator Memory allocator - */ -static void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, - const VkAllocationCallbacks* pAllocator); - -/** - * @brief Create a Render Pass - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - * VULKAN_ERROR_RENDERPASS_CREATION_FAILED - */ -static uint8_t createRenderPass(); - -/** - * @brief Create a Pipeline Layout - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - * VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED - */ -static uint8_t createPipelineLayout(); - -/** - * @brief Create a Descriptor Set Layout - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - */ -static uint8_t createDescriptorSetLayout(); - -/** - * @brief Create Graphics Pipeline - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - * VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED - */ -static uint8_t createGraphicsPipeline(); - -/** - * @brief Create Frambuffers - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - */ -static uint8_t createFrambuffers(); - -/** - * @brief Recreate swapchain - * - */ -static void recreateSwapchain(); - - -static uint8_t createBuffer(const VkDeviceSize size, VkBufferUsageFlags bufferUsage, VmaMemoryUsage memoryUsage, +uint8_t createBuffer(const VkDeviceSize size, VkBufferUsageFlags bufferUsage, VmaMemoryUsage memoryUsage, const VmaAllocationCreateFlags allocationFlags, Buffer& buffer) { VkBufferCreateInfo vertexBufferInfo; @@ -505,7 +435,15 @@ static uint8_t createBuffer(const VkDeviceSize size, VkBufferUsageFlags bufferUs return VULKAN_ERROR_NO_ERROR; } -static uint8_t copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size) +/** + * @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; @@ -553,71 +491,357 @@ static uint8_t copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size) return VULKAN_ERROR_NO_ERROR; } - - -static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData) +/** + * @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) { - (void)messageSeverity; - (void)messageType; - (void)pUserData; - - fprintf(stderr, "validation layer: %s\n", pCallbackData->pMessage); - - return VK_FALSE; -} - -static 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; + std::ifstream file{fileName, std::ios::ate | std::ios::binary}; + if (!file.is_open()) + { + return {}; } - return VK_SUCCESS; + /* 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; } -static void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, - const VkAllocationCallbacks* pAllocator) +/** + * @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) { - PFN_vkDestroyDebugUtilsMessengerEXT func = - (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); - if (func != nullptr) { - func(instance, debugMessenger, pAllocator); - } -} - -static void setupDebugMessenger() -{ - VkDebugUtilsMessengerCreateInfoEXT createInfo; - - createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + VkShaderModule shaderModule; + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; 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; + createInfo.codeSize = binaryData.size(); + createInfo.pCode = reinterpret_cast(binaryData.data()); - if (CreateDebugUtilsMessengerEXT(gVulkanInstance, &createInfo, nullptr, &gDebugMessenger) != VK_SUCCESS) { - fprintf(stderr, "failed to set up debug messenger!"); + 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 -static uint8_t createInstance() +#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; @@ -696,7 +920,12 @@ static uint8_t createInstance() return VULKAN_ERROR_NO_ERROR; } -static uint8_t createSurface() +/** + * @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]; @@ -706,7 +935,14 @@ static uint8_t createSurface() return VULKAN_ERROR_NO_ERROR; } -static uint8_t pickPhysicalDevice() +/** + * @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; @@ -748,7 +984,14 @@ static uint8_t pickPhysicalDevice() return VULKAN_ERROR_NO_ERROR; } -static uint8_t createDevice() +/** + * @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; @@ -846,7 +1089,13 @@ static uint8_t createDevice() return VULKAN_ERROR_NO_ERROR; } -static uint8_t createMemoryAllocator() +/** + * @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; @@ -867,200 +1116,13 @@ static uint8_t createMemoryAllocator() return VULKAN_ERROR_NO_ERROR; } -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} - -static 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; -} - -static uint8_t createSwapchain() +/** + * @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; @@ -1108,7 +1170,12 @@ static uint8_t createSwapchain() return VULKAN_ERROR_NO_ERROR; } -static uint8_t createSwapchainImageViews() +/** + * @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++) { @@ -1140,251 +1207,13 @@ static uint8_t createSwapchainImageViews() return VULKAN_ERROR_NO_ERROR; } -static 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; -} - - -static 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; -} - -static 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; -} - -static 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; -} - -static void freeDynamicPipelineStateInfo(VkPipelineDynamicStateCreateInfo* dynamicState) -{ - delete[] dynamicState->pDynamicStates; -} - -static 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; -} - -static void freeVertexInputInfo(VkPipelineVertexInputStateCreateInfo* vertexInputInfo) -{ - delete vertexInputInfo->pVertexBindingDescriptions; - delete[] vertexInputInfo->pVertexAttributeDescriptions; -} - -static 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 */ - } - -static void fillViewportInfo(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); -} - -static void freeViewportInfo(VkPipelineViewportStateCreateInfo* viewportState) -{ - delete viewportState->pViewports; - delete viewportState->pScissors; -} - -static 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; -} - -static void fillMultisamplingInfo(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; -} - -static void fillColorBlendInfo(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; -} - -static void freeColorBlendInfo(VkPipelineColorBlendStateCreateInfo* colorBlending) -{ - delete colorBlending->pAttachments; -} - - -static uint8_t createRenderPass() +/** + * @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; @@ -1421,28 +1250,12 @@ static uint8_t createRenderPass() return VULKAN_ERROR_NO_ERROR; } -static 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; -} - -static uint8_t createDescriptorSetLayout() +/** + * @brief Create a Descriptor Set Layout + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + */ +uint8_t createDescriptorSetLayout() { VkDescriptorSetLayoutCreateInfo layoutInfo; VkDescriptorSetLayoutBinding uboLayoutBinding; @@ -1466,8 +1279,40 @@ static uint8_t createDescriptorSetLayout() 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; -static uint8_t createGraphicsPipeline() + + 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; @@ -1483,10 +1328,10 @@ static uint8_t createGraphicsPipeline() STATUS_CHECK(fillShaderStages(&shaderStages)); fillVertexInputInfo(&vertexInputInfo); fillInputAssemblyInfo(&inputAssembly); - fillViewportInfo(&viewportState); + fillViewportStateInfo(&viewportState); fillRasterizationInfo(&rasterizer); - fillMultisamplingInfo(&multisampling); - fillColorBlendInfo(&colorBlending); + fillMultisamplingStateInfo(&multisampling); + fillColorBlendStateInfo(&colorBlending); fillDynamicPipelineStateInfo(&dynamicState); VkGraphicsPipelineCreateInfo pipelineInfo{}; @@ -1518,13 +1363,18 @@ static uint8_t createGraphicsPipeline() freeDynamicPipelineStateInfo(&dynamicState); freeColorBlendInfo(&colorBlending); - freeViewportInfo(&viewportState); + freeViewportStateInfo(&viewportState); freeVertexInputInfo(&vertexInputInfo); return status; } -static uint8_t createFrambuffers() +/** + * @brief Create Frambuffers + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + */ +uint8_t createFrambuffers() { gSwapchainFramebuffers.resize(gSwapChainImageViews.size()); @@ -1551,7 +1401,331 @@ static uint8_t createFrambuffers() return VULKAN_ERROR_NO_ERROR; } -static uint8_t recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) +/** + * @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::rotate(glm::mat4(1.0f), time * glm::radians(90.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; + 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}}}; @@ -1609,73 +1783,11 @@ static uint8_t recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t image return VULKAN_ERROR_NO_ERROR; } -static 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; -} - -static 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::rotate(glm::mat4(1.0f), time * glm::radians(90.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; - memcpy(gUniformBuffers[currentImage].allocationInfo.pMappedData, &ubo, sizeof(ubo)); -} - -static 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); -} - -static void recreateSwapchain() -{ - cleanupSwapchain(); - createSwapchain(); - createSwapchainImageViews(); - createFrambuffers(); -} - uint8_t initVulkan() { uint8_t status = VULKAN_ERROR_NO_ERROR; + setEnabledExtension(); STATUS_CHECK(createInstance()); #ifndef NDEBUG setupDebugMessenger();