Restructure things a little to enable multiple models
This commit is contained in:
1 parent
4c3fe8d8f7
commit
f3aa7435ec
3 files changed
+234
-183
No files matched your search
@@ -9,6 +9,10 @@
|
|||||||
#define SURFACE_FMT_BUFFER_SIZE 64
|
#define SURFACE_FMT_BUFFER_SIZE 64
|
||||||
#define PRESENT_MODE_BUFFER_SIZE 8
|
#define PRESENT_MODE_BUFFER_SIZE 8
|
||||||
|
|
||||||
|
#define STAGING_BUFFER_SIZE 0x4'0000 // 256 KiB
|
||||||
|
|
||||||
|
#define MAX_MODEL_COUNT 4
|
||||||
|
|
||||||
#define NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
|
#define NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||||
|
|
||||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
@@ -98,22 +102,17 @@ static void create_buffer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void load_buffer(
|
static void load_buffer(
|
||||||
const gpu_ctx_t *ctx, VkBuffer dest_buf, const void *data, size_t size,
|
const gpu_ctx_t *ctx, VkBuffer dest_buf, const void *data, size_t size)
|
||||||
VkBuffer staging_buf, VkDeviceMemory staging_buf_mem,
|
|
||||||
VkCommandPool cmd_pool)
|
|
||||||
{
|
{
|
||||||
VkResult result;
|
VkResult result;
|
||||||
|
|
||||||
// Copy data to staging buffer
|
// Copy data to staging buffer
|
||||||
void *staging_buf_ptr;
|
memcpy(ctx->staging_ptr, data, size);
|
||||||
vkMapMemory(ctx->dev, staging_buf_mem, 0, size, 0, &staging_buf_ptr);
|
|
||||||
memcpy(staging_buf_ptr, data, size);
|
|
||||||
vkUnmapMemory(ctx->dev, staging_buf_mem);
|
|
||||||
|
|
||||||
// Allocate command buffer
|
// Allocate command buffer
|
||||||
const VkCommandBufferAllocateInfo cmd_buf_config = {
|
const VkCommandBufferAllocateInfo cmd_buf_config = {
|
||||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||||
.commandPool = cmd_pool,
|
.commandPool = ctx->transient_cmd_pool,
|
||||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||||
.commandBufferCount = 1,
|
.commandBufferCount = 1,
|
||||||
};
|
};
|
||||||
@@ -128,7 +127,7 @@ static void load_buffer(
|
|||||||
};
|
};
|
||||||
vkBeginCommandBuffer(cmd_buf, &cmd_begin);
|
vkBeginCommandBuffer(cmd_buf, &cmd_begin);
|
||||||
VkBufferCopy cmd_copy = { .size = size };
|
VkBufferCopy cmd_copy = { .size = size };
|
||||||
vkCmdCopyBuffer(cmd_buf, staging_buf, dest_buf, 1, &cmd_copy);
|
vkCmdCopyBuffer(cmd_buf, ctx->staging_buf, dest_buf, 1, &cmd_copy);
|
||||||
vkEndCommandBuffer(cmd_buf);
|
vkEndCommandBuffer(cmd_buf);
|
||||||
|
|
||||||
// Send copy command
|
// Send copy command
|
||||||
@@ -140,18 +139,15 @@ static void load_buffer(
|
|||||||
vkQueueSubmit(ctx->queue, 1, ©_submit, VK_NULL_HANDLE);
|
vkQueueSubmit(ctx->queue, 1, ©_submit, VK_NULL_HANDLE);
|
||||||
vkQueueWaitIdle(ctx->queue);
|
vkQueueWaitIdle(ctx->queue);
|
||||||
|
|
||||||
vkFreeCommandBuffers(ctx->dev, cmd_pool, 1, &cmd_buf);
|
vkFreeCommandBuffers(ctx->dev, ctx->transient_cmd_pool, 1, &cmd_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpu_init(gpu_ctx_t *ctx, const gpu_config_t *config, model_t model)
|
void gpu_init(gpu_ctx_t *ctx, GLFWwindow *window, const char *name)
|
||||||
{
|
{
|
||||||
ctx->index_count = model.index_count;
|
|
||||||
ctx->bone_count = model.bone_count;
|
|
||||||
|
|
||||||
// Initialise Vulkan instance.
|
// Initialise Vulkan instance.
|
||||||
const VkApplicationInfo app_info = {
|
const VkApplicationInfo app_info = {
|
||||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||||
.pApplicationName = config->application_name,
|
.pApplicationName = name,
|
||||||
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
|
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||||
.pEngineName = "No Engine",
|
.pEngineName = "No Engine",
|
||||||
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
|
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||||
@@ -173,8 +169,8 @@ void gpu_init(gpu_ctx_t *ctx, const gpu_config_t *config, model_t model)
|
|||||||
assert(result == VK_SUCCESS);
|
assert(result == VK_SUCCESS);
|
||||||
|
|
||||||
// Create surface
|
// Create surface
|
||||||
result = glfwCreateWindowSurface(
|
result
|
||||||
ctx->inst, config->window, nullptr, &ctx->surface);
|
= glfwCreateWindowSurface(ctx->inst, window, nullptr, &ctx->surface);
|
||||||
assert(result == VK_SUCCESS);
|
assert(result == VK_SUCCESS);
|
||||||
|
|
||||||
// Select a physical device
|
// Select a physical device
|
||||||
@@ -273,7 +269,7 @@ void gpu_init(gpu_ctx_t *ctx, const gpu_config_t *config, model_t model)
|
|||||||
// Select swap chain extent
|
// Select swap chain extent
|
||||||
if (surface_caps.currentExtent.width == UINT32_MAX) {
|
if (surface_caps.currentExtent.width == UINT32_MAX) {
|
||||||
int width, height;
|
int width, height;
|
||||||
glfwGetFramebufferSize(config->window, &width, &height);
|
glfwGetFramebufferSize(window, &width, &height);
|
||||||
ctx->swapchain_extent.width = CLAMP(
|
ctx->swapchain_extent.width = CLAMP(
|
||||||
(uint32_t)width, surface_caps.minImageExtent.width,
|
(uint32_t)width, surface_caps.minImageExtent.width,
|
||||||
surface_caps.maxImageExtent.width);
|
surface_caps.maxImageExtent.width);
|
||||||
@@ -441,85 +437,15 @@ void gpu_init(gpu_ctx_t *ctx, const gpu_config_t *config, model_t model)
|
|||||||
.poolSizeCount = 1,
|
.poolSizeCount = 1,
|
||||||
.pPoolSizes = &(VkDescriptorPoolSize) {
|
.pPoolSizes = &(VkDescriptorPoolSize) {
|
||||||
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||||
.descriptorCount = 2 * ctx->swapchain_img_count,
|
.descriptorCount
|
||||||
|
= MAX_MODEL_COUNT * 2 * ctx->swapchain_img_count,
|
||||||
},
|
},
|
||||||
.maxSets = 2 * ctx->swapchain_img_count,
|
.maxSets = MAX_MODEL_COUNT * 2 * ctx->swapchain_img_count,
|
||||||
};
|
};
|
||||||
result = vkCreateDescriptorPool(
|
result = vkCreateDescriptorPool(
|
||||||
ctx->dev, &desc_pool_config, nullptr, &ctx->desc_pool);
|
ctx->dev, &desc_pool_config, nullptr, &ctx->desc_pool);
|
||||||
assert(result == VK_SUCCESS);
|
assert(result == VK_SUCCESS);
|
||||||
|
|
||||||
// Create and configure descriptor sets
|
|
||||||
VkDescriptorSetLayout desc_set_layouts[ctx->swapchain_img_count];
|
|
||||||
for (unsigned i = 0; i < ctx->swapchain_img_count; ++i)
|
|
||||||
desc_set_layouts[i] = ctx->desc_set_layout;
|
|
||||||
const VkDescriptorSetAllocateInfo desc_sets_alloc_config = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
|
||||||
.descriptorPool = ctx->desc_pool,
|
|
||||||
.descriptorSetCount = ctx->swapchain_img_count,
|
|
||||||
.pSetLayouts = desc_set_layouts,
|
|
||||||
};
|
|
||||||
result = vkAllocateDescriptorSets(
|
|
||||||
ctx->dev, &desc_sets_alloc_config, ctx->desc_sets);
|
|
||||||
assert(result == VK_SUCCESS);
|
|
||||||
|
|
||||||
// Create and map MVP buffer
|
|
||||||
const size_t mvp_buffer_size = ctx->swapchain_img_count * sizeof(mvp_t);
|
|
||||||
create_buffer(
|
|
||||||
ctx, mvp_buffer_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
|
||||||
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
||||||
&ctx->mvp_buffer, &ctx->mvp_buffer_mem);
|
|
||||||
vkMapMemory(
|
|
||||||
ctx->dev, ctx->mvp_buffer_mem, 0, mvp_buffer_size, 0,
|
|
||||||
(void **)&ctx->mvps);
|
|
||||||
|
|
||||||
// Create and map bone matrix buffer
|
|
||||||
const size_t bone_matrix_buffer_size
|
|
||||||
= ctx->swapchain_img_count * model.bone_count * sizeof(mat4_t);
|
|
||||||
create_buffer(
|
|
||||||
ctx, bone_matrix_buffer_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
|
||||||
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
||||||
&ctx->bone_matrix_buffer, &ctx->bone_matrix_buffer_mem);
|
|
||||||
vkMapMemory(
|
|
||||||
ctx->dev, ctx->bone_matrix_buffer_mem, 0, bone_matrix_buffer_size, 0,
|
|
||||||
(void **)&ctx->bone_matrices);
|
|
||||||
|
|
||||||
// Configure descriptor sets to use MVP and bone matrix buffers
|
|
||||||
for (unsigned i = 0; i < ctx->swapchain_img_count; ++i) {
|
|
||||||
const VkWriteDescriptorSet desc_writes[] = {
|
|
||||||
{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
||||||
.dstSet = ctx->desc_sets[i],
|
|
||||||
.dstBinding = 0,
|
|
||||||
.dstArrayElement = 0,
|
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.pBufferInfo = &(VkDescriptorBufferInfo) {
|
|
||||||
.buffer = ctx->mvp_buffer,
|
|
||||||
.offset = i * sizeof(mvp_t),
|
|
||||||
.range = sizeof(mvp_t),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
||||||
.dstSet = ctx->desc_sets[i],
|
|
||||||
.dstBinding = 1,
|
|
||||||
.dstArrayElement = 0,
|
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.pBufferInfo = &(VkDescriptorBufferInfo) {
|
|
||||||
.buffer = ctx->bone_matrix_buffer,
|
|
||||||
.offset = i * model.bone_count * sizeof(mat4_t),
|
|
||||||
.range = model.bone_count * sizeof(mat4_t),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
vkUpdateDescriptorSets(
|
|
||||||
ctx->dev, NELEMS(desc_writes), desc_writes, 0, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up input configuration
|
// Set up input configuration
|
||||||
const VkVertexInputBindingDescription input_binding_descs[] = {
|
const VkVertexInputBindingDescription input_binding_descs[] = {
|
||||||
{
|
{
|
||||||
@@ -776,27 +702,15 @@ void gpu_init(gpu_ctx_t *ctx, const gpu_config_t *config, model_t model)
|
|||||||
= vkCreateFence(ctx->dev, &fence_config, nullptr, &ctx->in_flight);
|
= vkCreateFence(ctx->dev, &fence_config, nullptr, &ctx->in_flight);
|
||||||
assert(result == VK_SUCCESS);
|
assert(result == VK_SUCCESS);
|
||||||
|
|
||||||
// Set up staging, vertex and index buffers
|
// Create and map staging buffer
|
||||||
const size_t vertex_data_size = model.vertex_count * sizeof(vertex_t);
|
|
||||||
const size_t index_data_size = model.index_count * sizeof(index_t);
|
|
||||||
VkBuffer staging_buf;
|
|
||||||
VkDeviceMemory staging_buf_mem;
|
|
||||||
const size_t staging_buf_size = MAX(vertex_data_size, index_data_size);
|
|
||||||
create_buffer(
|
create_buffer(
|
||||||
ctx, staging_buf_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
ctx, STAGING_BUFFER_SIZE, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||||
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||||
&staging_buf, &staging_buf_mem);
|
&ctx->staging_buf, &ctx->staging_mem);
|
||||||
create_buffer(
|
vkMapMemory(
|
||||||
ctx, vertex_data_size,
|
ctx->dev, ctx->staging_mem, 0, STAGING_BUFFER_SIZE, 0,
|
||||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
&ctx->staging_ptr);
|
||||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &ctx->vertex_buf,
|
|
||||||
&ctx->vertex_buf_mem);
|
|
||||||
create_buffer(
|
|
||||||
ctx, index_data_size,
|
|
||||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
|
||||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &ctx->index_buf,
|
|
||||||
&ctx->index_buf_mem);
|
|
||||||
|
|
||||||
// Create transient command pool
|
// Create transient command pool
|
||||||
const VkCommandPoolCreateInfo transient_cmd_pool_config = {
|
const VkCommandPoolCreateInfo transient_cmd_pool_config = {
|
||||||
@@ -804,33 +718,21 @@ void gpu_init(gpu_ctx_t *ctx, const gpu_config_t *config, model_t model)
|
|||||||
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
|
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
|
||||||
.queueFamilyIndex = queue_family,
|
.queueFamilyIndex = queue_family,
|
||||||
};
|
};
|
||||||
VkCommandPool transient_cmd_pool;
|
|
||||||
result = vkCreateCommandPool(
|
result = vkCreateCommandPool(
|
||||||
ctx->dev, &transient_cmd_pool_config, nullptr, &transient_cmd_pool);
|
ctx->dev, &transient_cmd_pool_config, nullptr,
|
||||||
|
&ctx->transient_cmd_pool);
|
||||||
assert(result == VK_SUCCESS);
|
assert(result == VK_SUCCESS);
|
||||||
|
|
||||||
// Load vertex and index data
|
ctx->active_img_index = UINT32_MAX;
|
||||||
load_buffer(
|
|
||||||
ctx, ctx->vertex_buf, model.vertices, vertex_data_size, staging_buf,
|
|
||||||
staging_buf_mem, transient_cmd_pool);
|
|
||||||
load_buffer(
|
|
||||||
ctx, ctx->index_buf, model.indices, index_data_size, staging_buf,
|
|
||||||
staging_buf_mem, transient_cmd_pool);
|
|
||||||
|
|
||||||
// Clean up staging buffer and transient command pool
|
|
||||||
vkDestroyCommandPool(ctx->dev, transient_cmd_pool, nullptr);
|
|
||||||
vkDestroyBuffer(ctx->dev, staging_buf, nullptr);
|
|
||||||
vkFreeMemory(ctx->dev, staging_buf_mem, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpu_cleanup(const gpu_ctx_t *ctx)
|
void gpu_cleanup(const gpu_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
vkDeviceWaitIdle(ctx->dev);
|
vkDeviceWaitIdle(ctx->dev);
|
||||||
|
|
||||||
vkDestroyBuffer(ctx->dev, ctx->index_buf, nullptr);
|
vkDestroyCommandPool(ctx->dev, ctx->transient_cmd_pool, nullptr);
|
||||||
vkFreeMemory(ctx->dev, ctx->index_buf_mem, nullptr);
|
vkDestroyBuffer(ctx->dev, ctx->staging_buf, nullptr);
|
||||||
vkDestroyBuffer(ctx->dev, ctx->vertex_buf, nullptr);
|
vkFreeMemory(ctx->dev, ctx->staging_mem, nullptr);
|
||||||
vkFreeMemory(ctx->dev, ctx->vertex_buf_mem, nullptr);
|
|
||||||
|
|
||||||
vkDestroySemaphore(ctx->dev, ctx->img_avail, nullptr);
|
vkDestroySemaphore(ctx->dev, ctx->img_avail, nullptr);
|
||||||
vkDestroySemaphore(ctx->dev, ctx->render_fin, nullptr);
|
vkDestroySemaphore(ctx->dev, ctx->render_fin, nullptr);
|
||||||
@@ -842,14 +744,6 @@ void gpu_cleanup(const gpu_ctx_t *ctx)
|
|||||||
vkDestroyRenderPass(ctx->dev, ctx->render_pass, nullptr);
|
vkDestroyRenderPass(ctx->dev, ctx->render_pass, nullptr);
|
||||||
vkDestroyPipelineLayout(ctx->dev, ctx->pipeline_layout, nullptr);
|
vkDestroyPipelineLayout(ctx->dev, ctx->pipeline_layout, nullptr);
|
||||||
|
|
||||||
vkUnmapMemory(ctx->dev, ctx->bone_matrix_buffer_mem);
|
|
||||||
vkDestroyBuffer(ctx->dev, ctx->bone_matrix_buffer, nullptr);
|
|
||||||
vkFreeMemory(ctx->dev, ctx->bone_matrix_buffer_mem, nullptr);
|
|
||||||
|
|
||||||
vkUnmapMemory(ctx->dev, ctx->mvp_buffer_mem);
|
|
||||||
vkDestroyBuffer(ctx->dev, ctx->mvp_buffer, nullptr);
|
|
||||||
vkFreeMemory(ctx->dev, ctx->mvp_buffer_mem, nullptr);
|
|
||||||
|
|
||||||
vkDestroyDescriptorPool(ctx->dev, ctx->desc_pool, nullptr);
|
vkDestroyDescriptorPool(ctx->dev, ctx->desc_pool, nullptr);
|
||||||
vkDestroyDescriptorSetLayout(ctx->dev, ctx->desc_set_layout, nullptr);
|
vkDestroyDescriptorSetLayout(ctx->dev, ctx->desc_set_layout, nullptr);
|
||||||
|
|
||||||
@@ -871,8 +765,121 @@ void gpu_cleanup(const gpu_ctx_t *ctx)
|
|||||||
vkDestroyInstance(ctx->inst, nullptr);
|
vkDestroyInstance(ctx->inst, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpu_render(
|
void gpu_model_load(
|
||||||
const gpu_ctx_t *ctx, const mvp_t *mvp, const mat4_t *bone_matrices)
|
const gpu_ctx_t *ctx, const model_t *model, gpu_model_t *out)
|
||||||
|
{
|
||||||
|
VkResult result;
|
||||||
|
|
||||||
|
out->bone_count = model->bone_count;
|
||||||
|
out->index_count = model->index_count;
|
||||||
|
|
||||||
|
// Create buffers
|
||||||
|
const size_t vertex_size = model->vertex_count * sizeof(vertex_t);
|
||||||
|
assert(vertex_size < STAGING_BUFFER_SIZE);
|
||||||
|
create_buffer(
|
||||||
|
ctx, vertex_size,
|
||||||
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||||
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &out->vertex_buf,
|
||||||
|
&out->vertex_mem);
|
||||||
|
load_buffer(ctx, out->vertex_buf, model->vertices, vertex_size);
|
||||||
|
|
||||||
|
// Load data into buffers
|
||||||
|
const size_t index_size = model->index_count * sizeof(index_t);
|
||||||
|
assert(index_size < STAGING_BUFFER_SIZE);
|
||||||
|
create_buffer(
|
||||||
|
ctx, index_size,
|
||||||
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
||||||
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &out->index_buf,
|
||||||
|
&out->index_mem);
|
||||||
|
load_buffer(ctx, out->index_buf, model->indices, index_size);
|
||||||
|
|
||||||
|
// Create and configure descriptor sets
|
||||||
|
VkDescriptorSetLayout desc_set_layouts[ctx->swapchain_img_count];
|
||||||
|
for (unsigned i = 0; i < ctx->swapchain_img_count; ++i)
|
||||||
|
desc_set_layouts[i] = ctx->desc_set_layout;
|
||||||
|
const VkDescriptorSetAllocateInfo desc_sets_alloc_config = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||||
|
.descriptorPool = ctx->desc_pool,
|
||||||
|
.descriptorSetCount = ctx->swapchain_img_count,
|
||||||
|
.pSetLayouts = desc_set_layouts,
|
||||||
|
};
|
||||||
|
result = vkAllocateDescriptorSets(
|
||||||
|
ctx->dev, &desc_sets_alloc_config, out->desc_sets);
|
||||||
|
assert(result == VK_SUCCESS);
|
||||||
|
|
||||||
|
// Create and map MVP buffer
|
||||||
|
const size_t mvp_size = ctx->swapchain_img_count * sizeof(mvp_t);
|
||||||
|
create_buffer(
|
||||||
|
ctx, mvp_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||||
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||||
|
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||||
|
&out->mvp_buf, &out->mvp_mem);
|
||||||
|
vkMapMemory(
|
||||||
|
ctx->dev, out->mvp_mem, 0, mvp_size, 0, (void **)&out->mvp_ptr);
|
||||||
|
|
||||||
|
// Create and map bone matrix buffer
|
||||||
|
const size_t bone_size
|
||||||
|
= ctx->swapchain_img_count * model->bone_count * sizeof(mat4_t);
|
||||||
|
create_buffer(
|
||||||
|
ctx, bone_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||||
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||||
|
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||||
|
&out->bone_buf, &out->bone_mem);
|
||||||
|
vkMapMemory(
|
||||||
|
ctx->dev, out->bone_mem, 0, bone_size, 0, (void **)&out->bone_ptr);
|
||||||
|
|
||||||
|
// Configure descriptor sets to use MVP and bone matrix buffers
|
||||||
|
for (unsigned i = 0; i < ctx->swapchain_img_count; ++i) {
|
||||||
|
const VkWriteDescriptorSet desc_writes[] = {
|
||||||
|
{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
|
.dstSet = out->desc_sets[i],
|
||||||
|
.dstBinding = 0,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.pBufferInfo = &(VkDescriptorBufferInfo) {
|
||||||
|
.buffer = out->mvp_buf,
|
||||||
|
.offset = i * sizeof(mvp_t),
|
||||||
|
.range = sizeof(mvp_t),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
|
.dstSet = out->desc_sets[i],
|
||||||
|
.dstBinding = 1,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.pBufferInfo = &(VkDescriptorBufferInfo) {
|
||||||
|
.buffer = out->bone_buf,
|
||||||
|
.offset = i * model->bone_count * sizeof(mat4_t),
|
||||||
|
.range = model->bone_count * sizeof(mat4_t),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
vkUpdateDescriptorSets(
|
||||||
|
ctx->dev, NELEMS(desc_writes), desc_writes, 0, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpu_model_cleanup(const gpu_ctx_t *ctx, const gpu_model_t *model)
|
||||||
|
{
|
||||||
|
vkDeviceWaitIdle(ctx->dev);
|
||||||
|
|
||||||
|
vkDestroyBuffer(ctx->dev, model->bone_buf, nullptr);
|
||||||
|
vkFreeMemory(ctx->dev, model->bone_mem, nullptr);
|
||||||
|
|
||||||
|
vkDestroyBuffer(ctx->dev, model->mvp_buf, nullptr);
|
||||||
|
vkFreeMemory(ctx->dev, model->mvp_mem, nullptr);
|
||||||
|
|
||||||
|
vkDestroyBuffer(ctx->dev, model->index_buf, nullptr);
|
||||||
|
vkFreeMemory(ctx->dev, model->index_mem, nullptr);
|
||||||
|
vkDestroyBuffer(ctx->dev, model->vertex_buf, nullptr);
|
||||||
|
vkFreeMemory(ctx->dev, model->vertex_mem, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpu_render_begin(gpu_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
VkResult result;
|
VkResult result;
|
||||||
|
|
||||||
@@ -881,18 +888,11 @@ void gpu_render(
|
|||||||
vkResetFences(ctx->dev, 1, &ctx->in_flight);
|
vkResetFences(ctx->dev, 1, &ctx->in_flight);
|
||||||
|
|
||||||
// Aquire image from swapchain
|
// Aquire image from swapchain
|
||||||
uint32_t img_index;
|
|
||||||
result = vkAcquireNextImageKHR(
|
result = vkAcquireNextImageKHR(
|
||||||
ctx->dev, ctx->swapchain, UINT64_MAX, ctx->img_avail, VK_NULL_HANDLE,
|
ctx->dev, ctx->swapchain, UINT64_MAX, ctx->img_avail, VK_NULL_HANDLE,
|
||||||
&img_index);
|
&ctx->active_img_index);
|
||||||
assert(result == VK_SUCCESS);
|
assert(result == VK_SUCCESS);
|
||||||
|
|
||||||
// Copy MVP and bone matricies into uniform buffer for this image
|
|
||||||
memcpy(ctx->mvps + img_index, mvp, sizeof(mvp_t));
|
|
||||||
memcpy(
|
|
||||||
ctx->bone_matrices + (ctx->bone_count * img_index), bone_matrices,
|
|
||||||
ctx->bone_count * sizeof(mat4_t));
|
|
||||||
|
|
||||||
// Reset command buffer, then begin writing
|
// Reset command buffer, then begin writing
|
||||||
vkResetCommandBuffer(ctx->cmd_buf, 0);
|
vkResetCommandBuffer(ctx->cmd_buf, 0);
|
||||||
const VkCommandBufferBeginInfo cmd_begin = {
|
const VkCommandBufferBeginInfo cmd_begin = {
|
||||||
@@ -909,7 +909,7 @@ void gpu_render(
|
|||||||
VkRenderPassBeginInfo render_pass_begin = {
|
VkRenderPassBeginInfo render_pass_begin = {
|
||||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||||
.renderPass = ctx->render_pass,
|
.renderPass = ctx->render_pass,
|
||||||
.framebuffer = ctx->framebuffers[img_index],
|
.framebuffer = ctx->framebuffers[ctx->active_img_index],
|
||||||
.renderArea.extent = ctx->swapchain_extent,
|
.renderArea.extent = ctx->swapchain_extent,
|
||||||
.clearValueCount = 2,
|
.clearValueCount = 2,
|
||||||
.pClearValues = clear_values,
|
.pClearValues = clear_values,
|
||||||
@@ -918,20 +918,34 @@ void gpu_render(
|
|||||||
ctx->cmd_buf, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
|
ctx->cmd_buf, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
vkCmdBindPipeline(
|
vkCmdBindPipeline(
|
||||||
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pipeline);
|
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpu_render_model(
|
||||||
|
const gpu_ctx_t *ctx, const gpu_model_t *model, const mvp_t *mvp,
|
||||||
|
const mat4_t *bone_matrices)
|
||||||
|
{
|
||||||
|
assert(ctx->active_img_index < ctx->swapchain_img_count);
|
||||||
|
|
||||||
|
memcpy(model->mvp_ptr + ctx->active_img_index, mvp, sizeof(mvp_t));
|
||||||
|
memcpy(
|
||||||
|
model->bone_ptr + (model->bone_count * ctx->active_img_index),
|
||||||
|
bone_matrices, model->bone_count * sizeof(mat4_t));
|
||||||
|
|
||||||
// Bind vertex and index buffers
|
|
||||||
vkCmdBindVertexBuffers(
|
vkCmdBindVertexBuffers(
|
||||||
ctx->cmd_buf, 0, 1, &ctx->vertex_buf, &(VkDeviceSize) { 0 });
|
ctx->cmd_buf, 0, 1, &model->vertex_buf, &(VkDeviceSize) { 0 });
|
||||||
vkCmdBindIndexBuffer(
|
vkCmdBindIndexBuffer(
|
||||||
ctx->cmd_buf, ctx->index_buf, 0, VK_INDEX_TYPE_UINT16);
|
ctx->cmd_buf, model->index_buf, 0, VK_INDEX_TYPE_UINT16);
|
||||||
|
|
||||||
// Bind descriptor set for this frame
|
|
||||||
vkCmdBindDescriptorSets(
|
vkCmdBindDescriptorSets(
|
||||||
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pipeline_layout,
|
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pipeline_layout,
|
||||||
0, 1, ctx->desc_sets + img_index, 0, nullptr);
|
0, 1, model->desc_sets + ctx->active_img_index, 0, nullptr);
|
||||||
|
|
||||||
// Draw
|
vkCmdDrawIndexed(ctx->cmd_buf, model->index_count, 1, 0, 0, 0);
|
||||||
vkCmdDrawIndexed(ctx->cmd_buf, ctx->index_count, 1, 0, 0, 0);
|
}
|
||||||
|
|
||||||
|
void gpu_render_submit(gpu_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
VkResult result;
|
||||||
|
|
||||||
// Finish render pass and end writing to command buffer
|
// Finish render pass and end writing to command buffer
|
||||||
vkCmdEndRenderPass(ctx->cmd_buf);
|
vkCmdEndRenderPass(ctx->cmd_buf);
|
||||||
@@ -961,8 +975,10 @@ void gpu_render(
|
|||||||
.pWaitSemaphores = &ctx->render_fin,
|
.pWaitSemaphores = &ctx->render_fin,
|
||||||
.swapchainCount = 1,
|
.swapchainCount = 1,
|
||||||
.pSwapchains = &ctx->swapchain,
|
.pSwapchains = &ctx->swapchain,
|
||||||
.pImageIndices = &img_index,
|
.pImageIndices = &ctx->active_img_index,
|
||||||
};
|
};
|
||||||
result = vkQueuePresentKHR(ctx->queue, &present_info);
|
result = vkQueuePresentKHR(ctx->queue, &present_info);
|
||||||
assert(result == VK_SUCCESS);
|
assert(result == VK_SUCCESS);
|
||||||
|
|
||||||
|
ctx->active_img_index = UINT32_MAX;
|
||||||
}
|
}
|
||||||
@@ -33,13 +33,6 @@ typedef struct {
|
|||||||
VkShaderModule frag_shader;
|
VkShaderModule frag_shader;
|
||||||
VkDescriptorSetLayout desc_set_layout;
|
VkDescriptorSetLayout desc_set_layout;
|
||||||
VkDescriptorPool desc_pool;
|
VkDescriptorPool desc_pool;
|
||||||
VkDescriptorSet desc_sets[MAX_FRAMES_IN_FLIGHT];
|
|
||||||
VkBuffer mvp_buffer;
|
|
||||||
VkDeviceMemory mvp_buffer_mem;
|
|
||||||
mvp_t *mvps;
|
|
||||||
VkBuffer bone_matrix_buffer;
|
|
||||||
VkDeviceMemory bone_matrix_buffer_mem;
|
|
||||||
mat4_t *bone_matrices;
|
|
||||||
VkPipelineLayout pipeline_layout;
|
VkPipelineLayout pipeline_layout;
|
||||||
VkRenderPass render_pass;
|
VkRenderPass render_pass;
|
||||||
VkPipeline pipeline;
|
VkPipeline pipeline;
|
||||||
@@ -48,22 +41,41 @@ typedef struct {
|
|||||||
VkFence in_flight;
|
VkFence in_flight;
|
||||||
VkSemaphore render_fin;
|
VkSemaphore render_fin;
|
||||||
VkSemaphore img_avail;
|
VkSemaphore img_avail;
|
||||||
VkBuffer vertex_buf;
|
VkBuffer staging_buf;
|
||||||
VkDeviceMemory vertex_buf_mem;
|
VkDeviceMemory staging_mem;
|
||||||
VkBuffer index_buf;
|
void *staging_ptr;
|
||||||
VkDeviceMemory index_buf_mem;
|
VkCommandPool transient_cmd_pool;
|
||||||
size_t index_count;
|
uint32_t active_img_index;
|
||||||
size_t bone_count;
|
|
||||||
} gpu_ctx_t;
|
} gpu_ctx_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *application_name;
|
mvp_t *mvp_ptr;
|
||||||
GLFWwindow *window;
|
mat4_t *bone_ptr;
|
||||||
} gpu_config_t;
|
VkBuffer vertex_buf;
|
||||||
|
VkDeviceMemory vertex_mem;
|
||||||
|
VkBuffer index_buf;
|
||||||
|
VkDeviceMemory index_mem;
|
||||||
|
VkDescriptorSet desc_sets[MAX_FRAMES_IN_FLIGHT];
|
||||||
|
VkBuffer mvp_buf;
|
||||||
|
VkDeviceMemory mvp_mem;
|
||||||
|
VkBuffer bone_buf;
|
||||||
|
VkDeviceMemory bone_mem;
|
||||||
|
size_t index_count;
|
||||||
|
size_t bone_count;
|
||||||
|
} gpu_model_t;
|
||||||
|
|
||||||
void gpu_init(gpu_ctx_t *ctx, const gpu_config_t *config, model_t model);
|
void gpu_init(gpu_ctx_t *ctx, GLFWwindow *window, const char *name);
|
||||||
void gpu_cleanup(const gpu_ctx_t *ctx);
|
void gpu_cleanup(const gpu_ctx_t *ctx);
|
||||||
void gpu_render(
|
|
||||||
const gpu_ctx_t *ctx, const mvp_t *mvp, const mat4_t *bone_matrices);
|
void gpu_model_load(
|
||||||
|
const gpu_ctx_t *ctx, const model_t *model, gpu_model_t *out);
|
||||||
|
void gpu_model_cleanup(const gpu_ctx_t *ctx, const gpu_model_t *model);
|
||||||
|
|
||||||
|
void gpu_render_begin(gpu_ctx_t *ctx);
|
||||||
|
void gpu_render_model(
|
||||||
|
const gpu_ctx_t *ctx, const gpu_model_t *model, const mvp_t *mvp,
|
||||||
|
|
||||||
|
const mat4_t *bone_matrices);
|
||||||
|
void gpu_render_submit(gpu_ctx_t *ctx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
#define DT (1'000'000 / 60) // Assume 60 Hz for now
|
#define DT (1'000'000 / 60) // Assume 60 Hz for now
|
||||||
|
|
||||||
|
#define LIL_GUY_COUNT 2
|
||||||
|
|
||||||
static void
|
static void
|
||||||
key_callback(GLFWwindow *window, int key, int scancode, int action, int mods)
|
key_callback(GLFWwindow *window, int key, int scancode, int action, int mods)
|
||||||
{
|
{
|
||||||
@@ -48,20 +50,19 @@ int main(int argc, char *argv[])
|
|||||||
// Register input callbacks
|
// Register input callbacks
|
||||||
glfwSetKeyCallback(window, key_callback);
|
glfwSetKeyCallback(window, key_callback);
|
||||||
|
|
||||||
// Initialise GPU
|
// Initialise GPU and load model
|
||||||
const gpu_config_t gpu_config = {
|
|
||||||
.application_name = "Foo",
|
|
||||||
.window = window,
|
|
||||||
};
|
|
||||||
gpu_ctx_t gpu;
|
gpu_ctx_t gpu;
|
||||||
gpu_init(&gpu, &gpu_config, model);
|
gpu_model_t gpu_model1, gpu_model2;
|
||||||
|
gpu_init(&gpu, window, "Foo");
|
||||||
|
gpu_model_load(&gpu, &model, &gpu_model1);
|
||||||
|
gpu_model_load(&gpu, &model, &gpu_model2);
|
||||||
|
|
||||||
// Initialise animation
|
// Initialise animation
|
||||||
animation_state_t anim = animation_init(&model);
|
animation_state_t anim = animation_init(&model);
|
||||||
|
|
||||||
const vec3_t cam = { 0, 4, -4 };
|
const vec3_t cam = { 0, 4, -4 };
|
||||||
const vec3_t tgt = { 0, 0.5, 0 };
|
const vec3_t tgt = { 0, 0.5, 0 };
|
||||||
mvp_t mvp = {
|
mvp_t mvp1 = {
|
||||||
.model = mat4_mul(
|
.model = mat4_mul(
|
||||||
MAT4_TRANSLATE(1, 0, 0),
|
MAT4_TRANSLATE(1, 0, 0),
|
||||||
(mat4_t) {
|
(mat4_t) {
|
||||||
@@ -73,20 +74,42 @@ int main(int argc, char *argv[])
|
|||||||
.view = mat4_lookat(cam, tgt, (vec3_t) { 0, 1, 0 }),
|
.view = mat4_lookat(cam, tgt, (vec3_t) { 0, 1, 0 }),
|
||||||
.proj = mat4_perspective((float)W / H, PI / 8, 0.1, 10.0),
|
.proj = mat4_perspective((float)W / H, PI / 8, 0.1, 10.0),
|
||||||
};
|
};
|
||||||
|
mvp_t mvp2 = {
|
||||||
|
.model = mat4_mul(
|
||||||
|
MAT4_TRANSLATE(2, 0, 0),
|
||||||
|
mat4_mul(
|
||||||
|
MAT4_ROTATE_Y(PI),
|
||||||
|
(mat4_t) {
|
||||||
|
{ 0, 0, -1, 0 },
|
||||||
|
{ -1, 0, 0, 0 },
|
||||||
|
{ 0, 1, 0, 0 },
|
||||||
|
{ 0, 0, 0, 1 },
|
||||||
|
})),
|
||||||
|
.view = mat4_lookat(cam, tgt, (vec3_t) { 0, 1, 0 }),
|
||||||
|
.proj = mat4_perspective((float)W / H, PI / 8, 0.1, 10.0),
|
||||||
|
};
|
||||||
|
|
||||||
const mat4_t rot = MAT4_ROTATE_Y(PI / 170);
|
const mat4_t rot1 = MAT4_ROTATE_Y(PI / 170);
|
||||||
|
const mat4_t rot2 = MAT4_ROTATE_Y(-PI / 340);
|
||||||
animation_start(&anim, &model, 0);
|
animation_start(&anim, &model, 0);
|
||||||
|
|
||||||
while (!glfwWindowShouldClose(window)) {
|
while (!glfwWindowShouldClose(window)) {
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
|
|
||||||
mvp.model = mat4_mul(rot, mvp.model);
|
mvp1.model = mat4_mul(rot1, mvp1.model);
|
||||||
|
mvp2.model = mat4_mul(rot2, mvp2.model);
|
||||||
animation_update(&anim, &model, DT);
|
animation_update(&anim, &model, DT);
|
||||||
gpu_render(&gpu, &mvp, anim.bone_matrices);
|
|
||||||
|
gpu_render_begin(&gpu);
|
||||||
|
gpu_render_model(&gpu, &gpu_model1, &mvp1, anim.bone_matrices);
|
||||||
|
gpu_render_model(&gpu, &gpu_model2, &mvp2, anim.bone_matrices);
|
||||||
|
gpu_render_submit(&gpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
animation_cleanup(&anim);
|
animation_cleanup(&anim);
|
||||||
|
gpu_model_cleanup(&gpu, &gpu_model1);
|
||||||
|
gpu_model_cleanup(&gpu, &gpu_model2);
|
||||||
gpu_cleanup(&gpu);
|
gpu_cleanup(&gpu);
|
||||||
model_cleanup(&model);
|
model_cleanup(&model);
|
||||||
glfwDestroyWindow(window);
|
glfwDestroyWindow(window);
|
||||||
|
|||||||
Reference in new issue
Block a user