revolver

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

commit f9d49901707dafcfc5ffe680b3b6631e6851d116
parent a5ca020c10ea2ae58feaaf1a1e89472d2095bf73
Author: Samdal <samdal@protonmail.com>
Date:   Tue, 25 Mar 2025 01:57:39 +0100

idraw2d

Diffstat:
Mbuild.sh | 1+
Aexamples/idraw2d.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mexamples/simple_texture.c | 4++--
Msrc/platform/platform_math.c | 4++++
Msrc/platform/platform_math.h | 2++
Msrc/platform/platform_types.h | 1+
Msrc/render/impl/opengl.c | 988+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/render/render.h | 18++++++++++++------
Asrc/render/render_idraw2d.c | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/render/render_idraw2d.h | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/render/render_inc.c | 1+
Msrc/render/render_inc.h | 1+
Msrc/render/render_vertex_cache.c | 12++++++------
Msrc/render/render_vertex_cache.h | 4++--
14 files changed, 979 insertions(+), 485 deletions(-)

diff --git a/build.sh b/build.sh @@ -15,6 +15,7 @@ do "hello_window") sources=(./examples/hello_window.c) ;; "instancing") sources=(./examples/instancing.c) ;; "vertex_cache") sources=(./examples/vertex_cache.c) ;; + "idraw2d") sources=(./examples/idraw2d.c) ;; esac done diff --git a/examples/idraw2d.c b/examples/idraw2d.c @@ -0,0 +1,73 @@ +////////////////////////////////////////////////////////////////// +// idraw2d.c + +#include "revolver_inc.h" +#include "revolver_inc.c" + + +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}; + + rv_render_pass_t* create_rpass = rv_render_push_render_pass(arena, &rpass_list); + + rv_idraw2d_ctx rvi = {0}; + rvi2d_create(&rvi, arena, &create_rpass->commands); + + while(1) { + rv_temp_arena scratch = rv_scratch_begin(0, 0); + + rvi2d_break_and_insert_default_bindings(&rvi); + + // 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)); + rvi2d_camera(&rvi, 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 using idraw2d + rvi2d_rect(&rvi, rv_rct(100, 100, 100, 100), RV_COLOR_GREEN); + + rvi2d_rect(&rvi, rv_rct(200, 200, 100, 100), RV_COLOR_RED); + + rvi2d_tex(&rvi, &ogl_ctx.default_tex); + rvi2d_rect(&rvi, rv_rct(300, 300, 100, 100), RV_COLOR_WHITE); + rvi2d_tex(&rvi, NULL); + + rvi2d_rect(&rvi, rv_rct(400, 400, 100, 100), RV_COLOR_GREEN); + + rvi2d_draw_and_reset(&rvi, scratch.arena, &rpass->commands); + } + + 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_texture.c b/examples/simple_texture.c @@ -4,7 +4,7 @@ #include "revolver_inc.h" #include "revolver_inc.c" -#define ROW_COL_CT 10 +#define ROW_COL_CT 14 ////////////////////////////////////////////////////////////////// // data @@ -60,7 +60,7 @@ int main(void) { // Generate procedural texture data (checkered texture) rv_color_t c0 = RV_COLOR_BLACK; - rv_color_t c1 = RV_COLOR_PURPLE; + rv_color_t c1 = RV_COLOR_BLUE; rv_color_t pixels[ROW_COL_CT * ROW_COL_CT] = {0}; for (uint32_t r = 0; r < ROW_COL_CT; ++r) { for (uint32_t c = 0; c < ROW_COL_CT; ++c) { diff --git a/src/platform/platform_math.c b/src/platform/platform_math.c @@ -204,6 +204,10 @@ RV_GLOBAL rv_vec4 rv_vec4_project_onto(rv_vec4 a, rv_vec4 b) return rv_vec4_scale(b, dot / len); } +RV_GLOBAL rv_vec4 rv_vec4_from_color(rv_color_t c) +{ + return rv_v4((float)c.r / 255.0f, (float)c.g / 255.0f, (float)c.b / 255.0f, (float)c.a / 255.0f); +} RV_GLOBAL f32 rv_vec4_len(rv_vec4 v) { diff --git a/src/platform/platform_math.h b/src/platform/platform_math.h @@ -84,6 +84,7 @@ RV_GLOBAL rv_vec4 rv_vec4_sub(rv_vec4 a, rv_vec4 b); RV_GLOBAL rv_vec4 rv_vec4_mul(rv_vec4 a, rv_vec4 b); RV_GLOBAL rv_vec4 rv_vec4_div(rv_vec4 a, rv_vec4 b); RV_GLOBAL rv_vec4 rv_vec4_project_onto(rv_vec4 a, rv_vec4 b); +RV_GLOBAL rv_vec4 rv_vec4_from_color(rv_color_t c); RV_GLOBAL f32 rv_vec4_len(rv_vec4 v); RV_GLOBAL f32 rv_vec4_dist(rv_vec4 a, rv_vec4 b); @@ -92,6 +93,7 @@ RV_GLOBAL f32 rv_vec4_dot(rv_vec4 a, rv_vec4 b); RV_GLOBAL bool32 rv_vec4_nan(rv_vec4 v); RV_GLOBAL bool32 rv_vec4_eq(rv_vec4 a, rv_vec4 b); + ////////////////////////////////////////////////////////////////// // matrix math diff --git a/src/platform/platform_types.h b/src/platform/platform_types.h @@ -126,6 +126,7 @@ typedef union { }; rv_vec4 vec4; } rv_rect; +#define rv_rct(...) (rv_rect){__VA_ARGS__} typedef struct { union { diff --git a/src/render/impl/opengl.c b/src/render/impl/opengl.c @@ -3,14 +3,6 @@ #define ogl_safe_set(ptr, hndl_arg) ((ptr) ? ((ptr)->hndl_arg.u) : (0)) -struct { - GLuint vao; - bool32 is_init; -} ogl_ctx; - -////////////////////////////////////////////////////////////////// -// Command buffer api - RV_INTERNAL GLenum rv_usage_to_gl(rv_buffer_usage_t usage) { GLenum mode = GL_STATIC_DRAW; switch (usage) { @@ -73,14 +65,19 @@ RV_INTERNAL GLenum rv_tex_wrap_to_gl(rv_texture_wrap_t type) { return wrap; } -struct { +RV_INTERNAL struct { + GLuint vao; + bool32 is_init; + + rv_texture_t default_tex; + rv_ibo_t* bound_ibo; rv_pipeline_t* bound_pip; rv_framebuffer_t* bound_fbuf; -} ogl_state; +} ogl_ctx; void ogl_bind_fbuf(rv_framebuffer_t* fbuf) { - ogl_state.bound_fbuf = fbuf; + ogl_ctx.bound_fbuf = fbuf; glBindFramebuffer(GL_FRAMEBUFFER, ogl_safe_set(fbuf, handle)); if (fbuf) { s32 r = 0; @@ -95,546 +92,599 @@ void ogl_bind_fbuf(rv_framebuffer_t* fbuf) { } +RV_INTERNAL void ogl_handle_command(rv_command_t* c); + RV_GLOBAL void rv_window_render_commit(rv_window_handle_t* window, rv_render_pass_list_t* passes) { rv_window_render_begin(window); + rv_temp_arena scratch = rv_scratch_begin(0, 0); + + rv_render_pass_t* initial_rpass = passes->first; + if (!ogl_ctx.is_init) { ogl_ctx.is_init = true; glGenVertexArrays(1, &ogl_ctx.vao); glBindVertexArray(ogl_ctx.vao); + + + #define OGL_DEFAULT_TEX_ROW_COL_CT 6 + // Generate default texture (checkered purple/black texture) + rv_color_t c0 = RV_COLOR_BLACK; + rv_color_t c1 = RV_COLOR_PURPLE; + rv_color_t* pixels = rv_push_array(scratch.arena, rv_color_t, OGL_DEFAULT_TEX_ROW_COL_CT * OGL_DEFAULT_TEX_ROW_COL_CT); + for (uint32_t r = 0; r < OGL_DEFAULT_TEX_ROW_COL_CT; ++r) { + for (uint32_t c = 0; c < OGL_DEFAULT_TEX_ROW_COL_CT; ++c) { + const bool32 re = (r % 2) == 0; + const bool32 ce = (c % 2) == 0; + uint32_t idx = r * OGL_DEFAULT_TEX_ROW_COL_CT + c; + pixels[idx] = (re && ce) ? c0 : (re) ? c1 : (ce) ? c1 : c0; + } + } + + rv_render_pass_t* extra_initial_rpass = rv_push(scratch.arena, rv_render_pass_t); + extra_initial_rpass->next = initial_rpass; + initial_rpass = extra_initial_rpass; + + ogl_ctx.default_tex = (rv_texture_t){.data = pixels, .size = rv_v2s(OGL_DEFAULT_TEX_ROW_COL_CT)}; + rv_cmd_push_obj(scratch.arena, &extra_initial_rpass->commands, RV_COMMAND_OBJ_TEXTURE, RV_RENDER_OBJ_OP_CREATE, &ogl_ctx.default_tex); } - for (rv_render_pass_t* p = passes->first; p; p = p->next) { + for (rv_render_pass_t* p = initial_rpass; p; p = p->next) { { // unbind everything - if (ogl_state.bound_ibo) { - ogl_state.bound_ibo = NULL; + if (ogl_ctx.bound_ibo) { + ogl_ctx.bound_ibo = NULL; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } - if (ogl_state.bound_pip) { - ogl_state.bound_pip = NULL; + if (ogl_ctx.bound_pip) { + ogl_ctx.bound_pip = NULL; glUseProgram(0); } - if (ogl_state.bound_fbuf || p->framebuffer) { + if (ogl_ctx.bound_fbuf || p->framebuffer) { ogl_bind_fbuf(p->framebuffer); } } for (rv_command_t* c = p->commands.first; c; c = c->next) { - switch (c->type) { - case RV_COMMAND_INVALID: { - rv_unreachable(); - } break; + ogl_handle_command(c); + } + } - case RV_COMMAND_CUSTOM: { - rv_assert(c->custom.func); - if (c->custom.func) { - c->custom.func(c->custom.data); - } - } break; + rv_scratch_end(scratch); - case RV_COMMAND_CLEAR: { - for (rv_render_clear_desc_t* clear = c->clear.first; clear; clear = clear->next) { - if (clear->flags == RV_RENDER_CLEAR_FLAG_NONE) continue; + rv_window_render_end(window); +} - int opengl_clear_mask = 0; - if (clear->flags & RV_RENDER_CLEAR_FLAG_COLOR) { - glClearColor(clear->color[0], clear->color[1], clear->color[2], clear->color[3]); - opengl_clear_mask |= GL_COLOR_BUFFER_BIT; - } - if (clear->flags & RV_RENDER_CLEAR_FLAG_DEPTH) opengl_clear_mask |= GL_DEPTH_BUFFER_BIT; - if (clear->flags & RV_RENDER_CLEAR_FLAG_STENCIL) opengl_clear_mask |= GL_STENCIL_BUFFER_BIT; +RV_INTERNAL void ogl_handle_command(rv_command_t* c) { + switch (c->type) { + case RV_COMMAND_INVALID: { + rv_unreachable(); + } break; - glClear(opengl_clear_mask); - } - } break; - - case RV_COMMAND_SET_VIEWPORT: { - glViewport(c->viewport.x, c->viewport.y, c->viewport.z, c->viewport.w); - } break; - - case RV_COMMAND_OBJ_VERTEX: { - rv_vbo_t* vbo = c->obj.vbo; - switch (c->obj.operation) { - case RV_RENDER_OBJ_OP_CREATE: { - 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, 0); - } break; + case RV_COMMAND_CUSTOM: { + rv_assert(c->custom.func); + if (c->custom.func) { + c->custom.func(c->custom.data); + } + } 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, 0); - } break; + case RV_COMMAND_COMMAND_LIST: { + for (rv_command_t* cc = c->command_list.first; cc; cc = cc->next) { + ogl_handle_command(cc); + } + } break; - case RV_RENDER_OBJ_OP_BIND: { - if (ogl_state.bound_pip) { - s32 bind_index = c->obj.vbo_bind.bind_index; - s32 base_offset = c->obj.vbo_bind.base_offset; - rv_vattr_bind_t* vattr_bind_override_list = c->obj.vbo_bind.vattr_override_first; - glBindBuffer(GL_ARRAY_BUFFER, vbo->handle.u); - - // set vattrs - s32 auto_stride = 0; - for (rv_vattr_t* v = ogl_state.bound_pip->vattr_first; v; v = v->next) { - if (v->bind_index == bind_index) { - bool32 found_override = false; - for (rv_vattr_bind_t* vo = vattr_bind_override_list; vo; vo = vo->next) { - if (vo->desc == v) { - found_override = true; - break; - } - } - if (!found_override) - auto_stride += rv_vattr_type_size(v->type); + case RV_COMMAND_CLEAR: { + for (rv_render_clear_desc_t* clear = c->clear.first; clear; clear = clear->next) { + if (clear->flags == RV_RENDER_CLEAR_FLAG_NONE) continue; + + int opengl_clear_mask = 0; + if (clear->flags & RV_RENDER_CLEAR_FLAG_COLOR) { + glClearColor(clear->color[0], clear->color[1], clear->color[2], clear->color[3]); + opengl_clear_mask |= GL_COLOR_BUFFER_BIT; + } + if (clear->flags & RV_RENDER_CLEAR_FLAG_DEPTH) opengl_clear_mask |= GL_DEPTH_BUFFER_BIT; + if (clear->flags & RV_RENDER_CLEAR_FLAG_STENCIL) opengl_clear_mask |= GL_STENCIL_BUFFER_BIT; + + glClear(opengl_clear_mask); + } + } break; + + case RV_COMMAND_SET_VIEWPORT: { + glViewport(c->viewport.x, c->viewport.y, c->viewport.z, c->viewport.w); + } break; + + case RV_COMMAND_OBJ_VERTEX: { + rv_vbo_t* vbo = c->obj.vbo; + switch (c->obj.operation) { + case RV_RENDER_OBJ_OP_CREATE: { + rv_assert(!ogl_ctx.bound_pip); + + 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, 0); + } break; + + case RV_RENDER_OBJ_OP_UPDATE: { + rv_assert(!ogl_ctx.bound_pip); + + 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, 0); + } break; + + case RV_RENDER_OBJ_OP_BIND: { + if (ogl_ctx.bound_pip) { + s32 bind_index = c->obj.vbo_bind.bind_index; + s32 base_offset = c->obj.vbo_bind.base_offset; + rv_vattr_bind_t* vattr_bind_override_list = c->obj.vbo_bind.vattr_override_first; + glBindBuffer(GL_ARRAY_BUFFER, vbo->handle.u); + + // set vattrs + s32 auto_stride = 0; + for (rv_vattr_t* v = ogl_ctx.bound_pip->vattr_first; v; v = v->next) { + if (v->bind_index == bind_index) { + bool32 found_override = false; + for (rv_vattr_bind_t* vo = vattr_bind_override_list; vo; vo = vo->next) { + if (vo->desc == v) { + found_override = true; + break; } } - s32 auto_offset = 0; - s32 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); - s32 offset = auto_offset; - s32 stride = auto_stride; - s32 divisor = v->divisor; - bool32 found_override = false; - for (rv_vattr_bind_t* vo = vattr_bind_override_list; vo; vo = vo->next) { - if (vo->desc == v) { - offset = vo->offset; - stride = vo->stride; - divisor = vo->divisor; - break; - } - } - switch (v->type) { - case RV_VATTR_TYPE_FLOAT4: glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, stride, (void*)(s64)(base_offset + offset)); break; - case RV_VATTR_TYPE_FLOAT3: glVertexAttribPointer(i, 3, GL_FLOAT, GL_FALSE, stride, (void*)(s64)(base_offset + offset)); break; - case RV_VATTR_TYPE_FLOAT2: glVertexAttribPointer(i, 2, GL_FLOAT, GL_FALSE, stride, (void*)(s64)(base_offset + offset)); break; - case RV_VATTR_TYPE_FLOAT: glVertexAttribPointer(i, 1, GL_FLOAT, GL_FALSE, stride, (void*)(s64)(base_offset + offset)); break; - case RV_VATTR_TYPE_UINT4: glVertexAttribIPointer(i, 4, GL_UNSIGNED_INT, stride, (void*)(s64)(base_offset + offset)); break; - case RV_VATTR_TYPE_UINT3: glVertexAttribIPointer(i, 3, GL_UNSIGNED_INT, stride, (void*)(s64)(base_offset + offset)); break; - case RV_VATTR_TYPE_UINT2: glVertexAttribIPointer(i, 2, GL_UNSIGNED_INT, stride, (void*)(s64)(base_offset + offset)); break; - case RV_VATTR_TYPE_UINT: glVertexAttribIPointer(i, 1, GL_UNSIGNED_INT, stride, (void*)(s64)(base_offset + offset)); break; - case RV_VATTR_TYPE_BYTE: glVertexAttribPointer(i, 1, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)(s64)(base_offset + offset)); break; - case RV_VATTR_TYPE_BYTE2: glVertexAttribPointer(i, 2, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)(s64)(base_offset + offset)); break; - case RV_VATTR_TYPE_BYTE3: glVertexAttribPointer(i, 3, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)(s64)(base_offset + offset)); break; - case RV_VATTR_TYPE_BYTE4: glVertexAttribPointer(i, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)(s64)(base_offset + offset)); break; - default: rv_unreachable(); break; - } - if (!found_override) - auto_offset += rv_vattr_type_size(v->type); - if (v->divisor) { - glVertexAttribDivisor(i, v->divisor); - } + if (!found_override) + auto_stride += rv_vattr_type_size(v->type); + } + } + s32 auto_offset = 0; + s32 i = 0; + for (rv_vattr_t* v = ogl_ctx.bound_pip->vattr_first; v; v = v->next, i++) { + if (v->bind_index == bind_index) { + glEnableVertexAttribArray(i); + s32 offset = auto_offset; + s32 stride = auto_stride; + s32 divisor = v->divisor; + bool32 found_override = false; + for (rv_vattr_bind_t* vo = vattr_bind_override_list; vo; vo = vo->next) { + if (vo->desc == v) { + offset = vo->offset; + stride = vo->stride; + divisor = vo->divisor; + break; } } - } else { - rv_unreachable(); + switch (v->type) { + case RV_VATTR_TYPE_FLOAT4: glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, stride, (void*)(s64)(base_offset + offset)); break; + case RV_VATTR_TYPE_FLOAT3: glVertexAttribPointer(i, 3, GL_FLOAT, GL_FALSE, stride, (void*)(s64)(base_offset + offset)); break; + case RV_VATTR_TYPE_FLOAT2: glVertexAttribPointer(i, 2, GL_FLOAT, GL_FALSE, stride, (void*)(s64)(base_offset + offset)); break; + case RV_VATTR_TYPE_FLOAT: glVertexAttribPointer(i, 1, GL_FLOAT, GL_FALSE, stride, (void*)(s64)(base_offset + offset)); break; + case RV_VATTR_TYPE_UINT4: glVertexAttribIPointer(i, 4, GL_UNSIGNED_INT, stride, (void*)(s64)(base_offset + offset)); break; + case RV_VATTR_TYPE_UINT3: glVertexAttribIPointer(i, 3, GL_UNSIGNED_INT, stride, (void*)(s64)(base_offset + offset)); break; + case RV_VATTR_TYPE_UINT2: glVertexAttribIPointer(i, 2, GL_UNSIGNED_INT, stride, (void*)(s64)(base_offset + offset)); break; + case RV_VATTR_TYPE_UINT: glVertexAttribIPointer(i, 1, GL_UNSIGNED_INT, stride, (void*)(s64)(base_offset + offset)); break; + case RV_VATTR_TYPE_BYTE: glVertexAttribPointer(i, 1, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)(s64)(base_offset + offset)); break; + case RV_VATTR_TYPE_BYTE2: glVertexAttribPointer(i, 2, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)(s64)(base_offset + offset)); break; + case RV_VATTR_TYPE_BYTE3: glVertexAttribPointer(i, 3, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)(s64)(base_offset + offset)); break; + case RV_VATTR_TYPE_BYTE4: glVertexAttribPointer(i, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)(s64)(base_offset + offset)); break; + default: rv_unreachable(); break; + } + if (!found_override) + auto_offset += rv_vattr_type_size(v->type); + if (v->divisor) { + glVertexAttribDivisor(i, v->divisor); + } } - } break; - - case RV_RENDER_OBJ_OP_DELETE: { - glDeleteBuffers(1, &vbo->handle.u); - vbo->handle.u = 0; - } break; } - } break; + } else { + rv_unreachable(); + } + } break; - case RV_COMMAND_OBJ_INDEX: { - rv_ibo_t* ibo = c->obj.ibo; - switch (c->obj.operation) { - case RV_RENDER_OBJ_OP_CREATE: { - glGenBuffers(1, &ibo->handle.u); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->handle.u); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, ibo->size, ibo->data, rv_usage_to_gl(ibo->usage)); + case RV_RENDER_OBJ_OP_DESTROY: { + rv_assert(!ogl_ctx.bound_pip); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ogl_safe_set(ogl_state.bound_ibo, handle)); - } break; + glDeleteBuffers(1, &vbo->handle.u); + vbo->handle.u = 0; + } break; + } + } break; - case RV_RENDER_OBJ_OP_UPDATE: { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->handle.u); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, c->obj.ibo_update.size, c->obj.ibo_update.data, rv_usage_to_gl(ibo->usage)); + case RV_COMMAND_OBJ_INDEX: { + rv_ibo_t* ibo = c->obj.ibo; + switch (c->obj.operation) { + case RV_RENDER_OBJ_OP_CREATE: { + rv_assert(!ogl_ctx.bound_pip); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ogl_safe_set(ogl_state.bound_ibo, handle)); - } break; + glGenBuffers(1, &ibo->handle.u); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->handle.u); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, ibo->size, ibo->data, rv_usage_to_gl(ibo->usage)); - case RV_RENDER_OBJ_OP_BIND: { - ogl_state.bound_ibo = ibo; - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ogl_safe_set(ogl_state.bound_ibo, handle)); - } break; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ogl_safe_set(ogl_ctx.bound_ibo, handle)); + } break; - case RV_RENDER_OBJ_OP_DELETE: { - glDeleteBuffers(1, &ibo->handle.u); - ibo->handle.u = 0; - if (ogl_state.bound_ibo == ibo) { - ogl_state.bound_ibo = NULL; - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - } break; - } - } break; - - case RV_COMMAND_OBJ_SHADER: { - rv_shader_t* shader = c->obj.shader; - switch (c->obj.operation) { - case RV_RENDER_OBJ_OP_CREATE: { - shader->handle.u = glCreateShader(rv_shader_to_gl(shader->type)); - glShaderSource(shader->handle.u, 1, (const char* const*)&shader->source.str, (s32*)&shader->source.len); - glCompileShader(shader->handle.u); - - { // check for errors - int shader_compile_success; - glGetShaderiv(shader->handle.u, GL_COMPILE_STATUS, &shader_compile_success); - - if (!shader_compile_success) { - char info_log[512]; - glGetShaderInfoLog(shader->handle.u, 512, NULL, info_log); - fprintf(stderr, "-------------ERROR------------\n" - "::OpenGL Failed to compile shader::\n%s\n", info_log); - fprintf(stderr, "-------------SOURCE------------\n"); - fprintf(stderr, "%.*s\n", rv_s8v(shader->source)); - fprintf(stderr, "\n------------END_SOURCE----------\n"); - rv_assert(shader_compile_success); - } - } - } break; + case RV_RENDER_OBJ_OP_UPDATE: { + rv_assert(!ogl_ctx.bound_pip); - case RV_RENDER_OBJ_OP_UPDATE: { - rv_unreachable(); - } break; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->handle.u); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, c->obj.ibo_update.size, c->obj.ibo_update.data, rv_usage_to_gl(ibo->usage)); - case RV_RENDER_OBJ_OP_BIND: { - rv_unreachable(); - } break; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ogl_safe_set(ogl_ctx.bound_ibo, handle)); + } break; - case RV_RENDER_OBJ_OP_DELETE: { - glDeleteShader(shader->handle.u); - shader->handle.u = 0; - } break; + case RV_RENDER_OBJ_OP_BIND: { + ogl_ctx.bound_ibo = ibo; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ogl_safe_set(ogl_ctx.bound_ibo, handle)); + } break; + + case RV_RENDER_OBJ_OP_DESTROY: { + rv_assert(!ogl_ctx.bound_pip); + + glDeleteBuffers(1, &ibo->handle.u); + ibo->handle.u = 0; + if (ogl_ctx.bound_ibo == ibo) { + ogl_ctx.bound_ibo = NULL; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + } break; + } + } break; + + case RV_COMMAND_OBJ_SHADER: { + rv_shader_t* shader = c->obj.shader; + switch (c->obj.operation) { + case RV_RENDER_OBJ_OP_CREATE: { + shader->handle.u = glCreateShader(rv_shader_to_gl(shader->type)); + glShaderSource(shader->handle.u, 1, (const char* const*)&shader->source.str, (s32*)&shader->source.len); + glCompileShader(shader->handle.u); + + { // check for errors + int shader_compile_success; + glGetShaderiv(shader->handle.u, GL_COMPILE_STATUS, &shader_compile_success); + + if (!shader_compile_success) { + char info_log[512]; + glGetShaderInfoLog(shader->handle.u, 512, NULL, info_log); + fprintf(stderr, "-------------ERROR------------\n" + "::OpenGL Failed to compile shader::\n%s\n", info_log); + fprintf(stderr, "-------------SOURCE------------\n"); + fprintf(stderr, "%.*s\n", rv_s8v(shader->source)); + fprintf(stderr, "\n------------END_SOURCE----------\n"); + rv_assert(shader_compile_success); } - } break; - - case RV_COMMAND_OBJ_PIPELINE: { - rv_pipeline_t* pipeline = c->obj.pipeline; - switch (c->obj.operation) { - case RV_RENDER_OBJ_OP_CREATE: { - pipeline->handle.u = glCreateProgram(); - for (rv_shader_node_t* s = pipeline->shader_first; s; s = s->next) { - glAttachShader(pipeline->handle.u, s->shader->handle.u); - } - glLinkProgram(pipeline->handle.u); - - { - int program_link_success; - glGetProgramiv(pipeline->handle.u, GL_LINK_STATUS, &program_link_success); - - if (!program_link_success) { - char info_log[512]; - glGetProgramInfoLog(pipeline->handle.u, 512, NULL, info_log); - fprintf(stderr, "-------------ERROR------------\n" - "::OpenGL Failed to link shader program::\n%s\n", info_log); - rv_assert(program_link_success); - } - } + } + } break; - // find uniform locations - for (rv_uniform_t* u = pipeline->uniform_desc_first; u; u = u->next) { - u->pipeline_uniform_handle.u = glGetUniformLocation(pipeline->handle.u, (const char*)u->name_null_terminated.str); - } + case RV_RENDER_OBJ_OP_UPDATE: { + rv_unreachable(); + } break; - glUseProgram(pipeline->handle.u); + case RV_RENDER_OBJ_OP_BIND: { + rv_unreachable(); + } break; - // set up sampler channels - s32 sampler_channel = 0; - for (rv_uniform_t* u = pipeline->uniform_desc_first; u; u = u->next) { - if (u->type == RV_UNIFORM_TEX) { - glUniform1i(u->pipeline_uniform_handle.u, sampler_channel); - sampler_channel++; - } - } - // turn back to previous state - glUseProgram(ogl_safe_set(ogl_state.bound_pip, handle)); - } break; + case RV_RENDER_OBJ_OP_DESTROY: { + glDeleteShader(shader->handle.u); + shader->handle.u = 0; + } break; + } + } break; + + case RV_COMMAND_OBJ_PIPELINE: { + rv_pipeline_t* pipeline = c->obj.pipeline; + switch (c->obj.operation) { + case RV_RENDER_OBJ_OP_CREATE: { + pipeline->handle.u = glCreateProgram(); + for (rv_shader_node_t* s = pipeline->shader_first; s; s = s->next) { + glAttachShader(pipeline->handle.u, s->shader->handle.u); + } + glLinkProgram(pipeline->handle.u); + + { + int program_link_success; + glGetProgramiv(pipeline->handle.u, GL_LINK_STATUS, &program_link_success); + + if (!program_link_success) { + char info_log[512]; + glGetProgramInfoLog(pipeline->handle.u, 512, NULL, info_log); + fprintf(stderr, "-------------ERROR------------\n" + "::OpenGL Failed to link shader program::\n%s\n", info_log); + rv_assert(program_link_success); + } + } - case RV_RENDER_OBJ_OP_UPDATE: { - rv_unreachable(); - } break; + // find uniform locations + for (rv_uniform_t* u = pipeline->uniform_desc_first; u; u = u->next) { + u->pipeline_uniform_handle.u = glGetUniformLocation(pipeline->handle.u, (const char*)u->name_null_terminated.str); + } - case RV_RENDER_OBJ_OP_BIND: { - ogl_state.bound_pip = pipeline; - if (pipeline) { - glUseProgram(pipeline->handle.u); - - // activate textures and sample2d, if any - s32 sampler_channel = 0; - for (rv_uniform_t* u = pipeline->uniform_desc_first; u; u = u->next) { - if (u->type == RV_UNIFORM_TEX) { - bool32 has_queued_update = false; - { - for (rv_command_t* c_future = c->next; c_future; c_future = c_future->next) { - if (c_future->type == RV_COMMAND_DRAW || c_future->type == RV_COMMAND_OBJ_PIPELINE) { - // reached end... - break; - } - if (c_future->type == RV_COMMAND_UNIFORM_UPDATE) { - if (c_future->uniform_update.desc == u) { - // found something that updates this uniform - has_queued_update = true; - break; - } - } - } + glUseProgram(pipeline->handle.u); + + // set up sampler channels + s32 sampler_channel = 0; + for (rv_uniform_t* u = pipeline->uniform_desc_first; u; u = u->next) { + if (u->type == RV_UNIFORM_TEX) { + glUniform1i(u->pipeline_uniform_handle.u, sampler_channel); + sampler_channel++; + } + } + // turn back to previous state + glUseProgram(ogl_safe_set(ogl_ctx.bound_pip, handle)); + } break; + + case RV_RENDER_OBJ_OP_UPDATE: { + rv_unreachable(); + } break; + + case RV_RENDER_OBJ_OP_BIND: { + ogl_ctx.bound_pip = pipeline; + if (pipeline) { + glUseProgram(pipeline->handle.u); + + // activate textures and sample2d, if any + s32 sampler_channel = 0; + for (rv_uniform_t* u = pipeline->uniform_desc_first; u; u = u->next) { + if (u->type == RV_UNIFORM_TEX) { + bool32 has_queued_update = false; + { + for (rv_command_t* c_future = c->next; c_future; c_future = c_future->next) { + if (c_future->type == RV_COMMAND_DRAW || c_future->type == RV_COMMAND_OBJ_PIPELINE) { + // reached end... + break; } - if (!has_queued_update) { - if (u->last_val.v_tex) { - glActiveTexture(GL_TEXTURE0 + sampler_channel); - glBindTexture(GL_TEXTURE_2D, u->last_val.v_tex->handle.u); - } else { - // Texture was null, cannot bind - // TODO(Samdal): bind default texture? - rv_unreachable(); + if (c_future->type == RV_COMMAND_UNIFORM_UPDATE) { + if (c_future->uniform_update.desc == u) { + // found something that updates this uniform + has_queued_update = true; + break; } } - sampler_channel++; } } - } else { - glUseProgram(0); - } - } break; - - case RV_RENDER_OBJ_OP_DELETE: { - glDeleteProgram(pipeline->handle.u); - pipeline->handle.u = 0; - for (rv_uniform_t* u = pipeline->uniform_desc_first; u; u = u->next) { - u->pipeline_uniform_handle.u = 0; - } - if (ogl_state.bound_pip == pipeline) { - ogl_state.bound_pip = NULL; - glUseProgram(0); - } - } break; - } - } break; - - case RV_COMMAND_OBJ_TEXTURE: { - rv_texture_t* tex = c->obj.tex; - switch (c->obj.operation) { - case RV_RENDER_OBJ_OP_CREATE: { - glGenTextures(1, &tex->handle.u); - - s32 sampler_channel = 0; - if (ogl_state.bound_pip) { - // need to update the existing bound sampler - // ... or make sure we don't override a bound one - for (rv_uniform_t* u = ogl_state.bound_pip->uniform_desc_first; u; u = u->next) { - if (u->type == RV_UNIFORM_TEX) { - if (u->last_val.v_tex == tex) { - // found our texture - break; - } - sampler_channel++; + if (!has_queued_update) { + glActiveTexture(GL_TEXTURE0 + sampler_channel); + if (u->last_val.v_tex) { + glBindTexture(GL_TEXTURE_2D, u->last_val.v_tex->handle.u); + } else { + glBindTexture(GL_TEXTURE_2D, ogl_ctx.default_tex.handle.u); } } + sampler_channel++; } - glActiveTexture(GL_TEXTURE0 + sampler_channel); - glBindTexture(GL_TEXTURE_2D, tex->handle.u); - - switch(tex->format) - { - case RV_TEXTURE_FORMAT_A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, tex->size.x, tex->size.y, 0, GL_ALPHA, GL_UNSIGNED_BYTE, tex->data); break; - case RV_TEXTURE_FORMAT_R8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, tex->size.x, tex->size.y, 0, GL_RED, GL_UNSIGNED_BYTE, tex->data); break; - case RV_TEXTURE_FORMAT_RGB8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, tex->size.x, tex->size.y, 0, GL_RGB, GL_UNSIGNED_BYTE, tex->data); break; - case RV_TEXTURE_FORMAT_RGBA8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex->size.x, tex->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->data); break; - case RV_TEXTURE_FORMAT_RGBA16F: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, tex->size.x, tex->size.y, 0, GL_RGBA, GL_FLOAT, tex->data); break; - case RV_TEXTURE_FORMAT_RGBA32F: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, tex->size.x, tex->size.y, 0, GL_RGBA, GL_FLOAT, tex->data); break; - case RV_TEXTURE_FORMAT_DEPTH8: glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, tex->size.x, tex->size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, tex->data); break; - case RV_TEXTURE_FORMAT_DEPTH16: glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, tex->size.x, tex->size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, tex->data); break; - case RV_TEXTURE_FORMAT_DEPTH24: glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, tex->size.x, tex->size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, tex->data); break; - case RV_TEXTURE_FORMAT_DEPTH32F: glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, tex->size.x, tex->size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, tex->data); break; - case RV_TEXTURE_FORMAT_DEPTH24_STENCIL8: glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, tex->size.x, tex->size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, tex->data); break; - case RV_TEXTURE_FORMAT_DEPTH32F_STENCIL8: glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH32F_STENCIL8, tex->size.x, tex->size.y, 0, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, tex->data); break; - default: rv_unreachable(); break; - } - - GLenum mag_filter = tex->mag_filter == RV_TEXTURE_FILTER_NEAREST ? GL_NEAREST : GL_LINEAR; - GLenum min_filter = tex->min_filter == RV_TEXTURE_FILTER_NEAREST ? GL_NEAREST : GL_LINEAR; + } + } else { + glUseProgram(0); + } + } break; - if (tex->num_mips) { - if (tex->min_filter == RV_TEXTURE_FILTER_NEAREST) { - min_filter = tex->mip_filter == RV_TEXTURE_FILTER_NEAREST ? GL_NEAREST_MIPMAP_NEAREST : - GL_NEAREST_MIPMAP_LINEAR; - } - else { - min_filter = tex->mip_filter == RV_TEXTURE_FILTER_NEAREST ? GL_LINEAR_MIPMAP_NEAREST : - GL_NEAREST_MIPMAP_LINEAR; + case RV_RENDER_OBJ_OP_DESTROY: { + glDeleteProgram(pipeline->handle.u); + pipeline->handle.u = 0; + for (rv_uniform_t* u = pipeline->uniform_desc_first; u; u = u->next) { + u->pipeline_uniform_handle.u = 0; + } + if (ogl_ctx.bound_pip == pipeline) { + ogl_ctx.bound_pip = NULL; + glUseProgram(0); + } + } break; + } + } break; + + case RV_COMMAND_OBJ_TEXTURE: { + rv_texture_t* tex = c->obj.tex; + switch (c->obj.operation) { + case RV_RENDER_OBJ_OP_CREATE: { + glGenTextures(1, &tex->handle.u); + + s32 sampler_channel = 0; + if (ogl_ctx.bound_pip) { + // need to update the existing bound sampler + // ... or make sure we don't override a bound one + for (rv_uniform_t* u = ogl_ctx.bound_pip->uniform_desc_first; u; u = u->next) { + if (u->type == RV_UNIFORM_TEX) { + if (u->last_val.v_tex == tex) { + // found our texture + break; } + sampler_channel++; } + } + } + glActiveTexture(GL_TEXTURE0 + sampler_channel); + glBindTexture(GL_TEXTURE_2D, tex->handle.u); + + switch(tex->format) + { + case RV_TEXTURE_FORMAT_A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, tex->size.x, tex->size.y, 0, GL_ALPHA, GL_UNSIGNED_BYTE, tex->data); break; + case RV_TEXTURE_FORMAT_R8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, tex->size.x, tex->size.y, 0, GL_RED, GL_UNSIGNED_BYTE, tex->data); break; + case RV_TEXTURE_FORMAT_RGB8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, tex->size.x, tex->size.y, 0, GL_RGB, GL_UNSIGNED_BYTE, tex->data); break; + case RV_TEXTURE_FORMAT_RGBA8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex->size.x, tex->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->data); break; + case RV_TEXTURE_FORMAT_RGBA16F: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, tex->size.x, tex->size.y, 0, GL_RGBA, GL_FLOAT, tex->data); break; + case RV_TEXTURE_FORMAT_RGBA32F: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, tex->size.x, tex->size.y, 0, GL_RGBA, GL_FLOAT, tex->data); break; + case RV_TEXTURE_FORMAT_DEPTH8: glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, tex->size.x, tex->size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, tex->data); break; + case RV_TEXTURE_FORMAT_DEPTH16: glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, tex->size.x, tex->size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, tex->data); break; + case RV_TEXTURE_FORMAT_DEPTH24: glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, tex->size.x, tex->size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, tex->data); break; + case RV_TEXTURE_FORMAT_DEPTH32F: glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, tex->size.x, tex->size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, tex->data); break; + case RV_TEXTURE_FORMAT_DEPTH24_STENCIL8: glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, tex->size.x, tex->size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, tex->data); break; + case RV_TEXTURE_FORMAT_DEPTH32F_STENCIL8: glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH32F_STENCIL8, tex->size.x, tex->size.y, 0, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, tex->data); break; + default: rv_unreachable(); break; + } - GLenum texture_wrap_s = rv_tex_wrap_to_gl(tex->wrap_s); - GLenum texture_wrap_t = rv_tex_wrap_to_gl(tex->wrap_t); + GLenum mag_filter = tex->mag_filter == RV_TEXTURE_FILTER_NEAREST ? GL_NEAREST : GL_LINEAR; + GLenum min_filter = tex->min_filter == RV_TEXTURE_FILTER_NEAREST ? GL_NEAREST : GL_LINEAR; - if (tex->num_mips) { - glGenerateMipmap(GL_TEXTURE_2D); - } + if (tex->num_mips) { + if (tex->min_filter == RV_TEXTURE_FILTER_NEAREST) { + min_filter = tex->mip_filter == RV_TEXTURE_FILTER_NEAREST ? GL_NEAREST_MIPMAP_NEAREST : + GL_NEAREST_MIPMAP_LINEAR; + } + else { + min_filter = tex->mip_filter == RV_TEXTURE_FILTER_NEAREST ? GL_LINEAR_MIPMAP_NEAREST : + GL_NEAREST_MIPMAP_LINEAR; + } + } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture_wrap_s); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texture_wrap_t); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); + GLenum texture_wrap_s = rv_tex_wrap_to_gl(tex->wrap_s); + GLenum texture_wrap_t = rv_tex_wrap_to_gl(tex->wrap_t); - } break; + if (tex->num_mips) { + glGenerateMipmap(GL_TEXTURE_2D); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture_wrap_s); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texture_wrap_t); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); + + } break; + + case RV_RENDER_OBJ_OP_UPDATE: { + s32 sampler_channel = 0; + if (ogl_ctx.bound_pip) { + // need to update the existing bound sampler + // ... or make sure we don't override a bound one + for (rv_uniform_t* u = ogl_ctx.bound_pip->uniform_desc_first; u; u = u->next) { + if (u->type == RV_UNIFORM_TEX) { + if (u ->last_val.v_tex == tex) { + // found our texture + break; + } + sampler_channel++; + } + } + } + glActiveTexture(GL_TEXTURE0 + sampler_channel); + glBindTexture(GL_TEXTURE_2D, tex->handle.u); + + switch(tex->format) + { + case RV_TEXTURE_FORMAT_A8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_ALPHA, GL_UNSIGNED_BYTE, c->obj.tex_update.data); break; + case RV_TEXTURE_FORMAT_R8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_RED, GL_UNSIGNED_BYTE, c->obj.tex_update.data); break; + case RV_TEXTURE_FORMAT_RGB8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_RGB, GL_UNSIGNED_BYTE, c->obj.tex_update.data); break; + case RV_TEXTURE_FORMAT_RGBA8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_RGBA, GL_UNSIGNED_BYTE, c->obj.tex_update.data); break; + case RV_TEXTURE_FORMAT_RGBA16F: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_RGBA, GL_FLOAT, c->obj.tex_update.data); break; + case RV_TEXTURE_FORMAT_RGBA32F: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_RGBA, GL_FLOAT, c->obj.tex_update.data); break; + case RV_TEXTURE_FORMAT_DEPTH8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_DEPTH_COMPONENT, GL_FLOAT, c->obj.tex_update.data); break; + case RV_TEXTURE_FORMAT_DEPTH16: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_DEPTH_COMPONENT, GL_FLOAT, c->obj.tex_update.data); break; + case RV_TEXTURE_FORMAT_DEPTH24: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_DEPTH_COMPONENT, GL_FLOAT, c->obj.tex_update.data); break; + case RV_TEXTURE_FORMAT_DEPTH32F: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_DEPTH_COMPONENT, GL_FLOAT, c->obj.tex_update.data); break; + case RV_TEXTURE_FORMAT_DEPTH24_STENCIL8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, c->obj.tex_update.data); break; + case RV_TEXTURE_FORMAT_DEPTH32F_STENCIL8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, c->obj.tex_update.data); break; + default: rv_unreachable(); break; + } + } break; - case RV_RENDER_OBJ_OP_UPDATE: { + case RV_RENDER_OBJ_OP_BIND: { + rv_unreachable(); // textures are bound automatically by uniforms + } break; + case RV_RENDER_OBJ_OP_DESTROY: { + glDeleteTextures(1, &tex->handle.u); + tex->handle.u = 0; + } break; + } + } break; + + case RV_COMMAND_UNIFORM_UPDATE: { + if (ogl_ctx.bound_pip) { + rv_uniform_t* desc = c->uniform_update.desc; + rv_uniform_variant_t* val = &c->uniform_update.value; + if (desc) { + desc->last_val = *val; + + switch (desc->type) { + case RV_UNIFORM_F32: glUniform1fv(desc->pipeline_uniform_handle.u, 1, &val->v_f32); break; + case RV_UNIFORM_VEC2: glUniform2fv(desc->pipeline_uniform_handle.u, 1, val->v_vec2.xy); break; + case RV_UNIFORM_VEC3: glUniform3fv(desc->pipeline_uniform_handle.u, 1, val->v_vec3.xyz); break; + case RV_UNIFORM_VEC4: glUniform4fv(desc->pipeline_uniform_handle.u, 1, val->v_vec4.xyzw); break; + case RV_UNIFORM_MAT4: glUniformMatrix4fv(desc->pipeline_uniform_handle.u, 1, GL_FALSE, val->v_mat4.elements); break; + case RV_UNIFORM_TEX: { s32 sampler_channel = 0; - if (ogl_state.bound_pip) { - // need to update the existing bound sampler - // ... or make sure we don't override a bound one - for (rv_uniform_t* u = ogl_state.bound_pip->uniform_desc_first; u; u = u->next) { - if (u->type == RV_UNIFORM_TEX) { - if (u ->last_val.v_tex == tex) { - // found our texture - break; + for (rv_uniform_t* u = ogl_ctx.bound_pip->uniform_desc_first; u; u = u->next) { + if (u->type == RV_UNIFORM_TEX) { + if (u == desc) { + // found our uniform, bind it + glActiveTexture(GL_TEXTURE0 + sampler_channel); + if (u->last_val.v_tex) { + glBindTexture(GL_TEXTURE_2D, u->last_val.v_tex->handle.u); + } else { + glBindTexture(GL_TEXTURE_2D, ogl_ctx.default_tex.handle.u); } - sampler_channel++; + break; } + sampler_channel++; } } - glActiveTexture(GL_TEXTURE0 + sampler_channel); - glBindTexture(GL_TEXTURE_2D, tex->handle.u); - - switch(tex->format) - { - case RV_TEXTURE_FORMAT_A8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_ALPHA, GL_UNSIGNED_BYTE, c->obj.tex_update.data); break; - case RV_TEXTURE_FORMAT_R8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_RED, GL_UNSIGNED_BYTE, c->obj.tex_update.data); break; - case RV_TEXTURE_FORMAT_RGB8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_RGB, GL_UNSIGNED_BYTE, c->obj.tex_update.data); break; - case RV_TEXTURE_FORMAT_RGBA8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_RGBA, GL_UNSIGNED_BYTE, c->obj.tex_update.data); break; - case RV_TEXTURE_FORMAT_RGBA16F: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_RGBA, GL_FLOAT, c->obj.tex_update.data); break; - case RV_TEXTURE_FORMAT_RGBA32F: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_RGBA, GL_FLOAT, c->obj.tex_update.data); break; - case RV_TEXTURE_FORMAT_DEPTH8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_DEPTH_COMPONENT, GL_FLOAT, c->obj.tex_update.data); break; - case RV_TEXTURE_FORMAT_DEPTH16: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_DEPTH_COMPONENT, GL_FLOAT, c->obj.tex_update.data); break; - case RV_TEXTURE_FORMAT_DEPTH24: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_DEPTH_COMPONENT, GL_FLOAT, c->obj.tex_update.data); break; - case RV_TEXTURE_FORMAT_DEPTH32F: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_DEPTH_COMPONENT, GL_FLOAT, c->obj.tex_update.data); break; - case RV_TEXTURE_FORMAT_DEPTH24_STENCIL8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, c->obj.tex_update.data); break; - case RV_TEXTURE_FORMAT_DEPTH32F_STENCIL8: glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, c->obj.tex_update.data); break; - default: rv_unreachable(); break; - } - } break; - - case RV_RENDER_OBJ_OP_BIND: { - rv_unreachable(); // textures are bound automatically by uniforms - } break; - case RV_RENDER_OBJ_OP_DELETE: { - glDeleteTextures(1, &tex->handle.u); - tex->handle.u = 0; } break; + case RV_UNIFORM_INVALID: rv_unreachable(); break; } - } break; - - case RV_COMMAND_UNIFORM_UPDATE: { - if (ogl_state.bound_pip) { - rv_uniform_t* desc = c->uniform_update.desc; - rv_uniform_variant_t* val = &c->uniform_update.value; - if (desc) { - desc->last_val = *val; - - switch (desc->type) { - case RV_UNIFORM_F32: glUniform1fv(desc->pipeline_uniform_handle.u, 1, &val->v_f32); break; - case RV_UNIFORM_VEC2: glUniform2fv(desc->pipeline_uniform_handle.u, 1, val->v_vec2.xy); break; - case RV_UNIFORM_VEC3: glUniform3fv(desc->pipeline_uniform_handle.u, 1, val->v_vec3.xyz); break; - case RV_UNIFORM_VEC4: glUniform4fv(desc->pipeline_uniform_handle.u, 1, val->v_vec4.xyzw); break; - case RV_UNIFORM_MAT4: glUniformMatrix4fv(desc->pipeline_uniform_handle.u, 1, GL_FALSE, val->v_mat4.elements); break; - case RV_UNIFORM_TEX: { - s32 sampler_channel = 0; - for (rv_uniform_t* u = ogl_state.bound_pip->uniform_desc_first; u; u = u->next) { - if (u->type == RV_UNIFORM_TEX) { - if (u == desc) { - // found our uniform - glActiveTexture(GL_TEXTURE0 + sampler_channel); - glBindTexture(GL_TEXTURE_2D, u->last_val.v_tex->handle.u); - break; - } - sampler_channel++; - } - } - - } break; - case RV_UNIFORM_INVALID: rv_unreachable(); break; - } - } else { - rv_unreachable(); - } - } else { - rv_unreachable(); + } else { + rv_unreachable(); + } + } else { + rv_unreachable(); + } + } break; + + case RV_COMMAND_DRAW: { + if (ogl_ctx.bound_pip) { + GLenum prim = rv_primitive_to_gl(c->draw.primitive); + // TODO(Samdal): instancing... + if (ogl_ctx.bound_ibo) { + + GLenum itype = GL_UNSIGNED_INT; + switch (ogl_ctx.bound_ibo->elem_size) { + default: rv_unreachable(); + case 4: itype = GL_UNSIGNED_INT; break; + case 2: itype = GL_UNSIGNED_SHORT; break; + case 1: itype = GL_UNSIGNED_BYTE; break; } - } break; - - case RV_COMMAND_DRAW: { - if (ogl_state.bound_pip) { - GLenum prim = rv_primitive_to_gl(c->draw.primitive); - // TODO(Samdal): instancing... - if (ogl_state.bound_ibo) { - - GLenum itype = GL_UNSIGNED_INT; - switch (ogl_state.bound_ibo->elem_size) { - default: rv_unreachable(); - case 4: itype = GL_UNSIGNED_INT; break; - case 2: itype = GL_UNSIGNED_SHORT; break; - case 1: itype = GL_UNSIGNED_BYTE; break; - } - if (c->draw.instances) { - glDrawElementsInstanced(prim, c->draw.count, itype, (void*)(s64)c->draw.first, c->draw.instances); - } else { - glDrawElements(prim, c->draw.count, itype, (void*)(s64)c->draw.first); - } - } else { - if (c->draw.instances) { - glDrawArraysInstanced(prim, c->draw.first, c->draw.count, c->draw.instances); - } else { - glDrawArrays(prim, c->draw.first, c->draw.count); - } - } + if (c->draw.instances) { + glDrawElementsInstanced(prim, c->draw.count, itype, (void*)(s64)c->draw.first, c->draw.instances); } else { - rv_unreachable(); + glDrawElements(prim, c->draw.count, itype, (void*)(s64)c->draw.first); } - } break; - - case RV_COMMAND_OBJ_FRAMEBUFFER: { - rv_framebuffer_t* fbuf = c->obj.framebuffer; - switch (c->obj.operation) { - case RV_RENDER_OBJ_OP_CREATE: { - glGenFramebuffers(1, &fbuf->handle.u); - } break; - - case RV_RENDER_OBJ_OP_UPDATE: { - rv_unreachable(); - } break; - - case RV_RENDER_OBJ_OP_BIND: { - ogl_bind_fbuf(fbuf); - } break; - - case RV_RENDER_OBJ_OP_DELETE: { - glDeleteFramebuffers(1, &fbuf->handle.u); - if (ogl_state.bound_fbuf == fbuf) { - ogl_state.bound_fbuf = NULL; - glBindFramebuffer(GL_FRAMEBUFFER, 0); - } - } break; + } else { + if (c->draw.instances) { + glDrawArraysInstanced(prim, c->draw.first, c->draw.count, c->draw.instances); + } else { + glDrawArrays(prim, c->draw.first, c->draw.count); } - } break; } + } else { + rv_unreachable(); + } + } break; + + case RV_COMMAND_OBJ_FRAMEBUFFER: { + rv_framebuffer_t* fbuf = c->obj.framebuffer; + switch (c->obj.operation) { + case RV_RENDER_OBJ_OP_CREATE: { + glGenFramebuffers(1, &fbuf->handle.u); + } break; + + case RV_RENDER_OBJ_OP_UPDATE: { + rv_unreachable(); + } break; + + case RV_RENDER_OBJ_OP_BIND: { + ogl_bind_fbuf(fbuf); + } break; + + case RV_RENDER_OBJ_OP_DESTROY: { + glDeleteFramebuffers(1, &fbuf->handle.u); + if (ogl_ctx.bound_fbuf == fbuf) { + ogl_ctx.bound_fbuf = NULL; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + } break; } + } break; } - - rv_window_render_end(window); } diff --git a/src/render/render.h b/src/render/render.h @@ -206,7 +206,7 @@ typedef enum { RV_RENDER_OBJ_OP_CREATE, RV_RENDER_OBJ_OP_UPDATE, RV_RENDER_OBJ_OP_BIND, - RV_RENDER_OBJ_OP_DELETE, + RV_RENDER_OBJ_OP_DESTROY, } rv_command_obj_operation_t; typedef enum { @@ -214,6 +214,8 @@ typedef enum { RV_COMMAND_CUSTOM, + RV_COMMAND_COMMAND_LIST, + RV_COMMAND_OBJ_VERTEX, RV_COMMAND_OBJ_INDEX, RV_COMMAND_OBJ_SHADER, @@ -230,6 +232,13 @@ typedef enum { } rv_command_type_t; typedef struct rv_command_t rv_command_t; + +typedef struct { + rv_command_t* first; + rv_command_t* last; +} rv_command_list_t; + + struct rv_command_t { rv_command_t* next; rv_command_t* prev; @@ -293,6 +302,8 @@ struct rv_command_t { s64 instances; } draw; + rv_command_list_t command_list; + struct { rv_uniform_t* desc; rv_uniform_variant_t value; @@ -300,11 +311,6 @@ struct rv_command_t { }; }; -typedef struct { - rv_command_t* first; - rv_command_t* last; -} rv_command_list_t; - typedef struct rv_render_pass_t rv_render_pass_t; struct rv_render_pass_t { rv_render_pass_t* next; diff --git a/src/render/render_idraw2d.c b/src/render/render_idraw2d.c @@ -0,0 +1,264 @@ +////////////////////////////////////////////////////////////////// +// render_idraw2d.c + +////////////////////////////////////////////////////////////////// +// data + +RV_INTERNAL rv_str8 rv_idraw2d_v_src = S("#version 330 core\n" rv_strify( + layout(location = 0) in vec2 a_pos; + layout(location = 1) in vec2 a_uv; + layout(location = 2) in vec4 a_color; + precision mediump float; + + uniform vec2 u_resolution; + + out vec4 f_color; + out vec2 f_uv; + + void main() + { + vec2 xy_pos = a_pos/u_resolution * 2.0; + xy_pos.y = -xy_pos.y; + xy_pos += vec2(-1.0, 1.0); + gl_Position = vec4(xy_pos, 0.0, 1.0); + f_color = a_color; + f_uv = a_uv; + } +)); + +RV_INTERNAL rv_str8 rv_idraw2d_f_src = S("#version 330 core\n" rv_strify( + precision mediump float; + in vec4 f_color; + in vec2 f_uv; + out vec4 frag_color; + uniform sampler2D u_tex; + uniform float u_omit_tex; + void main() + { + vec4 color = f_color; + if (u_omit_tex < 1.0) { + color *= texture(u_tex, f_uv); + } + frag_color = color; + } +)); + +// init + +RV_GLOBAL void rvi2d_create(rv_idraw2d_ctx* ctx, rv_arena* arena, rv_command_list_t* create_commands) +{ + rvi2d_destroy(ctx, arena, create_commands); + + ctx->arena = rv_arena_alloc(); + ctx->permanent_arena = rv_arena_alloc(.commit_size = KB(4), .reserve_size = KB(4)); + + // make render objects + ctx->vbo_vertex = rv_push_compound(ctx->permanent_arena, rv_vbo_t, {.usage = RV_BUFFER_USAGE_DYNAMIC}); + rv_shader_t* vert = rv_push_compound(ctx->permanent_arena, rv_shader_t, {.source = rv_idraw2d_v_src, .type = RV_SHADER_TYPE_VERTEX}); + rv_shader_t* frag = rv_push_compound(ctx->permanent_arena, rv_shader_t, {.source = rv_idraw2d_f_src, .type = RV_SHADER_TYPE_FRAGMENT}); + ctx->pip = rv_push(ctx->permanent_arena, rv_pipeline_t); + + // construct pipeline + rv_pipeline_push_shader(arena, ctx->pip, vert); + rv_pipeline_push_shader(arena, ctx->pip, frag); + rv_pipeline_push_vattr(arena, ctx->pip, RV_VATTR_TYPE_FLOAT2, 0, 0); + rv_pipeline_push_vattr(arena, ctx->pip, RV_VATTR_TYPE_FLOAT2, 0, 0); + rv_pipeline_push_vattr(arena, ctx->pip, RV_VATTR_TYPE_FLOAT4, 0, 0); + ctx->u_tex = rv_pipeline_push_uniform(ctx->permanent_arena, ctx->pip, S("u_tex"), RV_UNIFORM_TEX); + ctx->u_omit_tex = rv_pipeline_push_uniform(ctx->permanent_arena, ctx->pip, S("u_omit_tex"), RV_UNIFORM_F32); + ctx->u_resolution = rv_pipeline_push_uniform(ctx->permanent_arena, ctx->pip, S("u_resolution"), RV_UNIFORM_VEC2); + + // add creation commands + rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_CREATE, ctx->vbo_vertex); + rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_SHADER, RV_RENDER_OBJ_OP_CREATE, vert); + rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_SHADER, RV_RENDER_OBJ_OP_CREATE, frag); + rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_CREATE, ctx->pip); + rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_SHADER, RV_RENDER_OBJ_OP_DESTROY, frag); // don't need anymore + rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_SHADER, RV_RENDER_OBJ_OP_DESTROY, vert); // don't need anymore + rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_BIND, ctx->pip); + rv_cmd_push_uniform_update(arena, create_commands, ctx->u_omit_tex, {.v_f32 = 1.0f}); + rv_cmd_push_uniform_update(arena, create_commands, ctx->u_resolution, {.v_vec2 = rv_v2(1,1)}); + rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_BIND, NULL); + + rv_vertex_cache_create(&ctx->vertex, ctx->vbo_vertex, sizeof(rv_vec2) + sizeof(rv_vec2) + sizeof(rv_vec4)); +} + +RV_GLOBAL void rvi2d_destroy(rv_idraw2d_ctx* ctx, rv_arena* arena, rv_command_list_t* destroy_commands) +{ + // just use something to check if we're already destroyed + if (ctx->pip) { + // make copies, the memory will be invalid otherwise + rv_vbo_t* vbo_vertex_copy = rv_push_copy(arena, rv_vbo_t, ctx->vbo_vertex); + rv_ibo_t* ibo_copy = rv_push_copy(arena, rv_ibo_t, ctx->ibo); + rv_pipeline_t* pip_copy = rv_push_copy(arena, rv_pipeline_t, ctx->ibo); + + rv_cmd_push_obj(arena, destroy_commands, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_DESTROY, vbo_vertex_copy); + rv_cmd_push_obj(arena, destroy_commands, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_DESTROY, pip_copy); + + rv_arena_release(ctx->arena); + rv_arena_release(ctx->permanent_arena); + rv_vertex_cache_destroy(&ctx->vertex); + + *ctx = (rv_idraw2d_ctx){0}; + } +} + +////////////////////////////////////////////////////////////////// +// draw + +RV_GLOBAL rv_command_t* rvi2d_draw_and_reset(rv_idraw2d_ctx* ctx, rv_arena* arena, rv_command_list_t* render_commands) +{ + rv_command_t* res = rvi2d_break(ctx); + + rv_command_t update_vertex = rv_vetex_cache_upload_and_reset(&ctx->vertex, arena); + rv_cmd_push_copy(arena, render_commands, update_vertex); + + rv_render_copy_commands(arena, render_commands, &ctx->commands); + + ctx->commands = (rv_command_list_t){0}; + ctx->last_commited_command = NULL; + rv_arena_clear(ctx->arena); + + return res; +} + +////////////////////////////////////////////////////////////////// +// core update functions + + +RV_GLOBAL rv_command_t* rvi2d_break(rv_idraw2d_ctx* ctx) +{ + if (ctx->vertex.current_count == 0) { + // nothing has been pushed since last break + return NULL; + } + + s64 vertex_count; + rv_command_t vertex_bind; + rv_vertex_cache_break(&ctx->vertex, &vertex_bind, &vertex_count); + + rv_command_t draw_command = { + .type = RV_COMMAND_DRAW, + .draw = { + .count = vertex_count * ctx->vertex.vertex_element_size, + }}; + + rv_cmd_push_copy(ctx->arena, &ctx->commands, vertex_bind); + rv_cmd_push_copy(ctx->arena, &ctx->commands, draw_command); + + rv_command_t* first; + if (ctx->last_commited_command == NULL) { + first = ctx->commands.first; + } else { + first = ctx->last_commited_command->next; + } + ctx->last_commited_command = ctx->commands.last; + + return first; +} + +RV_GLOBAL rv_command_t* rvi2d_camera(rv_idraw2d_ctx* ctx, rv_vec2 screen_size) +{ + rv_command_t* res = rvi2d_break(ctx); + rv_cmd_push_uniform_update(ctx->arena, &ctx->commands, ctx->u_resolution, {.v_vec2 = screen_size}); + return res; +} + +RV_GLOBAL rv_command_t* rvi2d_tex(rv_idraw2d_ctx* ctx, rv_texture_t* tex) +{ + rv_command_t* res = NULL; + if (!ctx->has_tex && !tex) { + // nothing to change + } else { + res = rvi2d_break(ctx); + + rv_cmd_push_uniform_update(ctx->arena, &ctx->commands, ctx->u_tex, {.v_tex = tex}); + + if (!ctx->has_tex && tex) { + rv_cmd_push_uniform_update(ctx->arena, &ctx->commands, ctx->u_omit_tex, {.v_f32 = 0.0f}); + } else if (ctx->has_tex && !tex) { + rv_cmd_push_uniform_update(ctx->arena, &ctx->commands, ctx->u_omit_tex, {.v_f32 = 1.0f}); + } + + ctx->has_tex = tex != NULL; + } + + return res; +} + +RV_GLOBAL rv_command_t* rvi2d_break_and_begin_custom_bindings(rv_idraw2d_ctx* ctx, rv_command_list_t* copy_binds) +{ + rv_command_t* res = rvi2d_break(ctx); + rv_render_copy_commands(ctx->arena, &ctx->commands, copy_binds); + return res; + +} + +RV_GLOBAL rv_command_t* rvi2d_break_and_insert_default_bindings(rv_idraw2d_ctx* ctx) +{ + rv_command_t* res = rvi2d_break(ctx); + rv_cmd_push_obj(ctx->arena, &ctx->commands, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_BIND, ctx->pip); + return res; +} + +RV_GLOBAL void rvi2d_triangle(rv_idraw2d_ctx* ctx, + rv_vec2 pos_a, rv_vec2 pos_b, rv_vec2 pos_c, + rv_vec2 uv_a, rv_vec2 uv_b, rv_vec2 uv_c, + rv_color_t color) +{ + rv_vec4 c = rv_vec4_from_color(color); + struct { + rv_vec2 pos, uv; + rv_vec4 color; + } copy_from[3] = { + {.pos = pos_a, .uv = uv_a, .color = c}, + {.pos = pos_b, .uv = uv_b, .color = c}, + {.pos = pos_c, .uv = uv_c, .color = c}, + }; + rv_vertex_cache_push(&ctx->vertex, &copy_from[0]); + rv_vertex_cache_push(&ctx->vertex, &copy_from[1]); + rv_vertex_cache_push(&ctx->vertex, &copy_from[2]); +} + +RV_GLOBAL void rvi2d_quad(rv_idraw2d_ctx* ctx, + rv_vec2 pos_tl, rv_vec2 pos_tr, rv_vec2 pos_bl, rv_vec2 pos_br, + rv_vec2 uv_tl, rv_vec2 uv_tr, rv_vec2 uv_bl, rv_vec2 uv_br, + rv_color_t color) +{ + rvi2d_triangle(ctx, pos_tl, pos_tr, pos_bl, + uv_tl, uv_tr, uv_bl, color); + rvi2d_triangle(ctx, pos_tr, pos_bl, pos_br, + uv_tr, uv_bl, uv_br, color); +} + +////////////////////////////////////////////////////////////////// +// helpers functions + +#define rvi2d_triangle_no_uv(ctx, pos_a, pos_b, pos_c, color) \ + rvi2d_triangle(ctx, pos_a, pos_b, pos_c, rv_v2s(0,0), rv_v2s(0,0), rv_v2s(0,0), color) +#define rvi2d_quad_default_uv(ctx, pos_tl, pos_tr, pos_bl, pos_br, color)\ + rvi2d_quad(ctx, pos_tl, pos_tr, pos_bl, pos_br,\ + rv_v2s(1,0), rv_v2s(1,1), rv_v2s(0,0), rv_v2s(0,1),\ + color) +#define rvi2d_quad_yflipped_uv(ctx, pos_tl, pos_tr, pos_bl, pos_br, color)\ + rvi2d_quad(ctx, pos_tl, pos_tr, pos_bl, pos_br,\ + rv_v2s(0,0), rv_v2s(0,1), rv_v2s(1,0), rv_v2s(1,1),\ + color) + +RV_GLOBAL void rvi2d_rect(rv_idraw2d_ctx* ctx, rv_rect rect, rv_color_t color) +{ + rvi2d_quad(ctx, + rv_vec2_add(rect.xy, rv_v2(0, 0)), + rv_vec2_add(rect.xy, rv_v2(rect.w, 0)), + rv_vec2_add(rect.xy, rv_v2(0, rect.h)), + rv_vec2_add(rect.xy, rv_v2(rect.w, rect.h)), + rv_v2(0,0), rv_v2(1,0), rv_v2(0,1), rv_v2(1,1), color); +} +RV_GLOBAL void rvi2d_line(rv_idraw2d_ctx* ctx, rv_vec2 a, rv_vec2 b, f32 thickness, rv_color_t color) +{ + rv_unreachable(); // not implemented +} +RV_GLOBAL void rvi2d_circle(rv_idraw2d_ctx* ctx, rv_vec2 center, f32 radius, s32 segments) +{ + rv_unreachable(); // not implemented +} diff --git a/src/render/render_idraw2d.h b/src/render/render_idraw2d.h @@ -0,0 +1,91 @@ +////////////////////////////////////////////////////////////////// +// render_idraw2d.h +// +// TODO(Samdal): +// Make a stack for custom bindings, this would make it easy to auto-bind pipelines +// rvid_push_custom_binds() +// rvid_pop_custom_binds() +// Use an index buffer (?) +// Implement line drawing +// Implement circle drawing + +////////////////////////////////////////////////////////////////// +// types + +typedef struct { + rv_arena* permanent_arena; + rv_arena* arena; + + // gpu objects + rv_vbo_t* vbo_vertex; + rv_ibo_t* ibo; + rv_pipeline_t* pip; + rv_uniform_t* u_resolution; + rv_uniform_t* u_tex; + rv_uniform_t* u_omit_tex; + + // state + rv_vertex_cache_ctx_t vertex; + rv_command_list_t commands; + rv_command_t* last_commited_command; + bool32 has_tex; +} rv_idraw2d_ctx; + +////////////////////////////////////////////////////////////////// +// init + +RV_GLOBAL void rvi2d_create(rv_idraw2d_ctx* ctx, rv_arena* arena, rv_command_list_t* create_commands); +RV_GLOBAL void rvi2d_destroy(rv_idraw2d_ctx* ctx, rv_arena* arena, rv_command_list_t* destroy_commands); + +////////////////////////////////////////////////////////////////// +// draw + +RV_GLOBAL rv_command_t* rvi2d_draw_and_reset(rv_idraw2d_ctx* ctx, rv_arena* arena, rv_command_list_t* render_commands); + +////////////////////////////////////////////////////////////////// +// core update functions +// +// NOTE(Samdal): +// rvi2d_camera, rvi2d_tex and rvi2d_custom_bind(_clear) invalidates instancing +// +// NOTE(Samdal): +// fixed vertex layout ( you can add more binds if you want ): +// layout(location = 0) in vec2 a_pos; (bind index 0) +// layout(location = 1) in vec2 a_uv; (bind index 0) +// layout(location = 2) in vec4 a_color; (bind index 1) +// +// NOTE(Samdal): +// These functions return the first command if there was a break in the instancing. +// Otherwise they return NULL. + +RV_GLOBAL rv_command_t* rvi2d_break(rv_idraw2d_ctx* ctx); +RV_GLOBAL rv_command_t* rvi2d_camera(rv_idraw2d_ctx* ctx, rv_vec2 screen_size); +RV_GLOBAL rv_command_t* rvi2d_tex(rv_idraw2d_ctx* ctx, rv_texture_t* tex); +RV_GLOBAL rv_command_t* rvi2d_break_and_begin_custom_bindings(rv_idraw2d_ctx* ctx, rv_command_list_t* copy_binds); +RV_GLOBAL rv_command_t* rvi2d_break_and_begin_default_bindings(rv_idraw2d_ctx* ctx); +RV_GLOBAL void rvi2d_triangle(rv_idraw2d_ctx* ctx, + rv_vec2 pos_a, rv_vec2 pos_b, rv_vec2 pos_c, + rv_vec2 uv_a, rv_vec2 uv_b, rv_vec2 uv_c, + rv_color_t color); +RV_GLOBAL void rvi2d_quad(rv_idraw2d_ctx* ctx, + rv_vec2 pos_tl, rv_vec2 pos_tr, rv_vec2 pos_bl, rv_vec2 pos_br, + rv_vec2 uv_tl, rv_vec2 uv_tr, rv_vec2 uv_bl, rv_vec2 uv_br, + rv_color_t color); + +////////////////////////////////////////////////////////////////// +// helpers functions + +#define rvi2d_triangle_no_uv(ctx, pos_a, pos_b, pos_c, color) \ + rvi2d_triangle(ctx, pos_a, pos_b, pos_c, rv_v2s(0,0), rv_v2s(0,0), rv_v2s(0,0), color) +#define rvi2d_quad_default_uv(ctx, pos_tl, pos_tr, pos_bl, pos_br, color)\ + rvi2d_quad(ctx, pos_tl, pos_tr, pos_bl, pos_br,\ + rv_v2s(1,0), rv_v2s(1,1), rv_v2s(0,0), rv_v2s(0,1),\ + color) +#define rvi2d_quad_yflipped_uv(ctx, pos_tl, pos_tr, pos_bl, pos_br, color)\ + rvi2d_quad(ctx, pos_tl, pos_tr, pos_bl, pos_br,\ + rv_v2s(0,0), rv_v2s(0,1), rv_v2s(1,0), rv_v2s(1,1),\ + color) + +RV_GLOBAL void rvi2d_rect(rv_idraw2d_ctx* ctx, rv_rect rect, rv_color_t color); +RV_GLOBAL void rvi2d_line(rv_idraw2d_ctx* ctx, rv_vec2 a, rv_vec2 b, f32 thickness, rv_color_t color); +RV_GLOBAL void rvi2d_circle(rv_idraw2d_ctx* ctx, rv_vec2 center, f32 radius, s32 segments); diff --git a/src/render/render_inc.c b/src/render/render_inc.c @@ -4,6 +4,7 @@ #if RV_WIN_ENABLED #include "render_helpers.c" #include "render_vertex_cache.c" + #include "render_idraw2d.c" #if RV_RENDER_OPENGL #include "impl/opengl.c" diff --git a/src/render/render_inc.h b/src/render/render_inc.h @@ -4,4 +4,5 @@ #if RV_WIN_ENABLED #include "render.h" #include "render_vertex_cache.h" + #include "render_idraw2d.h" #endif // RV_WIN_ENABLED diff --git a/src/render/render_vertex_cache.c b/src/render/render_vertex_cache.c @@ -33,24 +33,24 @@ RV_GLOBAL void* rv_vertex_cache_push(rv_vertex_cache_ctx_t* vc, const void* new) rv_assert(vc->committed_vertecies_so_far == 0); } - vc->current_instance_count += 1; + vc->current_count += 1; vc->vertex_total_size += vc->vertex_element_size; return res; } -RV_GLOBAL void rv_vertex_cache_break(rv_vertex_cache_ctx_t* vc, rv_command_t* bind_out, s64* instances_out) { +RV_GLOBAL void rv_vertex_cache_break(rv_vertex_cache_ctx_t* vc, rv_command_t* bind_out, s64* count_out) { *bind_out = rv_cmd_obj(RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_BIND, vc->vbo); bind_out->obj.vbo_bind.base_offset = vc->committed_vertecies_so_far; - *instances_out = vc->current_instance_count; + *count_out = vc->current_count; rv_command_t res = {0}; - vc->committed_vertecies_so_far += vc->current_instance_count * vc->vertex_element_size; + vc->committed_vertecies_so_far += vc->current_count * vc->vertex_element_size; - vc->current_instance_count = 0; + vc->current_count = 0; } RV_GLOBAL rv_command_t rv_vetex_cache_upload_and_reset(rv_vertex_cache_ctx_t* vc, rv_arena* arena) @@ -68,7 +68,7 @@ RV_GLOBAL rv_command_t rv_vetex_cache_upload_and_reset(rv_vertex_cache_ctx_t* vc // reset rv_arena_clear(vc->vertex_arena); vc->vertex_begin = NULL; - vc->current_instance_count = 0; + vc->current_count = 0; vc->committed_vertecies_so_far = 0; vc->vertex_total_size = 0; diff --git a/src/render/render_vertex_cache.h b/src/render/render_vertex_cache.h @@ -8,7 +8,7 @@ typedef struct { rv_vbo_t* vbo; // construction - s64 current_instance_count; + s64 current_count; s64 committed_vertecies_so_far; // upload @@ -20,7 +20,7 @@ RV_GLOBAL void rv_vertex_cache_create(rv_vertex_cache_ctx_t* vc, rv_vbo_t* vbo, RV_GLOBAL void rv_vertex_cache_destroy(rv_vertex_cache_ctx_t* vc); RV_GLOBAL void* rv_vertex_cache_push(rv_vertex_cache_ctx_t* vc, const void* new); -RV_GLOBAL void rv_vertex_cache_break(rv_vertex_cache_ctx_t* vc, rv_command_t* bind_out, s64* instances_out); +RV_GLOBAL void rv_vertex_cache_break(rv_vertex_cache_ctx_t* vc, rv_command_t* bind_out, s64* count_out); // returns draw command with filled data RV_GLOBAL rv_command_t rv_vetex_cache_upload_and_reset(rv_vertex_cache_ctx_t* vc, rv_arena* arena);