revolver

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 6fad3dedd51e6f70b0d16596393c61329d6558de
parent 2aed9170f846b7c8036b4ea0c50634024803ed44
Author: Samdal <samdal@protonmail.com>
Date:   Sat, 22 Mar 2025 23:59:01 +0100

instancing

Diffstat:
Mbuild.sh | 1+
Aexamples/instancing.c | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mexamples/simple_triangle.c | 4++--
Mexamples/test.c | 8++++----
Msrc/render/impl/opengl.c | 95+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/render/render.h | 7++++++-
Msrc/render/render_helpers.c | 4++--
7 files changed, 203 insertions(+), 52 deletions(-)

diff --git a/build.sh b/build.sh @@ -13,6 +13,7 @@ do "simple_triangle") sources=(./examples/simple_triangle.c) ;; "simple_texture") sources=(./examples/simple_texture.c) ;; "hello_window") sources=(./examples/hello_window.c) ;; + "instancing") sources=(./examples/instancing.c) ;; esac done diff --git a/examples/instancing.c b/examples/instancing.c @@ -0,0 +1,136 @@ +////////////////////////////////////////////////////////////////// +// instancing.c + +#include "revolver_inc.h" +#include "revolver_inc.c" + +////////////////////////////////////////////////////////////////// +// data + +f32 v_data[] = { + 0.00, 0.05, 1, 0, 0, + -0.05, -0.05, 0, 1, 0, + 0.05, -0.05, 0, 0, 1, +}; + +rv_str8 v_src = S("#version 330 core\n" rv_strify( + layout(location = 0) in vec2 a_pos; + layout(location = 1) in vec3 a_color; + layout(location = 2) in vec2 a_offset; + precision mediump float; + out vec3 f_color; + void main() + { + gl_Position = vec4(a_pos + a_offset, 0.0, 1.0); + f_color = a_color; + } +)); + +rv_str8 f_src = S("#version 330 core\n" rv_strify( + precision mediump float; + in vec3 f_color; + out vec4 frag_color; + void main() + { + frag_color = vec4(f_color, 1.0); + } +)); + +rv_vec2 g_translations[100] = {0}; + +////////////////////////////////////////////////////////////////// + +int main(void) { + rv_window_desc_t desc = {.name = S("App"), .attach_render = true}; + rv_window_handle_t* window = rv_create_window(desc); + + rv_arena* arena = rv_arena_alloc(); + rv_render_pass_list_t rpass_list = {0}; + + // Translation data + int32_t index = 0; + float offset = 0.1f; + for (int32_t y = -10; y < 10; y += 2) { + for (int32_t x = -10; x < 10; x += 2) { + g_translations[index].x = (float)x / 10.0f + offset; + g_translations[index].y = (float)y / 10.0f + offset; + index++; + } + } + + // make render objects + rv_vbo_t* vbo = rv_push_compound(arena, rv_vbo_t, {.data = v_data, .size = sizeof(v_data)}); + rv_vbo_t* vboi = rv_push_compound(arena, rv_vbo_t, {.data = g_translations, .size = sizeof(g_translations)}); + rv_shader_t* vertex = rv_push_compound(arena, rv_shader_t, {.source = v_src, .type = RV_SHADER_TYPE_VERTEX}); + rv_shader_t* fragment = rv_push_compound(arena, rv_shader_t, {.source = f_src, .type = RV_SHADER_TYPE_FRAGMENT}); + rv_pipeline_t* pip = rv_push(arena, rv_pipeline_t); + + // construct pipeline + rv_pipeline_push_shader(arena, pip, vertex); + rv_pipeline_push_shader(arena, pip, fragment); + rv_pipeline_push_vattr(arena, pip, RV_VATTR_TYPE_FLOAT2, 0, 0); + rv_pipeline_push_vattr(arena, pip, RV_VATTR_TYPE_FLOAT3, 0, 0); + rv_pipeline_push_vattr(arena, pip, RV_VATTR_TYPE_FLOAT2, 1, 1); + + // instructions to create render objects + rv_command_list_t create_instructions = {0}; + rv_cmd_push_obj(arena, &create_instructions, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_CREATE, vbo); + rv_cmd_push_obj(arena, &create_instructions, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_CREATE, vboi); + rv_cmd_push_obj(arena, &create_instructions, RV_COMMAND_OBJ_SHADER, RV_RENDER_OBJ_OP_CREATE, vertex); + rv_cmd_push_obj(arena, &create_instructions, RV_COMMAND_OBJ_SHADER, RV_RENDER_OBJ_OP_CREATE, fragment); + rv_cmd_push_obj(arena, &create_instructions, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_CREATE, pip); + + // instructions to render + rv_command_list_t render_instructions = {0}; + rv_cmd_push_obj(arena, &render_instructions, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_BIND, pip); + rv_cmd_push_obj(arena, &render_instructions, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_BIND, vbo); + rv_cmd_push_obj(arena, &render_instructions, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_BIND, vboi)->obj.vbo_bind.bind_index = 1; + rv_cmd_push_compound(arena, &render_instructions, RV_COMMAND_DRAW, {.draw = {.count = 3, .instance_count = 100}}); + rv_cmd_push_obj(arena, &render_instructions, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_BIND, NULL); + + // copy over create commands + rv_render_pass_t* create_rpass = rv_render_push_render_pass(arena, &rpass_list); + rv_render_copy_commands(arena, &create_rpass->commands, &create_instructions); + + while(1) { + rv_temp_arena scratch = rv_scratch_begin(0, 0); + + // pick renderpass + rv_render_pass_t* rpass = rv_render_push_render_pass(scratch.arena, &rpass_list); + + // process events + for (rv_event_t* e = rv_get_events(scratch.arena, 0); e; e = e->next) { + if (e->type == RV_EVENT_WINDOW_CLOSE) { + if (e->window_close == window) { + goto exit_program; + } + } + if (e->type == RV_EVENT_WINDOW_RESIZE) { + // set viewport + rv_command_t* viewport = rv_cmd_push_type(scratch.arena, &rpass->commands, RV_COMMAND_SET_VIEWPORT); + viewport->viewport = rv_v4(.xy = {0, 0}, .zw = rv_window_size(window)); + } + } + + + { // clear + rv_command_t* clear = rv_cmd_push_type(scratch.arena, &rpass->commands, RV_COMMAND_CLEAR); + rv_render_clear_desc_t* clear_desc = rv_render_push_clear_desc(scratch.arena, clear, RV_RENDER_CLEAR_FLAG_COLOR); + clear_desc->color_v = rv_v4(0.1, 0.1, 0.1, 1.0); + } + + { // draw custom shader + // copy render commands + rv_render_copy_commands(scratch.arena, &rpass->commands, &render_instructions); + } + + if (window) { // render screen + rv_window_render_commit(window, &rpass_list); + } + rv_scratch_end(scratch); + rpass_list = (rv_render_pass_list_t){0}; + } +exit_program: + + return 0; +} diff --git a/examples/simple_triangle.c b/examples/simple_triangle.c @@ -53,8 +53,8 @@ int main(void) { // construct pipeline rv_pipeline_push_shader(arena, pip, vertex); rv_pipeline_push_shader(arena, pip, fragment); - rv_pipeline_push_vattr(arena, pip, RV_VATTR_TYPE_FLOAT2); - rv_pipeline_push_vattr(arena, pip, RV_VATTR_TYPE_FLOAT3); + rv_pipeline_push_vattr(arena, pip, RV_VATTR_TYPE_FLOAT2, 0, 0); + rv_pipeline_push_vattr(arena, pip, RV_VATTR_TYPE_FLOAT3, 0, 0); // instructions to create render objects rv_command_list_t create_instructions = {0}; diff --git a/examples/test.c b/examples/test.c @@ -105,8 +105,8 @@ int main(void) { // construct pipeline rv_pipeline_push_shader(arena, triangle_pip, triangle_vertex); rv_pipeline_push_shader(arena, triangle_pip, triangle_fragment); - rv_pipeline_push_vattr(arena, triangle_pip, RV_VATTR_TYPE_FLOAT2); - rv_pipeline_push_vattr(arena, triangle_pip, RV_VATTR_TYPE_FLOAT3); + rv_pipeline_push_vattr(arena, triangle_pip, RV_VATTR_TYPE_FLOAT2, 0, 0); + rv_pipeline_push_vattr(arena, triangle_pip, RV_VATTR_TYPE_FLOAT3, 0, 0); // instructions to create render objects rv_cmd_push_obj(arena, &create_instructions, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_CREATE, triangle_vbo); @@ -133,8 +133,8 @@ int main(void) { // construct pipeline rv_pipeline_push_shader(arena, post_pip, post_vertex); rv_pipeline_push_shader(arena, post_pip, post_fragment); - rv_pipeline_push_vattr(arena, post_pip, RV_VATTR_TYPE_FLOAT2); - rv_pipeline_push_vattr(arena, post_pip, RV_VATTR_TYPE_FLOAT2); + rv_pipeline_push_vattr(arena, post_pip, RV_VATTR_TYPE_FLOAT2, 0, 0); + rv_pipeline_push_vattr(arena, post_pip, RV_VATTR_TYPE_FLOAT2, 0, 0); rv_uniform_t* u_tex = rv_pipeline_push_uniform(arena, post_pip, S("u_tex"), RV_UNIFORM_TEX); rv_uniform_t* u_time = rv_pipeline_push_uniform(arena, post_pip, S("u_time"), RV_UNIFORM_F32); diff --git a/src/render/impl/opengl.c b/src/render/impl/opengl.c @@ -75,7 +75,6 @@ RV_INTERNAL GLenum rv_tex_wrap_to_gl(rv_texture_wrap_t type) { struct { rv_ibo_t* bound_ibo; - rv_vbo_t* bound_vbo; rv_pipeline_t* bound_pip; rv_framebuffer_t* bound_fbuf; } ogl_state; @@ -116,11 +115,6 @@ RV_GLOBAL void rv_window_render_commit(rv_window_handle_t* window, rv_render_pas glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } - if (ogl_state.bound_vbo) { - ogl_state.bound_vbo = NULL; - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - if (ogl_state.bound_pip) { ogl_state.bound_pip = NULL; glUseProgram(0); @@ -171,29 +165,61 @@ RV_GLOBAL void rv_window_render_commit(rv_window_handle_t* window, rv_render_pas glGenBuffers(1, &vbo->handle.u); glBindBuffer(GL_ARRAY_BUFFER, vbo->handle.u); glBufferData(GL_ARRAY_BUFFER, vbo->size, vbo->data, rv_usage_to_gl(vbo->usage)); - - glBindBuffer(GL_ARRAY_BUFFER, ogl_safe_set(ogl_state.bound_vbo, handle)); + glBindBuffer(GL_ARRAY_BUFFER, 0); } break; case RV_RENDER_OBJ_OP_UPDATE: { glBindBuffer(GL_ARRAY_BUFFER, vbo->handle.u); glBufferData(GL_ARRAY_BUFFER, c->obj.vbo_update.size, c->obj.vbo_update.data, rv_usage_to_gl(vbo->usage)); - - glBindBuffer(GL_ARRAY_BUFFER, ogl_safe_set(ogl_state.bound_vbo, handle)); + glBindBuffer(GL_ARRAY_BUFFER, 0); } break; case RV_RENDER_OBJ_OP_BIND: { - ogl_state.bound_vbo = vbo; - glBindBuffer(GL_ARRAY_BUFFER, ogl_safe_set(ogl_state.bound_vbo, handle)); + if (ogl_state.bound_pip) { + s32 bind_index = c->obj.vbo_bind.bind_index; + glBindBuffer(GL_ARRAY_BUFFER, vbo->handle.u); + + // set vattrs + s64 stride = 0; + for (rv_vattr_t* v = ogl_state.bound_pip->vattr_first; v; v = v->next) { + if (v->bind_index == bind_index) { + stride += rv_vattr_type_size(v->type); + } + } + s64 offset = 0; + s64 i = 0; + for (rv_vattr_t* v = ogl_state.bound_pip->vattr_first; v; v = v->next, i++) { + if (v->bind_index == bind_index) { + glEnableVertexAttribArray(i); + switch (v->type) { + case RV_VATTR_TYPE_FLOAT4: glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, stride, (void*)offset); break; + case RV_VATTR_TYPE_FLOAT3: glVertexAttribPointer(i, 3, GL_FLOAT, GL_FALSE, stride, (void*)offset); break; + case RV_VATTR_TYPE_FLOAT2: glVertexAttribPointer(i, 2, GL_FLOAT, GL_FALSE, stride, (void*)offset); break; + case RV_VATTR_TYPE_FLOAT: glVertexAttribPointer(i, 1, GL_FLOAT, GL_FALSE, stride, (void*)offset); break; + case RV_VATTR_TYPE_UINT4: glVertexAttribIPointer(i, 4, GL_UNSIGNED_INT, stride, (void*)offset); break; + case RV_VATTR_TYPE_UINT3: glVertexAttribIPointer(i, 3, GL_UNSIGNED_INT, stride, (void*)offset); break; + case RV_VATTR_TYPE_UINT2: glVertexAttribIPointer(i, 2, GL_UNSIGNED_INT, stride, (void*)offset); break; + case RV_VATTR_TYPE_UINT: glVertexAttribIPointer(i, 1, GL_UNSIGNED_INT, stride, (void*)offset); break; + case RV_VATTR_TYPE_BYTE: glVertexAttribPointer(i, 1, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)offset); break; + case RV_VATTR_TYPE_BYTE2: glVertexAttribPointer(i, 2, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)offset); break; + case RV_VATTR_TYPE_BYTE3: glVertexAttribPointer(i, 3, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)offset); break; + case RV_VATTR_TYPE_BYTE4: glVertexAttribPointer(i, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)offset); break; + default: rv_unreachable(); break; + } + offset += rv_vattr_type_size(v->type); + if (v->divisor) { + glVertexAttribDivisor(i, v->divisor); + } + } + } + } else { + rv_unreachable(); + } } break; case RV_RENDER_OBJ_OP_DELETE: { glDeleteBuffers(1, &vbo->handle.u); vbo->handle.u = 0; - if (ogl_state.bound_vbo == vbo) { - ogl_state.bound_vbo = NULL; - glBindBuffer(GL_ARRAY_BUFFER, 0); - } } break; } } break; @@ -531,31 +557,6 @@ RV_GLOBAL void rv_window_render_commit(rv_window_handle_t* window, rv_render_pas case RV_COMMAND_DRAW: { if (ogl_state.bound_pip) { - s64 stride = 0; - for (rv_vattr_t* v = ogl_state.bound_pip->vattr_first; v; v = v->next) { - stride += rv_vattr_type_size(v->type); - } - s64 offset = 0; - s64 i = 0; - for (rv_vattr_t* v = ogl_state.bound_pip->vattr_first; v; v = v->next, i++) { - glEnableVertexAttribArray(i); - switch (v->type) { - case RV_VATTR_TYPE_FLOAT4: glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, stride, (void*)offset); break; - case RV_VATTR_TYPE_FLOAT3: glVertexAttribPointer(i, 3, GL_FLOAT, GL_FALSE, stride, (void*)offset); break; - case RV_VATTR_TYPE_FLOAT2: glVertexAttribPointer(i, 2, GL_FLOAT, GL_FALSE, stride, (void*)offset); break; - case RV_VATTR_TYPE_FLOAT: glVertexAttribPointer(i, 1, GL_FLOAT, GL_FALSE, stride, (void*)offset); break; - case RV_VATTR_TYPE_UINT4: glVertexAttribIPointer(i, 4, GL_UNSIGNED_INT, stride, (void*)offset); break; - case RV_VATTR_TYPE_UINT3: glVertexAttribIPointer(i, 3, GL_UNSIGNED_INT, stride, (void*)offset); break; - case RV_VATTR_TYPE_UINT2: glVertexAttribIPointer(i, 2, GL_UNSIGNED_INT, stride, (void*)offset); break; - case RV_VATTR_TYPE_UINT: glVertexAttribIPointer(i, 1, GL_UNSIGNED_INT, stride, (void*)offset); break; - case RV_VATTR_TYPE_BYTE: glVertexAttribPointer(i, 1, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)offset); break; - case RV_VATTR_TYPE_BYTE2: glVertexAttribPointer(i, 2, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)offset); break; - case RV_VATTR_TYPE_BYTE3: glVertexAttribPointer(i, 3, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)offset); break; - case RV_VATTR_TYPE_BYTE4: glVertexAttribPointer(i, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)offset); break; - default: rv_unreachable(); break; - } - offset += rv_vattr_type_size(v->type); - } GLenum prim = rv_primitive_to_gl(c->draw.primitive); // TODO(Samdal): instancing... if (ogl_state.bound_ibo) { @@ -567,9 +568,17 @@ RV_GLOBAL void rv_window_render_commit(rv_window_handle_t* window, rv_render_pas case 2: itype = GL_UNSIGNED_SHORT; break; case 1: itype = GL_UNSIGNED_BYTE; break; } - glDrawElements(prim, c->draw.count, itype, (void*)(s64)c->draw.first); + if (c->draw.instance_count) { + glDrawElementsInstanced(prim, c->draw.count, itype, (void*)(s64)c->draw.first, c->draw.instance_count); + } else { + glDrawElements(prim, c->draw.count, itype, (void*)(s64)c->draw.first); + } } else { - glDrawArrays(prim, c->draw.first, c->draw.count); + if (c->draw.instance_count) { + glDrawArraysInstanced(prim, c->draw.first, c->draw.count, c->draw.instance_count); + } else { + glDrawArrays(prim, c->draw.first, c->draw.count); + } } } else { rv_unreachable(); diff --git a/src/render/render.h b/src/render/render.h @@ -155,6 +155,8 @@ typedef struct rv_vattr_t rv_vattr_t; struct rv_vattr_t { rv_vattr_t* next; rv_vattr_type_t type; + s32 bind_index; + s32 divisor; }; typedef struct { @@ -248,6 +250,8 @@ struct rv_command_t { struct {void* data; s64 size;} ibo_update; struct {void* data; s64 size;} vbo_update; + struct { s32 bind_index; } vbo_bind; + struct { void* data; rv_rect sub_part; @@ -275,6 +279,7 @@ struct rv_command_t { rv_primitive_type_t primitive; s32 first; s64 count; + s64 instance_count; } draw; struct { @@ -344,7 +349,7 @@ RV_GLOBAL rv_framebuffer_t* rv_framebuffer_color_simple(rv_arena* arena, rv_vec2 // pipeline modificatoin helpers RV_GLOBAL rv_shader_node_t* rv_pipeline_push_shader(rv_arena* arena, rv_pipeline_t* pipeline, rv_shader_t* shader); RV_GLOBAL rv_uniform_t* rv_pipeline_push_uniform(rv_arena* arena, rv_pipeline_t* pipeline, rv_str8 name, rv_uniform_type_t type); -RV_GLOBAL rv_vattr_t* rv_pipeline_push_vattr(rv_arena* arena, rv_pipeline_t* pipeline, rv_vattr_type_t type); +RV_GLOBAL rv_vattr_t* rv_pipeline_push_vattr(rv_arena* arena, rv_pipeline_t* pipeline, rv_vattr_type_t type, s32 bind_index, s32 divisor); ////////////////////////////////////////////////////////////////// // Submit diff --git a/src/render/render_helpers.c b/src/render/render_helpers.c @@ -121,9 +121,9 @@ RV_GLOBAL rv_uniform_t* rv_pipeline_push_uniform(rv_arena* arena, rv_pipeline_t* return res; } -RV_GLOBAL rv_vattr_t* rv_pipeline_push_vattr(rv_arena* arena, rv_pipeline_t* pipeline, rv_vattr_type_t type) +RV_GLOBAL rv_vattr_t* rv_pipeline_push_vattr(rv_arena* arena, rv_pipeline_t* pipeline, rv_vattr_type_t type, s32 bind_index, s32 divisor) { - rv_vattr_t* res = rv_push_compound(arena, rv_vattr_t, {.type = type}); + rv_vattr_t* res = rv_push_compound(arena, rv_vattr_t, {.type = type, .bind_index = bind_index, .divisor = divisor}); rv_llist_queue_push(pipeline->vattr_first, pipeline->vattr_last, res); return res; }