vtgl

terminal emulator implemented in OpenGL
git clone anongit@rnpnr.xyz:vtgl.git
Log | Files | Refs | Feed | LICENSE

Commit: 157e7b8e182a21d809e6578a0a88f2ee4ebb021d
Parent: 5396fbf72d4e7ed0384f542a8e4a34b61514b015
Author: Randy Palamar
Date:   Sat,  6 Jul 2024 16:25:19 -0600

use push buffer to reduce draw calls

Diffstat:
Mfrag_render.glsl | 6+++---
Mutil.h | 11+++++++++++
Mvert_render.glsl | 4++--
Mvtgl.c | 128++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
4 files changed, 90 insertions(+), 59 deletions(-)

diff --git a/frag_render.glsl b/frag_render.glsl @@ -8,9 +8,9 @@ in VS_OUT { } fs_in; uniform sampler2DArray u_texslot; -uniform int u_charmap[512]; -uniform vec2 u_texscale[512]; -uniform uvec2 u_texcolour[512]; +uniform int u_charmap[256]; +uniform vec2 u_texscale[256]; +uniform uvec2 u_texcolour[256]; void main() { diff --git a/util.h b/util.h @@ -202,6 +202,17 @@ typedef struct { Glyph *glyph_cache; } GLCtx; +/* NOTE: must match count in render shaders */ +#define PUSH_BUFFER_CAP 256 +typedef struct { + v2 vertscales[PUSH_BUFFER_CAP]; + v2 vertoffsets[PUSH_BUFFER_CAP]; + v2 texscales[PUSH_BUFFER_CAP]; + uv2 texcolours[PUSH_BUFFER_CAP]; + i32 charmap[PUSH_BUFFER_CAP]; + u32 count; +} RenderPushBuffer; + #include <ft2build.h> #include FT_FREETYPE_H #include "util.c" diff --git a/vert_render.glsl b/vert_render.glsl @@ -3,8 +3,8 @@ in vec2 position; uniform mat4 u_Pmat; -uniform vec2 u_vertscale[512]; -uniform vec2 u_vertoff[512]; +uniform vec2 u_vertscale[256]; +uniform vec2 u_vertoff[256]; out VS_OUT { vec2 tex_coord; diff --git a/vtgl.c b/vtgl.c @@ -7,7 +7,7 @@ #include "util.h" #define MAX_FONT_SIZE 128.0f -#define TEXTURE_GLYPH_COUNT 128 +#define TEXTURE_GLYPH_COUNT PUSH_BUFFER_CAP #define X(name) "u_"#name, static char *render_uniform_names[] = { GL_RENDER_UNIFORMS }; @@ -118,19 +118,41 @@ measure_text(GLCtx *gl, s8 text, b32 monospaced) } static void -push_char(GLCtx *gl, v2 vertscale, v2 vertoff, v2 texscale, uv2 colours, i32 char_idx) +flush_render_push_buffer(RenderPushBuffer *rpb, GLCtx *gl) { - glUniform2uiv(gl->render.texcolour, 1, colours.E); - glUniform1iv(gl->render.charmap, 1, &char_idx); - glUniform2fv(gl->render.texscale, 1, texscale.E); - glUniform2fv(gl->render.vertscale, 1, vertscale.E); - glUniform2fv(gl->render.vertoff, 1, vertoff.E); + glUniform2fv(gl->render.vertscale, rpb->count, (f32 *)rpb->vertscales); + glUniform2fv(gl->render.vertoff, rpb->count, (f32 *)rpb->vertoffsets); + glUniform2fv(gl->render.texscale, rpb->count, (f32 *)rpb->texscales); + glUniform2uiv(gl->render.texcolour, rpb->count, (u32 *)rpb->texcolours); + glUniform1iv(gl->render.charmap, rpb->count, (i32 *)rpb->charmap); + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, rpb->count); + rpb->count = 0; +} + +static u32 +get_render_push_buffer_idx(RenderPushBuffer *rpb, GLCtx *gl, u32 count) +{ + if (rpb->count + count > PUSH_BUFFER_CAP) + flush_render_push_buffer(rpb, gl); + u32 result = rpb->count; + rpb->count += count; + return result; +} - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, 1); +static void +push_char(RenderPushBuffer *rpb, GLCtx *gl, v2 vertscale, v2 vertoff, v2 texscale, + uv2 colours, i32 char_idx) +{ + u32 idx = get_render_push_buffer_idx(rpb, gl, 1); + rpb->vertscales[idx] = vertscale; + rpb->vertoffsets[idx] = vertoff; + rpb->texscales[idx] = texscale; + rpb->texcolours[idx] = colours; + rpb->charmap[idx] = char_idx; } static void -push_empty_cell_rect(Term *t, u32 minrow, u32 maxrow, u32 mincol, u32 maxcol) +push_empty_cell_rect(RenderPushBuffer *rpb, Term *t, u32 minrow, u32 maxrow, u32 mincol, u32 maxcol) { ASSERT(minrow <= maxrow && mincol <= maxcol); ASSERT(maxrow <= t->size.h && maxcol <= t->size.w); @@ -141,11 +163,11 @@ push_empty_cell_rect(Term *t, u32 minrow, u32 maxrow, u32 mincol, u32 maxcol) /* TODO: global colour table */ Colour colour = {.r = 20, .g = 20, .b = 20, .a = 255}; - push_char(&t->gl, size, pos, (v2){0}, (uv2){.y = colour.rgba}, 0); + push_char(rpb, &t->gl, size, pos, (v2){0}, (uv2){.y = colour.rgba}, 0); } static void -draw_text(GLCtx *gl, s8 text, v2 position, Colour colour, b32 monospaced) +draw_text(RenderPushBuffer *rpb, GLCtx *gl, s8 text, v2 position, Colour colour, b32 monospaced) { f32 single_space_width = gl->glyph_cache[0].size.w; for (size i = 0; i < text.len; i++) { @@ -157,73 +179,63 @@ draw_text(GLCtx *gl, s8 text, v2 position, Colour colour, b32 monospaced) v2 texscale = {.x = g.size.w / MAX_FONT_SIZE, .y = g.size.h / MAX_FONT_SIZE}; v2 vertscale = {.x = g.size.w, .y = g.size.h}; v2 vertoff = {.x = position.x + g.delta.x, .y = position.y + g.delta.y}; - push_char(gl, vertscale, vertoff, texscale, (uv2){.x = colour.rgba}, glyph_idx); + push_char(rpb, gl, vertscale, vertoff, texscale, (uv2){.x = colour.rgba}, glyph_idx); position.x += monospaced? single_space_width : g.size.w; } } static void -draw_rectangle(GLCtx *gl, Rect r, Colour colour) +draw_rectangle(RenderPushBuffer *rpb, GLCtx *gl, Rect r, Colour colour) { - push_char(gl, r.size, r.pos, (v2){0}, (uv2){.y = colour.rgba}, 0); + push_char(rpb, gl, r.size, r.pos, (v2){0}, (uv2){.y = colour.rgba}, 0); } static void -push_cell(GLCtx *gl, Cell c, Rect r, f32 font_text_dy) +push_cell(RenderPushBuffer *rpb, GLCtx *gl, Cell c, Rect r, f32 font_text_dy) { + u32 idx = get_render_push_buffer_idx(rpb, gl, 2); + Glyph g; i32 depth_idx = get_gpu_glyph_index(gl, c.cp, &g); - v2 texscale[2]; - texscale[0] = (v2){0}; - texscale[1] = (v2){ + rpb->vertscales[idx + 0] = r.size; + rpb->vertscales[idx + 1] = (v2){.x = g.size.w, .y = g.size.h}; + + rpb->vertoffsets[idx + 0] = r.pos; + rpb->vertoffsets[idx + 1] = (v2){ + .x = r.pos.x + g.delta.x, + .y = r.pos.y + g.delta.y + font_text_dy, + }; + + rpb->texscales[idx] = (v2){0}; + rpb->texscales[idx + 1] = (v2){ .x = g.size.w / MAX_FONT_SIZE, .y = g.size.h / MAX_FONT_SIZE, }; - i32 charmap[2]; - charmap[0] = depth_idx; - charmap[1] = depth_idx; - - v2 vertscale[2]; - v2 vertoff[2]; - vertscale[0] = r.size; - vertoff[0] = r.pos; - - v2 texture_position; - texture_position.x = r.pos.x + g.delta.x; - texture_position.y = r.pos.y + g.delta.y + font_text_dy; - - vertscale[1] = (v2){.x = g.size.w, .y = g.size.h}; - vertoff[1] = texture_position; - - u32 colours[4]; - colours[0] = (c.style.attr & ATTR_INVERSE)? c.style.bg.rgba : c.style.fg.rgba; - colours[1] = (c.style.attr & ATTR_INVERSE)? c.style.fg.rgba : c.style.bg.rgba; - colours[2] = (c.style.attr & ATTR_INVERSE)? c.style.bg.rgba : c.style.fg.rgba; - colours[3] = (c.style.attr & ATTR_INVERSE)? c.style.fg.rgba : c.style.bg.rgba; + rpb->charmap[idx + 0] = depth_idx; + rpb->charmap[idx + 1] = depth_idx; - if (c.style.attr & ATTR_FAINT) - c.style.fg.a = 0.5 * 255; + CellStyle cs = c.style; + /* TODO: what should this be if the cell is inverse? */ + if (cs.attr & ATTR_FAINT) + cs.fg.a = 0.5 * 255; - glUniform2uiv(gl->render.texcolour, 2, colours); - glUniform1iv(gl->render.charmap, 2, charmap); - glUniform2fv(gl->render.texscale, 2, (f32 *)texscale); - glUniform2fv(gl->render.vertscale, 2, (f32 *)vertscale); - glUniform2fv(gl->render.vertoff, 2, (f32 *)vertoff); - - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, 2); + rpb->texcolours[idx + 0].x = (cs.attr & ATTR_INVERSE)? cs.bg.rgba : cs.fg.rgba; + rpb->texcolours[idx + 0].y = (cs.attr & ATTR_INVERSE)? cs.fg.rgba : cs.bg.rgba; + rpb->texcolours[idx + 1].x = (cs.attr & ATTR_INVERSE)? cs.bg.rgba : cs.fg.rgba; + rpb->texcolours[idx + 1].y = (cs.attr & ATTR_INVERSE)? cs.fg.rgba : cs.bg.rgba; } static void -render_framebuffer(Term *t) +render_framebuffer(Term *t, RenderPushBuffer *rpb) { v2 cs = get_cell_size(t); Rect cr = {.pos = { .y = t->gl.window_size.h - cs.h }, .size = cs}; for (u32 r = 0; r < t->size.h; r++) { for (u32 c = 0; c < t->size.w; c++) { - push_cell(&t->gl, t->fb.rows[r][c], cr, t->fa.deltay); + push_cell(rpb, &t->gl, t->fb.rows[r][c], cr, t->fa.deltay); cr.pos.x += cs.w; } cr.pos.x = 0; @@ -322,11 +334,15 @@ init_callbacks(GLCtx *gl) DEBUG_EXPORT void do_terminal(Term *t, Arena a) { + static f32 last_frame_time; + f32 frame_start_time = (f32)glfwGetTime(); if (t->gl.flags & UPDATE_RENDER_UNIFORMS) update_uniforms(t, SHADER_RENDER); if (t->gl.flags & UPDATE_POST_UNIFORMS) update_uniforms(t, SHADER_POST); + RenderPushBuffer *rpb = alloc(&a, RenderPushBuffer, 1); + /* TODO: don't let the input splitting cause draw calls */ glUseProgram(t->gl.programs[SHADER_RENDER]); @@ -356,7 +372,7 @@ do_terminal(Term *t, Arena a) clear_colour(); blit_lines(t); - render_framebuffer(t); + render_framebuffer(t, rpb); v2 cell_size = get_cell_size(t); v2 cursor_pos = { @@ -374,7 +390,7 @@ do_terminal(Term *t, Arena a) { s8 fps = s8alloc(&a, 64); - fps.len = snprintf((char *)fps.data, fps.len, "%0.01f ms/f", t->gl.dt * 1e3); + fps.len = snprintf((char *)fps.data, fps.len, "Render Time: %0.02f ms/f", last_frame_time * 1e3); v2 ts = measure_text(&t->gl, fps, 1); v2 pos = { .x = ws.w - ts.x - 10, @@ -386,10 +402,12 @@ do_terminal(Term *t, Arena a) .size = {.w = ts.w * 1.1, .h = ts.h * 1.2}, }; - draw_rectangle(&t->gl, r, (Colour){.rgba = 0x303030ff}); - draw_text(&t->gl, fps, pos, (Colour){.rgba = 0x1e9e33ff}, 1); + draw_rectangle(rpb, &t->gl, r, (Colour){.rgba = 0x303030ff}); + draw_text(rpb, &t->gl, fps, pos, (Colour){.rgba = 0x1e9e33ff}, 1); } + flush_render_push_buffer(rpb, &t->gl); + static f32 param = 0; static f32 p_scale = 1; param += p_scale * 0.005 * t->gl.dt; @@ -404,4 +422,6 @@ do_terminal(Term *t, Arena a) glUniform1f(t->gl.post.param, param); glUniform2fv(t->gl.post.vertscale, 1, (f32 []){ws.w, ws.h}); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + last_frame_time = (f32)glfwGetTime() - frame_start_time; }