Move to bindless design

Replaced DrawIndexed with DrawIndexedIndirect function. This way
less parameters are passed from CPU and are store in GPU
mapped memory. Multiple meshes with multiple instances also work.
This commit is contained in:
Piotr Krygier committed 2023-09-26 14:15:21 +02:00
1 parent 3a58bd8756
commit 252a0820ed
6 files changed
+167 -94

No files matched your search

+56 -25
View File
@@ -7,16 +7,37 @@
#include <stdlib.h>
#include <string.h>
#define MAX_MESH_NUMBER 256
#define MAX_INSTANCE_NUMBER 256
#define OBJECT_FREE 0
#define OBJECT_TAKEN 1
#define MESH_ID_FREE 0
#define MESH_ID_TAKEN 1
struct rse_mesh_t g_meshes[MAX_MESH_NUMBER] = {OBJECT_FREE};
uint16_t g_free_ids[MAX_MESH_NUMBER] = {OBJECT_FREE};
/**
* @brief Represents instance of a mesh and shows where it's located in instance buffer
*
*/
struct rse_internal_instance_data_t
{
uint8_t instance_taken;
uint8_t instance_buffer_offset;
};
uint8_t g_instance_free_ids[MAX_MESH_NUMBER][MAX_INSTANCE_NUMBER] = {OBJECT_FREE};
/**
* @brief Mesh data, with unique id and dynamic arrays holding vertices and indices.
* One mesh can have multiple instances.
*
*/
struct rse_mesh_t
{
uint8_t is_mesh_id_taken;
uint32_t unique_id;
uint8_t instance_count;
struct rse_buffer_data_t vertex_data;
struct rse_buffer_data_t index_data;
struct rse_internal_instance_data_t instance_data[MAX_INSTANCE_NUMBER];
};
struct rse_mesh_t g_meshes[MAX_MESH_NUMBER] = {MESH_ID_FREE};
uint16_t create_mesh(struct rse_vertex_t* vertices,
uint16_t* indices,
@@ -26,7 +47,7 @@ uint16_t create_mesh(struct rse_vertex_t* vertices,
size_t iter = 0U;
for(iter = 0U; iter < MAX_MESH_NUMBER; ++iter) {
if(g_free_ids[iter] == OBJECT_FREE) {
if(g_meshes[iter].is_mesh_id_taken == MESH_ID_FREE) {
break;
}
}
@@ -37,14 +58,15 @@ uint16_t create_mesh(struct rse_vertex_t* vertices,
return MAX_MESH_NUMBER;
}
g_free_ids[iter] = OBJECT_TAKEN;
g_meshes[iter].is_mesh_id_taken = MESH_ID_TAKEN;
g_meshes[iter].unique_id = iter;
g_meshes[iter].vertices_count = vertices_count;
g_meshes[iter].indices_count = indices_count;
g_meshes[iter].instances_count = 0;
g_meshes[iter].vertex_data.count = vertices_count;
g_meshes[iter].index_data.count = indices_count;
g_meshes[iter].instance_count = 0;
memset(g_meshes[iter].instance_data, 0, sizeof(struct rse_internal_instance_data_t) * MAX_INSTANCE_NUMBER);
update_mesh_buffers(vertices, indices, vertices_count, indices_count);
rse_add_vertices(iter, vertices, indices, &g_meshes[iter].vertex_data, &g_meshes[iter].index_data);
return iter;
}
@@ -53,10 +75,9 @@ void create_mesh_instance(uint16_t mesh_id,
struct rse_instance_data_t instance_data)
{
size_t iter = 0U;
uint8_t* free_instance_ids = g_instance_free_ids[mesh_id];
for(iter = 0U; iter < MAX_INSTANCE_NUMBER; ++iter) {
if(free_instance_ids[iter] == OBJECT_FREE) {
if(g_meshes[mesh_id].instance_data[iter].instance_taken == MESH_ID_FREE) {
break;
}
}
@@ -67,26 +88,36 @@ void create_mesh_instance(uint16_t mesh_id,
return;
}
free_instance_ids[iter] = OBJECT_TAKEN;
g_meshes[mesh_id].instance_data[iter].instance_taken = MESH_ID_TAKEN;
if(iter >= g_meshes[mesh_id].instances_count) {
g_meshes[mesh_id].instances_count++;
if(iter >= g_meshes[mesh_id].instance_count) {
g_meshes[mesh_id].instance_count++;
}
update_mesh_instances(&instance_data);
update_mesh_instances(mesh_id, &instance_data);
}
size_t get_vertices_count(uint16_t mesh_id)
{
return g_meshes[mesh_id].vertices_count;
}
size_t get_instances_count(uint16_t mesh_id)
{
return g_meshes[mesh_id].instances_count;
return g_meshes[mesh_id].vertex_data.count;
}
size_t get_indices_count(uint16_t mesh_id)
{
return g_meshes[mesh_id].indices_count;
return g_meshes[mesh_id].index_data.count;
}
size_t get_instances_count(uint16_t mesh_id)
{
return g_meshes[mesh_id].instance_count;
}
size_t get_vertex_offset(uint16_t mesh_id)
{
return g_meshes[mesh_id].vertex_data.buffer_offset;
}
VkDrawIndexedIndirectCommand get_index_data_buffer(uint16_t mesh_id)
{
}
+4 -13
View File
@@ -14,19 +14,6 @@
#include "vulkan_commons.h"
/**
* @brief Mesh data, with unique id and dynamic arrays holding vertices and indices.
* One mesh can have multiple instances.
*
*/
struct rse_mesh_t
{
uint32_t unique_id;
size_t vertices_count;
size_t indices_count;
size_t instances_count;
};
/**
* @brief Create a new mesh for provided vertices and indices.
*
@@ -73,6 +60,10 @@ size_t get_indices_count(uint16_t mesh_id);
*/
size_t get_instances_count(uint16_t mesh_id);
size_t get_vertex_offset(uint16_t mesh_id);
VkDrawIndexedIndirectCommand get_indices_data_buffer(uint16_t mesh_id);
#endif /* RSE_MESH_CONTROLLER_H */
+2 -3
View File
@@ -48,7 +48,6 @@ uint8_t rse_graphics_run(void)
{{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}},
{{-0.5f, 0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}}};
struct rse_vertex_t vertices2[] = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}},
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}},
{{0.5f, 0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}},
{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 0.0f}}};
@@ -56,7 +55,7 @@ uint8_t rse_graphics_run(void)
// FIXME: Temporary array of vertices, for testing purposes
uint16_t indices[] = {0, 1, 2, 2, 3, 0};
uint16_t mesh_id = create_mesh(vertices, indices, 24, 6);
uint16_t mesh_id = create_mesh(vertices, indices, 8, 6);
create_mesh_instance(mesh_id, (struct rse_instance_data_t){
{0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, rse_math_deg_to_radians(45.0f)},
@@ -69,7 +68,7 @@ uint8_t rse_graphics_run(void)
1.0f
});
mesh_id = create_mesh(vertices, indices, 24, 6);
mesh_id = create_mesh(vertices2, indices, 8, 6);
create_mesh_instance(mesh_id, (struct rse_instance_data_t){
{0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, rse_math_deg_to_radians(0.0f)},
+84 -45
View File
@@ -10,13 +10,14 @@
#include <string.h>
#define MAX_VERTEX_BUFFER_SIZE 33554432 /* 32 MB*/
#define MAX_VULKAN_BUFFERS_COUNT 1
#define MAX_INSTANCE_BUFFER_SIZE 8192 /* 8 MB*/
/* GPU visible buffer */
struct rse_vulkan_buffer_t g_vertex_buffers[MAX_VULKAN_BUFFERS_COUNT];
struct rse_vulkan_buffer_t g_index_buffers[MAX_VULKAN_BUFFERS_COUNT];
struct rse_vulkan_buffer_t g_instance_buffers[MAX_VULKAN_BUFFERS_COUNT];
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;
VkCommandPool g_command_pool = VK_NULL_HANDLE;
@@ -49,7 +50,6 @@ static uint8_t create_buffer(const VkDeviceSize size,
vertex_buffer_info.queueFamilyIndexCount = 0U;
vertex_buffer_info.pQueueFamilyIndices = NULL;
/* Disalows random access to memory. TODO: Consider changing to VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT? */
create_info.flags = allocation_flags;
create_info.usage = memory_usage;
create_info.memoryTypeBits = 0U;
@@ -175,12 +175,12 @@ static uint8_t create_vertex_buffer()
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_buffers)) {
&g_vertex_buffer)) {
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
g_vertex_buffers->allocated_size = 0;
g_vertex_buffer.allocated_size = 0;
return VULKAN_ERROR_NO_ERROR;
}
@@ -200,32 +200,46 @@ static uint8_t 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_buffers)) {
&g_index_buffer)) {
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
g_index_buffers->allocated_size = 0;
g_index_buffer.allocated_size = 0;
return VULKAN_ERROR_NO_ERROR;
}
static uint8_t create_instance_buffer()
static uint8_t create_instance_buffers()
{
size_t iter = 0U;
VkDeviceSize buffer_size;
buffer_size = MAX_VERTEX_BUFFER_SIZE;
/* Create Instance Buffer*/
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)) {
&g_instance_buffers[iter])) {
LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]);
return VULKAN_ERROR_BUFFER_CREATION_FAILED;
}
g_instance_buffers->allocated_size = 0;
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;
}
@@ -301,17 +315,18 @@ void update_uniform_buffers()
memcpy(g_vulkan_state.uniform_buffers[g_vulkan_state.current_frame].allocation_info.pMappedData, &ubo, sizeof(ubo));
}
uint8_t update_mesh_buffers(const struct rse_vertex_t* vertices,
uint8_t rse_add_vertices(uint16_t mesh_id,
const struct rse_vertex_t* vertices,
const uint16_t* indices,
size_t vertices_num,
size_t indices_num)
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]) * vertices_num;
size_t indices_size = sizeof(indices[0]) * indices_num;
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* last_vertex_buffer = &g_vertex_buffers[MAX_VULKAN_BUFFERS_COUNT - 1];
struct rse_vulkan_buffer_t* last_index_buffer = &g_index_buffers[MAX_VULKAN_BUFFERS_COUNT - 1];
struct rse_vulkan_buffer_t* index_buffer = &g_index_buffer;
/* Creating staging buffer*/
if (VK_SUCCESS != create_buffer(MAX_VERTEX_BUFFER_SIZE,
@@ -328,28 +343,42 @@ uint8_t update_mesh_buffers(const struct rse_vertex_t* vertices,
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,
last_vertex_buffer->buffer,
g_vertex_buffer.buffer,
vertices_size,
last_vertex_buffer->allocated_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,
last_index_buffer->buffer,
index_buffer->buffer,
indices_size,
last_index_buffer->allocated_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(struct rse_instance_data_t* instance_data)
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* last_instance_buffer = &g_instance_buffers[MAX_VULKAN_BUFFERS_COUNT - 1];
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*/
@@ -363,15 +392,20 @@ uint8_t update_mesh_instances(struct rse_instance_data_t* instance_data)
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,
last_instance_buffer->buffer,
instance_buffer->buffer,
instance_size,
last_instance_buffer->allocated_size);
instance_buffer->allocated_size);
vmaDestroyBuffer(g_vulkan_state.allocator, staging_buffer.buffer, staging_buffer.allocation);
last_instance_buffer->allocated_size += instance_size;
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;
}
@@ -429,19 +463,21 @@ uint8_t record_command_buffer(uint32_t image_index)
scissor.extent = g_vulkan_state.swapchain_extent;
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
for (i = 0; i < MAX_VULKAN_BUFFERS_COUNT; ++i) {
VkBuffer vertex_buffers[] = {g_vertex_buffers[i].buffer};
VkBuffer index_buffers = {g_index_buffers[i].buffer};
VkBuffer instance_buffers[] = {g_instance_buffers[i].buffer};
/* TODO: Figure out the offset */
vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, offsets);
vkCmdBindVertexBuffers(command_buffer, 1, 1, instance_buffers, offsets);
vkCmdBindIndexBuffer(command_buffer, index_buffers, 0, VK_INDEX_TYPE_UINT16);
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, 1,
&g_vulkan_state.descriptor_sets[g_vulkan_state.current_frame], 0, NULL);
vkCmdDrawIndexed(command_buffer, (uint32_t)(get_indices_count(0)+3), get_instances_count(0), 0, 0, 0);
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);
@@ -461,7 +497,7 @@ uint8_t create_buffers()
STATUS_CHECK(create_command_pools());
STATUS_CHECK(create_vertex_buffer());
STATUS_CHECK(create_index_buffer());
STATUS_CHECK(create_instance_buffer());
STATUS_CHECK(create_instance_buffers());
STATUS_CHECK(create_uniform_buffer());
STATUS_CHECK(allocate_command_buffers());
@@ -483,10 +519,13 @@ void destroy_buffers()
g_vulkan_state.uniform_buffers[i].allocation);
}
for (i = 0; i < MAX_VULKAN_BUFFERS_COUNT; ++i) {
vmaDestroyBuffer(g_vulkan_state.allocator, g_vertex_buffers[i].buffer, g_vertex_buffers[i].allocation);
vmaDestroyBuffer(g_vulkan_state.allocator, g_index_buffers[i].buffer, g_index_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);
}
vkDestroyCommandPool(g_vulkan_state.device, g_command_pool, NULL);
}
+20 -7
View File
@@ -6,6 +6,17 @@
#include <stdint.h>
#define MAX_MESH_NUMBER 256
/**
* @brief Represents data within vertex or instance buffer.
*
*/
struct rse_buffer_data_t
{
size_t count;
size_t buffer_offset;
};
/**
* @brief Create buffers needed by vulkan pipeline
@@ -25,22 +36,24 @@ void update_uniform_buffers();
*
* @param vertices Vertices to add
* @param indices Indices to add
* @param vertices_num Number of vertices to add
* @param indices_num Number of indices to add
* @return uint8_t VULKAN_ERROR_NO_ERROR on success
* @param vertex_data Mutable vertex data. Count will be read and buffer offset will be set
* @param index_data Mutable index data. Count will be read and buffer offset will be set
* @return uint8_t
*/
uint8_t update_mesh_buffers(const struct rse_vertex_t* vertices,
uint8_t rse_add_vertices(uint16_t mesh_id,
const struct rse_vertex_t* vertices,
const uint16_t* indices,
size_t vertices_num,
size_t indices_num);
struct rse_buffer_data_t* vertex_data,
struct rse_buffer_data_t* index_data);
/**
* @brief Add instance data to vulkan buffers
*
* @param mesh_id Mesh ID
* @param instance_data Instance data
* @return uint8_t VULKAN_ERROR_NO_ERROR on success
*/
uint8_t update_mesh_instances(struct rse_instance_data_t* instance_data);
uint8_t update_mesh_instances(uint16_t mesh_id, struct rse_instance_data_t* instance_data);
/**
* @brief Record commands for given image index
+1 -1
View File
@@ -38,7 +38,7 @@ struct rse_vertex_t
*/
struct rse_vulkan_buffer_t
{
uint32_t allocated_size;
size_t allocated_size;
VkBuffer buffer;
VmaAllocation allocation;
VmaAllocationInfo allocation_info;