Instancing

The renderer now allows instancing. The way this works is by having to
vertex buffers. One contains the normal vertex geometry data, the other
contains per-instance data.
This commit is contained in:
FroggyGreen 2024-05-09 16:39:50 -05:00
parent 583a6e67b3
commit 1ec76f56c8
7 changed files with 379 additions and 239 deletions

View File

@ -1,9 +0,0 @@
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}

View File

@ -1,12 +0,0 @@
#version 460
layout(location = 0) in vec2 inPos;
layout(location = 1) in vec3 inColor;
layout(location = 0) out vec3 fragColor;
void main()
{
gl_Position = vec4(inPos, 0.0, 1.0);
fragColor = inColor;
}

View File

@ -0,0 +1,10 @@
#version 460
layout(location = 0) in vec4 fragColor;
layout(location = 0) out vec4 outColor;
void main()
{
outColor = fragColor;
}

View File

@ -0,0 +1,18 @@
#version 460
// vertex geometry data
layout(location = 0) in vec2 inPos;
// instance data
layout(location = 1) in vec2 inInstancePos;
layout(location = 2) in vec2 inInstanceScale;
layout(location = 3) in vec4 inInstanceColor;
layout(location = 0) out vec4 fragColor;
void main()
{
vec2 pos = inPos * inInstanceScale + inInstancePos;
gl_Position = vec4(pos, 0.0, 1.0);
fragColor = inInstanceColor;
}

View File

