diff --git a/include/localeVulkan.hpp b/include/localeVulkan.hpp index c1f759d6..1733de00 100644 --- a/include/localeVulkan.hpp +++ b/include/localeVulkan.hpp @@ -31,6 +31,8 @@ enum Errors VULKAN_FAILED_TO_CREATE_DEVICE, VULKAN_ALLOCATOR_CREATION_FAILED, VULKAN_COMMAND_POOL_CREATION_FAILED, + VULKAN_VERTEX_BUFFER_CREATION_FAILED, + VULKAN_VERTEX_BUFFER_MAPPING_FAILED, VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED, VULKAN_SURFACE_CREATION_FAILED, VULKAN_SWAPCHAIN_CREATION_FAILED, @@ -62,6 +64,10 @@ std::array, LAST_LANGUAGE> vulkanErrorMess "Failed to create Vulkan Memory Allocator", /* VULKAN_COMMAND_POOL_CREATION_FAILED */ "Failed to create command pool", + /* VULKAN_VERTEX_BUFFER_CREATION_FAILED */ + "Failed to create a vertex buffer", + /* VULKAN_VERTEX_BUFFER_MAPPING_FAILED */ + "Failed to map vulkan memory buffer", /* VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED */ "Failed to allocate command buffers", /* VULKAN_SURFACE_CREATION_FAILED */ diff --git a/include/vulkanBase.hpp b/include/vulkanBase.hpp index d6d31262..bd5a48d1 100644 --- a/include/vulkanBase.hpp +++ b/include/vulkanBase.hpp @@ -16,9 +16,25 @@ namespace rse::graphics::vulkanbase { +/** + * @brief Initialize Vulkan backend + * + * @return uint8_t 0 on success. Status code on failure + */ + + uint8_t initVulkan(); + +/** + * @brief Deinitialize Vulkan backend + * + */ void deinitVulkan(); +/** + * @brief Draw frame on the screen. + * + */ void drawFrame(); } /* namespace rse::graphics::vulkanbase */ \ No newline at end of file diff --git a/shaders/shader.frag b/shaders/shader.frag index 13009da8..e948fd6e 100644 --- a/shaders/shader.frag +++ b/shaders/shader.frag @@ -1,7 +1,6 @@ #version 450 layout(location = 0) in vec3 fragColor; - layout(location = 0) out vec4 outColor; void main() { diff --git a/shaders/shader.vert b/shaders/shader.vert index 66d67662..cb40e1e9 100644 --- a/shaders/shader.vert +++ b/shaders/shader.vert @@ -1,20 +1,11 @@ #version 450 +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec3 inColor; + layout(location = 0) out vec3 fragColor; -vec2 positions[3] = vec2[]( - vec2(0.0, -0.5), - vec2(0.5, 0.5), - vec2(-0.5, 0.5) -); - -vec3 colors[3] = vec3[]( - vec3(1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, 0.0, 1.0) -); - void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - fragColor = colors[gl_VertexIndex]; + gl_Position = vec4(inPosition, 0.0, 1.0); + fragColor = inColor; } \ No newline at end of file diff --git a/src/vulkanBase.cpp b/src/vulkanBase.cpp index a454d1bd..16a907c1 100644 --- a/src/vulkanBase.cpp +++ b/src/vulkanBase.cpp @@ -33,6 +33,8 @@ #include "logger.hpp" #include "window.hpp" +#include + #include #include #include @@ -66,6 +68,8 @@ enum VULKAN_ERROR VULKAN_ERROR_DEVICE_CREATION_FAILED, VULKAN_ERROR_ALLOCATOR_CREATION_FAILED, VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED, + VULKAN_ERROR_VERTEX_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, @@ -86,6 +90,60 @@ enum class ShaderStage 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; + } + +}; + +/** + * @brief Wrapper for Vulan buffer. Holds allocation data; + * + */ +struct Buffer +{ + VkBuffer buffer; + VmaAllocation allocation; +}; + +// FIXME: Temporary array of vertices, for testing purposes +const std::vector vertices = { + {{0.0f, -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}} +}; + /* Define Validation Layers for debug build */ #ifndef NDEBUG const std::vector gEnabledInstanceLayersNames = {"VK_LAYER_KHRONOS_validation"}; @@ -143,6 +201,7 @@ std::vector gSwapchainFramebuffers; std::vector gImageAvailableSemaphores; std::vector gRenderFinishedSemaphores; std::vector gInFlightFences; +Buffer gVertexBuffer; /** @@ -201,6 +260,15 @@ static uint8_t createMemoryAllocator(); */ 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_VERTEX_BUFFER_CREATION_FAILED +* VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED + */ +static uint8_t createVertexBuffer(); + /** * @brief Allocate Command Buffers * @@ -326,7 +394,10 @@ static uint8_t createGraphicsPipeline(); */ static uint8_t createFrambuffers(); - +/** + * @brief Recreate swapchain + * + */ static void recreateSwapchain(); @@ -595,7 +666,7 @@ static uint8_t createMemoryAllocator() vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; VmaAllocatorCreateInfo allocatorCreateInfo = {0}; - allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2; + allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_3; allocatorCreateInfo.physicalDevice = gPhysicalDevice; allocatorCreateInfo.device = gDevice; allocatorCreateInfo.instance = gVulkanInstance; @@ -626,6 +697,53 @@ static uint8_t createCommandPools() return VULKAN_ERROR_NO_ERROR; } +static uint8_t createVertexBuffer() +{ + VkBufferCreateInfo vertexBufferInfo; + VmaAllocationCreateInfo allocInfo; + void* mappedData; + + vertexBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + vertexBufferInfo.pNext = nullptr; + vertexBufferInfo.flags = 0U; + vertexBufferInfo.size = sizeof(vertices[0]) * vertices.size(); // FIXME: Harcoded for now + vertexBufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + 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? */ + allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + allocInfo.usage = VMA_MEMORY_USAGE_AUTO; + allocInfo.memoryTypeBits = 0U; + allocInfo.requiredFlags = 0U; + allocInfo.preferredFlags = 0U; + allocInfo.pool = VK_NULL_HANDLE; + allocInfo.pUserData = VK_NULL_HANDLE; + allocInfo.priority = 0.0f; + + if (VK_SUCCESS != vmaCreateBuffer(gAllocator, + &vertexBufferInfo, + &allocInfo, + &gVertexBuffer.buffer, + &gVertexBuffer.allocation, + nullptr)) { + LOGF << vulkanErrorMesssages[VULKAN_VERTEX_BUFFER_CREATION_FAILED]; + return VULKAN_ERROR_VERTEX_BUFFER_CREATION_FAILED; + } + + /* Fill vertex buffer */ + if (VK_SUCCESS != vmaMapMemory(gAllocator, gVertexBuffer.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, gVertexBuffer.allocation); + + return VULKAN_ERROR_NO_ERROR; +} + static uint8_t allocateCommandBuffers() { VkCommandBufferAllocateInfo allocateInfo; @@ -807,7 +925,6 @@ static uint8_t fillShaderStages(std::vector* sh shaderStages->push_back(vertShaderStageInfo); shaderStages->push_back(fragShaderStageInfo); - // FIXME: Actually use those shader stages gShaderModules.push_back(std::move(vertexShaderModule.value())); gShaderModules.push_back(std::move(fragmentsShaderModule.value())); @@ -846,15 +963,26 @@ static void freeDynamicPipelineStateInfo(VkPipelineDynamicStateCreateInfo* dynam 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 = 0U; - vertexInputInfo->pVertexBindingDescriptions = nullptr; - vertexInputInfo->vertexAttributeDescriptionCount = 0U; - vertexInputInfo->pVertexAttributeDescriptions = nullptr; + vertexInputInfo->vertexBindingDescriptionCount = 1U; + vertexInputInfo->pVertexBindingDescriptions = new VkVertexInputBindingDescription(bindingDescription); + vertexInputInfo->vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); - // FIXME: Shaders have hardcoded vertices for now. Change this later + 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) @@ -1072,8 +1200,9 @@ static uint8_t createGraphicsPipeline() } freeDynamicPipelineStateInfo(&dynamicState); - freeViewportInfo(&viewportState); freeColorBlendInfo(&colorBlending); + freeViewportInfo(&viewportState); + freeVertexInputInfo(&vertexInputInfo); return status; } @@ -1108,6 +1237,13 @@ static uint8_t createFrambuffers() static 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 @@ -1116,8 +1252,6 @@ static uint8_t recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t image throw std::runtime_error("failed to begin recording command buffer!"); } - VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; - VkRenderPassBeginInfo renderPassInfo{}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.renderPass = gRenderPass; renderPassInfo.framebuffer = gSwapchainFramebuffers[imageIndex]; @@ -1129,7 +1263,7 @@ static uint8_t recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t image vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, gGraphicsPipeline); - VkViewport viewport{}; + viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = static_cast(gSwapchainExtent.width); @@ -1138,12 +1272,12 @@ static uint8_t recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t image viewport.maxDepth = 1.0f; vkCmdSetViewport(commandBuffer, 0, 1, &viewport); - VkRect2D scissor{}; scissor.offset = {0, 0}; scissor.extent = gSwapchainExtent; vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - vkCmdDraw(commandBuffer, 3, 1, 0, 0); + vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + vkCmdDraw(commandBuffer, static_cast(vertices.size()), 1, 0, 0); vkCmdEndRenderPass(commandBuffer); @@ -1216,6 +1350,7 @@ uint8_t initVulkan() STATUS_CHECK(createDevice()); STATUS_CHECK(createMemoryAllocator()); STATUS_CHECK(createCommandPools()); + STATUS_CHECK(createVertexBuffer()); STATUS_CHECK(allocateCommandBuffers()); STATUS_CHECK(createSwapchain()); STATUS_CHECK(createSwapchainImageViews()); @@ -1290,6 +1425,7 @@ void deinitVulkan() { cleanupSwapchain(); + vmaDestroyBuffer(gAllocator, gVertexBuffer.buffer, gVertexBuffer.allocation); for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) { vkDestroySemaphore(gDevice, gImageAvailableSemaphores[i], nullptr); vkDestroySemaphore(gDevice, gRenderFinishedSemaphores[i], nullptr);