commit a5ca020c10ea2ae58feaaf1a1e89472d2095bf73
parent efcc620832e3f1851f4798cf501c5f3305633eb7
Author: Samdal <samdal@protonmail.com>
Date: Mon, 24 Mar 2025 19:16:47 +0100
vertex cache
Diffstat:
17 files changed, 485 insertions(+), 148 deletions(-)
diff --git a/build.sh b/build.sh
@@ -14,6 +14,7 @@ do
"simple_texture") sources=(./examples/simple_texture.c) ;;
"hello_window") sources=(./examples/hello_window.c) ;;
"instancing") sources=(./examples/instancing.c) ;;
+ "vertex_cache") sources=(./examples/vertex_cache.c) ;;
esac
done
diff --git a/examples/vertex_cache.c b/examples/vertex_cache.c
@@ -0,0 +1,196 @@
+//////////////////////////////////////////////////////////////////
+// vertex_cache.c
+
+#include "revolver_inc.h"
+#include "revolver_inc.c"
+
+//////////////////////////////////////////////////////////////////
+// data
+
+f32 v_data[] = {
+ 0.00, 0.05, 1, 0, 0,
+ -0.05, -0.05, 0, 1, 0,
+ 0.05, -0.05, 0, 0, 1,
+};
+
+rv_str8 v_src = S("#version 330 core\n" rv_strify(
+ layout(location = 0) in vec2 a_pos;
+ layout(location = 1) in vec3 a_color;
+ layout(location = 2) in vec2 a_offset;
+ precision mediump float;
+ out vec3 f_color;
+ void main()
+ {
+ gl_Position = vec4(a_pos + a_offset, 0.0, 1.0);
+ f_color = a_color;
+ }
+));
+
+rv_str8 f_src = S("#version 330 core\n" rv_strify(
+ precision mediump float;
+ in vec3 f_color;
+ out vec4 frag_color;
+ void main()
+ {
+ frag_color = vec4(f_color, 1.0);
+ }
+));
+
+//////////////////////////////////////////////////////////////////
+
+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};
+
+ // make render objects
+ rv_vbo_t* vbo = rv_push_compound(arena, rv_vbo_t, {.data = v_data, .size = sizeof(v_data)});
+ rv_vbo_t* vboi = rv_push_compound(arena, rv_vbo_t, {.usage = RV_BUFFER_USAGE_DYNAMIC});
+ rv_shader_t* vertex = rv_push_compound(arena, rv_shader_t, {.source = v_src, .type = RV_SHADER_TYPE_VERTEX});
+ rv_shader_t* fragment = rv_push_compound(arena, rv_shader_t, {.source = f_src, .type = RV_SHADER_TYPE_FRAGMENT});
+ rv_pipeline_t* pip = rv_push(arena, rv_pipeline_t);
+
+ // construct pipeline
+ rv_pipeline_push_shader(arena, pip, vertex);
+ rv_pipeline_push_shader(arena, pip, fragment);
+ rv_pipeline_push_vattr(arena, pip, RV_VATTR_TYPE_FLOAT2, 0, 0);
+ rv_pipeline_push_vattr(arena, pip, RV_VATTR_TYPE_FLOAT3, 0, 0);
+ rv_pipeline_push_vattr(arena, pip, RV_VATTR_TYPE_FLOAT2, 1, 1);
+
+ // instructions to create render objects
+ rv_command_list_t create_instructions = {0};
+ rv_cmd_push_obj(arena, &create_instructions, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_CREATE, vbo);
+ rv_cmd_push_obj(arena, &create_instructions, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_CREATE, vboi);
+ rv_cmd_push_obj(arena, &create_instructions, RV_COMMAND_OBJ_SHADER, RV_RENDER_OBJ_OP_CREATE, vertex);
+ rv_cmd_push_obj(arena, &create_instructions, RV_COMMAND_OBJ_SHADER, RV_RENDER_OBJ_OP_CREATE, fragment);
+ rv_cmd_push_obj(arena, &create_instructions, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_CREATE, pip);
+
+ // copy over create commands
+ rv_render_pass_t* create_rpass = rv_render_push_render_pass(arena, &rpass_list);
+ rv_render_copy_commands(arena, &create_rpass->commands, &create_instructions);
+
+ // create vertex cache
+ rv_vertex_cache_ctx_t vc = {0};
+ rv_vertex_cache_create(&vc, // vertex cache context
+ vboi, // vertex buffer to change and bind
+ sizeof(rv_vec2) // element size
+ );
+
+ while(1) {
+ rv_temp_arena scratch = rv_scratch_begin(0, 0);
+
+ // pick renderpass
+ rv_render_pass_t* rpass = rv_render_push_render_pass(scratch.arena, &rpass_list);
+
+ // process events
+ for (rv_event_t* e = rv_get_events(scratch.arena, 0); e; e = e->next) {
+ if (e->type == RV_EVENT_WINDOW_CLOSE) {
+ if (e->window_close == window) {
+ goto exit_program;
+ }
+ }
+ if (e->type == RV_EVENT_WINDOW_RESIZE) {
+ // set viewport
+ rv_command_t* viewport = rv_cmd_push_type(scratch.arena, &rpass->commands, RV_COMMAND_SET_VIEWPORT);
+ viewport->viewport = rv_v4(.xy = {0, 0}, .zw = rv_window_size(window));
+ }
+ }
+
+
+ { // clear
+ rv_command_t* clear = rv_cmd_push_type(scratch.arena, &rpass->commands, RV_COMMAND_CLEAR);
+ rv_render_clear_desc_t* clear_desc = rv_render_push_clear_desc(scratch.arena, clear, RV_RENDER_CLEAR_FLAG_COLOR);
+ clear_desc->color_v = rv_v4(0.1, 0.1, 0.1, 1.0);
+ }
+
+ { // draw using vertex cache
+ // bind pipeline
+ rv_cmd_push_obj(arena, &rpass->commands, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_BIND, pip);
+ rv_cmd_push_obj(arena, &rpass->commands, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_BIND, vbo);
+
+ { // draw custom shader first time
+ { // Translation data
+ int32_t index = 0;
+ float offset = 0.1f;
+ s32 peak_to_peak = 9.0;
+ for (int32_t y = -peak_to_peak; y < peak_to_peak; y += 2) {
+ if (sinf(rv_time()*5) * 9.0f < y)
+ break;
+ for (int32_t x = -peak_to_peak; x < peak_to_peak; x += 2) {
+ if (cosf(rv_time()*5) * 9.0f < x)
+ break;
+ rv_vec2 vertex = {0};
+ vertex.x = (float)x / 10.0f + offset;
+ vertex.y = (float)y / 10.0f + offset;
+ rv_vertex_cache_push(&vc, &vertex);
+ index++;
+ }
+ }
+ }
+
+ // insert break, push draw command
+ rv_command_t bind_command;
+ rv_command_t draw_command = rv_cmd(RV_COMMAND_DRAW, {.draw.count = 3});
+
+ rv_vertex_cache_break(&vc, &bind_command, &draw_command.draw.instances);
+ bind_command.obj.vbo_bind.bind_index = 1;
+
+ if (draw_command.draw.instances) {
+ rv_cmd_push_copy(scratch.arena, &rpass->commands, bind_command);
+ rv_cmd_push_copy(scratch.arena, &rpass->commands, draw_command);
+ }
+ }
+
+ { // draw custom shader second time
+ { // Translation data
+ rv_vec2 extra_offset = rv_v2(sinf(rv_time()*10) * 0.05, cosf(rv_time()*10) * 0.05);
+ int32_t index = 0;
+ float offset = 0.1f;
+ s32 peak_to_peak = 10;
+ for (int32_t y = -peak_to_peak; y < peak_to_peak; y += 2) {
+ for (int32_t x = -peak_to_peak; x < peak_to_peak; x += 2) {
+ rv_vec2 vertex = {0};
+ vertex.x = (float)x / 10.0f + offset + extra_offset.x;
+ vertex.y = (float)y / 10.0f + offset + extra_offset.y;
+ rv_vertex_cache_push(&vc, &vertex);
+ index++;
+ }
+ }
+ }
+
+ // insert break, push draw command
+ rv_command_t bind_command;
+ rv_command_t draw_command = rv_cmd(RV_COMMAND_DRAW, {.draw.count = 3});
+
+ rv_vertex_cache_break(&vc, &bind_command, &draw_command.draw.instances);
+ bind_command.obj.vbo_bind.bind_index = 1;
+
+ if (draw_command.draw.instances) {
+ rv_cmd_push_copy(scratch.arena, &rpass->commands, bind_command);
+ rv_cmd_push_copy(scratch.arena, &rpass->commands, draw_command);
+ }
+ }
+
+ // unbind pipeline
+ rv_cmd_push_obj(arena, &rpass->commands, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_BIND, NULL);
+
+ {
+ // when done, upload all the data
+ // must be done before render commands of course
+ rv_command_t change_command = rv_vetex_cache_upload_and_reset(&vc, scratch.arena);
+ rv_cmd_insert_copy(scratch.arena, &rpass->commands, rpass->commands.first, change_command);
+ }
+ }
+
+ 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/src/platform/platform_arena.c b/src/platform/platform_arena.c
@@ -28,11 +28,11 @@ RV_INTERNAL void rv_arena_pop_to(rv_arena* arena, u64 pos) {
}
// Arena Push/Pop Helpers
-RV_INTERNAL void arena_clear(rv_arena* arena)
+RV_INTERNAL void rv_arena_clear(rv_arena* arena)
{
}
-RV_INTERNAL void arena_pop(rv_arena* arena, u64 amt)
+RV_INTERNAL void rv_arena_pop(rv_arena* arena, u64 amt)
{
}
@@ -92,8 +92,8 @@ rv_arena_alloc_(rv_arena_params* params) {
arena->free_size = 0;
arena->free_last = 0;
#endif
- RV_ASAN_POISOIN(base, commit_size);
- RV_ASAN_UNPOISOIN(base, RV_ARENA_HEADER_SIZE);
+ RV_ASAN_POISON(base, commit_size);
+ RV_ASAN_UNPOISON(base, RV_ARENA_HEADER_SIZE);
return arena;
}
@@ -111,6 +111,7 @@ rv_arena_release(rv_arena* arena)
RV_INTERNAL void*
rv_arena_push(rv_arena* arena, u64 size, u64 align)
{
+ rv_assert(align > 0);
rv_arena* current = arena->current;
u64 pos_pre = rv_align_pow2(current->pos, align);
u64 pos_pst = pos_pre + size;
@@ -129,7 +130,7 @@ rv_arena_push(rv_arena* arena, u64 size, u64 align)
arena->free_last = new_block->prev;
}
arena->free_size -= new_block->res_size;
- RV_ASAN_UNPOISOIN((u8*)new_block + RV_ARENA_HEADER_SIZE, new_block->res_size - RV_ARENA_HEADER_SIZE);
+ RV_ASAN_UNPOISON((u8*)new_block + RV_ARENA_HEADER_SIZE, new_block->res_size - RV_ARENA_HEADER_SIZE);
break;
}
}
@@ -175,7 +176,7 @@ rv_arena_push(rv_arena* arena, u64 size, u64 align)
if (current->cmt >= pos_pst) {
result = (u8 *)current+pos_pre;
current->pos = pos_pst;
- RV_ASAN_UNPOISOIN(result, size);
+ RV_ASAN_UNPOISON(result, size);
}
if (result == 0) {
@@ -205,7 +206,7 @@ rv_arena_pop_to(rv_arena* arena, u64 pos)
current->pos = RV_ARENA_HEADER_SIZE;
arena->free_size += current->res_size;
RV_STACK_PUSH_N(arena->free_last, current, prev);
- RV_ASAN_POISOIN((u8*)current + RV_ARENA_HEADER_SIZE, current->res_size - RV_ARENA_HEADER_SIZE);
+ RV_ASAN_POISON((u8*)current + RV_ARENA_HEADER_SIZE, current->res_size - RV_ARENA_HEADER_SIZE);
}
#else
for (rv_arena* prev = 0; current->base_pos >= big_pos; current = prev) {
@@ -216,7 +217,7 @@ rv_arena_pop_to(rv_arena* arena, u64 pos)
arena->current = current;
u64 new_pos = big_pos - current->base_pos;
rv_assert(new_pos <= current->pos);
- RV_ASAN_POISOIN((u8*)current + new_pos, (current->pos - new_pos));
+ RV_ASAN_POISON((u8*)current + new_pos, (current->pos - new_pos));
current->pos = new_pos;
}
diff --git a/src/platform/platform_arena.h b/src/platform/platform_arena.h
@@ -70,8 +70,8 @@ RV_INTERNAL u64 rv_arena_pos(rv_arena* arena);
RV_INTERNAL void rv_arena_pop_to(rv_arena* arena, u64 pos);
// Arena Push/Pop Helpers
-RV_INTERNAL void arena_clear(rv_arena* arena);
-RV_INTERNAL void arena_pop(rv_arena* arena, u64 amt);
+RV_INTERNAL void rv_arena_clear(rv_arena* arena);
+RV_INTERNAL void rv_arena_pop(rv_arena* arena, u64 amt);
// Temporary Arena Scopes
RV_INTERNAL rv_temp_arena rv_temp_begin(rv_arena* arena);
@@ -79,19 +79,19 @@ RV_INTERNAL void rv_temp_end(rv_temp_arena temp);
// Push Helper Macros
#define rv_push_no_zero_aligned(a, T, align) (T*)rv_arena_push((a), sizeof(T), (align))
-#define rv_push_no_zero(a, T) rv_push_no_zero_aligned(a, T, rv_max(8, rv_alignof(T)))
+#define rv_push_no_zero(a, T) rv_push_no_zero_aligned(a, T, rv_alignof(T))
#define rv_push_aligned(a, T, align) (T*)rv_mem_zero(rv_push_no_zero_aligned(a, T, align), sizeof(T))
-#define rv_push(a, T) rv_push_aligned(a, T, rv_max(8, rv_alignof(T)))
+#define rv_push(a, T) rv_push_aligned(a, T, rv_alignof(T))
-#define rv_push_compound(a, T, ...) rv_mem_copy(rv_push_no_zero_aligned(a, T, rv_max(8, rv_alignof(T))), &(T)__VA_ARGS__, sizeof(T))
-#define rv_push_copy(a, T, v) rv_mem_copy(rv_push_no_zero_aligned(a, T, rv_max(8, rv_alignof(T))), (T*)v, sizeof(T))
+#define rv_push_compound(a, T, ...) rv_mem_copy(rv_push_no_zero_aligned(a, T, rv_alignof(T)), &(T)__VA_ARGS__, sizeof(T))
+#define rv_push_copy(a, T, v) rv_mem_copy(rv_push_no_zero_aligned(a, T, rv_alignof(T)), (T*)v, sizeof(T))
#define rv_push_array_no_zero_aligned(a, T, c, align) (T*)rv_arena_push((a), sizeof(T)*(c), (align))
-#define rv_push_array_no_zero(a, T, c) rv_push_array_no_zero_aligned(a, T, c, rv_max(8, rv_alignof(T)))
+#define rv_push_array_no_zero(a, T, c) rv_push_array_no_zero_aligned(a, T, c, rv_alignof(T))
#define rv_push_array_aligned(a, T, c, align) (T*)rv_mem_zero(rv_push_array_no_zero_aligned(a, T, c, align), sizeof(T)*(c))
-#define rv_push_array(a, T, c) rv_push_array_aligned(a, T, c, rv_max(8, rv_alignof(T)))
+#define rv_push_array(a, T, c) rv_push_array_aligned(a, T, c, rv_alignof(T))
-#define rv_push_array_compound(a, T, c, ...) rv_mem_copy(rv_push_array_no_zero_aligned(a, T, c, rv_max(8, rv_alignof(T))), (T[c])__VA_ARGS__, sizeof(T)*c)
+#define rv_push_array_compound(a, T, c, ...) rv_mem_copy(rv_push_array_no_zero_aligned(a, T, c, rv_alignof(T)), (T[c])__VA_ARGS__, sizeof(T)*c)
// Scratch
RV_GLOBAL rv_arena* rv_scratch_begin_(rv_arena **conflicts, u64 count);
diff --git a/src/platform/platform_core.c b/src/platform/platform_core.c
@@ -13,34 +13,34 @@ RV_GLOBAL void rv_abort_msg_(const char* file, const char* func, s32 line_no, s3
vsnprintf(buf, sizeof(buf), message, args);
va_end(args);
- fprintf(stderr, "rv_abort[%d]: '%s' at %s:%d (%s)\n", exit_code, buf, file, line_no, func);
+ fprintf(stderr, "rv_abort[%d]: '%s' at %s:%d (%s)\n", exit_code, buf, file, line_no, func);
#if RV_OS_WINDOWS
- ExitProcess(exit_code);
+ ExitProcess(exit_code);
#else
- _exit(exit_code);
+ _exit(exit_code);
#endif
}
f64 rv_time(void) {
- f64 res = 0;
+ f64 res = 0;
#if RV_OS_WINDOWS
- s64 wintime;
- GetSystemTimeAsFileTime((FILETIME*)&wintime);
- wintime -= 116444736000000000i64; // 1jan1601 to 1jan1970
- res += wintime / 10000000i64; // seconds
- res += (wintime % 10000000i64 * 100) * 1e-9; // nano-seconds
+ s64 wintime;
+ GetSystemTimeAsFileTime((FILETIME*)&wintime);
+ wintime -= 116444736000000000i64; // 1jan1601 to 1jan1970
+ res += wintime / 10000000i64; // seconds
+ res += (wintime % 10000000i64 * 100) * 1e-9; // nano-seconds
#else
- struct timespec ts_now;
- clock_gettime(CLOCK_MONOTONIC, &ts_now);
+ struct timespec ts_now;
+ clock_gettime(CLOCK_MONOTONIC, &ts_now);
- res = ts_now.tv_sec;
- res += ts_now.tv_nsec * 1e-9;
+ res = ts_now.tv_sec;
+ res += ts_now.tv_nsec * 1e-9;
#endif
- return res;
+ return res;
}
//////////////////////////////////////////////////////////////////
@@ -48,26 +48,26 @@ f64 rv_time(void) {
RV_GLOBAL void* rv_mem_reserve(s64 size)
{
- void* result = NULL;
+ void* result = NULL;
#if RV_OS_WINDOWS
result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
#else
result = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, (off_t)0);
- if (result == MAP_FAILED) result = 0;
+ if (result == MAP_FAILED) result = 0;
#endif
- return result;
+ return result;
}
RV_GLOBAL void* rv_mem_reserve_large(s64 size)
{
- void* result = NULL;
+ void* result = NULL;
#if RV_OS_WINDOWS
- // we commit on reserve because windows
- result = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_LARGE_PAGES, PAGE_READWRITE);
+ // we commit on reserve because windows
+ result = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_LARGE_PAGES, PAGE_READWRITE);
#else
result = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB, -1, (off_t)0);
- if (result == MAP_FAILED) result = 0;
+ if (result == MAP_FAILED) result = 0;
#endif
- return result;
+ return result;
}
RV_GLOBAL void rv_mem_commit(void* ptr, s64 size)
@@ -108,104 +108,104 @@ RV_GLOBAL void rv_mem_release(void* ptr, s64 size)
RV_GLOBAL s64 rv_mem_get_page_size(bool32 is_large)
{
- s64 result = 0;
+ s64 result = 0;
#if RV_OS_WINDOWS
- if (is_large) {
- result = GetLargePageMinimum();
- } else {
- SYSTEM_INFO sysinfo = {0};
- GetSystemInfo(&sysinfo);
- result = sysinfo.dwPageSiz;
- }
+ if (is_large) {
+ result = GetLargePageMinimum();
+ } else {
+ SYSTEM_INFO sysinfo = {0};
+ GetSystemInfo(&sysinfo);
+ result = sysinfo.dwPageSiz;
+ }
#else
- if (is_large) {
- // I hate this
+ if (is_large) {
+ // I hate this
- rv_str8 filename = S("/proc/meminfo");
+ rv_str8 filename = S("/proc/meminfo");
- int readerr;
- long val;
+ int readerr;
+ long val;
- rv_temp_arena scratch = rv_scratch_begin(0, 0);
+ rv_temp_arena scratch = rv_scratch_begin(0, 0);
- char* buffer = rv_push_array(scratch.arena, char, KB(4));
- FILE* f = fopen("/proc/meminfo", "rb");
- if (f) {
- fread(buffer, KB(4) - 1, 1, f);
- }
- rv_str8 proc_meminfo = rv_str8_from_cstr(buffer);
+ char* buffer = rv_push_array(scratch.arena, char, KB(4));
+ FILE* f = fopen("/proc/meminfo", "rb");
+ if (f) {
+ fread(buffer, KB(4) - 1, 1, f);
+ }
+ rv_str8 proc_meminfo = rv_str8_from_cstr(buffer);
- rv_str8 tag = S("Hugepagesize:");
- rv_str8 found = rv_str8_skip(proc_meminfo, rv_str8_find_needle(proc_meminfo, 0, tag, 0) + tag.len);
+ rv_str8 tag = S("Hugepagesize:");
+ rv_str8 found = rv_str8_skip(proc_meminfo, rv_str8_find_needle(proc_meminfo, 0, tag, 0) + tag.len);
- // chop around number + byte size
- found = rv_str8_skip_chop_whitespace(found);
- found = rv_str8_prefix(found, rv_str8_find_needle(found, 0, S("\n"), 0));
+ // chop around number + byte size
+ found = rv_str8_skip_chop_whitespace(found);
+ found = rv_str8_prefix(found, rv_str8_find_needle(found, 0, S("\n"), 0));
- bool32 kb = rv_str8_find_needle(found, 0, S("kb"), rv_str8_match_flag_case_insensetive) < found.len;
- bool32 mb = rv_str8_find_needle(found, 0, S("mb"), rv_str8_match_flag_case_insensetive) < found.len;
- bool32 gb = rv_str8_find_needle(found, 0, S("gb"), rv_str8_match_flag_case_insensetive) < found.len;
+ bool32 kb = rv_str8_find_needle(found, 0, S("kb"), rv_str8_match_flag_case_insensetive) < found.len;
+ bool32 mb = rv_str8_find_needle(found, 0, S("mb"), rv_str8_match_flag_case_insensetive) < found.len;
+ bool32 gb = rv_str8_find_needle(found, 0, S("gb"), rv_str8_match_flag_case_insensetive) < found.len;
- // remove byte part
- found = rv_str8_prefix(found, rv_str8_find_needle(found, 0, S(" "), 0));
+ // remove byte part
+ found = rv_str8_prefix(found, rv_str8_find_needle(found, 0, S(" "), 0));
- result = rv_s64_from_str8(found, 10);
- if (kb) result = KB(result);
- if (mb) result = MB(result);
- if (gb) result = GB(result);
+ result = rv_s64_from_str8(found, 10);
+ if (kb) result = KB(result);
+ if (mb) result = MB(result);
+ if (gb) result = GB(result);
- rv_scratch_end(scratch);
- } else {
- result = getpagesize();
- }
+ rv_scratch_end(scratch);
+ } else {
+ result = getpagesize();
+ }
#endif
- return result;
+ return result;
}
RV_GLOBAL void* rv_mem_set(void* mem, u8 set_byte, s64 size)
{
- u8* bytes = mem;
- for (s64 i = 0; i < size; i++) {
- bytes[i] = set_byte;
- }
- return mem;
+ u8* bytes = mem;
+ for (s64 i = 0; i < size; i++) {
+ bytes[i] = set_byte;
+ }
+ return mem;
}
RV_GLOBAL void* rv_mem_zero(void* mem, s64 size)
{
- return rv_mem_set(mem, 0, size);
+ return rv_mem_set(mem, 0, size);
}
RV_GLOBAL void* rv_mem_copy(void* restrict target, const void* restrict source, s64 size)
{
- u8* t = target;
- const u8* s = source;
- for (s64 i = 0; i < size; i++) {
- t[i] = s[i];
- }
- return target;
+ u8* t = target;
+ const u8* s = source;
+ for (s64 i = 0; i < size; i++) {
+ t[i] = s[i];
+ }
+ return target;
}
RV_GLOBAL void* rv_mem_move(void* target, const void* source, s64 size)
{
- rv_temp_arena scratch = rv_scratch_begin(0, 0);
+ rv_temp_arena scratch = rv_scratch_begin(0, 0);
- u8* temp = rv_push_array_no_zero(scratch.arena, u8, size);
+ u8* temp = rv_push_array_no_zero(scratch.arena, u8, size);
- rv_mem_copy(temp, source, size);
- rv_mem_copy(target, temp, size);
+ rv_mem_copy(temp, source, size);
+ rv_mem_copy(target, temp, size);
- rv_scratch_end(scratch);
+ rv_scratch_end(scratch);
- return target;
+ return target;
}
#if !defined(RV_NO_GLOBAL_ALLOC)
-RV_GLOBAL void* rv_mem_global_alloc_nz_(s64 size, s64 align) { return malloc(size); }
+RV_GLOBAL void* rv_mem_global_alloc_nz_(s64 size, s64 align) { return malloc(size); }
RV_GLOBAL void* rv_mem_global_realloc_nz_(void* old_ptr, s64 new_size, s64 align) { return realloc(old_ptr, new_size);}
-RV_GLOBAL void rv_mem_global_alloc_free(void* ptr, s64 size) { free(ptr); }
+RV_GLOBAL void rv_mem_global_alloc_free(void* ptr, s64 size) { free(ptr); }
#endif
diff --git a/src/platform/platform_core.h b/src/platform/platform_core.h
@@ -86,7 +86,7 @@
#define rv_align_pow2(x,b) (((x) + (b) - 1)&(~((b) - 1)))
-#define rv_array_size(__ARR) sizeof(__ARR) / sizeof(__ARR[0])
+#define rv_array_size(...) sizeof(__VA_ARGS__) / sizeof(__VA_ARGS__[0])
#define rv_abort(exit_code) rv_abort_msg_(__FILE__, __FUNCTION__, __LINE__, exit_code, "(no message)")
#define rv_abort_msg(exit_code, ...) rv_abort_msg_(__FILE__, __FUNCTION__, __LINE__, exit_code, __VA_ARGS__)
diff --git a/src/platform/platform_cracker.h b/src/platform/platform_cracker.h
@@ -28,8 +28,8 @@
// Address sanitizer:
// RV_ASAN_ENABLED
// RV_NO_ASAN
-// RV_ASAN_POISOIN(ptr, sz)
-// RV_ASAN_UNPOISOIN(ptr, sz)
+// RV_ASAN_POISON(ptr, sz)
+// RV_ASAN_UNPOISON(ptr, sz)
//
// Language:
// RV_LANG_C
@@ -410,11 +410,11 @@
#if RV_ASAN_ENABLED
#include <sanitizer/asan_interface.h>
- #define RV_ASAN_POISOIN(ptr, sz) ASAN_POISON_MEMORY_REGION(ptr, sz)
- #define RV_ASAN_UNPOISOIN(ptr, sz) ASAN_UNPOISON_MEMORY_REGION(ptr, sz)
+ #define RV_ASAN_POISON(ptr, sz) ASAN_POISON_MEMORY_REGION(ptr, sz)
+ #define RV_ASAN_UNPOISON(ptr, sz) ASAN_UNPOISON_MEMORY_REGION(ptr, sz)
#else
- #define RV_ASAN_POISOIN(ptr, sz) ((void)(ptr), (void)(sz))
- #define RV_ASAN_UNPOISOIN(ptr, sz) ((void)(ptr), (void)(sz))
+ #define RV_ASAN_POISON(ptr, sz) ((void)(ptr), (void)(sz))
+ #define RV_ASAN_UNPOISON(ptr, sz) ((void)(ptr), (void)(sz))
#endif
#if RV_COMPILER_CL
@@ -425,7 +425,8 @@
#define rv_alignof(T) __alignof__(T)
#else
#if !defined(rv_alignof)
- #error alignof not defined for this compiler, try doing '#define rv_alignof(T) _Alignof(T)'
+ #define rv_alignof(T) offsetof(struct {char _; T z;}, z)
+ #warning alignof not defined for this compiler, try doing '#define rv_alignof(T) _Alignof(T)'?
#endif
#endif
diff --git a/src/platform/platform_math.c b/src/platform/platform_math.c
@@ -482,15 +482,13 @@ RV_GLOBAL rv_mat4 rv_mat4_mul(rv_mat4 m0, rv_mat4 m1)
return m_res;
}
-RV_GLOBAL rv_mat4 rv_mat4_mul_list(s32 count, ...)
+RV_GLOBAL rv_mat4 rv_mat4_mul_list_(rv_mat4* m_list, s64 m_list_len)
{
va_list ap;
rv_mat4 m = rv_mat4_identity();
- va_start(ap, count);
- for (uint32_t i = 0; i < count; ++i) {
- m = rv_mat4_mul(m, va_arg(ap, rv_mat4));
+ for (uint32_t i = 0; i < m_list_len; ++i) {
+ m = rv_mat4_mul(m, m_list[i]);
}
- va_end(ap);
return m;
}
RV_GLOBAL rv_vec4 rv_mat4_mul_vec4(rv_mat4 m, rv_vec4 v)
@@ -584,7 +582,7 @@ RV_GLOBAL rv_mat4 rv_mat4_recompose(rv_vec3 translation, rv_vec3 rotation, rv_ve
rot[i] = rv_mat4_rotatev(rv_deg2rad(rotation.xyz[i]), direction_unary[i]);
}
- mat = rv_mat4_mul_list(3, rot[2], rot[1], rot[0]);
+ mat = rv_mat4_mul_list(rot[2], rot[1], rot[0]);
float valid_scale[3] = {0};
for (uint32_t i = 0; i < 3; ++i) {
diff --git a/src/platform/platform_math.h b/src/platform/platform_math.h
@@ -103,7 +103,8 @@ RV_GLOBAL rv_mat4 rv_mat4_inverse(rv_mat4 m);
RV_GLOBAL rv_mat4 rv_mat4_look_at(rv_vec3 position, rv_vec3 target, rv_vec3 up);
RV_GLOBAL rv_mat4 rv_mat4_mul(rv_mat4 m0, rv_mat4 m1);
-RV_GLOBAL rv_mat4 rv_mat4_mul_list(s32 count, ...);
+RV_GLOBAL rv_mat4 rv_mat4_mul_list_(rv_mat4* m_list, s64 m_list_len);
+#define rv_mat4_mul_list(...) rv_mat4_mul_list_((rv_mat4[]){__VA_ARGS__}, rv_array_size((rv_mat4[]){__VA_ARGS__}))
RV_GLOBAL rv_vec4 rv_mat4_mul_vec4(rv_mat4 m, rv_vec4 v);
RV_GLOBAL rv_vec3 rv_mat4_mul_vec3(rv_mat4 m, rv_vec3 v);
diff --git a/src/platform/platform_types.h b/src/platform/platform_types.h
@@ -96,7 +96,7 @@ typedef struct {
#define rv_v4(...) ((rv_vec4){__VA_ARGS__})
#define rv_v4s(...) (rv_vec4){(s), (s), (s), (s)})
-typedef struct gs_mat4 {
+typedef struct {
union {
rv_vec4 rows[4];
f32 m[4][4];
diff --git a/src/render/impl/opengl.c b/src/render/impl/opengl.c
@@ -177,36 +177,59 @@ RV_GLOBAL void rv_window_render_commit(rv_window_handle_t* window, rv_render_pas
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
- s64 stride = 0;
+ 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) {
- stride += rv_vattr_type_size(v->type);
+ 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);
}
}
- s64 offset = 0;
- s64 i = 0;
+ 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*)offset); break;
- case RV_VATTR_TYPE_FLOAT3: glVertexAttribPointer(i, 3, GL_FLOAT, GL_FALSE, stride, (void*)offset); break;
- case RV_VATTR_TYPE_FLOAT2: glVertexAttribPointer(i, 2, GL_FLOAT, GL_FALSE, stride, (void*)offset); break;
- case RV_VATTR_TYPE_FLOAT: glVertexAttribPointer(i, 1, GL_FLOAT, GL_FALSE, stride, (void*)offset); break;
- case RV_VATTR_TYPE_UINT4: glVertexAttribIPointer(i, 4, GL_UNSIGNED_INT, stride, (void*)offset); break;
- case RV_VATTR_TYPE_UINT3: glVertexAttribIPointer(i, 3, GL_UNSIGNED_INT, stride, (void*)offset); break;
- case RV_VATTR_TYPE_UINT2: glVertexAttribIPointer(i, 2, GL_UNSIGNED_INT, stride, (void*)offset); break;
- case RV_VATTR_TYPE_UINT: glVertexAttribIPointer(i, 1, GL_UNSIGNED_INT, stride, (void*)offset); break;
- case RV_VATTR_TYPE_BYTE: glVertexAttribPointer(i, 1, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)offset); break;
- case RV_VATTR_TYPE_BYTE2: glVertexAttribPointer(i, 2, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)offset); break;
- case RV_VATTR_TYPE_BYTE3: glVertexAttribPointer(i, 3, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)offset); break;
- case RV_VATTR_TYPE_BYTE4: glVertexAttribPointer(i, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)offset); break;
+ 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;
}
- offset += rv_vattr_type_size(v->type);
+ if (!found_override)
+ auto_offset += rv_vattr_type_size(v->type);
if (v->divisor) {
glVertexAttribDivisor(i, v->divisor);
}
diff --git a/src/render/render.h b/src/render/render.h
@@ -100,22 +100,20 @@ typedef enum {
RV_BUFFER_USAGE_STREAM,
} rv_buffer_usage_t;
-typedef struct rv_vbo_t rv_vbo_t;
-struct rv_vbo_t {
+typedef struct {
void* data;
s32 size;
rv_buffer_usage_t usage;
rv_render_handle_t handle;
-};
+} rv_vbo_t;
-typedef struct rv_ibo_t rv_ibo_t;
-struct rv_ibo_t {
+typedef struct {
void* data;
s32 size;
s32 elem_size;
rv_buffer_usage_t usage;
rv_render_handle_t handle;
-};
+} rv_ibo_t;
typedef enum {
RV_SHADER_TYPE_INVALID,
@@ -159,6 +157,15 @@ struct rv_vattr_t {
s32 divisor;
};
+typedef struct rv_vattr_bind_t rv_vattr_bind_t;
+struct rv_vattr_bind_t {
+ rv_vattr_bind_t* next;
+ rv_vattr_t* desc;
+ s32 offset;
+ s32 stride;
+ s32 divisor;
+};
+
typedef struct {
rv_uniform_t* uniform_desc_first;
rv_shader_node_t* shader_first;
@@ -250,7 +257,11 @@ struct rv_command_t {
struct {void* data; s64 size;} ibo_update;
struct {void* data; s64 size;} vbo_update;
- struct { s32 bind_index; } vbo_bind;
+ struct {
+ s32 bind_index;
+ s32 base_offset;
+ rv_vattr_bind_t* vattr_override_first;
+ } vbo_bind;
struct {
void* data;
@@ -326,17 +337,20 @@ RV_GLOBAL rv_command_t rv_cmd_type(rv_command_type_t type);
RV_GLOBAL rv_command_t rv_cmd_obj(rv_command_type_t type, rv_command_obj_operation_t operation, void* obj);
RV_GLOBAL rv_command_t rv_cmd_uniform_update(rv_uniform_t* uniform, rv_uniform_variant_t value);
-#define rv_cmd_push_type(arena, commands, _type) rv_cmd_copy(rv_cmd_push(arena, commands), rv_cmd_type(_type))
-#define rv_cmd_insert_type(arena, commands, after, _type) rv_cmd_copy(rv_cmd_insert(arena, commands, after), rv_cmd_type(_type))
+#define rv_cmd_push_copy(arena, commands, ...) rv_cmd_copy(rv_cmd_push(arena, commands), (rv_command_t)__VA_ARGS__)
+#define rv_cmd_insert_copy(arena, commands, after, ...) rv_cmd_copy(rv_cmd_insert(arena, commands, after), (rv_command_t)__VA_ARGS__)
+
+#define rv_cmd_push_type(arena, commands, _type) rv_cmd_copy(rv_cmd_push(arena, commands), rv_cmd_type(_type))
+#define rv_cmd_insert_type(arena, commands, after, _type) rv_cmd_copy(rv_cmd_insert(arena, commands, after), rv_cmd_type(_type))
-#define rv_cmd_push_compound(arena, commands, _type, ...) rv_cmd_copy(rv_cmd_push(arena, commands), rv_cmd(_type, __VA_ARGS__))
-#define rv_cmd_insert_compound(arena, commands, after, _type, ...) rv_cmd_copy(rv_cmd_insert(arena, commands, after), rv_cmd(_type, __VA_ARGS__))
+#define rv_cmd_push_compound(arena, commands, _type, ...) rv_cmd_copy(rv_cmd_push(arena, commands), rv_cmd(_type, __VA_ARGS__))
+#define rv_cmd_insert_compound(arena, commands, after, _type, ...) rv_cmd_copy(rv_cmd_insert(arena, commands, after), rv_cmd(_type, __VA_ARGS__))
-#define rv_cmd_push_obj(arena, commands, _type, op, obj) rv_cmd_copy(rv_cmd_push(arena, commands), rv_cmd_obj(_type, op, obj))
-#define rv_cmd_insert_obj(arena, commands, after, _type, op, obj) rv_cmd_copy(rv_cmd_insert(arena, commands, after), rv_cmd_obj(_type, op, obj))
+#define rv_cmd_push_obj(arena, commands, _type, op, obj) rv_cmd_copy(rv_cmd_push(arena, commands), rv_cmd_obj(_type, op, obj))
+#define rv_cmd_insert_obj(arena, commands, after, _type, op, obj) rv_cmd_copy(rv_cmd_insert(arena, commands, after), rv_cmd_obj(_type, op, obj))
-#define rv_cmd_push_uniform_update(arena, commands, uniform, ...) rv_cmd_copy(rv_cmd_push(arena, commands), rv_cmd_uniform_update(uniform, (rv_uniform_variant_t)__VA_ARGS__))
-#define rv_cmd_insert_uniform_update(arena, commands, after, uniform, ...) rv_cmd_copy(rv_cmd_insert(arena, commands, after), rv_cmd_uniform_update(uniform, (rv_uniform_variant_t)__VA_ARGS__))
+#define rv_cmd_push_uniform_update(arena, commands, uniform, ...) rv_cmd_copy(rv_cmd_push(arena, commands), rv_cmd_uniform_update(uniform, (rv_uniform_variant_t)__VA_ARGS__))
+#define rv_cmd_insert_uniform_update(arena, commands, after, uniform, ...) rv_cmd_copy(rv_cmd_insert(arena, commands, after), rv_cmd_uniform_update(uniform, (rv_uniform_variant_t)__VA_ARGS__))
// command modification helpers
RV_GLOBAL rv_render_clear_desc_t* rv_render_push_clear_desc(rv_arena* arena, rv_command_t* clear, rv_graphics_clear_flag_t flags);
diff --git a/src/render/render_imdraw.h b/src/render/render_imdraw.h
@@ -1,2 +0,0 @@
-//////////////////////////////////////////////////////////////////
-// render_imdraw.h
diff --git a/src/render/render_inc.c b/src/render/render_inc.c
@@ -3,6 +3,7 @@
#if RV_WIN_ENABLED
#include "render_helpers.c"
+ #include "render_vertex_cache.c"
#if RV_RENDER_OPENGL
#include "impl/opengl.c"
diff --git a/src/render/render_inc.h b/src/render/render_inc.h
@@ -3,4 +3,5 @@
#if RV_WIN_ENABLED
#include "render.h"
+ #include "render_vertex_cache.h"
#endif // RV_WIN_ENABLED
diff --git a/src/render/render_vertex_cache.c b/src/render/render_vertex_cache.c
@@ -0,0 +1,76 @@
+//////////////////////////////////////////////////////////////////
+// render_vertex_cache.c
+
+RV_GLOBAL void rv_vertex_cache_create(rv_vertex_cache_ctx_t* vc, rv_vbo_t* vbo, s64 vertex_element_size)
+{
+ rv_vertex_cache_destroy(vc);
+ vc->vertex_element_size = vertex_element_size;
+ vc->vbo = vbo;
+
+ // TODO(Samdal):
+ // Check if the arena implementation supports virtual paging first...
+ vc->vertex_arena = rv_arena_alloc(.flags = rv_arena_flag_no_chain, .reserve_size = GB(1));
+}
+
+RV_GLOBAL void rv_vertex_cache_destroy(rv_vertex_cache_ctx_t* vc)
+{
+ if (vc->vertex_arena) {
+ rv_arena_release(vc->vertex_arena);
+ }
+ *vc = (rv_vertex_cache_ctx_t){0};
+}
+
+RV_GLOBAL void* rv_vertex_cache_push(rv_vertex_cache_ctx_t* vc, const void* new)
+{
+ rv_assert(vc->vertex_element_size > 0);
+
+ void* res = rv_arena_push(vc->vertex_arena, vc->vertex_element_size, 1);
+ rv_mem_copy(res, new, vc->vertex_element_size);
+
+ if (!vc->vertex_begin) {
+ vc->vertex_begin = res;
+ rv_assert(vc->vertex_total_size == 0);
+ rv_assert(vc->committed_vertecies_so_far == 0);
+ }
+
+ vc->current_instance_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) {
+
+ *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;
+
+ rv_command_t res = {0};
+
+ vc->committed_vertecies_so_far += vc->current_instance_count * vc->vertex_element_size;
+
+ vc->current_instance_count = 0;
+}
+
+RV_GLOBAL rv_command_t rv_vetex_cache_upload_and_reset(rv_vertex_cache_ctx_t* vc, rv_arena* arena)
+{
+ // make and push command
+ rv_command_t update_cmd = rv_cmd_obj(RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_UPDATE, vc->vbo);
+
+ // copy over data into new arena
+
+ void* data = rv_arena_push(arena, vc->vertex_total_size, 8);
+ rv_mem_copy(data, vc->vertex_begin, vc->vertex_total_size);
+ update_cmd.obj.vbo_update.data = data;
+ update_cmd.obj.vbo_update.size = vc->vertex_total_size;
+
+ // reset
+ rv_arena_clear(vc->vertex_arena);
+ vc->vertex_begin = NULL;
+ vc->current_instance_count = 0;
+ vc->committed_vertecies_so_far = 0;
+ vc->vertex_total_size = 0;
+
+ return update_cmd;
+}
diff --git a/src/render/render_vertex_cache.h b/src/render/render_vertex_cache.h
@@ -0,0 +1,26 @@
+//////////////////////////////////////////////////////////////////
+// render_vertex_cache.h
+
+typedef struct {
+ // initialization
+ s64 vertex_element_size;
+ rv_arena* vertex_arena;
+ rv_vbo_t* vbo;
+
+ // construction
+ s64 current_instance_count;
+ s64 committed_vertecies_so_far;
+
+ // upload
+ void* vertex_begin;
+ s64 vertex_total_size;
+} rv_vertex_cache_ctx_t;
+
+RV_GLOBAL void rv_vertex_cache_create(rv_vertex_cache_ctx_t* vc, rv_vbo_t* vbo, s64 vertex_element_size);
+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);
+// 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);