vtgl

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

Commit: 06f92343a2176078d1cc750fa8904cf8f6287a0e
Parent: 0b2029e8c1e0a921d674643938032ad7965740f7
Author: Randy Palamar
Date:   Thu, 27 Jun 2024 23:11:37 -0600

draw to a secondary framebuffer

The use of this is analogous to using a retained cell matrix on
the CPU except this sucks way less.

Diffstat:
Mmain.c | 21++++++++++++++++++++-
Mterminal.c | 1-
Mutil.h | 6++++++
Mvtgl.c | 71++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
4 files changed, 86 insertions(+), 13 deletions(-)

diff --git a/main.c b/main.c @@ -151,6 +151,25 @@ init_window(Term *t) glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* NOTE: Scale up the size of the Render Framebuffer */ + t->gl.window_size.h *= FB_HEIGHT_SCALE; + + /* NOTE: Generate an oversized intermediate Framebuffer for rendering to */ + glGenFramebuffers(1, &t->gl.fb); + glBindFramebuffer(GL_FRAMEBUFFER, t->gl.fb); + + t->gl.fb_tex_unit = 1; + glActiveTexture(GL_TEXTURE0 + t->gl.fb_tex_unit); + glGenTextures(1, &t->gl.fb_tex); + glBindTexture(GL_TEXTURE_2D, t->gl.fb_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + t->gl.window_size.w, t->gl.window_size.h, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, t->gl.fb_tex, 0); } static u32 @@ -277,9 +296,9 @@ main(void) term.gl.dt = current_time - last_time; last_time = current_time; - glfwPollEvents(); do_terminal(&term, memory); glfwSwapBuffers(term.gl.window); + glfwPollEvents(); } return 0; diff --git a/terminal.c b/terminal.c @@ -98,7 +98,6 @@ cursor_step_column(Term *t, i32 step) if (t->cursor.row < t->size.h) return; t->cursor.row = t->size.h - 1; - /* TODO: scroll term up by a line */ } static void diff --git a/util.h b/util.h @@ -27,6 +27,10 @@ #define ISPRINT(c) BETWEEN((c), ' ', '~') +/* NOTE: Framebuffer Height is oversized by this to allow + * for text reflow without cpu side memory reallocations */ +#define FB_HEIGHT_SCALE 2 + /* NOTE: GLFW does not sequentially number keys so switch statement will never be optimized */ #define ENCODE_KEY(action, mod, key) (((action) << 24) | ((mod) << 16) | ((key) & 0xFFFF)) @@ -148,6 +152,8 @@ typedef struct { u32 vao, vbo; + u32 fb, fb_tex, fb_tex_unit; + u32 program; #define X(name) i32 name; union { diff --git a/vtgl.c b/vtgl.c @@ -25,6 +25,7 @@ static void clear_colour(void) { v4 cc = normalized_colour((Colour){.r = 64, .g = 64, .b = 64, .a = 255}); + //v4 cc = normalized_colour((Colour){.r = 20, .g = 20, .b = 20, .a = 255}); glClearColor(cc.r, cc.g, cc.b, cc.a); glClear(GL_COLOR_BUFFER_BIT); } @@ -33,7 +34,6 @@ static void resize(GLCtx *gl) { v2 ws = gl->window_size; - glViewport(0, 0, ws.w, ws.h); f32 pmat[4 * 4] = { 2.0 / ws.w, 0.0, 0.0, -1.0, 0.0, 2.0 / ws.h, 0.0, -1.0, @@ -72,10 +72,10 @@ update_uniforms(Term *t) t->gl.uniforms[i] = glGetUniformLocation(t->gl.program, uniform_names[i]); t->gl.flags &= ~UPDATE_UNIFORMS; - resize(&t->gl); - glUniform1i(t->gl.texslot, 0); update_font_textures(t); + + resize(&t->gl); } static i32 @@ -241,7 +241,7 @@ push_line(Term *t, Line *line, v2 start_pos) static void blit_lines(Term *t) { - size line_count = t->size.h - 1; + size line_count = t->size.h / FB_HEIGHT_SCALE - 1; CLAMP(line_count, 0, t->log_lines.filled); v2 cs = get_cell_size(t); /* TODO: handle case where widx has wrapped around */ @@ -266,13 +266,21 @@ fb_callback(GLFWwindow *win, i32 w, i32 h) { Term *t = glfwGetWindowUserPointer(win); t->gl.window_size.w = w; - t->gl.window_size.h = h; + t->gl.window_size.h = FB_HEIGHT_SCALE * h; t->gl.flags |= NEEDS_RESIZE; v2 cs = get_cell_size(t); t->size.w = (u32)(w / cs.w); - t->size.h = (u32)(h / cs.h); - os_set_term_size(t->child, t->size, w, h); + t->size.h = FB_HEIGHT_SCALE * (u32)(h / cs.h); + + /* NOTE: The terminal size still needs to have the correct number + * of rows so that terminal programs know how to size themselves. */ + os_set_term_size(t->child, (uv2){.w = t->size.w, .h = t->size.h/FB_HEIGHT_SCALE}, w, h); + + glActiveTexture(GL_TEXTURE0 + t->gl.fb_tex_unit); + glBindTexture(GL_TEXTURE_2D, t->gl.fb_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, t->gl.window_size.w, t->gl.window_size.h, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); } static void @@ -326,8 +334,6 @@ do_terminal(Term *t, Arena a) if (t->gl.flags & NEEDS_RESIZE) resize(&t->gl); - clear_colour(); - if (os_child_data_available(t->child)) { if (os_child_exited(t->child)) glfwSetWindowShouldClose(t->gl.window, GL_TRUE); @@ -340,16 +346,59 @@ do_terminal(Term *t, Arena a) /* TODO: think about only blitting update lines? */ (void)parsed_lines; } + + v2 ws = t->gl.window_size; + + /* NOTE: reset the camera/viewport */ + glBindFramebuffer(GL_FRAMEBUFFER, t->gl.fb); + clear_colour(); + glViewport(0, 0, ws.w, ws.h); blit_lines(t); + v2 cell_size = get_cell_size(t); + v2 cursor_pos = { + .x = t->cursor.col * cell_size.w, + .y = ws.h - cell_size.h * (t->cursor.row + 1), + }; + + v2 src_bl = { + .x = 0, + .y = ws.h / FB_HEIGHT_SCALE, + }; + v2 src_tr = { + .x = ws.w, + .y = src_bl.y + ws.h / FB_HEIGHT_SCALE, + }; + + if (t->cursor.row > t->size.h / FB_HEIGHT_SCALE) { + src_tr.y = cursor_pos.y + ws.h / FB_HEIGHT_SCALE; + src_bl.y = cursor_pos.y; + } + { s8 fps = s8alloc(&a, 64); fps.len = snprintf((char *)fps.data, fps.len, "%0.01f ms/f", t->gl.dt * 1e3); v2 ts = measure_text(&t->gl, fps, 1); v2 pos = { - .x = t->gl.window_size.w - ts.x - 10, - .y = t->gl.window_size.h - ts.y - 10 + .x = ws.w - ts.x - 10, + .y = src_tr.y - ts.y - 10 + }; + + Rect r = { + .pos = {.x = pos.x - 0.05 * ts.x, .y = pos.y - 0.25 * ts.y}, + .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); } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, t->gl.fb); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBlitFramebuffer(src_bl.x, src_bl.y, src_tr.x, src_tr.y, + 0, 0, ws.w, ws.h / FB_HEIGHT_SCALE, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + + //glActiveTexture(GL_TEXTURE0 + t->gl.fb_tex_unit); + //glBindTexture(GL_TEXTURE_2D, t->gl.fb_tex); }