@ -46,20 +46,25 @@ typedef struct vulkan_buffer
typedef struct mesh
{
vulkan_buffer vertex_buffer;
vulkan_buffer instance_buffer;
vulkan_buffer index_buffer;
u64 index_count;
u64 instance_count;
} mesh;
typedef struct graphics_pipeline
typedef struct mesh_pipeline
{
VkPipelineLayout layout;
VkPipeline handle;
VkPipeline main;
VkPipeline wireframe;
vertex_input_type geometry_type;
vertex_input_type instance_type;
hash_table meshes;
} graphics_pipeline;
} mesh_pipeline;
#define MAX_FRAMES_IN_FLIGHT 2 // must be a power of two!
@ -110,7 +115,7 @@ typedef struct vulkan_context
} single_submit_context;
hash_table graphics_pipelines;
hash_table mesh_pipelines;
} vulkan_context;
@ -140,16 +145,6 @@ static const char* validation_layers[] = { "VK_LAYER_KHRONOS_validation" };
#endif
static const char* device_extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
static VkVertexInputBindingDescription pos2_color3_vertex_input_binding =
{
.binding = 0, .stride = sizeof(pos2_color3_vertex), .inputRate = VK_VERTEX_INPUT_RATE_VERTEX
};
static VkVertexInputAttributeDescription pos2_color3_vertex_input_attributes[] =
{
(VkVertexInputAttributeDescription){ .location = 0, .binding = 0, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(pos2_color3_vertex, pos) },
(VkVertexInputAttributeDescription){ .location = 1, .binding = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(pos2_color3_vertex, color) }
};
//
// Window
//
@ -1119,7 +1114,7 @@ static void shutdown_frame_data()
static u8 init_frame_data()
{
trace_log(LOG_INFO, "Initializing Vulkan frame data ...");
trace_log(LOG_DEBUG, "Initializing Vulkan frame data ...");
VkCommandBuffer command_buffers[MAX_FRAMES_IN_FLIGHT];
if (!allocate_command_buffers(context.command_pool, command_buffers, MAX_FRAMES_IN_FLIGHT)) return 0;
@ -1189,7 +1184,7 @@ u8 init_single_submit_context()
return 0;
}
trace_log(LOG_DEBUG, "Vulkan upload single-submit successfully created.");
trace_log(LOG_DEBUG, "Vulkan single-submit context successfully created.");
signal_for_shutdown(shutdown_single_submit_context);
return 1;
@ -1219,7 +1214,7 @@ static u8 end_single_submit_command_recording(VkCommandBuffer command_buffer)
};
if (vkQueueSubmit(context.device.graphics_queue, 1, &submit_info, context.single_submit_context.fence) != VK_SUCCESS)
{
trace_log(LOG_ERROR, "vkQueueSubmit call failed in end_upload.");
trace_log(LOG_ERROR, "vkQueueSubmit call failed in end_single_submit_command_recording.");
return 0;
}
@ -1294,6 +1289,14 @@ static u8 init_vulkan_staging_buffer(vulkan_buffer* buffer, VkDeviceSize byte_si
return init_vulkan_buffer(buffer, byte_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
}
// uniform buffer, CPU accessible memory
/*
static u8 init_vulkan_uniform_buffer(vulkan_buffer* buffer, VkDeviceSize byte_size)
{
return init_vulkan_buffer(buffer, byte_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
}
*/
// vertex buffer, device memory
static u8 init_vulkan_vertex_buffer(vulkan_buffer* buffer, VkDeviceSize byte_size)
{
@ -1340,7 +1343,7 @@ static u8 load_vulkan_buffer(vulkan_buffer* buffer, VkDeviceSize offset, VkDevic
return 1;
}
u8 copy_vulkan_buffer(vulkan_buffer* dst, vulkan_buffer* src, VkDeviceSize dst_offset, VkDeviceSize src_offset, VkDeviceSize byte_size)
static u8 copy_vulkan_buffer(vulkan_buffer* dst, vulkan_buffer* src, VkDeviceSize dst_offset, VkDeviceSize src_offset, VkDeviceSize byte_size)
{
VkCommandBuffer command_buffer;
if (!allocate_command_buffers(context.single_submit_context.command_pool, &command_buffer, 1)) return 0;
@ -1361,29 +1364,64 @@ u8 copy_vulkan_buffer(vulkan_buffer* dst, vulkan_buffer* src, VkDeviceSize dst_o
return 1;
}
static u8 init_mesh(mesh* msh, const mesh_geometry* geometry)
static u8 init_mesh(mesh* msh, const mesh_geometry* geometry, vertex_input_type geometry_type, const mesh_instance_data* instance_data, vertex_input_type instance_type)
{
VkDeviceSize vertices_byte_size = 0;
switch (geometry->vert_type)
VkDeviceSize geometry_vertices_byte_size = 0, instance_data_byte_size = 0;
for (u8 i = 0; i < 2; i++)
{
case VERTEX_TYPE_POS2_COLOR3:
vertices_byte_size = geometry->vertex_count * sizeof(pos2_color3_vertex);
break;
default:
trace_log(LOG_ERROR, "Failed to create Vulkan mesh; invalid vertex type.");
return 0;
vertex_input_type t;
VkDeviceSize* ds;
u64 count;
if (i < 1)
{
t = geometry_type;
ds = &geometry_vertices_byte_size;
count = geometry->vertex_count;
}
else
{
t = instance_type;
ds = &instance_data_byte_size;
count = instance_data->instance_count;
}
switch (t)
{
case VERTEX_INPUT_TYPE_POS2:
*ds = count * sizeof(pos2);
break;
case VERTEX_INPUT_TYPE_POS2_POS2_COLOR4:
*ds = count * sizeof(pos2_pos2_color4);
break;
default:
trace_log(LOG_ERROR, "Failed to create mesh; invalid vertex type (binding = %d).", i);
return 0;
}
}
VkDeviceSize indices_byte_size = geometry->index_count * sizeof(u32);
if (geometry->vertex_count && geometry->vertices)
{
vulkan_buffer staging_buffer;
if (!init_vulkan_staging_buffer(&staging_buffer, vertices_byte_size)) return 0;
if (!load_vulkan_buffer(&staging_buffer, 0, vertices_byte_size, 0, geometry->vertices)) return 0;
if (!init_vulkan_vertex_buffer(&msh->vertex_buffer, vertices_byte_size)) return 0;
if (!copy_vulkan_buffer(&msh->vertex_buffer, &staging_buffer, 0, 0, vertices_byte_size)) return 0;
if (!init_vulkan_staging_buffer(&staging_buffer, geometry_vertices_byte_size)) return 0;
if (!load_vulkan_buffer(&staging_buffer, 0, geometry_vertices_byte_size, 0, geometry->vertices)) return 0;
if (!init_vulkan_vertex_buffer(&msh->vertex_buffer, geometry_vertices_byte_size)) return 0;
if (!copy_vulkan_buffer(&msh->vertex_buffer, &staging_buffer, 0, 0, geometry_vertices_byte_size)) return 0;
shutdown_vulkan_buffer(&staging_buffer);
}
if (instance_data->instance_count && instance_data)
{
vulkan_buffer staging_buffer;
if (!init_vulkan_staging_buffer(&staging_buffer, instance_data_byte_size)) return 0;
if (!load_vulkan_buffer(&staging_buffer, 0, instance_data_byte_size, 0, instance_data->instances)) return 0;
if (!init_vulkan_vertex_buffer(&msh->instance_buffer, instance_data_byte_size)) return 0;
if (!copy_vulkan_buffer(&msh->instance_buffer, &staging_buffer, 0, 0, instance_data_byte_size)) return 0;
shutdown_vulkan_buffer(&staging_buffer);
}
msh->instance_count = instance_data->instance_count;
if (geometry->index_count && geometry->indices)
{
vulkan_buffer staging_buffer;
if (!init_vulkan_staging_buffer(&staging_buffer, indices_byte_size)) return 0;
@ -1400,6 +1438,7 @@ static u8 init_mesh(mesh* msh, const mesh_geometry* geometry)
static void shutdown_mesh(mesh* msh)
{
shutdown_vulkan_buffer(&msh->index_buffer);
shutdown_vulkan_buffer(&msh->instance_buffer);
shutdown_vulkan_buffer(&msh->vertex_buffer);
}
@ -1443,7 +1482,7 @@ static void shutdown_shader_module(VkShaderModule module)
vkDestroyShaderModule(context.device.logical, module, context.allocator);
}
static u8 init_graphics_pipeline(graphics_pipeline* pipeline, const graphics_pipeline_template* template)
static u8 init_mesh_pipeline(mesh_pipeline* pipeline, const mesh_pipeline_template* template)
{
// device features
VkPhysicalDeviceFeatures device_features;
@ -1454,10 +1493,8 @@ static u8 init_graphics_pipeline(graphics_pipeline* pipeline, const graphics_pip
VkShaderModule modules[2];
const VkShaderStageFlagBits stage_flag_bits[2] = { VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT };
const char* stage_names[2] = { "vert", "frag" };
const u8 is_stage[2] = { template->is_vertex, template->is_fragment };
for (u8 i = 0; i < 2; i++)
{
if (!is_stage[i]) continue;
char path[512];
snprintf(path, 512, "renderer/shaders/%s.%s.spv", template->name, stage_names[i]);
if (!init_shader_module(modules + i, path)) return 0;
@ -1474,31 +1511,60 @@ static u8 init_graphics_pipeline(graphics_pipeline* pipeline, const graphics_pip
stage_infos = darray_pushback(stage_infos, &stage_info, 1);
}
// vertex input
VkPipelineVertexInputStateCreateInfo vertex_input_info =
VkVertexInputBindingDescription* binding_descriptions = create_darray(2, sizeof(VkVertexInputBindingDescription));
VkVertexInputAttributeDescription* attribute_descriptions = create_darray(8, sizeof(VkVertexInputAttributeDescription));
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
for (u8 i = 0; i < 2; i++)
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 0,
.pVertexBindingDescriptions = NULL,
.vertexAttributeDescriptionCount = 0,
.pVertexAttributeDescriptions = NULL,
.flags = 0,
.pNext = NULL
};
switch (template->vert_type)
{
case VERTEX_TYPE_POS2_COLOR3:
vertex_input_info.vertexBindingDescriptionCount = 1;
vertex_input_info.pVertexBindingDescriptions = &pos2_color3_vertex_input_binding;
vertex_input_info.vertexAttributeDescriptionCount = 2;
vertex_input_info.pVertexAttributeDescriptions = pos2_color3_vertex_input_attributes;
break;
vertex_input_type t = i < 1 ? template->geometry_type : template->instance_type;
VkVertexInputRate input_rate = i < 1 ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
const u8 idx = get_darray_size(attribute_descriptions);
switch (t)
{
case VERTEX_INPUT_TYPE_POS2:
{
u8 idx = get_darray_size(attribute_descriptions);
const VkVertexInputBindingDescription binding =
{
.binding = i, .stride = sizeof(pos2), .inputRate = input_rate
};
const VkVertexInputAttributeDescription attributes[] =
{
(VkVertexInputAttributeDescription){ .location = idx, .binding = i, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(pos2, pos) }
};
binding_descriptions = darray_pushback(binding_descriptions, &binding, 1);
attribute_descriptions = darray_pushback(attribute_descriptions, attributes, 1);
}
break;
case VERTEX_INPUT_TYPE_POS2_POS2_COLOR4:
{
const VkVertexInputBindingDescription binding =
{
.binding = i, .stride = sizeof(pos2_pos2_color4), .inputRate = input_rate
};
const VkVertexInputAttributeDescription attributes[] =
{
(VkVertexInputAttributeDescription){ .location = idx, .binding = i, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(pos2_pos2_color4, posa) },
(VkVertexInputAttributeDescription){ .location = idx + 1, .binding = i, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(pos2_pos2_color4, posb) },
(VkVertexInputAttributeDescription){ .location = idx + 2, .binding = i, .format = VK_FORMAT_R32G32B32A32_SFLOAT, .offset = offsetof(pos2_pos2_color4, color) }
};
binding_descriptions = darray_pushback(binding_descriptions, &binding, 1);
attribute_descriptions = darray_pushback(attribute_descriptions, attributes, 3);
}
break;
default:
trace_log(LOG_ERROR, "Failed to receive a valid vertex type for graphics pipeline '%s'.", template->name);
return 0;
default:
trace_log(LOG_ERROR, "Failed to receive a valid vertex input type (binding = %d) for mesh pipeline '%s'.", i, template->name);
return 0;
}
}
vertex_input_info.vertexBindingDescriptionCount = get_darray_size(binding_descriptions);
vertex_input_info.pVertexBindingDescriptions = binding_descriptions;
vertex_input_info.vertexAttributeDescriptionCount = get_darray_size(attribute_descriptions);
vertex_input_info.pVertexAttributeDescriptions = attribute_descriptions;
// input assembly
const VkPipelineInputAssemblyStateCreateInfo input_assembly_info =
@ -1602,7 +1668,7 @@ static u8 init_graphics_pipeline(graphics_pipeline* pipeline, const graphics_pip
};
if (vkCreatePipelineLayout(context.device.logical, &layout_info, context.allocator, &pipeline->layout) != VK_SUCCESS)
{
trace_log(LOG_ERROR, "Failed to create layout for graphics pipeline '%s'", template->name);
trace_log(LOG_ERROR, "Failed to create layout for mesh pipeline '%s'", template->name);
return 0;
}
@ -1631,7 +1697,7 @@ static u8 init_graphics_pipeline(graphics_pipeline* pipeline, const graphics_pip
};
if (vkCreateGraphicsPipelines(context.device.logical, VK_NULL_HANDLE, 1, &pipeline_info, context.allocator, &pipeline->main) != VK_SUCCESS)
{
trace_log(LOG_ERROR, "Failed to create main handle graphics pipeline '%s'.", template->name);
trace_log(LOG_ERROR, "Failed to create main handle for mesh pipeline '%s'.", template->name);
return 0;
}
if (device_features.fillModeNonSolid == VK_TRUE)
@ -1645,26 +1711,29 @@ static u8 init_graphics_pipeline(graphics_pipeline* pipeline, const graphics_pip
if (vkCreateGraphicsPipelines(context.device.logical, VK_NULL_HANDLE, 1, &wireframe_create_info, context.allocator, &pipeline->wireframe) != VK_SUCCESS)
{
trace_log(LOG_ERROR, "Failed to create wireframe handle for graphics pipeline '%s'.", template->name);
trace_log(LOG_ERROR, "Failed to create wireframe handle for mesh pipeline '%s'.", template->name);
return 0;
}
}
else pipeline->wireframe = VK_NULL_HANDLE;
pipeline->handle = pipeline->main;
trace_log(LOG_DEBUG, "Successfully created graphics pipeline '%s'.", template->name);
trace_log(LOG_DEBUG, "Successfully created mesh pipeline '%s'.", template->name);
// clean up
destroy_darray(binding_descriptions);
destroy_darray(attribute_descriptions);
destroy_darray(stage_infos);
for (u8 i = 0; i < 2; i++)
if (is_stage[i]) shutdown_shader_module(modules[i]);
for (u8 i = 0; i < 2; i++) shutdown_shader_module(modules[i]);
// mesh hashtable
// meshes
pipeline->geometry_type = template->geometry_type;
pipeline->instance_type = template->instance_type;
init_hash_table(&pipeline->meshes, 8, sizeof(mesh), basic_hash);
return 1;
}
static void shutdown_graphics_pipeline(graphics_pipeline* pipeline)
static void shutdown_mesh_pipeline(mesh_pipeline* pipeline)
{
// clean up meshes
for (u32 i = 0; i < pipeline->meshes.slot_count; i++)
@ -1680,13 +1749,12 @@ static void shutdown_graphics_pipeline(graphics_pipeline* pipeline)
vkDestroyPipelineLayout(context.device.logical, pipeline->layout, context.allocator);
}
// TO DO: instanced rendering, for now just draw every mesh belonging to a pipeline once
u8 draw_graphics_pipeline(const char* name)
u8 draw_mesh_pipeline(const char* name)
{
graphics_pipeline* pipeline = hash_table_lookup(&context.graphics_pipelines, name);
mesh_pipeline* pipeline = hash_table_lookup(&context.mesh_pipelines, name);
if (!pipeline)
{
trace_log(LOG_ERROR, "Failed to draw graphics pipeline '%s'; lookup failed." , name);
trace_log(LOG_ERROR, "Failed to draw mesh pipeline '%s'; lookup failed." , name);
return 0;
}
@ -1698,60 +1766,62 @@ u8 draw_graphics_pipeline(const char* name)
{
mesh* msh = entry->data;
// bind vertex and index buffer
const VkDeviceSize vertex_offsets[] = { 0 };
// bind vertex, instance, and index buffers
const VkDeviceSize offsets[] = { 0 };
const VkBuffer vertex_buffers[] = { msh->vertex_buffer.handle };
vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, vertex_offsets);
const VkBuffer instance_buffers[] = { msh->instance_buffer.handle };
vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, offsets);
vkCmdBindVertexBuffers(command_buffer, 1, 1, instance_buffers, offsets);
vkCmdBindIndexBuffer(command_buffer, msh->index_buffer.handle, 0, VK_INDEX_TYPE_UINT32);
// draw
vkCmdDrawIndexed(command_buffer, msh->index_count, 1, 0, 0, 0);
vkCmdDrawIndexed(command_buffer, msh->index_count, msh->instance_count, 0, 0, 0);
}
return 1;
}
u8 insert_graphics_pipeline(const graphics_pipeline_template* template)
u8 insert_mesh_pipeline(const mesh_pipeline_template* template)
{
graphics_pipeline pipeline;
if (!init_graphics_pipeline(&pipeline, template)) return 0;
if (!hash_table_insert(&context.graphics_pipelines, template->name, &pipeline))
mesh_pipeline pipeline;
if (!init_mesh_pipeline(&pipeline, template)) return 0;
if (!hash_table_insert(&context.mesh_pipelines, template->name, &pipeline))
{
shutdown_graphics_pipeline(&pipeline);
shutdown_mesh_pipeline(&pipeline);
return 0;
}
return 1;
}
u8 remove_graphics_pipeline(const char* name)
u8 remove_mesh_pipeline(const char* name)
{
graphics_pipeline* pipeline = hash_table_lookup(&context.graphics_pipelines, name);
mesh_pipeline* pipeline = hash_table_lookup(&context.mesh_pipelines, name);
if (!pipeline)
{
trace_log(LOG_ERROR, "Failed to remove graphics pipeline '%s'; lookup failed.");
trace_log(LOG_ERROR, "Failed to remove mesh pipeline '%s'; lookup failed.");
return 0;
}
shutdown_graphics_pipeline(pipeline);
return hash_table_remove(&context.graphics_pipelines, name, NULL);
shutdown_mesh_pipeline(pipeline);
return hash_table_remove(&context.mesh_pipelines, name, NULL);
}
u8 change_graphics_pipeline_mode(const char* name, graphics_pipeline_mode mode)
u8 change_mesh_pipeline_mode(const char* name, mesh_pipeline_mode mode)
{
graphics_pipeline* pipeline = hash_table_lookup(&context.graphics_pipelines, name);
mesh_pipeline* pipeline = hash_table_lookup(&context.mesh_pipelines, name);
if (!pipeline)
{
trace_log(LOG_ERROR, "Failed to change mode of graphics pipeline '%s'; lookup failed.");
trace_log(LOG_ERROR, "Failed to change mode of mesh pipeline '%s'; lookup failed.");
return 0;
}
switch (mode)
{
case GRAPHICS_PIPELINE_MODE_MAIN:
case MESH_PIPELINE_MODE_MAIN:
pipeline->handle = pipeline->main;
return 1;
case GRAPHICS_PIPELINE_MODE_WIREFRAME:
case MESH_PIPELINE_MODE_WIREFRAME:
if (pipeline->wireframe != VK_NULL_HANDLE)
{
pipeline->handle = pipeline->wireframe;
@ -1759,24 +1829,24 @@ u8 change_graphics_pipeline_mode(const char* name, graphics_pipeline_mode mode)
}
else
{
trace_log(LOG_ERROR, "Failed to change mode of graphics pipeline '%s'; wireframe mode not supported on this device");
trace_log(LOG_ERROR, "Failed to change mode of mesh pipeline '%s'; wireframe mode not supported on this device");
return 0;
}
default: return 0;
}
}
u8 insert_mesh(const char* pipeline_name, const char* mesh_name, const mesh_geometry* geometry)
u8 insert_mesh(const char* pipeline_name, const char* mesh_name, const mesh_geometry* geometry, const mesh_instance_data* instance_data)
{
graphics_pipeline* pipeline = hash_table_lookup(&context.graphics_pipelines, pipeline_name);
mesh_pipeline* pipeline = hash_table_lookup(&context.mesh_pipelines, pipeline_name);
if (!pipeline)
{
trace_log(LOG_ERROR, "Failed to insert mesh '%s'; graphics pipeline '%s' lookup failed.", mesh_name, pipeline_name);
trace_log(LOG_ERROR, "Failed to insert mesh '%s'; mesh pipeline '%s' lookup failed.", mesh_name, pipeline_name);
return 0;
}
mesh msh;
if (!init_mesh(&msh, geometry)) return 0;
if (!init_mesh(&msh, geometry, pipeline->geometry_type, instance_data, pipeline->instance_type)) return 0;
if (!hash_table_insert(&pipeline->meshes, mesh_name, &msh))
{
shutdown_mesh(&msh);
@ -1788,13 +1858,12 @@ u8 insert_mesh(const char* pipeline_name, const char* mesh_name, const mesh_geom
u8 remove_mesh(const char* pipeline_name, const char* mesh_name)
{
graphics_pipeline* pipeline = hash_table_lookup(&context.graphics_pipelines, pipeline_name);
mesh_pipeline* pipeline = hash_table_lookup(&context.mesh_pipelines, pipeline_name);
if (!pipeline)
{
trace_log(LOG_ERROR, "Failed to remove mesh '%s'; graphics pipeline '%s' lookup failed.", mesh_name, pipeline_name);
trace_log(LOG_ERROR, "Failed to remove mesh '%s'; mesh pipeline '%s' lookup failed.", mesh_name, pipeline_name);
return 0;
}
mesh* msh = hash_table_lookup(&pipeline->meshes, mesh_name);
if (!msh)
@ -1823,7 +1892,7 @@ u8 init_vulkan()
if (!init_frame_data()) return 0;
if (!init_single_submit_context()) return 0;
init_hash_table(&context.graphics_pipelines, 8, sizeof(graphics_pipeline), basic_hash);
init_hash_table(&context.mesh_pipelines, 8, sizeof(mesh_pipeline), basic_hash);
context.image_index = 0;
context.current_frame = 0;
@ -1836,13 +1905,13 @@ void shutdown_vulkan()
{
vkDeviceWaitIdle(context.device.logical);
for (u32 i = 0; i < context.graphics_pipelines.slot_count; i++)
for (hash_table_entry* entry = context.graphics_pipelines.entries[i]; entry != NULL; entry = entry->next)
for (u32 i = 0; i < context.mesh_pipelines.slot_count; i++)
for (hash_table_entry* entry = context.mesh_pipelines.entries[i]; entry != NULL; entry = entry->next)
{
graphics_pipeline* pipeline = entry->data;
shutdown_graphics_pipeline(pipeline);
mesh_pipeline* pipeline = entry->data;
shutdown_mesh_pipeline(pipeline);
}
shutdown_hash_table(&context.graphics_pipelines);
shutdown_hash_table(&context.mesh_pipelines);
typedef void (*fn_ptr)();
while (context.deletion_stack.head != NULL)

View File

@ -17,43 +17,55 @@
typedef enum
{
VERTEX_TYPE_NONE,
VERTEX_TYPE_POS2_COLOR3
VERTEX_INPUT_TYPE_POS2,
VERTEX_INPUT_TYPE_POS2_POS2_COLOR4
} vertex_type;
} vertex_input_type;
typedef struct pos2_color3_vertex
typedef struct pos2
{
f32 pos[2];
f32 color[3];
} pos2_color3_vertex;
} pos2;
typedef struct pos2_pos2_color4
{
f32 posa[2];
f32 posb[2];
f32 color[4];
} pos2_pos2_color4;
typedef struct mesh_geometry
{
const void* vertices;
vertex_type vert_type;
u64 vertex_count;
const u32* indices;
u64 index_count;
} mesh_geometry;
typedef struct graphics_pipeline_template
typedef struct mesh_instance_data
{
const void* instances;
u64 instance_count;
} mesh_instance_data;
typedef struct mesh_pipeline_template
{
const char* name;
u8 is_vertex;
u8 is_fragment;
vertex_type vert_type;
vertex_input_type geometry_type;
vertex_input_type instance_type;
} graphics_pipeline_template;
} mesh_pipeline_template;
typedef enum
{
GRAPHICS_PIPELINE_MODE_MAIN,
GRAPHICS_PIPELINE_MODE_WIREFRAME
MESH_PIPELINE_MODE_MAIN,
MESH_PIPELINE_MODE_WIREFRAME
} graphics_pipeline_mode;
} mesh_pipeline_mode;
//
//
@ -66,11 +78,11 @@ void shutdown_window();
u8 is_window_open();
void poll_window_input();
u8 draw_graphics_pipeline(const char* name);
u8 insert_graphics_pipeline(const graphics_pipeline_template* template);
u8 remove_graphics_pipeline(const char* name);
u8 change_graphics_pipeline_mode(const char* name, graphics_pipeline_mode mode);
u8 insert_mesh(const char* pipeline_name, const char* mesh_name, const mesh_geometry* geometry);
u8 draw_mesh_pipeline(const char* name);
u8 insert_mesh_pipeline(const mesh_pipeline_template* template);
u8 remove_mesh_pipeline(const char* name);
u8 change_mesh_pipeline_mode(const char* name, mesh_pipeline_mode mode);
u8 insert_mesh(const char* pipeline_name, const char* mesh_name, const mesh_geometry* geometry, const mesh_instance_data* instance_data);
u8 remove_mesh(const char* pipeline_name, const char* mesh_name);
u8 init_vulkan();

View File

@ -28,108 +28,150 @@ You should have received a copy of the GNU General Public License along with Sha
(unless (cffi:foreign-library-loaded-p 'libsofrenderer) (cffi:use-foreign-library libsofrenderer))
(cffi:defcenum vertex-type
:vertex-type-none
:vertex-type-pos2-color3)
(cffi:defcenum vertex-input-type
:vertex-input-type-pos2
:vertex-input-type-pos2-pos2-color4)
(cffi:defcstruct (%pos2-color3-vertex :class c-pos2-color3-vertex)
(pos :float :count 2)
(color :float :count 3))
(cffi:defcstruct (%pos2 :class c-pos2)
(pos :float :count 2))
(defstruct pos2-color3-vertex
(pos (vector 0.0 0.0))
(color (vector 0.0 0.0 0.0)))
(defstruct pos2
(pos (vector 0.0 0.0)))
(defmethod cffi:translate-from-foreign (ptr (type c-pos2-color3-vertex))
(cffi:with-foreign-slots ((pos color) ptr (:struct %pos2-color3-vertex))
(make-pos2-color3-vertex
(defmethod cffi:translate-from-foreign (ptr (type c-pos2))
(cffi:with-foreign-slots ((pos) ptr (:struct %pos2))
(make-pos2
:pos (vector (cffi:mem-aref pos :float 0)
(cffi:mem-aref pos :float 1))
(cffi:mem-aref pos :float 1)))))
(defmethod cffi:expand-from-foreign (ptr (type c-pos2))
`(cffi:with-foreign-slots ((pos) ,ptr (:struct %pos2))
(make-pos2
:pos (vector (cffi:mem-aref pos :float 0)
(cffi:mem-aref pos :float 1)))))
(defmethod cffi:translate-into-foreign-memory (value (type c-pos2) ptr)
(cffi:with-foreign-slots ((pos) ptr (:struct %pos2))
(setf
(cffi:mem-aref pos :float 0) (aref (pos2-pos value) 0)
(cffi:mem-aref pos :float 1) (aref (pos2-pos value) 1))))
(defmethod cffi:expand-into-foreign-memory (value (type c-pos2) ptr)
`(cffi:with-foreign-slots ((pos color) ,ptr (:struct %pos2))
(setf
(cffi:mem-aref pos :float 0) (aref (pos2-pos ,value) 0)
(cffi:mem-aref pos :float 1) (aref (pos2-pos ,value) 1))))
(cffi:defcstruct (%pos2-pos2-color4 :class c-pos2-pos2-color4)
(posa :float :count 2)
(posb :float :count 2)
(color :float :count 4))
(defstruct pos2-pos2-color4
(posa (vector 0.0 0.0))
(posb (vector 0.0 0.0))
(color (vector 0.0 0.0 0.0 0.0)))
(defmethod cffi:translate-from-foreign (ptr (type c-pos2-pos2-color4))
(cffi:with-foreign-slots ((posa posb color) ptr (:struct %pos2-pos2-color4))
(make-pos2-pos2-color4
:posa (vector (cffi:mem-aref posa :float 0)
(cffi:mem-aref posa :float 1))
:posb (vector (cffi:mem-aref posb :float 0)
(cffi:mem-aref posb :float 1))
:color (vector (cffi:mem-aref color :float 0)
(cffi:mem-aref color :float 1)
(cffi:mem-aref color :float 2)))))
(cffi:mem-aref color :float 2)
(cffi:mem-aref color :float 4)))))
(defmethod cffi:expand-from-foreign (ptr (type c-pos2-color3-vertex))
`(cffi:with-foreign-slots ((pos color) ,ptr (:struct %pos2-color3-vertex))
(make-pos2-color3-vertex
:pos (vector (cffi:mem-aref pos :float 0)
(cffi:mem-aref pos :float 1))
(defmethod cffi:expand-from-foreign (ptr (type c-pos2-pos2-color4))
`(cffi:with-foreign-slots ((posa posb color) ,ptr (:struct %pos2-pos2-color4))
(make-pos2-pos2-color4
:posa (vector (cffi:mem-aref posa :float 0)
(cffi:mem-aref posa :float 1))
:posb (vector (cffi:mem-aref posb :float 0)
(cffi:mem-aref posb :float 1))
:color (vector (cffi:mem-aref color :float 0)
(cffi:mem-aref color :float 1)
(cffi:mem-aref color :float 2)))))
(defmethod cffi:translate-into-foreign-memory (value (type c-pos2-color3-vertex) ptr)
(cffi:with-foreign-slots ((pos color) ptr (:struct %pos2-color3-vertex))
(cffi:mem-aref color :float 2)
(cffi:mem-aref color :float 4)))))
(defmethod cffi:translate-into-foreign-memory (value (type c-pos2-pos2-color4) ptr)
(cffi:with-foreign-slots ((posa posb color) ptr (:struct %pos2-pos2-color4))
(setf
(cffi:mem-aref pos :float 0) (aref (pos2-color3-vertex-pos value) 0)
(cffi:mem-aref pos :float 1) (aref (pos2-color3-vertex-pos value) 1)
(cffi:mem-aref color :float 0) (aref (pos2-color3-vertex-color value) 0)
(cffi:mem-aref color :float 1) (aref (pos2-color3-vertex-color value) 1)
(cffi:mem-aref color :float 2) (aref (pos2-color3-vertex-color value) 2))))
(cffi:mem-aref posa :float 0) (aref (pos2-pos2-color4-posa value) 0)
(cffi:mem-aref posa :float 1) (aref (pos2-pos2-color4-posa value) 1)
(cffi:mem-aref posb :float 0) (aref (pos2-pos2-color4-posb value) 0)
(cffi:mem-aref posb :float 1) (aref (pos2-pos2-color4-posb value) 1)
(cffi:mem-aref color :float 0) (aref (pos2-pos2-color4-color value) 0)
(cffi:mem-aref color :float 1) (aref (pos2-pos2-color4-color value) 1)
(cffi:mem-aref color :float 2) (aref (pos2-pos2-color4-color value) 2)
(cffi:mem-aref color :float 3) (aref (pos2-pos2-color4-color value) 3))))
(defmethod cffi:expand-into-foreign-memory (value (type c-pos2-color3-vertex) ptr)
`(cffi:with-foreign-slots ((pos color) ,ptr (:struct %pos2-color3-vertex))
(setf
(cffi:mem-aref pos :float 0) (aref (pos2-color3-vertex-pos ,value) 0)
(cffi:mem-aref pos :float 1) (aref (pos2-color3-vertex-pos ,value) 1)
(cffi:mem-aref color :float 0) (aref (pos2-color3-vertex-color ,value) 0)
(cffi:mem-aref color :float 1) (aref (pos2-color3-vertex-color ,value) 1)
(cffi:mem-aref color :float 2) (aref (pos2-color3-vertex-color ,value) 2))))
(defmethod cffi:expand-into-foreign-memory (value (type c-pos2-pos2-color4) ptr)
`(cffi:with-foreign-slots ((posa posb color) ,ptr (:struct %pos2-pos2-color4))
(setf
(cffi:mem-aref posa :float 0) (aref (pos2-pos2-color4-posa ,value) 0)
(cffi:mem-aref posa :float 1) (aref (pos2-pos2-color4-posa ,value) 1)
(cffi:mem-aref posb :float 0) (aref (pos2-pos2-color4-posb ,value) 0)
(cffi:mem-aref posb :float 1) (aref (pos2-pos2-color4-posb ,value) 1)
(cffi:mem-aref color :float 0) (aref (pos2-pos2-color4-color ,value) 0)
(cffi:mem-aref color :float 1) (aref (pos2-pos2-color4-color ,value) 1)
(cffi:mem-aref color :float 2) (aref (pos2-pos2-color4-color ,value) 2)
(cffi:mem-aref color :float 3) (aref (pos2-pos2-color4-color ,value) 3))))
(cffi:defcstruct %mesh-geometry
(vertices :pointer)
(vert-type vertex-type)
(vertex-count :uint64)
(indices (:pointer :uint32))
(index-count :uint64))
(cffi:defcstruct (%graphics-pipeline-template :class c-graphics-pipeline-template)
(cffi:defcstruct %mesh-instance-data
(instances :pointer)
(instance-count :uint64))
(cffi:defcstruct (%mesh-pipeline-template :class c-mesh-pipeline-template)
(name :string)
(vertex? :bool)
(fragment? :bool)
(vert-type vertex-type))
(geometry-type vertex-input-type)
(instance-type vertex-input-type))
(defstruct graphics-pipeline-template
(defstruct mesh-pipeline-template
(name "" :type string)
(vertex? t :type boolean)
(fragment? t :type boolean)
(vert-type 0 :type integer))
(geometry-type 0 :type integer)
(instance-type 0 :type integer))
(defmethod cffi:translate-from-foreign (ptr (type c-graphics-pipeline-template))
(cffi:with-foreign-slots ((name vertex? fragment? vert-type) ptr (:struct %graphics-pipeline-template))
(make-graphics-pipeline-template
(defmethod cffi:translate-from-foreign (ptr (type c-mesh-pipeline-template))
(cffi:with-foreign-slots ((name geometry-type instance-type) ptr (:struct %mesh-pipeline-template))
(make-mesh-pipeline-template
:name name
:vertex? vertex?
:fragment? fragment?
:vert-type vert-type)))
:geometry-type geometry-type
:instance-type instance-type)))
(defmethod cffi:expand-from-foreign (ptr (type c-graphics-pipeline-template))
`(cffi:with-foreign-slots ((name vertex? fragment? vert-type) ,ptr (:struct %graphics-pipeline-template))
(make-graphics-pipeline-template
(defmethod cffi:expand-from-foreign (ptr (type c-mesh-pipeline-template))
`(cffi:with-foreign-slots ((name geometry-type instance-type) ,ptr (:struct %mesh-pipeline-template))
(make-mesh-pipeline-template
:name name
:vertex? vertex?
:fragment? fragment?
:vert-type vert-type)))
:geometry-type geometry-type
:instance-type instance-type)))
(defmethod cffi:translate-into-foreign-memory (value (type c-graphics-pipeline-template) ptr)
(cffi:with-foreign-slots ((name vertex? fragment? vert-type) ptr (:struct %graphics-pipeline-template))
(defmethod cffi:translate-into-foreign-memory (value (type c-mesh-pipeline-template) ptr)
(cffi:with-foreign-slots ((name geometry-type instance-type) ptr (:struct %mesh-pipeline-template))
(setf
name (graphics-pipeline-template-name value)
vertex? (graphics-pipeline-template-vertex? value)
fragment? (graphics-pipeline-template-fragment? value)
vert-type (graphics-pipeline-template-vert-type value))))
name (mesh-pipeline-template-name value)
geometry-type (mesh-pipeline-template-geometry-type value)
instance-type (mesh-pipeline-template-instance-type value))))
(defmethod cffi:expand-into-foreign-memory (value (type c-graphics-pipeline-template) ptr)
`(cffi:with-foreign-slots ((name vertex? fragment? vert-type) ,ptr (:struct %graphics-pipeline-template))
(defmethod cffi:expand-into-foreign-memory (value (type c-mesh-pipeline-template) ptr)
`(cffi:with-foreign-slots ((name geometry-type instance-type) ,ptr (:struct %mesh-pipeline-template))
(setf
name (graphics-pipeline-template-name ,value)
vertex? (graphics-pipeline-template-vertex? ,value)
fragment? (graphics-pipeline-template-fragment? ,value)
vert-type (graphics-pipeline-template-vert-type ,value))))
name (mesh-pipeline-template-name ,value)
geometry-type (mesh-pipeline-template-geometry-type ,value)
instance-type (mesh-pipeline-template-instance-type ,value))))
(cffi:defcenum graphics-pipeline-mode
:graphics-pipeline-mode-main
:graphics-pipeline-mode-wireframe)
(cffi:defcenum mesh-pipeline-mode
:mesh-pipeline-mode-main
:mesh-pipeline-mode-wireframe)
(cffi:defcfun ("init_window" init-window) :boolean
(name :string)
@ -142,29 +184,30 @@ You should have received a copy of the GNU General Public License along with Sha
(cffi:defcfun ("poll_window_input" poll-window-input) :void)
(cffi:defcfun ("draw_graphics_pipeline" draw-graphics-pipeline) :boolean
(cffi:defcfun ("draw_mesh_pipeline" draw-mesh-pipeline) :boolean
(name :string))
(cffi:defcfun ("insert_graphics_pipeline" %insert-graphics-pipeline) :boolean
(template (:pointer (:struct %graphics-pipeline-template))))
(cffi:defcfun ("insert_mesh_pipeline" %insert-mesh-pipeline) :boolean
(template (:pointer (:struct %mesh-pipeline-template))))
(defun insert-graphics-pipeline (name vert-type &key (vertex? t) (fragment? t))
(let ((template (make-graphics-pipeline-template :name name :vertex? vertex? :fragment? fragment? :vert-type vert-type)))
(cffi:with-foreign-object (ptr '(:struct %graphics-pipeline-template))
(setf (cffi:mem-aref ptr '(:struct %graphics-pipeline-template)) template)
(%insert-graphics-pipeline ptr))))
(defun insert-mesh-pipeline (name geometry-type instance-type)
(let ((template (make-mesh-pipeline-template :name name :geometry-type geometry-type :instance-type instance-type)))
(cffi:with-foreign-object (ptr '(:struct %mesh-pipeline-template))
(setf (cffi:mem-aref ptr '(:struct %mesh-pipeline-template)) template)
(%insert-mesh-pipeline ptr))))
(cffi:defcfun ("remove_graphics_pipeline" remove-graphics-pipeline) :boolean
(cffi:defcfun ("remove_mesh_pipeline" remove-mesh-pipeline) :boolean
(name :string))
(cffi:defcfun ("change_graphics_pipeline_mode" change-graphics-pipeline-mode) :boolean
(cffi:defcfun ("change_mesh_pipeline_mode" change-mesh-pipeline-mode) :boolean
(name :string)
(mode graphics-pipeline-mode))
(mode mesh-pipeline-mode))
(cffi:defcfun ("insert_mesh" insert-mesh) :boolean
(pipeline-name :string)
(mesh-name :string)
(geometry (:pointer (:struct %mesh-geometry))))
(geometry (:pointer (:struct %mesh-geometry)))
(instance-data (:pointer (:struct %mesh-instance-data))))
(cffi:defcfun ("remove_mesh" remove-mesh) :boolean
(pipeline-name :string)
@ -191,18 +234,17 @@ You should have received a copy of the GNU General Public License along with Sha
(when (init-window name width height)
(when (init-vulkan) t)))
(insert-graphics-pipeline "pos2color3" (cffi:foreign-enum-value 'vertex-type :vertex-type-pos2-color3))
(insert-mesh-pipeline
"ui-pane"
(cffi:foreign-enum-value 'vertex-input-type :vertex-input-type-pos2)
(cffi:foreign-enum-value 'vertex-input-type :vertex-input-type-pos2-pos2-color4))
(cffi:with-foreign-object (quad-vertices '(:struct %pos2-color3-vertex) 4)
(cffi:with-foreign-object (quad-vertices '(:struct %pos2) 4)
(setf
(cffi:mem-aref quad-vertices '(:struct %pos2-color3-vertex) 0)
(make-pos2-color3-vertex :pos (vector -0.5 -0.5) :color (vector 1.0 0.0 0.0))
(cffi:mem-aref quad-vertices '(:struct %pos2-color3-vertex) 1)
(make-pos2-color3-vertex :pos (vector 0.5 0.5) :color (vector 0.0 0.0 1.0))
(cffi:mem-aref quad-vertices '(:struct %pos2-color3-vertex) 2)
(make-pos2-color3-vertex :pos (vector 0.5 -0.5) :color (vector 0.0 1.0 0.0))
(cffi:mem-aref quad-vertices '(:struct %pos2-color3-vertex) 3)
(make-pos2-color3-vertex :pos (vector -0.5 0.5) :color (vector 1.0 1.0 1.0)))
(cffi:mem-aref quad-vertices '(:struct %pos2) 0) (make-pos2 :pos (vector 0.0 0.0))
(cffi:mem-aref quad-vertices '(:struct %pos2) 1) (make-pos2 :pos (vector 1.0 1.0))
(cffi:mem-aref quad-vertices '(:struct %pos2) 2) (make-pos2 :pos (vector 1.0 0.0))
(cffi:mem-aref quad-vertices '(:struct %pos2) 3) (make-pos2 :pos (vector 0.0 1.0)))
(cffi:with-foreign-object (quad-indices :uint32 6)
(setf
(cffi:mem-aref quad-indices :uint32 0) 0
@ -212,14 +254,24 @@ You should have received a copy of the GNU General Public License along with Sha
(cffi:mem-aref quad-indices :uint32 4) 3
(cffi:mem-aref quad-indices :uint32 5) 1)
(cffi:with-foreign-object (geometry '(:struct %mesh-geometry))
(cffi:with-foreign-slots ((vertices vert-type vertex-count indices index-count) geometry (:struct %mesh-geometry))
(cffi:with-foreign-slots ((vertices vertex-count indices index-count) geometry (:struct %mesh-geometry))
(setf
vertices quad-vertices
vert-type (cffi:foreign-enum-value 'vertex-type :vertex-type-pos2-color3)
vertex-count 4
indices quad-indices
index-count 6)
(insert-mesh "pos2color3" "quad" geometry))))))
(cffi:with-foreign-object (quad-instances '(:struct %pos2-pos2-color4) 2)
(setf
(cffi:mem-aref quad-instances '(:struct %pos2-pos2-color4) 0)
(make-pos2-pos2-color4 :posa (vector -0.5 0.5) :posb (vector 0.25 0.25) :color (vector 0.7 0.0 0.0 1.0))
(cffi:mem-aref quad-instances '(:struct %pos2-pos2-color4) 1)
(make-pos2-pos2-color4 :posa (vector 0.5 0.5) :posb (vector 0.4 0.4) :color (vector 0.0 0.5 0.5 0.6)))
(cffi:with-foreign-object (instance-data '(:struct %mesh-instance-data))
(cffi:with-foreign-slots ((instances instance-count) instance-data (:struct %mesh-instance-data))
(setf
instances quad-instances
instance-count 2)
(insert-mesh "ui-pane" "quad" geometry instance-data)))))))))
(defun shutdown-game ()
(shutdown-vulkan)
@ -230,7 +282,7 @@ You should have received a copy of the GNU General Public License along with Sha
(defun render-game (l)
(declare (ignore l))
(when (begin-render-frame)
(when (draw-graphics-pipeline "pos2color3")
(when (draw-mesh-pipeline "ui-pane")
(when (end-render-frame) t))))
;; Only compile this ONCE!