Commit: ba72915bf16cf84ddee5f940c76783e64318d191
Parent: 953277e43538e5d3e19699ee27917982e90edd36
Author: Randy Palamar
Date: Tue, 22 Apr 2025 20:35:48 -0600
core: refactor shader reloading and terminal initialization
Terminal initialization only happens once so it doesn't need to be
in the debug shared library.
shader reloading needs a stable function pointer for the file
watch callback and thus needed to be partially moved out of the
the debug shared library.
Diffstat:
M | config.def.h | | | 2 | +- |
M | font.c | | | 8 | +++++++- |
M | os_linux_x11.c | | | 26 | +++++++++++++++++--------- |
M | terminal.c | | | 7 | ------- |
M | util.c | | | 19 | +++++++++++++++++-- |
M | util.h | | | 39 | +++++++++++++++++++++++++++++++-------- |
M | vtgl.c | | | 275 | ++++++++++++++++++++++++------------------------------------------------------- |
M | vtgl.h | | | 8 | ++------ |
A | vtgl_static.c | | | 88 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
9 files changed, 247 insertions(+), 225 deletions(-)
diff --git a/config.def.h b/config.def.h
@@ -33,7 +33,7 @@ global u8 g_tabstop = 8;
/* NOTE: number of blinks per second */
global f32 g_blink_speed = 1.0f;
-global s8 g_shader_path_prefix = s8("");
+#define SHADER_PATH_PREFIX ""
global Colour base16_colours[16] = {
[0] = { .rgba = 0x000000ff }, /* black */
diff --git a/font.c b/font.c
@@ -71,7 +71,6 @@ cached_glyph_to_uv(FontAtlas *fa, CachedGlyph *cg, v2 *start, v2 *end, v2 scale)
*end = (v2){.x = max_x * scale.x, .y = min_y * scale.y};
}
-
function void
recycle_cache(GlyphCache *gc)
{
@@ -396,6 +395,13 @@ font_atlas_update(FontAtlas *fa, iv2 glyph_bitmap_dim)
gc->glyphs[i].next_with_same_hash = i + 1;
}
+function v2
+fa_cell_size(FontAtlas *fa)
+{
+ v2 result = {.w = fa->info.w, .h = fa->info.h};
+ return result;
+}
+
function void
shift_font_sizes(FontAtlas *fa, i32 size_delta)
{
diff --git a/os_linux_x11.c b/os_linux_x11.c
@@ -25,14 +25,14 @@ i32 XPending(void *display);
#define DEBUG_LIB_NAME "./vtgl.so"
-#define LIB_FNS \
- X(vtgl_active_selection) \
- X(vtgl_initialize) \
- X(vtgl_handle_keys) \
- X(vtgl_frame_step)
-
-#define X(name) static name ## _fn *name;
-LIB_FNS
+#define DEBUG_LIB_FUNCTIONS \
+ X(vtgl_active_selection) \
+ X(vtgl_handle_keys) \
+ X(vtgl_frame_step) \
+ X(vtgl_render_thread_entry)
+
+#define X(name) global name ## _fn *name;
+DEBUG_LIB_FUNCTIONS
#undef X
function OS_FILE_WATCH_CALLBACK_FN(debug_reload_library)
@@ -59,7 +59,7 @@ function OS_FILE_WATCH_CALLBACK_FN(debug_reload_library)
name = dlsym(ctx->library_handle, #name); \
if (!name) stream_push_s8s(&ctx->error_stream, 3, (s8 []){s8("dlsym: "), \
c_str_to_s8(dlerror()), nl});
- LIB_FNS
+ DEBUG_LIB_FUNCTIONS
#undef X
stream_push_s8(&ctx->error_stream, s8("Reloaded Main Program\n"));
@@ -67,8 +67,16 @@ function OS_FILE_WATCH_CALLBACK_FN(debug_reload_library)
os_write_err_msg(stream_to_s8(&ctx->error_stream));
stream_reset(&ctx->error_stream, 0);
}
+
+#include "config.h"
+#include "font.c"
+
#endif /* _DEBUG */
+#define OS_PATH_SEPERATOR "/"
+
+#include "vtgl_static.c"
+
function void
glfw_error_callback(int code, const char *desc)
{
diff --git a/terminal.c b/terminal.c
@@ -157,13 +157,6 @@ feed_line(LineBuffer *lb, u8 *position, CellStyle cursor_state)
}
function void
-selection_clear(Selection *s)
-{
- s->range.end = INVALID_RANGE_END;
- s->state = SS_NONE;
-}
-
-function void
selection_scroll(Term *t, i32 origin, i32 n)
{
Selection *s = &t->selection;
diff --git a/util.c b/util.c
@@ -178,8 +178,8 @@ work_queue_insert(WorkQueue *q, WorkQueueWorkKind kind, void *ctx)
b32 result = 0;
/* NOTE(rnp): if we ever fill this up we need to resize the queue */
if (work) {
- work->kind = kind;
- work->ctx = ctx;
+ work->kind = kind;
+ work->generic = ctx;
work_queue_push_commit(q);
}
return result;
@@ -396,6 +396,14 @@ stream_reset(Stream *s, iz index)
if (!s->errors) s->count = index;
}
+function void
+stream_commit(Stream *s, iz count)
+{
+ s->errors |= !BETWEEN(s->count + count, 0, s->capacity);
+ if (!s->errors)
+ s->count += count;
+}
+
function Stream
stream_alloc(Arena *a, i32 capacity)
{
@@ -534,6 +542,13 @@ stream_push_f64(Stream *s, f64 f, i64 prec)
}
}
+function void
+selection_clear(Selection *s)
+{
+ s->range.end = INVALID_RANGE_END;
+ s->state = SS_NONE;
+}
+
function SelectionIterator
selection_iterator(Range s, Row *rows, u32 term_width)
{
diff --git a/util.h b/util.h
@@ -204,12 +204,18 @@ enum gl_flags {
DRAW_DEBUG_OVERLAY = 1 << 30,
};
-enum shader_stages {
- SHADER_RENDER,
- SHADER_RECTS,
- SHADER_POST,
- SHADER_COUNT
-};
+/* X(enumerant, file_name) */
+#define FRAGMENT_SHADERS \
+ X(RENDER, "frag_render") \
+ X(RECTS, "frag_for_rects") \
+ X(POST, "frag_post")
+
+typedef enum {
+ #define X(en, fn) SID_ ##en,
+ FRAGMENT_SHADERS
+ #undef X
+ SID_LAST
+} ShaderID;
typedef struct {
iv2 cell_size;
@@ -232,6 +238,9 @@ typedef struct {
u32 _pad[1];
} ShaderParameters;
+static_assert((sizeof(ShaderParameters) & 15) == 0,
+ "sizeof(ShaderParameters) must be a multiple of 16");
+
typedef struct {
iv2 window_size;
@@ -239,7 +248,7 @@ typedef struct {
u32 fb, fb_tex, fb_tex_unit;
- u32 programs[SHADER_COUNT];
+ ShaderID programs[SID_LAST];
#define X(name) i32 name;
struct { GL_POST_UNIFORMS } post;
#undef X
@@ -426,6 +435,8 @@ enum charsets {
CS_GRAPHIC0,
};
+typedef struct ShaderReloadContext ShaderReloadContext;
+
typedef struct RenderCtx {
RenderPushBuffer *rpb;
GLCtx *gl;
@@ -439,7 +450,10 @@ typedef enum {
} WorkQueueWorkKind;
typedef struct {
- void *ctx;
+ union {
+ void *generic;
+ ShaderReloadContext *shader_reload_context;
+ };
WorkQueueWorkKind kind;
} WorkQueueWork;
@@ -451,6 +465,13 @@ typedef struct {
WorkQueueWork items[1 << 6];
} WorkQueue;
+struct ShaderReloadContext {
+ WorkQueue *work_queue;
+ s8 path;
+ s8 label;
+ ShaderID shader;
+};
+
typedef struct {
GLCtx gl;
FontAtlas fa;
@@ -471,6 +492,8 @@ typedef struct {
Stream error_stream;
i32 *sync;
+
+ ShaderReloadContext shader_reload_contexts[SID_LAST];
} RenderThreadContext;
typedef enum {
diff --git a/vtgl.c b/vtgl.c
@@ -7,7 +7,6 @@
* [ ]: refactor: stream_ensure_terminator
* [ ]: refactor: push_s8 (into arena); rename push_s8 to draw_s8
* [ ]: refactor: resize renderer work item
- * [ ]: refactor: reload shader shouldn't need so much context
* [ ]: refactor: remove queued_render (just use a work item)
* [ ]: refactor: why is there interaction code in both threads?
*/
@@ -65,127 +64,105 @@ set_projection_matrix(GLCtx *gl, u32 stage)
}
function u32
-compile_shader(Arena a, u32 type, s8 shader)
+compile_shader(Arena a, u32 type, s8 shader, s8 name)
{
- u32 sid = glCreateShader(type);
-
- glShaderSource(sid, 1, (const char **)&shader.data, (int *)&shader.len);
- glCompileShader(sid);
+ u32 result = glCreateShader(type);
+ glShaderSource(result, 1, (const char **)&shader.data, (int *)&shader.len);
+ glCompileShader(result);
i32 res = 0;
- glGetShaderiv(sid, GL_COMPILE_STATUS, &res);
+ glGetShaderiv(result, GL_COMPILE_STATUS, &res);
if (res != GL_TRUE) {
- i32 len;
- glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &len);
- s8 err = s8alloc(&a, len);
- glGetShaderInfoLog(sid, len, (int *)&err.len, (char *)err.data);
- os_write_err_msg(s8("compile_shader: "));
- os_write_err_msg(err);
- glDeleteShader(sid);
- return 0;
+ Stream s = arena_stream(a);
+ stream_push_s8(&s, name);
+ stream_push_s8(&s, s8(": failed to compile\n"));
+
+ i32 len = 0, out_len = 0;
+ glGetShaderiv(result, GL_INFO_LOG_LENGTH, &len);
+ glGetShaderInfoLog(result, len, &out_len, (c8 *)(s.data + s.count));
+ stream_commit(&s, out_len);
+ os_write_err_msg(stream_to_s8(&s));
+ glDeleteShader(result);
+ result = 0;
}
- return sid;
+ return result;
}
function u32
-program_from_shader_text(s8 vertex, s8 fragment, Arena a)
+link_shader_program(Arena a, u32 *shader_ids, u32 shader_id_count)
{
- u32 pid = glCreateProgram();
-
- u32 vid = compile_shader(a, GL_VERTEX_SHADER, vertex);
- if (vid == 0) {
- glDeleteProgram(pid);
- return 0;
+ u32 result = glCreateProgram();
+ for (u32 i = 0; i < shader_id_count; i++)
+ glAttachShader(result, shader_ids[i]);
+ glLinkProgram(result);
+
+ i32 success = 0;
+ glGetProgramiv(result, GL_LINK_STATUS, &success);
+ if (success == GL_FALSE) {
+ Stream s = arena_stream(a);
+ stream_push_s8(&s, s8("shader link error: "));
+
+ i32 len = 0;
+ glGetProgramInfoLog(result, s.capacity - s.count, &len, (c8 *)(s.data + s.count));
+ stream_commit(&s, len);
+ os_write_err_msg(stream_to_s8(&s));
+ glDeleteProgram(result);
+ result = 0;
}
- u32 fid = compile_shader(a, GL_FRAGMENT_SHADER, fragment);
- if (fid == 0) {
- glDeleteShader(vid);
- glDeleteProgram(pid);
- return 0;
- }
+ return result;
+}
- glAttachShader(pid, vid);
- glAttachShader(pid, fid);
- glLinkProgram(pid);
- glValidateProgram(pid);
- glUseProgram(pid);
- glDeleteShader(vid);
- glDeleteShader(fid);
+function u32
+load_shader(Arena arena, s8 vertex_text, s8 fragment_text, s8 info, s8 label)
+{
+ u32 result = 0;
+ u32 vs_id = compile_shader(arena, GL_VERTEX_SHADER, vertex_text, s8("Vertex Shader"));
+ u32 fs_id = compile_shader(arena, GL_FRAGMENT_SHADER, fragment_text, info);
+
+ if (vs_id && fs_id) result = link_shader_program(arena, (u32 []){vs_id, fs_id}, 2);
+ glDeleteShader(vs_id);
+ glDeleteShader(fs_id);
+
+ if (result) {
+ Stream s = arena_stream(arena);
+ stream_push_s8(&s, s8("loaded: "));
+ stream_push_s8(&s, info);
+ stream_push_byte(&s, '\n');
+ os_write_err_msg(stream_to_s8(&s));
+ LABEL_GL_OBJECT(GL_PROGRAM, result, label);
+ }
- return pid;
+ return result;
}
-typedef struct {
- Term *t;
- u8 *path;
- s8 info;
- u32 stage;
-} queue_shader_reload_ctx;
-
function void
-update_uniforms(GLCtx *gl, enum shader_stages stage)
+update_uniforms(GLCtx *gl, ShaderID stage)
{
switch (stage) {
- case SHADER_RENDER:
- case SHADER_RECTS:
- break;
- case SHADER_POST:
+ case SID_POST: {
#define X(name) gl->post.name = glGetUniformLocation(gl->programs[stage], "u_" #name);
GL_POST_UNIFORMS
#undef X
- break;
- case SHADER_COUNT: ASSERT(0); break;
+ } break;
+ default: break;
}
}
function void
-reload_shader(GLCtx *gl, OS *os, u8 *path, u32 stage, s8 info, Arena a)
+reload_shader(OS *os, GLCtx *gl, ShaderReloadContext *src, Arena a)
{
- s8 fs_text = os->read_file(path, &a);
+ s8 fs_text = os->read_file(src->path.data, &a);
if (fs_text.len) {
- u32 program = program_from_shader_text(s8(VERTEX_SHADER_TEXT), fs_text, a);
- if (program) {
- glDeleteProgram(gl->programs[stage]);
- gl->programs[stage] = program;
- update_uniforms(gl, stage);
- set_projection_matrix(gl, stage);
+ u32 new_program = load_shader(a, s8(VERTEX_SHADER_TEXT), fs_text, src->path, src->label);
+ if (new_program) {
+ glDeleteProgram(gl->programs[src->shader]);
+ gl->programs[src->shader] = new_program;
+ update_uniforms(gl, src->shader);
+ set_projection_matrix(gl, src->shader);
}
}
- if (info.len) os_write_err_msg(info);
-}
-
-global s8 fs_name[SHADER_COUNT] = {
- [SHADER_RENDER] = s8("frag_render.glsl"),
- [SHADER_RECTS] = s8("frag_for_rects.glsl"),
- [SHADER_POST] = s8("frag_post.glsl"),
-};
-
-function void
-reload_all_shaders(GLCtx *gl, OS *os, Arena a)
-{
- Stream fs_path = stream_alloc(&a, KB(4));
- stream_push_s8(&fs_path, g_shader_path_prefix);
- if (fs_path.count && fs_path.data[fs_path.count - 1] != os->path_separator)
- stream_push_byte(&fs_path, os->path_separator);
-
- i32 sidx = fs_path.count;
- for (u32 i = 0; i < SHADER_COUNT; i++) {
- stream_push_s8(&fs_path, fs_name[i]);
- stream_push_byte(&fs_path, 0);
- reload_shader(gl, os, fs_path.data, i, (s8){0}, a);
- stream_reset(&fs_path, sidx);
- }
-
- os_write_err_msg(s8("Reloaded Shaders\n"));
-}
-
-function OS_FILE_WATCH_CALLBACK_FN(queue_shader_reload)
-{
- queue_shader_reload_ctx *ctx = user_ctx;
- ctx->path = path;
- work_queue_insert(&ctx->t->render_thread.work_queue, WQK_RELOAD_SHADER, ctx);
}
function v4
@@ -204,16 +181,9 @@ clear_colour(void)
}
function v2
-get_cell_size(FontAtlas *fa)
-{
- v2 result = {.w = fa->info.w, .h = fa->info.h};
- return result;
-}
-
-function v2
get_occupied_size(Term *t)
{
- v2 cs = get_cell_size(&t->render_thread.fa);
+ v2 cs = fa_cell_size(&t->render_thread.fa);
v2 result = {.x = t->size.w * cs.w, .y = t->size.h * cs.h};
return result;
}
@@ -236,7 +206,7 @@ resize_terminal(Term *t, OS *os, iv2 window_size)
ws.h -= 2 * g_term_margin.h;
iv2 old_size = t->size;
- v2 cs = get_cell_size(&t->render_thread.fa);
+ v2 cs = fa_cell_size(&t->render_thread.fa);
t->size.w = (i32)(ws.w / cs.w);
t->size.h = (i32)(ws.h / cs.h);
@@ -289,7 +259,7 @@ resize(Term *t, OS *os, iv2 window_size)
FontAtlas *fa = &t->render_thread.fa;
- v2 cs = get_cell_size(fa);
+ v2 cs = fa_cell_size(fa);
ShaderParameters *sp = &gl->shader_parameters;
sp->cell_size = (iv2){.w = cs.w, .h = cs.h};
sp->strike_min = (fa->info.baseline + 0.40 * fa->info.h);
@@ -303,7 +273,7 @@ resize(Term *t, OS *os, iv2 window_size)
sp->term_size_in_pixels = gl->window_size;
sp->term_size_in_cells = t->size;
- for (u32 i = 0; i < SHADER_COUNT; i++)
+ for (u32 i = 0; i < SID_LAST; i++)
set_projection_matrix(gl, i);
gl->flags &= ~RESIZE_RENDERER;
@@ -337,7 +307,7 @@ get_gpu_glyph_index(Arena a, GLCtx *gl, FontAtlas *fa, u32 codepoint, u32 font_i
CachedGlyph *cg = *out;
cg->uploaded_to_gpu = 1;
- v2 cell_size = get_cell_size(fa);
+ v2 cell_size = fa_cell_size(fa);
iv2 glyph_position = get_gpu_texture_position(cell_size, cg->gpu_tile_index);
ASSERT(glyph_position.x + cell_size.w * cg->tile_count < gl->glyph_bitmap_dim.x);
ASSERT(glyph_position.y + cell_size.h < gl->glyph_bitmap_dim.y);
@@ -596,7 +566,7 @@ render_cursor(Term *t, b32 focused, Arena a)
function iv2
mouse_to_cell_space(Term *t, v2 mouse)
{
- v2 cell_size = get_cell_size(&t->render_thread.fa);
+ v2 cell_size = fa_cell_size(&t->render_thread.fa);
v2 top_left = get_terminal_top_left(t);
iv2 result;
@@ -1096,11 +1066,13 @@ vtgl_render_frame(RenderThreadContext *ctx, TerminalMemory *memory, TerminalInpu
while (work) {
switch (work->kind) {
case WQK_RELOAD_SHADER: {
- queue_shader_reload_ctx *qsr = work->ctx;
- reload_shader(&ctx->gl, ctx->os, qsr->path, qsr->stage, qsr->info, arena);
+ reload_shader(ctx->os, &ctx->gl, work->shader_reload_context, arena);
} break;
case WQK_RELOAD_ALL_SHADERS: {
- reload_all_shaders(&ctx->gl, ctx->os, arena);
+ for (i32 i = 0; i < SID_LAST; i++) {
+ reload_shader(ctx->os, &ctx->gl, ctx->shader_reload_contexts + i,
+ arena);
+ }
} break;
default: INVALID_CODE_PATH;
}
@@ -1120,7 +1092,7 @@ vtgl_render_frame(RenderThreadContext *ctx, TerminalMemory *memory, TerminalInpu
RenderCtx rc = make_render_ctx(&arena, &ctx->gl, &ctx->fa);
- glUseProgram(ctx->gl.programs[SHADER_RENDER]);
+ glUseProgram(ctx->gl.programs[SID_RENDER]);
glBindFramebuffer(GL_FRAMEBUFFER, ctx->gl.fb);
clear_colour();
@@ -1162,7 +1134,7 @@ vtgl_render_frame(RenderThreadContext *ctx, TerminalMemory *memory, TerminalInpu
glBindFramebuffer(GL_FRAMEBUFFER, 0);
clear_colour();
- glUseProgram(ctx->gl.programs[SHADER_POST]);
+ glUseProgram(ctx->gl.programs[SID_POST]);
glUniform1i(ctx->gl.post.texslot, ctx->gl.fb_tex_unit);
glUniform1f(ctx->gl.post.param, param);
@@ -1172,7 +1144,7 @@ vtgl_render_frame(RenderThreadContext *ctx, TerminalMemory *memory, TerminalInpu
/* NOTE: this happens at the end so that ui stuff doesn't go through the post
* processing/effects shader */
- glUseProgram(ctx->gl.programs[SHADER_RECTS]);
+ glUseProgram(ctx->gl.programs[SID_RECTS]);
BEGIN_NAMED_BLOCK(debug_overlay);
draw_debug_overlay(memory, input, &rc);
@@ -1267,11 +1239,9 @@ vtgl_render_thread_initialize(RenderThreadContext *ctx)
LABEL_GL_OBJECT(GL_BUFFER, ctx->gl.render_shader_ubo, s8("ShaderParameters"));
glActiveTexture(GL_TEXTURE0);
-
- reload_all_shaders(&ctx->gl, ctx->os, a);
}
-function OS_THREAD_ENTRY_POINT_FN(vtgl_render_thread_entry)
+DEBUG_EXPORT OS_THREAD_ENTRY_POINT_FN(vtgl_render_thread_entry)
{
RenderThreadContext *ctx = (RenderThreadContext *)context;
@@ -1284,83 +1254,6 @@ function OS_THREAD_ENTRY_POINT_FN(vtgl_render_thread_entry)
return 0;
}
-DEBUG_EXPORT VTGL_INITIALIZE_FN(vtgl_initialize)
-{
- Term *t = (Term *)memory->memory;
- Arena a = {.beg = (u8 *)(t + 1), .end = memory->memory + memory->memory_size};
-
- t->os = &memory->os;
-
- t->cursor.state = CURSOR_NORMAL;
- cursor_reset(t);
-
- t->views[0].log = memory->os.allocate_ring_buffer(BACKLOG_SIZE);
- t->views[0].lines = line_buffer_alloc(&a, t->views[0].log.data, t->cursor.style, BACKLOG_LINES);
- t->views[1].log = memory->os.allocate_ring_buffer(ALT_BACKLOG_SIZE);
- t->views[1].lines = line_buffer_alloc(&a, t->views[1].log.data, t->cursor.style, ALT_BACKLOG_LINES);
-
- t->views[0].fb.backing_store = memory_block_from_arena(&a, MB(2));
- t->views[1].fb.backing_store = memory_block_from_arena(&a, MB(2));
-
- RenderThreadContext *rtc = &t->render_thread;
- rtc->arena = sub_arena(&a, MB(8));
- rtc->os = &memory->os;
- rtc->input = input;
- rtc->memory = memory;
- rtc->window = (iptr)window;
- rtc->monitor_size = monitor_size;
- rtc->gl.glyph_bitmap_dim = monitor_size;
-
- selection_clear(&t->selection);
-
- init_fonts(&rtc->fa, &a, rtc->gl.glyph_bitmap_dim);
- v2 cs = get_cell_size(&rtc->fa);
- iv2 requested_size = {
- .x = cs.x * requested_cells.x + 2 * g_term_margin.x,
- .y = cs.y * requested_cells.y + 2 * g_term_margin.y,
- };
- if (requested_cells.x < 0 || requested_cells.y < 0)
- requested_size = (iv2){.x = -1, .y = -1};
-
- memory->os.gl_make_context_current(0);
-
- rtc->sync = memory->os.spawn_thread(vtgl_render_thread_entry, "[render]", (iptr)rtc);
- t->size = (iv2){.x = 1, .y = 1};
- initialize_framebuffer(&t->views[0].fb, t->size);
- initialize_framebuffer(&t->views[1].fb, t->size);
-
- queue_shader_reload_ctx *reload_ctxs = alloc(&a, typeof(*reload_ctxs), SHADER_COUNT);
-
- s8 shader_infos[SHADER_COUNT] = {
- [SHADER_POST] = s8("Post Processing Shader Reloaded!\n"),
- [SHADER_RECTS] = s8("UI Shader Reloaded!\n"),
- [SHADER_RENDER] = s8("Render Shader Reloaded!\n"),
- };
-
- for (u32 i = 0; i < SHADER_COUNT; i++) {
- Stream path = arena_stream(a);
- stream_push_s8(&path, g_shader_path_prefix);
- if (path.count && path.data[path.count - 1] != memory->os.path_separator)
- stream_push_byte(&path, memory->os.path_separator);
-
- queue_shader_reload_ctx *src = reload_ctxs + i;
- src->info = shader_infos[i];
- src->stage = i;
- src->t = t;
- stream_push_s8(&path, fs_name[i]);
- stream_push_byte(&path, 0);
- memory->os.add_file_watch(path.data, queue_shader_reload, src);
- a.beg = path.data + path.count;
- }
-
- t->error_stream = stream_alloc(&a, KB(256));
- t->saved_title = stream_alloc(&a, KB(16));
- t->arena_for_frame = a;
- t->child = child;
-
- return requested_size;
-}
-
DEBUG_EXPORT VTGL_ACTIVE_SELECTION_FN(vtgl_active_selection)
{
Term *t = memory->memory;
diff --git a/vtgl.h b/vtgl.h
@@ -81,7 +81,7 @@ typedef size_t uz;
#define DEBUG_DECL(a) a
#else
#define ASSERT(c) do { (void)(c); } while(0)
-#define DEBUG_EXPORT static
+#define DEBUG_EXPORT function
#define DEBUG_DECL(a)
#endif
@@ -312,10 +312,6 @@ typedef struct TerminalMemory {
/************************************************************/
/* NOTE: functions provided by the terminal to the platform */
/************************************************************/
-#define VTGL_INITIALIZE_FN(name) iv2 name(TerminalMemory *memory, TerminalInput *input, void *window, \
- iptr child, iv2 requested_cells, iv2 monitor_size)
-typedef VTGL_INITIALIZE_FN(vtgl_initialize_fn);
-
#define VTGL_FRAME_STEP_FN(name) void name(TerminalMemory *memory, TerminalInput *input)
typedef VTGL_FRAME_STEP_FN(vtgl_frame_step_fn);
@@ -326,7 +322,7 @@ typedef VTGL_ACTIVE_SELECTION_FN(vtgl_active_selection_fn);
KeyboardKey key, ButtonAction action, InputModifier modifiers)
typedef VTGL_HANDLE_KEYS_FN(vtgl_handle_keys_fn);
-#include "os.h"
+typedef OS_THREAD_ENTRY_POINT_FN(vtgl_render_thread_entry_fn);
#include "util.h"
#include "util.c"
diff --git a/vtgl_static.c b/vtgl_static.c
@@ -0,0 +1,88 @@
+/* See LICENSE for copyright details */
+#include "vtgl.h"
+
+#define static_path_join(a, b) (a OS_PATH_SEPERATOR b)
+
+function OS_FILE_WATCH_CALLBACK_FN(queue_shader_reload)
+{
+ ShaderReloadContext *src = user_ctx;
+ WorkQueueWork *work = work_queue_push(src->work_queue);
+ if (work) {
+ work->kind = WQK_RELOAD_SHADER;
+ work->shader_reload_context = src;
+ work_queue_push_commit(src->work_queue);
+ }
+}
+
+function iv2
+vtgl_initialize(TerminalMemory *memory, TerminalInput *input, void *window, iptr child,
+ iv2 requested_cells, iv2 monitor_size)
+{
+ Term *t = (Term *)memory->memory;
+ Arena a = {.beg = (u8 *)(t + 1), .end = memory->memory + memory->memory_size};
+
+ t->os = &memory->os;
+
+ t->cursor.state = CURSOR_NORMAL;
+ t->cursor.style.fg = g_colours.data[g_colours.fgidx];
+ t->cursor.style.bg = g_colours.data[g_colours.bgidx];
+
+ t->views[0].log = memory->os.allocate_ring_buffer(BACKLOG_SIZE);
+ t->views[0].lines = line_buffer_alloc(&a, t->views[0].log.data, t->cursor.style, BACKLOG_LINES);
+ t->views[1].log = memory->os.allocate_ring_buffer(ALT_BACKLOG_SIZE);
+ t->views[1].lines = line_buffer_alloc(&a, t->views[1].log.data, t->cursor.style, ALT_BACKLOG_LINES);
+
+ t->views[0].fb.backing_store = memory_block_from_arena(&a, MB(2));
+ t->views[1].fb.backing_store = memory_block_from_arena(&a, MB(2));
+
+ selection_clear(&t->selection);
+
+ RenderThreadContext *rtc = &t->render_thread;
+ rtc->arena = sub_arena(&a, MB(8));
+ rtc->os = &memory->os;
+ rtc->input = input;
+ rtc->memory = memory;
+ rtc->window = (iptr)window;
+ rtc->monitor_size = monitor_size;
+ rtc->gl.glyph_bitmap_dim = monitor_size;
+
+ init_fonts(&rtc->fa, &a, rtc->gl.glyph_bitmap_dim);
+
+ v2 cs = fa_cell_size(&rtc->fa);
+ iv2 requested_size = {
+ .x = cs.x * requested_cells.x + 2 * g_term_margin.x,
+ .y = cs.y * requested_cells.y + 2 * g_term_margin.y,
+ };
+ if (requested_cells.x < 0 || requested_cells.y < 0)
+ requested_size = (iv2){.x = -1, .y = -1};
+
+ #define X(en, sn) do { \
+ ShaderReloadContext *src = rtc->shader_reload_contexts + SID_ ##en; \
+ src->work_queue = &rtc->work_queue; \
+ src->label = s8(#en); \
+ src->path = s8(static_path_join(SHADER_PATH_PREFIX, sn ".glsl")); \
+ src->shader = SID_ ## en; \
+ memory->os.add_file_watch(src->path.data, queue_shader_reload, src); \
+ } while (0);
+ FRAGMENT_SHADERS
+ #undef X
+
+ WorkQueueWork *work = work_queue_push(&rtc->work_queue);
+ if (work) {
+ work->kind = WQK_RELOAD_ALL_SHADERS;
+ work_queue_push_commit(&rtc->work_queue);
+ }
+
+ memory->os.gl_make_context_current(0);
+ rtc->sync = memory->os.spawn_thread(vtgl_render_thread_entry, "[render]", (iptr)rtc);
+
+ t->size = (iv2){.x = 1, .y = 1};
+ t->state |= TS_NEEDS_RESIZE;
+
+ t->error_stream = stream_alloc(&a, KB(256));
+ t->saved_title = stream_alloc(&a, KB(16));
+ t->arena_for_frame = a;
+ t->child = child;
+
+ return requested_size;
+}