Commit: c59f7f3220bce900dc34675b20d20dc022c7b193
Parent: 7b84d363f59f92a8c04278366957a77dc90d03e2
Author: Randy Palamar
Date: Fri, 8 Nov 2024 08:02:45 -0700
move shader loading into the terminal code
Diffstat:
M | config.def.h | | | 2 | ++ |
M | os_unix.c | | | 59 | +++++++++++++++++++++++++---------------------------------- |
M | platform_linux_x11.c | | | 123 | ++++--------------------------------------------------------------------------- |
M | util.h | | | 13 | ++++++++++--- |
M | vtgl.c | | | 173 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- |
M | vtgl.h | | | 12 | ++++++++++++ |
6 files changed, 208 insertions(+), 174 deletions(-)
diff --git a/config.def.h b/config.def.h
@@ -30,6 +30,8 @@ static u8 g_tabstop = 8;
/* NOTE: number of blinks per second */
static f32 g_blink_speed = 1.0f;
+static s8 g_shader_path_prefix = s8("");
+
static Colour base16_colours[16] = {
[0] = { .rgba = 0x000000ff }, /* black */
[1] = { .rgba = 0xaa0000ff }, /* red */
diff --git a/os_unix.c b/os_unix.c
@@ -9,13 +9,7 @@
#include <time.h>
#include <unistd.h>
-typedef struct timespec os_filetime;
-typedef s8 os_mapped_file;
-
-typedef struct {
- size filesize;
- os_filetime timestamp;
-} os_file_stats;
+typedef s8 os_mapped_file;
typedef struct {
iptr handle;
@@ -64,18 +58,15 @@ os_new_arena(size cap)
return a;
}
-static os_file_stats
-os_get_file_stats(char *name)
-{
- struct stat sb = {0};
- stat(name, &sb);
- return (os_file_stats){.timestamp = sb.st_mtim, .filesize = sb.st_size};
-}
-
-static b32
-os_filetime_changed(os_filetime a, os_filetime b)
+static PLATFORM_GET_FILESTATS_FN(posix_get_file_stats)
{
- return ((a.tv_sec - b.tv_sec) + (a.tv_nsec - b.tv_nsec)) != 0;
+ struct stat sb = {0};
+ FileStats result = {0};
+ if (stat((c8 *)path, &sb) == 0) {
+ result.size = sb.st_size;
+ result.timestamp = sb.st_mtim.tv_sec * 1e9 + sb.st_mtim.tv_nsec;
+ }
+ return result;
}
static u32
@@ -133,21 +124,21 @@ static PLATFORM_READ_FN(posix_read)
return total_bytes_read;
}
-static s8
-os_read_file(Arena *a, char *name, size filesize)
+static PLATFORM_READ_FILE_FN(posix_read_file)
{
- i32 fd = open(name, O_RDONLY);
- if (fd < 0)
- return (s8){0};
-
- s8 text = s8alloc(a, filesize);
- size rlen = posix_read(fd, text, 0);
- close(fd);
+ i32 fd = open((c8 *)path, O_RDONLY);
+ buffer->errors |= fd < 0;
- if (text.len != rlen)
- return (s8){0};
+ if (!buffer->errors) {
+ s8 text = {.len = buffer->cap - buffer->widx, .data = buffer->buf + buffer->widx};
+ size rlen = posix_read(fd, text, 0);
+ close(fd);
+ buffer->errors |= text.len != rlen;
+ if (!buffer->errors)
+ buffer->widx += rlen;
+ }
- return text;
+ return !buffer->errors;
}
static os_mapped_file
@@ -161,13 +152,13 @@ os_map_file(char *path, i32 mode, i32 perm)
default: ASSERT(0);
}
- i32 fd = open(path, open_mode);
- os_file_stats fs = os_get_file_stats(path);
+ i32 fd = open(path, open_mode);
+ FileStats fs = posix_get_file_stats((u8 *)path);
if (fd != -1) {
- res.data = mmap(NULL, fs.filesize, mode, perm, fd, 0);
+ res.data = mmap(NULL, fs.size, mode, perm, fd, 0);
if (res.data != MAP_FAILED)
- res.len = fs.filesize;
+ res.len = fs.size;
close(fd);
}
diff --git a/platform_linux_x11.c b/platform_linux_x11.c
@@ -65,10 +65,10 @@ load_library(const char *lib, Stream *err)
static void
do_debug(TerminalMemory *t, Stream *err)
{
- static os_file_stats updated;
- os_file_stats test = os_get_file_stats(libname);
+ static FileStats updated;
+ FileStats test = posix_get_file_stats((u8 *)libname);
- if (os_filetime_changed(test.timestamp, updated.timestamp)) {
+ if (test.timestamp > updated.timestamp) {
updated = test;
/* NOTE: sucks but seems to be easiest reliable way to make sure lib is written */
struct timespec sleep_time = { .tv_nsec = 100e6 };
@@ -198,114 +198,6 @@ init_window(tmp_user_ctx *ctx, iv2 window_size)
return window;
}
-static u32
-compile_shader(Arena a, u32 type, s8 shader)
-{
- u32 sid = glCreateShader(type);
-
- glShaderSource(sid, 1, (const char **)&shader.data, (int *)&shader.len);
- glCompileShader(sid);
-
- i32 res = 0;
- glGetShaderiv(sid, 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;
- }
-
- return sid;
-}
-
-static u32
-program_from_shader_text(s8 vertex, s8 fragment, Arena a)
-{
- u32 pid = glCreateProgram();
-
- u32 vid = compile_shader(a, GL_VERTEX_SHADER, vertex);
- if (vid == 0) {
- glDeleteProgram(pid);
- return 0;
- }
-
- u32 fid = compile_shader(a, GL_FRAGMENT_SHADER, fragment);
- if (fid == 0) {
- glDeleteShader(vid);
- glDeleteProgram(pid);
- return 0;
- }
-
- glAttachShader(pid, vid);
- glAttachShader(pid, fid);
- glLinkProgram(pid);
- glValidateProgram(pid);
- glUseProgram(pid);
- glDeleteShader(vid);
- glDeleteShader(fid);
-
- return pid;
-}
-
-static void
-check_shaders(GLCtx *gl, Arena a, Stream *err)
-{
- static char *fs_name[SHADER_LAST] = {
- "frag_render.glsl",
- "frag_for_rects.glsl",
- "frag_post.glsl",
- };
- static char *vs_name[SHADER_LAST] = {
- "vert_for_rects.glsl",
- "vert_for_rects.glsl",
- "vert_for_rects.glsl",
- };
-
- static os_file_stats fs_stats[SHADER_LAST], vs_stats[SHADER_LAST];
- static struct {
- s8 name;
- u32 flag;
- } map[SHADER_LAST] = {
- [SHADER_RENDER] = {.name = s8("Render"), .flag = 0},
- [SHADER_RECTS] = {.name = s8("Rects"), .flag = 0},
- [SHADER_POST] = {.name = s8("Post"), .flag = UPDATE_POST_UNIFORMS},
- };
-
- for (u32 i = 0; i < SHADER_LAST; i++) {
- os_file_stats fs_test = os_get_file_stats(fs_name[i]);
- os_file_stats vs_test = os_get_file_stats(vs_name[i]);
-
- if (!os_filetime_changed(fs_test.timestamp, fs_stats[i].timestamp) &&
- !os_filetime_changed(vs_test.timestamp, vs_stats[i].timestamp))
- continue;
-
- stream_push_s8s(err, 3, (s8 []){s8("Reloading "), map[i].name, s8(" Shader!\n")});
- fs_stats[i] = fs_test;
- vs_stats[i] = vs_test;
-
- s8 vs_text = os_read_file(&a, vs_name[i], vs_stats[i].filesize);
- s8 fs_text = os_read_file(&a, fs_name[i], fs_stats[i].filesize);
- ASSERT(vs_text.len > 0 && fs_text.len > 0);
-
- u32 program = program_from_shader_text(vs_text, fs_text, a);
- if (!program)
- continue;
- glDeleteProgram(gl->programs[i]);
- gl->programs[i] = program;
- gl->flags |= map[i].flag;
- stream_push_s8s(err, 2, (s8 []){map[i].name, s8(" Program Updated!\n")});
- }
-
- if (err->widx) {
- os_write_err_msg(stream_to_s8(err));
- err->widx = 0;
- }
-}
-
static void
update_input(GLFWwindow *win, TerminalInput *input, posix_platform_process child)
{
@@ -406,10 +298,13 @@ main(i32 argc, char *argv[], char *envp[])
term_memory.platform_api.read = posix_read;
term_memory.platform_api.write = posix_write;
term_memory.platform_api.allocate_ring_buffer = posix_allocate_ring_buffer;
+ term_memory.platform_api.get_file_stats = posix_get_file_stats;
+ term_memory.platform_api.read_file = posix_read_file;
term_memory.platform_api.get_clipboard = x11_get_clipboard;
term_memory.platform_api.set_clipboard = x11_set_clipboard;
term_memory.platform_api.get_window_title = x11_get_window_title;
term_memory.platform_api.set_window_title = x11_set_window_title;
+ term_memory.platform_api.path_separator = '/';
Arena platform_arena = os_new_arena(2 * MEGABYTE);
Stream error_stream = stream_alloc(&platform_arena, MEGABYTE / 4);
@@ -535,12 +430,6 @@ main(i32 argc, char *argv[], char *envp[])
debug_begin_frame(&term_memory, input.dt);
- /* TODO: push this into the terminal */
- {
- Term *t = term_memory.memory;
- check_shaders(&t->gl, t->arena_for_frame, &error_stream);
- }
-
vtgl_frame_step(&term_memory, &input);
Range current_sel = vtgl_active_selection(&term_memory, 0);
diff --git a/util.h b/util.h
@@ -109,6 +109,11 @@ typedef struct {
b32 errors;
} Stream;
+typedef struct {
+ size size;
+ u64 timestamp;
+} FileStats;
+
#define INVALID_FILE (-1)
enum file_attribute {
FA_READ = 1 << 0,
@@ -220,7 +225,6 @@ enum gl_flags {
NEEDS_FULL_REFILL = 1 << 2,
UPDATE_RENDER_BUFFER = 1 << 3,
- UPDATE_POST_UNIFORMS = 1 << 29,
DRAW_DEBUG_OVERLAY = 1 << 30,
};
@@ -236,7 +240,7 @@ enum shader_stages {
SHADER_RENDER,
SHADER_RECTS,
SHADER_POST,
- SHADER_LAST
+ SHADER_COUNT
};
typedef struct {
@@ -268,11 +272,14 @@ typedef struct {
u32 fb, fb_tex, fb_tex_unit;
- u32 programs[SHADER_LAST];
+ u32 programs[SHADER_COUNT];
#define X(name) i32 name;
struct { GL_POST_UNIFORMS } post;
#undef X
+ /* TODO: os file watcher */
+ FileStats fs_stats[SHADER_COUNT], vs_stats[SHADER_COUNT];
+
_Alignas(16) ShaderParameters shader_parameters;
u32 render_shader_ubo;
u32 render_shader_ssbo;
diff --git a/vtgl.c b/vtgl.c
@@ -15,6 +15,158 @@ static u32 get_gpu_glyph_index(Arena, GLCtx *, FontAtlas *, u32, u32, enum face_
#define REVERSE_VIDEO_MASK (Colour){.r = 0xff, .g = 0xff, .b = 0xff}.rgba
+static u32
+compile_shader(Arena a, u32 type, s8 shader)
+{
+ u32 sid = glCreateShader(type);
+
+ glShaderSource(sid, 1, (const char **)&shader.data, (int *)&shader.len);
+ glCompileShader(sid);
+
+ i32 res = 0;
+ glGetShaderiv(sid, 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;
+ }
+
+ return sid;
+}
+
+static u32
+program_from_shader_text(s8 vertex, s8 fragment, Arena a)
+{
+ u32 pid = glCreateProgram();
+
+ u32 vid = compile_shader(a, GL_VERTEX_SHADER, vertex);
+ if (vid == 0) {
+ glDeleteProgram(pid);
+ return 0;
+ }
+
+ u32 fid = compile_shader(a, GL_FRAGMENT_SHADER, fragment);
+ if (fid == 0) {
+ glDeleteShader(vid);
+ glDeleteProgram(pid);
+ return 0;
+ }
+
+ glAttachShader(pid, vid);
+ glAttachShader(pid, fid);
+ glLinkProgram(pid);
+ glValidateProgram(pid);
+ glUseProgram(pid);
+ glDeleteShader(vid);
+ glDeleteShader(fid);
+
+ return pid;
+}
+
+static void
+update_uniforms(GLCtx *gl, enum shader_stages stage)
+{
+ switch (stage) {
+ case SHADER_RENDER:
+ case SHADER_RECTS:
+ break;
+ case SHADER_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;
+ }
+}
+
+static void
+check_shaders(GLCtx *gl, Arena a, Stream *err, PlatformAPI *platform)
+{
+ static s8 fs_name[SHADER_COUNT] = {
+ [SHADER_RENDER] = s8("frag_render.glsl"),
+ [SHADER_RECTS] = s8("frag_for_rects.glsl"),
+ [SHADER_POST] = s8("frag_post.glsl"),
+ };
+ static s8 vs_name[SHADER_COUNT] = {
+ [SHADER_RENDER] = s8("vert_for_rects.glsl"),
+ [SHADER_RECTS] = s8("vert_for_rects.glsl"),
+ [SHADER_POST] = s8("vert_for_rects.glsl"),
+ };
+
+ static struct {
+ s8 name;
+ b32 update_uniforms;
+ } map[SHADER_COUNT] = {
+ [SHADER_RENDER] = {.name = s8("Render"), .update_uniforms = 0},
+ [SHADER_RECTS] = {.name = s8("Rects"), .update_uniforms = 0},
+ [SHADER_POST] = {.name = s8("Post"), .update_uniforms = 1},
+ };
+
+ /* TODO: this is nasty but will be cleaned up with a file watcher */
+ Stream vs_path = stream_alloc(&a, 4 * KILOBYTE);
+ stream_push_s8(&vs_path, g_shader_path_prefix);
+ if (vs_path.widx && vs_path.buf[vs_path.widx - 1] != platform->path_separator)
+ stream_push_byte(&vs_path, platform->path_separator);
+
+ Stream fs_path = stream_alloc(&a, 4 * KILOBYTE);
+ stream_push_s8(&fs_path, g_shader_path_prefix);
+ if (fs_path.widx && fs_path.buf[fs_path.widx - 1] != platform->path_separator)
+ stream_push_byte(&fs_path, platform->path_separator);
+
+ u32 fs_sidx = fs_path.widx;
+ u32 vs_sidx = vs_path.widx;
+ for (u32 i = 0; i < SHADER_COUNT; i++) {
+ fs_path.widx = fs_sidx;
+ vs_path.widx = vs_sidx;
+
+ stream_push_s8(&fs_path, fs_name[i]);
+ stream_push_byte(&fs_path, 0);
+ stream_push_s8(&vs_path, vs_name[i]);
+ stream_push_byte(&vs_path, 0);
+
+ FileStats fs_test = platform->get_file_stats(fs_path.buf);
+ FileStats vs_test = platform->get_file_stats(vs_path.buf);
+
+ if ((fs_test.timestamp <= gl->fs_stats[i].timestamp) &&
+ (vs_test.timestamp <= gl->vs_stats[i].timestamp))
+ continue;
+
+ b32 success = 1;
+
+ Stream vs_text = stream_alloc(&a, vs_test.size);
+ success &= platform->read_file(vs_path.buf, &vs_text, vs_test.size);
+
+ Stream fs_text = stream_alloc(&a, fs_test.size);
+ success &= platform->read_file(fs_path.buf, &fs_text, fs_test.size);
+
+ if (success) {
+ stream_push_s8s(err, 3, (s8 []){s8("Reloading "), map[i].name, s8(" Shader!\n")});
+ gl->vs_stats[i] = vs_test;
+ gl->fs_stats[i] = fs_test;
+
+ u32 program = program_from_shader_text(stream_to_s8(&vs_text),
+ stream_to_s8(&fs_text), a);
+ if (!program)
+ continue;
+ glDeleteProgram(gl->programs[i]);
+ gl->programs[i] = program;
+ if (map[i].update_uniforms)
+ update_uniforms(gl, i);
+ stream_push_s8s(err, 2, (s8 []){map[i].name, s8(" Program Updated!\n")});
+ }
+ }
+
+ if (err->widx) {
+ os_write_err_msg(stream_to_s8(err));
+ err->widx = 0;
+ }
+}
+
static v4
normalize_colour(Colour c)
{
@@ -142,24 +294,6 @@ resize(Term *t, iv2 window_size)
gl->flags &= ~NEEDS_RESIZE;
}
-static void
-update_uniforms(Term *t, enum shader_stages stage)
-{
- switch (stage) {
- case SHADER_RENDER:
- case SHADER_RECTS:
- break;
- case SHADER_POST:
- #define X(name) \
- t->gl.post.name = glGetUniformLocation(t->gl.programs[stage], "u_" #name);
- GL_POST_UNIFORMS
- #undef X
- t->gl.flags &= ~UPDATE_POST_UNIFORMS;
- break;
- case SHADER_LAST: ASSERT(0); break;
- }
-}
-
static RenderCtx
make_render_ctx(Arena *a, GLCtx *gl, FontAtlas *fa)
{
@@ -927,8 +1061,7 @@ DEBUG_EXPORT VTGL_FRAME_STEP_FN(vtgl_frame_step)
t->temp_arena = begin_temp_arena(&t->arena_for_frame);
- if (t->gl.flags & UPDATE_POST_UNIFORMS)
- update_uniforms(t, SHADER_POST);
+ check_shaders(&t->gl, t->arena_for_frame, &t->error_stream, &memory->platform_api);
if (!equal_iv2(input->window_size, t->gl.window_size))
t->gl.flags |= NEEDS_RESIZE;
diff --git a/vtgl.h b/vtgl.h
@@ -8,9 +8,16 @@ typedef PLATFORM_ALLOCATE_RING_BUFFER_FN(platform_allocate_ring_buffer_fn);
#define PLATFORM_WRITE_FN(name) b32 name(iptr file, s8 raw, size offset)
typedef PLATFORM_WRITE_FN(platform_write_fn);
+/* TODO: this should possibly just take a stream buffer */
#define PLATFORM_READ_FN(name) size name(iptr file, s8 buffer, size offset)
typedef PLATFORM_READ_FN(platform_read_fn);
+#define PLATFORM_READ_FILE_FN(name) b32 name(u8 *path, Stream *buffer, size file_size)
+typedef PLATFORM_READ_FILE_FN(platform_read_file_fn);
+
+#define PLATFORM_GET_FILESTATS_FN(name) FileStats name(u8 *path)
+typedef PLATFORM_GET_FILESTATS_FN(platform_get_file_stats_fn);
+
#define PLATFORM_CLIPBOARD_FN(name) b32 name(Stream *buffer, u32 clipboard)
typedef PLATFORM_CLIPBOARD_FN(platform_clipboard_fn);
@@ -22,11 +29,16 @@ typedef struct {
platform_read_fn *read;
platform_write_fn *write;
+ platform_get_file_stats_fn *get_file_stats;
+ platform_read_file_fn *read_file;
+
platform_clipboard_fn *get_clipboard;
platform_clipboard_fn *set_clipboard;
platform_window_title_fn *get_window_title;
platform_window_title_fn *set_window_title;
+
+ u8 path_separator;
} PlatformAPI;
/* NOTE: CLIPBOARD_1 need not be supported on all platforms */