Split vulkan_base into smaller units

Closes #30. This is a basic refactoring of the graphics module, so
everything won't be in single file.
This commit is contained in:
Piotr Krygier committed 2023-03-29 09:41:04 +02:00
1 parent 649e7f76fe
commit 6a09350902
32 files changed
+2354 -2002

No files matched your search

+419
View File
@@ -0,0 +1,419 @@
#include "vulkan_buffers.hpp"
#include "vulkan_errors.hpp"
#include "locale_vulkan.hpp"
#include "utilities/logger.hpp"
#include <cstdint>
#define GLM_FORCE_RADIANS
#include <glm/gtc/matrix_transform.hpp>
namespace rse::graphics::vulkanbase
{
using namespace locale::vulkan;
// FIXME: Temporary array of vertices, for testing purposes
const std::vector<Vertex> 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<uint16_t> indices = {0, 1, 2, 2, 3, 0};
/* GPU visible buffer */
Buffer gVertexBuffer = {};
/* CPU visible buffer. TODO: Do we need this global? */
Buffer gVertexStagingBuffer = {};
Buffer gIndexBuffer = {};
Buffer gIndexStagingBuffer = {};
VkCommandPool gCommandPool = VK_NULL_HANDLE;
static uint8_t createBuffer(VulkanState& vulkan_state, const VkDeviceSize size, VkBufferUsageFlags bufferUsage, VmaMemoryUsage memoryUsage,
const VmaAllocationCreateFlags allocationFlags, Buffer& buffer);
static uint8_t copyBuffer(VulkanState& vulkan_state, VkBuffer src, VkBuffer dst, VkDeviceSize size);
static uint8_t createCommandPools(VulkanState& vulkan_state);
static uint8_t createVertexBuffer(VulkanState& vulkan_state);
static uint8_t createIndexBuffer(VulkanState& vulkan_state);
static uint8_t createUniformBuffers(VulkanState& vulkan_state);
static uint8_t allocateCommandBuffers(VulkanState& vulkan_state);
/**
* @brief Helper function. Creates a buffer object
*
* @param size Size of the target buffer
* @param bufferUsage Buffer usage flags
* @param memoryUsage memory usage flags (mostly VMA_MEMORY_USAGE_AUTO)
* @param allocationFlags VMA allocation flags
* @param buffer Buffer, that will be allocated
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED
*/
uint8_t createBuffer(VulkanState& vulkan_state, const VkDeviceSize size, VkBufferUsageFlags bufferUsage,
VmaMemoryUsage memoryUsage, const VmaAllocationCreateFlags allocationFlags, Buffer& buffer)
{
VkBufferCreateInfo vertexBufferInfo;
VmaAllocationCreateInfo createInfo;
vertexBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
vertexBufferInfo.pNext = nullptr;
vertexBufferInfo.flags = 0U;
vertexBufferInfo.size = size;
vertexBufferInfo.usage = bufferUsage;
vertexBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vertexBufferInfo.queueFamilyIndexCount = 0U;
vertexBufferInfo.pQueueFamilyIndices = nullptr;
/* Disalows random access to memory. TODO: Consider changing to VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT? */
createInfo.flags = allocationFlags;
createInfo.usage = memoryUsage;
createInfo.memoryTypeBits = 0U;
createInfo.requiredFlags = 0U;
createInfo.preferredFlags = 0U;
createInfo.pool = VK_NULL_HANDLE;
createInfo.pUserData = VK_NULL_HANDLE;
createInfo.priority = 0.0f;
if (VK_SUCCESS != vmaCreateBuffer(vulkan_state.allocator, &vertexBufferInfo, &createInfo, &buffer.buffer, &buffer.allocation,
&buffer.allocationInfo)) {
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Copy one buffer's data to another
*
* @param src Source buffer
* @param dst Destination buffer
* @param size Size of buffer to copy
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
*/
uint8_t copyBuffer(VulkanState& vulkan_state, VkBuffer src, VkBuffer dst, VkDeviceSize size)
{
/* Vulkan buffers can only be copied using command buffers */
VkBufferCopy copyRegion;
VkSubmitInfo submitInfo;
VkCommandBuffer copyCommandBuffer;
VkCommandBufferBeginInfo beginInfo;
VkCommandBufferAllocateInfo allocInfo;
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.pNext = nullptr;
allocInfo.commandPool = gCommandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.pNext = nullptr;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
beginInfo.pInheritanceInfo = nullptr;
copyRegion.srcOffset = 0;
copyRegion.dstOffset = 0;
copyRegion.size = size;
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pNext = nullptr;
submitInfo.waitSemaphoreCount = 0;
submitInfo.pWaitSemaphores = nullptr;
submitInfo.pWaitDstStageMask = nullptr;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &copyCommandBuffer;
submitInfo.signalSemaphoreCount = 0;
submitInfo.pSignalSemaphores = nullptr;
// FIXME: At result checks
vkAllocateCommandBuffers(vulkan_state.device, &allocInfo, &copyCommandBuffer);
vkBeginCommandBuffer(copyCommandBuffer, &beginInfo);
vkCmdCopyBuffer(copyCommandBuffer, src, dst, 1, &copyRegion);
vkEndCommandBuffer(copyCommandBuffer);
vkQueueSubmit(vulkan_state.graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
/* TODO: Use fences to wait instead of idle */
vkQueueWaitIdle(vulkan_state.graphicsQueue);
vkFreeCommandBuffers(vulkan_state.device, gCommandPool, 1, &copyCommandBuffer);
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create Command Pools
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED
*/
uint8_t createCommandPools(VulkanState& vulkan_state)
{
VkCommandPoolCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
createInfo.queueFamilyIndex = vulkan_state.queueFamilyIndices[0]; /* TODO: Change when more families are needed */
if (VK_SUCCESS != vkCreateCommandPool(vulkan_state.device, &createInfo, nullptr, &gCommandPool)) {
LOGF << vulkanErrorMesssages[VULKAN_COMMAND_POOL_CREATION_FAILED];
return VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED;
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create a buffer holding all vertex data
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_BUFFER_CREATION_FAILED
* VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED
*/
uint8_t createVertexBuffer(VulkanState& vulkan_state)
{
void* mappedData;
VkDeviceSize bufferSize;
bufferSize = sizeof(vertices[0]) * vertices.size();
/* Creating staging buffer*/
if (VK_SUCCESS != createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, gVertexStagingBuffer)) {
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
/* Create Vertex Buffer*/
if (VK_SUCCESS != createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0, /* Will not be mapped with vmaMapMemory */
gVertexBuffer)) {
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
/* Fill staging buffer */
if (VK_SUCCESS != vmaMapMemory(vulkan_state.allocator, gVertexStagingBuffer.allocation, &mappedData)) {
LOGF << vulkanErrorMesssages[VULKAN_VERTEX_BUFFER_MAPPING_FAILED];
return VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED;
}
memcpy(mappedData, vertices.data(), sizeof(Vertex) * vertices.size());
vmaUnmapMemory(vulkan_state.allocator, gVertexStagingBuffer.allocation);
copyBuffer(vulkan_state, gVertexStagingBuffer.buffer, gVertexBuffer.buffer, bufferSize);
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create an index buffer, connected with vertex buffer
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_BUFFER_CREATION_FAILED
*/
uint8_t createIndexBuffer(VulkanState& vulkan_state)
{
void* mappedData;
VkDeviceSize bufferSize;
bufferSize = sizeof(indices[0]) * indices.size();
/* Creating staging buffer*/
if (VK_SUCCESS != createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, gIndexStagingBuffer)) {
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
/* Create Vertex Buffer*/
if (VK_SUCCESS != createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0, /* Will not be mapped with vmaMapMemory */
gIndexBuffer)) {
LOGF << vulkanErrorMesssages[VULKAN_BUFFER_CREATION_FAILED];
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
/* Fill staging buffer */
if (VK_SUCCESS != vmaMapMemory(vulkan_state.allocator, gIndexStagingBuffer.allocation, &mappedData)) {
LOGF << vulkanErrorMesssages[VULKAN_VERTEX_BUFFER_MAPPING_FAILED];
return VULKAN_ERROR_VERTEX_BUFFER_MAPPING_FAILED;
}
memcpy(mappedData, indices.data(), static_cast<size_t>(bufferSize));
vmaUnmapMemory(vulkan_state.allocator, gIndexStagingBuffer.allocation);
copyBuffer(vulkan_state, gIndexStagingBuffer.buffer, gIndexBuffer.buffer, bufferSize);
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Create a Uniform Buffers
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
*/
uint8_t createUniformBuffers(VulkanState& vulkan_state)
{
VkDeviceSize bufferSize = sizeof(UniformBufferObject);
vulkan_state.uniformBuffers.resize(SWAP_BUFFER_COUNT);
for (size_t i = 0; i < SWAP_BUFFER_COUNT; i++) {
createBuffer(vulkan_state, bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
vulkan_state.uniformBuffers[i]);
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Allocate Command Buffers
*
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED
*/
uint8_t allocateCommandBuffers(VulkanState& vulkan_state)
{
VkCommandBufferAllocateInfo allocateInfo;
/* Allocate memory for command buffers */
vulkan_state.pCommandBuffers.resize(SWAP_BUFFER_COUNT);
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocateInfo.pNext = nullptr;
allocateInfo.commandPool = gCommandPool;
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; /* TODO: When needed, add secondary command buffer */
allocateInfo.commandBufferCount = SWAP_BUFFER_COUNT;
if (VK_SUCCESS != vkAllocateCommandBuffers(vulkan_state.device, &allocateInfo, vulkan_state.pCommandBuffers.data())) {
LOGF << vulkanErrorMesssages[VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED];
return VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED;
}
return VULKAN_ERROR_NO_ERROR;
}
/**
* @brief Update uniform buffers
*
* @param currentImage Current image index
*/
void updateUniformBuffer(VulkanState& vulkan_state)
{
UniformBufferObject ubo{};
ubo.model = glm::mat4(1.0f);
ubo.view = glm::lookAt(glm::vec3(0.0f, 0.0f, 5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
ubo.proj =
glm::perspective(glm::radians(45.0f), vulkan_state.swapchainExtent.width / (float)vulkan_state.swapchainExtent.height, 0.1f, 20.0f);
ubo.proj[1][1] *= -1; /* GLM was developed for OpenGL, where Y coordinate of the clip is inverted */
memcpy(vulkan_state.uniformBuffers[vulkan_state.currentFrame].allocationInfo.pMappedData, &ubo, sizeof(ubo));
}
/**
* @brief Records commands for provided command buffer
*
* @param commandBuffer Command buffer, that will hold commands
* @param imageIndex Image index
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED
*/
uint8_t recordCommandBuffer(VulkanState& vulkan_state, uint32_t imageIndex)
{
VkCommandBuffer commandBuffer = vulkan_state.pCommandBuffers[vulkan_state.currentFrame];
VkCommandBufferBeginInfo beginInfo{};
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
VkRenderPassBeginInfo renderPassInfo{};
VkViewport viewport{};
VkRect2D scissor{};
VkBuffer vertexBuffers[] = {gVertexBuffer.buffer};
VkDeviceSize offsets[] = {0};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0;
beginInfo.pInheritanceInfo = nullptr;
if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
LOGF << vulkanErrorMesssages[VULKAN_RECORD_COMMAND_BEGIN_FAILED];
return VULKAN_ERROR_RECORD_COMMAND_BEGIN_FAILED;
}
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = vulkan_state.renderPass;
renderPassInfo.framebuffer = vulkan_state.swapchainFramebuffers[imageIndex];
renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = vulkan_state.swapchainExtent;
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_state.graphicsPipeline);
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(vulkan_state.swapchainExtent.width);
viewport.height = static_cast<float>(vulkan_state.swapchainExtent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
scissor.offset = {0, 0};
scissor.extent = vulkan_state.swapchainExtent;
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(commandBuffer, gIndexBuffer.buffer, 0, VK_INDEX_TYPE_UINT16);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_state.pipelineLayout, 0, 1,
&vulkan_state.descriptorSets[vulkan_state.currentFrame], 0, nullptr);
vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(indices.size()), 1, 0, 0, 0);
vkCmdEndRenderPass(commandBuffer);
if (VK_SUCCESS != vkEndCommandBuffer(commandBuffer)) {
LOGF << vulkanErrorMesssages[VULKAN_RECORD_COMMAND_BUFFER_FAILED];
return VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED;
}
return VULKAN_ERROR_NO_ERROR;
}
uint8_t create_buffers(VulkanState& vulkan_state)
{
uint8_t status = VULKAN_ERROR_NO_ERROR;\
STATUS_CHECK(createCommandPools(vulkan_state));
STATUS_CHECK(createVertexBuffer(vulkan_state));
STATUS_CHECK(createIndexBuffer(vulkan_state));
STATUS_CHECK(createUniformBuffers(vulkan_state));
STATUS_CHECK(allocateCommandBuffers(vulkan_state));
return status;
}
void reset_command_buffer(VulkanState& vulkan_state)
{
vkResetCommandBuffer(vulkan_state.pCommandBuffers[vulkan_state.currentFrame], /*VkCommandBufferResetFlagBits*/ 0);
}
void destroy_buffers(VulkanState& vulkan_state)
{
for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) {
vmaDestroyBuffer(vulkan_state.allocator, vulkan_state.uniformBuffers[i].buffer, vulkan_state.uniformBuffers[i].allocation);
}
vmaDestroyBuffer(vulkan_state.allocator, gVertexBuffer.buffer, gVertexBuffer.allocation);
vmaDestroyBuffer(vulkan_state.allocator, gVertexStagingBuffer.buffer, gVertexStagingBuffer.allocation);
vmaDestroyBuffer(vulkan_state.allocator, gIndexBuffer.buffer, gIndexBuffer.allocation);
vmaDestroyBuffer(vulkan_state.allocator, gIndexStagingBuffer.buffer, gIndexStagingBuffer.allocation);
vkDestroyCommandPool(vulkan_state.device, gCommandPool, nullptr);
}
}