#include "vulkan_buffers.hpp" #include #define GLM_FORCE_RADIANS #include #include "vulkan_errors.hpp" #include "locale_vulkan.hpp" #include "utilities/logger.hpp" #include "mesh_controller.hpp" namespace rse::graphics::vulkanbase { #define MAX_VERTEX_BUFFER_SIZE 33554432 /* 32 MB*/ 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 */ std::vector gVertexBuffers = {}; std::vector gIndexBuffers = {}; std::vector gInstanceBuffers = {}; /* CPU visible buffer. TODO: Do we need this global? */ VkCommandPool gCommandPool = VK_NULL_HANDLE; static uint8_t createBuffer(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, VkDeviceSize dest_offset = 0); 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, VkDeviceSize dest_offset) { /* Vulkan buffers can only be copied using command buffers */ VkBufferCopy copyRegion; VkSubmitInfo submitInfo; VkCommandBuffer copyCommandBuffer; VkCommandBufferBeginInfo beginInfo; VkCommandBufferAllocateInfo allocInfo; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.pNext = nullptr; allocInfo.commandPool = gCommandPool; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.commandBufferCount = 1; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.pNext = nullptr; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; beginInfo.pInheritanceInfo = nullptr; copyRegion.srcOffset = 0; copyRegion.dstOffset = dest_offset; copyRegion.size = size; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.pWaitDstStageMask = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = ©CommandBuffer; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; // FIXME: At result checks VkResult status; status = vkAllocateCommandBuffers(vulkan_state.device, &allocInfo, ©CommandBuffer); status = vkBeginCommandBuffer(copyCommandBuffer, &beginInfo); vkCmdCopyBuffer(copyCommandBuffer, src, dst, 1, ©Region); status = vkEndCommandBuffer(copyCommandBuffer); status = vkQueueSubmit(vulkan_state.graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); /* TODO: Use fences to wait instead of idle */ status = vkQueueWaitIdle(vulkan_state.graphicsQueue); vkFreeCommandBuffers(vulkan_state.device, gCommandPool, 1, ©CommandBuffer); if (status != VK_SUCCESS) { return VULKAN_ERROR_NO_ERROR; } return VULKAN_ERROR_NO_ERROR; } /** * @brief Create Command Pools * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED */ uint8_t createCommandPools(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) { VkDeviceSize bufferSize; Buffer vertex_buffer; bufferSize = MAX_VERTEX_BUFFER_SIZE; /* 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 */ vertex_buffer)) { LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; return VULKAN_ERROR_BUFFER_CREATION_FAILED; } vertex_buffer.allocated_size = 0; gVertexBuffers.push_back(std::move(vertex_buffer)); return VULKAN_ERROR_NO_ERROR; } /** * @brief Create an index buffer, connected with vertex buffer * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_BUFFER_CREATION_FAILED */ uint8_t createIndexBuffer(VulkanState& vulkan_state) { VkDeviceSize bufferSize; Buffer index_buffer; bufferSize = MAX_VERTEX_BUFFER_SIZE; /* Create Index 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 */ index_buffer)) { LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; return VULKAN_ERROR_BUFFER_CREATION_FAILED; } index_buffer.allocated_size = 0; gIndexBuffers.push_back(std::move(index_buffer)); return VULKAN_ERROR_NO_ERROR; } uint8_t createInstanceBuffer(VulkanState& vulkan_state) { VkDeviceSize bufferSize; Buffer instance_buffer; bufferSize = MAX_VERTEX_BUFFER_SIZE; /* Create Instance 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 */ instance_buffer)) { LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; return VULKAN_ERROR_BUFFER_CREATION_FAILED; } instance_buffer.allocated_size = 0; gInstanceBuffers.push_back(std::move(instance_buffer)); return VULKAN_ERROR_NO_ERROR; } /** * @brief Create a Uniform Buffers * * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: */ uint8_t createUniformBuffers(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::translate(glm::mat4(1.0f), glm::vec3(1.0f, 0.0f, 0.0f)) * glm::rotate(glm::mat4(1.0f), glm::radians(45.0f), glm::vec3(0.0f, 0.0f, 1.0f)) * glm::scale(glm::mat4(1.0f), glm::vec3(2, 1, 1)); 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)); } uint8_t update_mesh_buffers(VulkanState& vulkan_state, const std::vector& vertices, const std::vector& indices) { // void* mapped_data; Buffer staging_buffer; auto vertices_size = sizeof(vertices[0]) * vertices.size(); auto indices_size = sizeof(indices[0]) * indices.size(); auto last_vertex_buffer = &gVertexBuffers[gVertexBuffers.size() - 1]; auto last_index_buffer = &gIndexBuffers[gIndexBuffers.size() - 1]; /* Creating staging buffer*/ if (VK_SUCCESS != createBuffer(vulkan_state, MAX_VERTEX_BUFFER_SIZE, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, staging_buffer)) { LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; return VULKAN_ERROR_BUFFER_CREATION_FAILED; } /* Fill staging buffer with vertex data */ memset(staging_buffer.allocationInfo.pMappedData, 0, MAX_VERTEX_BUFFER_SIZE); memcpy(staging_buffer.allocationInfo.pMappedData, vertices.data(), vertices_size); copyBuffer(vulkan_state, staging_buffer.buffer, last_vertex_buffer->buffer, vertices_size, last_vertex_buffer->allocated_size); /* Fill staging buffer with index data */ memset(staging_buffer.allocationInfo.pMappedData, 0, MAX_VERTEX_BUFFER_SIZE); memcpy(staging_buffer.allocationInfo.pMappedData, indices.data(), indices_size); copyBuffer(vulkan_state, staging_buffer.buffer, last_index_buffer->buffer, indices_size, last_index_buffer->allocated_size); vmaDestroyBuffer(vulkan_state.allocator, staging_buffer.buffer, staging_buffer.allocation); return VULKAN_ERROR_NO_ERROR; } uint8_t update_mesh_instances(VulkanState& vulkan_state, rse::graphics::mesh::InstanceData& instance_data) { Buffer staging_buffer; auto last_instance_buffer = &gInstanceBuffers[gInstanceBuffers.size() - 1]; auto instance_size = sizeof(instance_data); /* Creating staging buffer*/ if (VK_SUCCESS != createBuffer(vulkan_state, MAX_VERTEX_BUFFER_SIZE, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, staging_buffer)) { LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED]; return VULKAN_ERROR_BUFFER_CREATION_FAILED; } memcpy(staging_buffer.allocationInfo.pMappedData, &instance_data, instance_size); copyBuffer(vulkan_state, staging_buffer.buffer, last_instance_buffer->buffer, instance_size, last_instance_buffer->allocated_size); vmaDestroyBuffer(vulkan_state.allocator, staging_buffer.buffer, staging_buffer.allocation); last_instance_buffer->allocated_size += instance_size; return VULKAN_ERROR_NO_ERROR; } /** * @brief Records commands for provided command buffer * * @param commandBuffer Command buffer, that will hold commands * @param imageIndex Image index * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED */ uint8_t recordCommandBuffer(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{}; VkDeviceSize offsets[] = {0}; auto indices = rse::graphics::mesh::get_mesh_indices(0); auto instances = rse::graphics::mesh::get_instance_data_for_mesh(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); for (size_t i = 0; i < gVertexBuffers.size(); ++i) { VkBuffer vertexBuffers[] = {gVertexBuffers[i].buffer}; VkBuffer indexBuffer = {gIndexBuffers[i].buffer}; VkBuffer instanceBuffers[] = {gInstanceBuffers[i].buffer}; /* TODO: Figure out the offset */ vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); vkCmdBindVertexBuffers(commandBuffer, 1, 1, instanceBuffers, offsets); vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT16); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_state.pipelineLayout, 0, 1, &vulkan_state.descriptorSets[vulkan_state.currentFrame], 0, nullptr); vkCmdDrawIndexed(commandBuffer, static_cast(indices.size()), instances.size(), 0, 0, 0); } vkCmdEndRenderPass(commandBuffer); if (VK_SUCCESS != vkEndCommandBuffer(commandBuffer)) { LOGF << vulkanErrorMesssages[VULKAN_RECORD_COMMAND_BUFFER_FAILED]; return VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED; } return VULKAN_ERROR_NO_ERROR; } uint8_t create_buffers(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(createInstanceBuffer(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); } for (size_t i = 0; i < gVertexBuffers.size(); ++i) { vmaDestroyBuffer(vulkan_state.allocator, gVertexBuffers[i].buffer, gVertexBuffers[i].allocation); vmaDestroyBuffer(vulkan_state.allocator, gIndexBuffers[i].buffer, gIndexBuffers[i].allocation); vmaDestroyBuffer(vulkan_state.allocator, gInstanceBuffers[i].buffer, gInstanceBuffers[i].allocation); } vkDestroyCommandPool(vulkan_state.device, gCommandPool, nullptr); } }