446 lines
17 KiB
C
446 lines
17 KiB
C
#include "vulkan_buffers.h"
|
|
|
|
#include "vulkan_errors.h"
|
|
#include "locale_vulkan.h"
|
|
#include "utilities/logger.h"
|
|
#include "mesh_controller.h"
|
|
#include "rse_math.h"
|
|
#include "rse_vulkan_commands.h"
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#define MAX_VERTEX_BUFFER_SIZE 33554432 /* 32 MB*/
|
|
#define MAX_INSTANCE_BUFFER_SIZE 8192 /* 8 MB*/
|
|
|
|
/* GPU visible buffer */
|
|
size_t g_vertex_offset = 0;
|
|
struct rse_vulkan_buffer_t g_vertex_buffer;
|
|
struct rse_vulkan_buffer_t g_index_buffer;
|
|
struct rse_vulkan_buffer_t g_instance_buffers[MAX_MESH_NUMBER];
|
|
struct rse_vulkan_buffer_t g_draw_indirect_command_buffer;
|
|
|
|
/**
|
|
* @brief Copy one buffer's data to another
|
|
*
|
|
* @param src Source buffer
|
|
* @param dst Destination buffer
|
|
* @param size Size of buffer to copy
|
|
*/
|
|
static void copy_buffer(VkBuffer src, VkBuffer dst, VkDeviceSize size, VkDeviceSize dest_offset)
|
|
{
|
|
/* Vulkan buffers can only be copied using command buffers */
|
|
VkBufferCopy copy_region = {};
|
|
VkCommandBuffer command_buffer = {0};
|
|
|
|
command_buffer = rse_begin_single_time_command();
|
|
|
|
copy_region.srcOffset = 0U;
|
|
copy_region.dstOffset = dest_offset;
|
|
copy_region.size = size;
|
|
|
|
vkCmdCopyBuffer(command_buffer, src, dst, 1, ©_region);
|
|
|
|
rse_end_single_time_comands(command_buffer);
|
|
}
|
|
|
|
|
|
/**
|
|
* @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 create_vertex_buffer()
|
|
{
|
|
VkDeviceSize buffer_size;
|
|
|
|
buffer_size = MAX_VERTEX_BUFFER_SIZE;
|
|
|
|
/* Create Vertex Buffer*/
|
|
if (VK_SUCCESS != create_buffer(buffer_size,
|
|
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 */
|
|
&g_vertex_buffer)) {
|
|
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
|
|
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
|
}
|
|
|
|
g_vertex_buffer.allocated_size = 0;
|
|
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
|
|
*/
|
|
static uint8_t create_index_buffer()
|
|
{
|
|
VkDeviceSize buffer_size;
|
|
|
|
buffer_size = MAX_VERTEX_BUFFER_SIZE;
|
|
|
|
/* Create Index Buffer*/
|
|
if (VK_SUCCESS != create_buffer(buffer_size, 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 */
|
|
&g_index_buffer)) {
|
|
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
|
|
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
|
}
|
|
|
|
g_index_buffer.allocated_size = 0;
|
|
return VULKAN_ERROR_NO_ERROR;
|
|
}
|
|
|
|
static uint8_t create_instance_buffers()
|
|
{
|
|
size_t iter = 0U;
|
|
VkDeviceSize buffer_size;
|
|
|
|
buffer_size = MAX_VERTEX_BUFFER_SIZE;
|
|
|
|
for (iter = 0; iter < MAX_MESH_NUMBER; ++iter) {
|
|
/* Create Instance Buffer */
|
|
if (VK_SUCCESS != create_buffer(buffer_size,
|
|
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 */
|
|
&g_instance_buffers[iter])) {
|
|
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
|
|
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
|
}
|
|
|
|
g_instance_buffers[iter].allocated_size = 0;
|
|
|
|
}
|
|
|
|
if (VK_SUCCESS != create_buffer(sizeof(VkDrawIndexedIndirectCommand) * MAX_MESH_NUMBER,
|
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT
|
|
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
|
|
| VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
|
|
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
|
|
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
|
&g_draw_indirect_command_buffer)) {
|
|
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
|
|
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
|
}
|
|
return VULKAN_ERROR_NO_ERROR;
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief Create a Uniform Buffers
|
|
*
|
|
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
|
|
*/
|
|
static uint8_t create_uniform_buffer()
|
|
{
|
|
VkDeviceSize buffer_size = sizeof(struct rse_uniform_buffer_object_t);
|
|
|
|
for (size_t i = 0; i < SWAP_BUFFER_COUNT; i++) {
|
|
create_buffer(buffer_size, 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,
|
|
&g_vulkan_state.uniform_buffers[i]);
|
|
}
|
|
|
|
return VULKAN_ERROR_NO_ERROR;
|
|
}
|
|
|
|
uint8_t create_buffer(const VkDeviceSize size,
|
|
VkBufferUsageFlags buffer_usage,
|
|
VmaMemoryUsage memory_usage,
|
|
const VmaAllocationCreateFlags allocation_flags,
|
|
struct rse_vulkan_buffer_t* buffer)
|
|
{
|
|
VkBufferCreateInfo vertex_buffer_info = {};
|
|
VmaAllocationCreateInfo create_info = {};
|
|
|
|
vertex_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
vertex_buffer_info.pNext = NULL;
|
|
vertex_buffer_info.flags = 0U;
|
|
vertex_buffer_info.size = size;
|
|
vertex_buffer_info.usage = buffer_usage;
|
|
vertex_buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
vertex_buffer_info.queueFamilyIndexCount = 0U;
|
|
vertex_buffer_info.pQueueFamilyIndices = NULL;
|
|
|
|
create_info.flags = allocation_flags;
|
|
create_info.usage = memory_usage;
|
|
create_info.memoryTypeBits = 0U;
|
|
create_info.requiredFlags = 0U;
|
|
create_info.preferredFlags = 0U;
|
|
create_info.pool = VK_NULL_HANDLE;
|
|
create_info.pUserData = VK_NULL_HANDLE;
|
|
create_info.priority = 0.0f;
|
|
|
|
if (VK_SUCCESS != vmaCreateBuffer(g_vulkan_state.allocator,
|
|
&vertex_buffer_info,
|
|
&create_info,
|
|
&buffer->buffer,
|
|
&buffer->allocation,
|
|
&buffer->allocation_info)) {
|
|
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
|
|
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
|
}
|
|
|
|
return VULKAN_ERROR_NO_ERROR;
|
|
}
|
|
|
|
void destroy_buffer(struct rse_vulkan_buffer_t* buffer)
|
|
{
|
|
vmaDestroyBuffer(g_vulkan_state.allocator, buffer->buffer, buffer->allocation);
|
|
}
|
|
|
|
|
|
void update_uniform_buffers()
|
|
{
|
|
struct rse_uniform_buffer_object_t ubo = {};
|
|
struct vec3_t eye = {0};
|
|
struct vec3_t center = {0};
|
|
struct vec3_t up = {0};
|
|
struct vec3_t rotation_vec = {1.0f, 0.0f, 0.0f};
|
|
float fovy;
|
|
float z_near;
|
|
float z_far;
|
|
|
|
eye.z = 5.0f;
|
|
|
|
up.y = 1.0f;
|
|
|
|
fovy = rse_math_deg_to_radians(45.0f);
|
|
z_near = 0.1f;
|
|
z_far = 20.0f;
|
|
|
|
ubo.model = rse_math_rotate(rse_math_uniform_mat4(), rse_math_deg_to_radians(0.0f), rotation_vec);
|
|
ubo.view = rse_math_look_at(&eye, ¢er, &up);
|
|
ubo.proj = rse_math_perspective(fovy,
|
|
g_vulkan_state.swapchain_extent.width / (float)g_vulkan_state.swapchain_extent.height,
|
|
z_near,
|
|
z_far);
|
|
memcpy(g_vulkan_state.uniform_buffers[g_vulkan_state.current_frame].allocation_info.pMappedData, &ubo, sizeof(ubo));
|
|
}
|
|
|
|
uint8_t rse_add_vertices(uint16_t mesh_id,
|
|
const struct rse_vertex_t* vertices,
|
|
const uint16_t* indices,
|
|
struct rse_buffer_data_t* vertex_data,
|
|
struct rse_buffer_data_t* index_data)
|
|
{
|
|
VkDrawIndexedIndirectCommand draw_indirect_command;
|
|
struct rse_vulkan_buffer_t staging_buffer;
|
|
size_t vertices_size = sizeof(vertices[0]) * vertex_data->count;
|
|
size_t indices_size = sizeof(indices[0]) * index_data->count;
|
|
|
|
struct rse_vulkan_buffer_t* index_buffer = &g_index_buffer;
|
|
|
|
/* Creating staging buffer*/
|
|
if (VK_SUCCESS != create_buffer(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(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
|
|
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
|
}
|
|
|
|
/* Fill staging buffer with vertex data */
|
|
memset(staging_buffer.allocation_info.pMappedData, 0, MAX_VERTEX_BUFFER_SIZE);
|
|
memcpy(staging_buffer.allocation_info.pMappedData, vertices, vertices_size);
|
|
copy_buffer(staging_buffer.buffer,
|
|
g_vertex_buffer.buffer,
|
|
vertices_size,
|
|
g_vertex_buffer.allocated_size);
|
|
|
|
vertex_data->buffer_offset = g_vertex_offset;
|
|
g_vertex_offset += vertex_data->count;
|
|
g_vertex_buffer.allocated_size += vertices_size;
|
|
|
|
/* Fill staging buffer with index data */
|
|
memset(staging_buffer.allocation_info.pMappedData, 0, MAX_VERTEX_BUFFER_SIZE);
|
|
memcpy(staging_buffer.allocation_info.pMappedData, indices, indices_size);
|
|
copy_buffer(staging_buffer.buffer,
|
|
index_buffer->buffer,
|
|
indices_size,
|
|
index_buffer->allocated_size);
|
|
index_data->buffer_offset = index_buffer->allocated_size;
|
|
index_buffer->allocated_size += indices_size;
|
|
|
|
vmaDestroyBuffer(g_vulkan_state.allocator, staging_buffer.buffer, staging_buffer.allocation);
|
|
|
|
draw_indirect_command.indexCount = 6;
|
|
draw_indirect_command.instanceCount = 0;
|
|
draw_indirect_command.firstIndex = 0;
|
|
draw_indirect_command.vertexOffset = vertex_data->buffer_offset;
|
|
draw_indirect_command.firstInstance = 0;
|
|
|
|
memcpy(g_draw_indirect_command_buffer.allocation_info.pMappedData + (mesh_id * sizeof(VkDrawIndexedIndirectCommand)), &draw_indirect_command, sizeof(VkDrawIndexedIndirectCommand));
|
|
|
|
return VULKAN_ERROR_NO_ERROR;
|
|
}
|
|
|
|
uint8_t update_mesh_instances(uint16_t mesh_id, struct rse_instance_data_t* instance_data)
|
|
{
|
|
VkDrawIndexedIndirectCommand draw_indirect_command;
|
|
struct rse_vulkan_buffer_t staging_buffer;
|
|
struct rse_vulkan_buffer_t* instance_buffer = &g_instance_buffers[mesh_id];
|
|
size_t instance_size = sizeof(struct rse_instance_data_t);
|
|
|
|
/* Creating staging buffer*/
|
|
if (VK_SUCCESS != create_buffer(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(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
|
|
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
|
|
}
|
|
|
|
/* Copy transformation information about instance */
|
|
memcpy(staging_buffer.allocation_info.pMappedData, instance_data, instance_size);
|
|
copy_buffer(staging_buffer.buffer,
|
|
instance_buffer->buffer,
|
|
instance_size,
|
|
instance_buffer->allocated_size);
|
|
|
|
vmaDestroyBuffer(g_vulkan_state.allocator, staging_buffer.buffer, staging_buffer.allocation);
|
|
|
|
instance_buffer->allocated_size += instance_size;
|
|
|
|
memcpy(&draw_indirect_command, g_draw_indirect_command_buffer.allocation_info.pMappedData + (mesh_id * sizeof(VkDrawIndexedIndirectCommand)), sizeof(VkDrawIndexedIndirectCommand));
|
|
draw_indirect_command.instanceCount++;
|
|
memcpy(g_draw_indirect_command_buffer.allocation_info.pMappedData + (mesh_id * sizeof(VkDrawIndexedIndirectCommand)), &draw_indirect_command, sizeof(VkDrawIndexedIndirectCommand));
|
|
|
|
return VULKAN_ERROR_NO_ERROR;
|
|
}
|
|
|
|
/**
|
|
* @brief Records commands for provided swapchain framebuffer
|
|
*
|
|
* @param command_buffer Command buffer, that will hold commands
|
|
* @param image_index Image index
|
|
* @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors:
|
|
* VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED
|
|
*/
|
|
uint8_t record_command_buffer(uint32_t image_index)
|
|
{
|
|
VkCommandBuffer command_buffer = g_vulkan_state.command_buffers[g_vulkan_state.current_frame];
|
|
VkDescriptorSet descriptor_sets[] = {g_vulkan_state.descriptor_sets[g_vulkan_state.current_frame],
|
|
g_vulkan_state.descriptor_sets_bindless};
|
|
VkCommandBufferBeginInfo begin_info = {};
|
|
VkRenderPassBeginInfo render_pass_info = {};
|
|
VkViewport viewport = {};
|
|
VkRect2D scissor = {};
|
|
VkDeviceSize offsets[] = {0};
|
|
VkClearValue clear_values[2] = {}; //For color and depth stencil
|
|
size_t i = 0U;
|
|
|
|
clear_values[0].color.float32[0] = 0.0f;
|
|
clear_values[0].color.float32[1] = 0.0f;
|
|
clear_values[0].color.float32[2] = 0.0f;
|
|
|
|
clear_values[1].depthStencil.depth = 1.0f;
|
|
clear_values[1].depthStencil.stencil = 0.0f;
|
|
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
begin_info.flags = 0;
|
|
begin_info.pInheritanceInfo = NULL;
|
|
|
|
if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {
|
|
LOGF(vulkan_error_messages[VULKAN_RECORD_COMMAND_BEGIN_FAILED]);
|
|
return VULKAN_ERROR_RECORD_COMMAND_BEGIN_FAILED;
|
|
}
|
|
|
|
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
|
render_pass_info.renderPass = g_vulkan_state.render_pass;
|
|
render_pass_info.framebuffer = g_vulkan_state.swapchain_framebuffers[image_index];
|
|
render_pass_info.renderArea.offset.x = 0;
|
|
render_pass_info.renderArea.offset.y = 0;
|
|
render_pass_info.renderArea.extent = g_vulkan_state.swapchain_extent;
|
|
render_pass_info.clearValueCount = 2;
|
|
render_pass_info.pClearValues = clear_values;
|
|
|
|
vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
|
|
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_vulkan_state.graphics_pipeline);
|
|
|
|
viewport.x = 0.0f;
|
|
viewport.y = 0.0f;
|
|
viewport.width = (float)(g_vulkan_state.swapchain_extent.width);
|
|
viewport.height = (float)(g_vulkan_state.swapchain_extent.height);
|
|
viewport.minDepth = 0.0f;
|
|
viewport.maxDepth = 1.0f;
|
|
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
|
|
|
|
scissor.offset.x = 0;
|
|
scissor.offset.y = 0;
|
|
scissor.extent = g_vulkan_state.swapchain_extent;
|
|
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
|
|
|
vkCmdBindVertexBuffers(command_buffer, 0, 1, &g_vertex_buffer.buffer, offsets);
|
|
vkCmdBindIndexBuffer(command_buffer, g_index_buffer.buffer, 0, VK_INDEX_TYPE_UINT16);
|
|
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_vulkan_state.pipeline_layout, 0, 2,
|
|
descriptor_sets, 0, NULL);
|
|
|
|
for (i = 0; i < 2; ++i) {
|
|
VkBuffer instance_buffer = {g_instance_buffers[i].buffer};
|
|
|
|
vkCmdBindVertexBuffers(command_buffer, 1, 1, &instance_buffer, offsets);
|
|
|
|
vkCmdDrawIndexedIndirect(command_buffer,
|
|
g_draw_indirect_command_buffer.buffer,
|
|
sizeof(VkDrawIndexedIndirectCommand) * i,
|
|
1,
|
|
0); /* Can be 0 since we are not doing multiple draws each loop */
|
|
}
|
|
|
|
vkCmdEndRenderPass(command_buffer);
|
|
|
|
if (VK_SUCCESS != vkEndCommandBuffer(command_buffer)) {
|
|
LOGF(vulkan_error_messages[VULKAN_RECORD_COMMAND_BUFFER_FAILED]);
|
|
return VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED;
|
|
}
|
|
|
|
return VULKAN_ERROR_NO_ERROR;
|
|
}
|
|
|
|
uint8_t create_buffers()
|
|
{
|
|
uint8_t status = VULKAN_ERROR_NO_ERROR;
|
|
|
|
STATUS_CHECK(create_vertex_buffer());
|
|
STATUS_CHECK(create_index_buffer());
|
|
STATUS_CHECK(create_instance_buffers());
|
|
STATUS_CHECK(create_uniform_buffer());
|
|
|
|
return status;
|
|
}
|
|
|
|
void reset_command_buffer()
|
|
{
|
|
vkResetCommandBuffer(g_vulkan_state.command_buffers[g_vulkan_state.current_frame], /*VkCommandBufferResetFlagBits*/ 0);
|
|
}
|
|
|
|
|
|
void destroy_buffers()
|
|
{
|
|
size_t i = 0U;
|
|
|
|
for (i = 0; i < SWAP_BUFFER_COUNT; ++i) {
|
|
vmaDestroyBuffer(g_vulkan_state.allocator,
|
|
g_vulkan_state.uniform_buffers[i].buffer,
|
|
g_vulkan_state.uniform_buffers[i].allocation);
|
|
}
|
|
|
|
vmaDestroyBuffer(g_vulkan_state.allocator, g_vertex_buffer.buffer, g_vertex_buffer.allocation);
|
|
vmaDestroyBuffer(g_vulkan_state.allocator, g_index_buffer.buffer, g_index_buffer.allocation);
|
|
vmaDestroyBuffer(g_vulkan_state.allocator, g_draw_indirect_command_buffer.buffer, g_draw_indirect_command_buffer.allocation);
|
|
|
|
for(i = 0; i < MAX_MESH_NUMBER; ++i) {
|
|
vmaDestroyBuffer(g_vulkan_state.allocator, g_instance_buffers[i].buffer, g_instance_buffers[i].allocation);
|
|
}
|
|
} |