commit f9d49901707dafcfc5ffe680b3b6631e6851d116
parent a5ca020c10ea2ae58feaaf1a1e89472d2095bf73
Author: Samdal <samdal@protonmail.com>
Date: Tue, 25 Mar 2025 01:57:39 +0100
idraw2d
Diffstat:
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, ©_from[0]);
+ rv_vertex_cache_push(&ctx->vertex, ©_from[1]);
+ rv_vertex_cache_push(&ctx->vertex, ©_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);