Commit: 8f2090b23db12a1eb3d00073924cea207d0a2b4e
Parent: 0ca3fddb96126960f05807675b2da3d7a7fdf1ba
Author: Randy Palamar
Date: Sun, 13 Apr 2025 21:42:56 -0600
core/ui: don't use raylib for rendering beamformed data
Diffstat:
M | beamformer.c | | | 81 | ++++++++++++------------------------------------------------------------------- |
M | beamformer.h | | | 26 | +++++++++++++++----------- |
M | shaders/render.glsl | | | 13 | ++----------- |
M | static.c | | | 77 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------- |
M | ui.c | | | 151 | +++++++++++++++++++++++++++++++++++++++++++++++-------------------------------- |
M | util.c | | | 79 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | util.h | | | 2 | ++ |
7 files changed, 262 insertions(+), 167 deletions(-)
diff --git a/beamformer.c b/beamformer.c
@@ -391,56 +391,6 @@ do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformComputeFrame *frame,
}
}
-static u32
-compile_shader(OS *os, Arena a, u32 type, s8 shader, s8 name)
-{
- 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_FALSE) {
- Stream buf = arena_stream(&a);
- stream_append_s8(&buf, name);
- stream_append_s8(&buf, s8(": failed to compile\n"));
-
- i32 len = 0, out_len = 0;
- glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &len);
- glGetShaderInfoLog(sid, len, &out_len, (char *)(buf.data + buf.widx));
- stream_commit(&buf, out_len);
- glDeleteShader(sid);
- os->write_file(os->stderr, stream_to_s8(&buf));
-
- sid = 0;
- }
-
- return sid;
-}
-
-static u32
-link_program(OS *os, Arena a, u32 shader_id)
-{
- i32 success = 0;
- u32 result = glCreateProgram();
- glAttachShader(result, shader_id);
- glLinkProgram(result);
- glGetProgramiv(result, GL_LINK_STATUS, &success);
- if (success == GL_FALSE) {
- i32 len = 0;
- Stream buf = arena_stream(&a);
- stream_append_s8(&buf, s8("shader link error: "));
- glGetProgramInfoLog(result, buf.cap - buf.widx, &len, (c8 *)(buf.data + buf.widx));
- stream_reset(&buf, len);
- stream_append_byte(&buf, '\n');
- os->write_file(os->stderr, stream_to_s8(&buf));
- glDeleteProgram(result);
- result = 0;
- }
- return result;
-}
-
function s8
push_compute_shader_header(Arena *a, b32 parameters, ComputeShaderID shader)
{
@@ -510,24 +460,17 @@ reload_compute_shader(BeamformerCtx *ctx, s8 path, s8 extra, ComputeShaderReload
shader_text.len += header.len;
if (shader_text.data == header.data) {
- u32 shader_id = compile_shader(&ctx->os, tmp, GL_COMPUTE_SHADER, shader_text, path);
- if (shader_id) {
- u32 new_program = link_program(&ctx->os, tmp, shader_id);
- if (new_program) {
- Stream buf = arena_stream(&tmp);
- stream_append_s8(&buf, s8("loaded: "));
- stream_append_s8(&buf, path);
- stream_append_s8(&buf, extra);
- stream_append_byte(&buf, '\n');
- ctx->os.write_file(ctx->os.stderr, stream_to_s8(&buf));
- glDeleteProgram(cs->programs[csr->shader]);
- cs->programs[csr->shader] = new_program;
- glUseProgram(cs->programs[csr->shader]);
- glBindBufferBase(GL_UNIFORM_BUFFER, 0, cs->shared_ubo);
- LABEL_GL_OBJECT(GL_PROGRAM, cs->programs[csr->shader], csr->label);
- result = 1;
- }
- glDeleteShader(shader_id);
+ s8 info = {.data = tmp.beg};
+ push_s8(&tmp, path);
+ push_s8(&tmp, extra);
+ info.len = tmp.beg - info.data;
+ u32 new_program = load_shader(&ctx->os, tmp, 1, (s8){0}, (s8){0}, shader_text,
+ info, csr->label);
+ if (new_program) {
+ glDeleteProgram(cs->programs[csr->shader]);
+ cs->programs[csr->shader] = new_program;
+ glUseProgram(cs->programs[csr->shader]);
+ glBindBufferBase(GL_UNIFORM_BUFFER, 0, cs->shared_ubo);
}
} else {
Stream buf = arena_stream(&tmp);
@@ -826,7 +769,7 @@ DEBUG_EXPORT BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step)
draw_ui(ctx, input, frame_to_draw->ready_to_present? &frame_to_draw->frame : 0,
frame_to_draw->image_plane_tag, &frame_to_draw->stats);
- ctx->fsctx.updated = 0;
+ ctx->frame_view_render_context.updated = 0;
if (WindowShouldClose())
ctx->should_exit = 1;
diff --git a/beamformer.h b/beamformer.h
@@ -53,15 +53,18 @@ typedef struct {
#undef X
} CudaLib;
+#define FRAME_VIEW_RENDER_DYNAMIC_RANGE_LOC 1
+#define FRAME_VIEW_RENDER_THRESHOLD_LOC 2
+#define FRAME_VIEW_RENDER_GAMMA_LOC 3
+#define FRAME_VIEW_RENDER_LOG_SCALE_LOC 4
+
typedef struct {
- Shader shader;
- b32 updated;
- /* TODO(rnp): cleanup! */
- i32 db_cutoff_id;
- i32 threshold_id;
- i32 gamma_id;
- i32 log_scale_id;
-} FragmentShaderCtx;
+ u32 shader;
+ u32 framebuffer;
+ u32 vao;
+ u32 vbo;
+ b32 updated;
+} FrameViewRenderContext;
#include "beamformer_parameters.h"
#include "beamformer_work_queue.h"
@@ -172,7 +175,10 @@ typedef struct {
BeamformComputeFrame averaged_frames[2];
ComputeShaderCtx csctx;
- FragmentShaderCtx fsctx;
+
+ /* TODO(rnp): ideally this would go in the UI but its hard to manage with the UI
+ * destroying itself on hot-reload */
+ FrameViewRenderContext frame_view_render_context;
Arena export_buffer;
@@ -193,8 +199,6 @@ struct ComputeShaderReloadContext {
b32 needs_header;
};
-#define LABEL_GL_OBJECT(type, id, s) {s8 _s = (s); glObjectLabel(type, id, _s.len, (c8 *)_s.data);}
-
#define BEAMFORMER_FRAME_STEP_FN(name) void name(BeamformerCtx *ctx, Arena *arena, \
BeamformerInput *input)
typedef BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step_fn);
diff --git a/shaders/render.glsl b/shaders/render.glsl
@@ -1,14 +1,5 @@
/* See LICENSE for license details. */
-#version 430 core
-
-in vec2 fragTexCoord;
-out vec4 v_out_colour;
-
-layout(binding = 0) uniform sampler3D u_out_data_tex;
-layout(location = 1) uniform float u_db_cutoff = 60;
-layout(location = 2) uniform float u_threshold = 40;
-layout(location = 3) uniform float u_gamma = 1;
-layout(location = 4) uniform bool u_log_scale;
+layout(binding = 0) uniform sampler3D u_out_data_tex;
/* input: h [0,360] | s,v [0, 1] *
* output: rgb [0,1] */
@@ -26,7 +17,7 @@ void main()
//vec2 min_max = texelFetch(u_out_data_tex, ivec3(0), textureQueryLevels(u_out_data_tex) - 1).xy;
/* TODO(rnp): select between x and y and specify slice */
- ivec2 coord = ivec2(fragTexCoord * vec2(out_data_dim));
+ ivec2 coord = ivec2(fragment_texture_coordinate * vec2(out_data_dim));
ivec3 smp_coord = ivec3(coord.x, 0, coord.y);
float smp = length(texelFetch(u_out_data_tex, smp_coord, 0).xy);
diff --git a/static.c b/static.c
@@ -184,20 +184,45 @@ dump_gl_params(GLParams *gl, Arena a, OS *os)
#endif
}
-static FILE_WATCH_CALLBACK_FN(reload_render_shader)
+function FILE_WATCH_CALLBACK_FN(reload_render_shader)
{
- FragmentShaderCtx *ctx = (FragmentShaderCtx *)user_data;
- Shader updated_fs = LoadShader(0, (c8 *)path.data);
-
- if (updated_fs.id) {
- UnloadShader(ctx->shader);
- LABEL_GL_OBJECT(GL_PROGRAM, updated_fs.id, s8("Render Shader"));
- ctx->shader = updated_fs;
- ctx->db_cutoff_id = GetShaderLocation(updated_fs, "u_db_cutoff");
- ctx->threshold_id = GetShaderLocation(updated_fs, "u_threshold");
- ctx->gamma_id = GetShaderLocation(updated_fs, "u_gamma");
- ctx->log_scale_id = GetShaderLocation(updated_fs, "u_log_scale");
- ctx->updated = 1;
+ FrameViewRenderContext *ctx = (typeof(ctx))user_data;
+
+ local_persist s8 vertex = s8(""
+ "#version 460 core\n"
+ "\n"
+ "layout(location = 0) in vec2 vertex_position;\n"
+ "layout(location = 1) in vec2 vertex_texture_coordinate;\n"
+ "\n"
+ "layout(location = 0) out vec2 fragment_texture_coordinate;\n"
+ "\n"
+ "void main()\n"
+ "{\n"
+ "\tfragment_texture_coordinate = vertex_texture_coordinate;\n"
+ "\tgl_Position = vec4(vertex_position, 0, 1);\n"
+ "}\n");
+
+ Arena *a = &tmp;
+ s8 header = {.data = a->beg};
+ push_s8(a, s8("#version 460 core\n\n"));
+ push_s8(a, s8("layout(location = 0) in vec2 fragment_texture_coordinate;\n"));
+ push_s8(a, s8("layout(location = 0) out vec4 v_out_colour;\n\n"));
+ push_s8(a, s8("layout(location = " str(FRAME_VIEW_RENDER_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n"));
+ push_s8(a, s8("layout(location = " str(FRAME_VIEW_RENDER_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n"));
+ push_s8(a, s8("layout(location = " str(FRAME_VIEW_RENDER_GAMMA_LOC) ") uniform float u_gamma = 1;\n"));
+ push_s8(a, s8("layout(location = " str(FRAME_VIEW_RENDER_LOG_SCALE_LOC) ") uniform bool u_log_scale;\n"));
+ push_s8(a, s8("\n#line 1\n"));
+ header.len = a->beg - header.data;
+
+ s8 fragment = os->read_whole_file(a, (c8 *)path.data);
+ fragment.data -= header.len;
+ fragment.len += header.len;
+ ASSERT(fragment.data == header.data);
+ u32 new_program = load_shader(os, tmp, 0, vertex, fragment, (s8){0}, path, s8("Render Shader"));
+ if (new_program) {
+ glDeleteProgram(ctx->shader);
+ ctx->shader = new_program;
+ ctx->updated = 1;
}
return 1;
@@ -355,7 +380,29 @@ setup_beamformer(BeamformerCtx *ctx, Arena *memory)
#undef X
os_wake_waiters(&worker->sync_variable);
+ FrameViewRenderContext *fvr = &ctx->frame_view_render_context;
+ glGenFramebuffers(1, &fvr->framebuffer);
+ LABEL_GL_OBJECT(GL_FRAMEBUFFER, fvr->framebuffer, s8("Frame View Render Framebuffer"));
+ f32 vertices[] = {
+ -1, 1, 0, 0,
+ -1, -1, 0, 1,
+ 1, -1, 1, 1,
+ -1, 1, 0, 0,
+ 1, -1, 1, 1,
+ 1, 1, 1, 0,
+ };
+ glGenVertexArrays(1, &fvr->vao);
+ glBindVertexArray(fvr->vao);
+ glGenBuffers(1, &fvr->vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, fvr->vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+ glVertexAttribPointer(0, 2, GL_FLOAT, 0, 4 * sizeof(f32), 0);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(1, 2, GL_FLOAT, 0, 4 * sizeof(f32), (void *)(2 * sizeof(f32)));
+ glEnableVertexAttribArray(1);
+ glBindVertexArray(0);
+
s8 render = s8(static_path_join("shaders", "render.glsl"));
- reload_render_shader(&ctx->os, render, (iptr)&ctx->fsctx, *memory);
- os_add_file_watch(&ctx->os, memory, render, reload_render_shader, (iptr)&ctx->fsctx);
+ reload_render_shader(&ctx->os, render, (iptr)fvr, *memory);
+ os_add_file_watch(&ctx->os, memory, render, reload_render_shader, (iptr)fvr);
}
diff --git a/ui.c b/ui.c
@@ -1,13 +1,24 @@
/* See LICENSE for license details. */
/* TODO(rnp):
- * [ ]: ui should be in its own thread and that thread should only be concerned with the ui
+ * [ ]: refactor: ui should be in its own thread and that thread should only be concerned with the ui
+ * [ ]: refactor: ui shouldn't fully destroy itself on hot reload
+ * [ ]: refactor: move remaining fragment shader stuff into ui
+ * [ ]: refactor: re-add next_hot variable. this will simplify the code and number of checks
+ * being performed inline. example:
+ * if (hovering)
+ * next_hot = var;
+ * draw_text(..., var->hover_t);
+ * // elsewhere in code
+ * if (!is->active)
+ * hot = next_hot ....
+ *
+ * hot->hover_t += hover_speed * dt_for_frame
* [ ]: scroll bar for views that don't have enough space
* [ ]: compute times through same path as parameter list ?
* [ ]: allow views to collapse to just their title bar
* - title bar struct with expanded. Check when pushing onto draw stack; if expanded
* do normal behaviour else make size title bar size and ignore the splits fraction.
* [ ]: enforce a minimum region size or allow regions themselves to scroll
- * [ ]: move remaining fragment shader stuff into ui
* [ ]: refactor: draw_left_aligned_list()
* [ ]: refactor: draw_variable_table()
* [ ]: refactor: add_variable_no_link()
@@ -295,11 +306,14 @@ typedef struct BeamformerFrameView {
Variable dynamic_range;
Variable gamma;
- FragmentShaderCtx *ctx;
- BeamformFrame *frame;
+ FrameViewRenderContext *ctx;
+ BeamformFrame *frame;
struct BeamformerFrameView *prev, *next;
- RenderTexture2D rendered_view;
+ uv2 texture_dim;
+ u32 texture_mipmaps;
+ u32 texture;
+
BeamformerFrameViewType type;
b32 needs_update;
} BeamformerFrameView;
@@ -343,7 +357,7 @@ struct BeamformerUI {
BeamformerUIParameters params;
b32 flush_params;
- FragmentShaderCtx *fsctx;
+ FrameViewRenderContext *frame_view_render_context;
OS *os;
};
@@ -401,6 +415,18 @@ clamp_text_to_width(Font font, s8 text, f32 limit)
return result;
}
+function Texture
+make_raylib_texture(BeamformerFrameView *v)
+{
+ Texture result;
+ result.id = v->texture;
+ result.width = v->texture_dim.w;
+ result.height = v->texture_dim.h;
+ result.mipmaps = v->texture_mipmaps;
+ result.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
+ return result;
+}
+
static void
stream_append_variable_base(Stream *s, VariableType type, void *var, void *scale)
{
@@ -447,31 +473,27 @@ stream_append_variable(Stream *s, Variable *var)
}
}
-static void
+function void
resize_frame_view(BeamformerFrameView *view, uv2 dim)
{
- UnloadRenderTexture(view->rendered_view);
- /* TODO(rnp): sometimes when accepting data on w32 something happens
- * and the program will stall in vprintf in TraceLog(...) here.
- * for now do this to avoid the problem */
- //SetTraceLogLevel(LOG_NONE);
- view->rendered_view = LoadRenderTexture(dim.x, dim.y);
- //SetTraceLogLevel(LOG_INFO);
-
- /* TODO(rnp): add some ID for the specific view here */
- LABEL_GL_OBJECT(GL_FRAMEBUFFER, view->rendered_view.id, s8("Frame View"));
- LABEL_GL_OBJECT(GL_TEXTURE, view->rendered_view.texture.id, s8("Frame View Texture"));
- glGenerateTextureMipmap(view->rendered_view.texture.id);
+ glDeleteTextures(1, &view->texture);
+ glCreateTextures(GL_TEXTURE_2D, 1, &view->texture);
- //SetTextureFilter(view->rendered_view.texture, TEXTURE_FILTER_ANISOTROPIC_8X);
- //SetTextureFilter(view->rendered_view.texture, TEXTURE_FILTER_TRILINEAR);
- //SetTextureFilter(view->rendered_view.texture, TEXTURE_FILTER_BILINEAR);
+ view->texture_dim = dim;
+ view->texture_mipmaps = ctz_u32(MAX(dim.x, dim.y)) + 1;
+ /* TODO(rnp): HDR? */
+ glTextureStorage2D(view->texture, view->texture_mipmaps, GL_RGBA8, dim.x, dim.y);
+ glGenerateTextureMipmap(view->texture);
/* NOTE(rnp): work around raylib's janky texture sampling */
- i32 id = view->rendered_view.texture.id;
- glTextureParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- glTextureParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
- glTextureParameterfv(id, GL_TEXTURE_BORDER_COLOR, (f32 []){0, 0, 0, 1});
+ glTextureParameteri(view->texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ glTextureParameteri(view->texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ glTextureParameterfv(view->texture, GL_TEXTURE_BORDER_COLOR, (f32 []){0, 0, 0, 1});
+ glTextureParameteri(view->texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTextureParameteri(view->texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ /* TODO(rnp): add some ID for the specific view here */
+ LABEL_GL_OBJECT(GL_TEXTURE, view->texture, s8("Frame View Texture"));
}
static void
@@ -842,7 +864,7 @@ ui_fill_live_frame_view(BeamformerUI *ui, BeamformerFrameView *bv)
bv->lateral_scale_bar.causes_compute = 1;
bv->axial_scale_bar_active->u.b32 = 1;
bv->lateral_scale_bar_active->u.b32 = 1;
- bv->ctx = ui->fsctx;
+ bv->ctx = ui->frame_view_render_context;
}
function void
@@ -906,7 +928,7 @@ static b32
view_update(BeamformerUI *ui, BeamformerFrameView *view)
{
b32 needs_resize = 0;
- uv2 current = {.w = view->rendered_view.texture.width, .h = view->rendered_view.texture.height};
+ uv2 current = view->texture_dim;
if (view->type == FVT_LATEST) {
u32 index = view->cycler->u.cycler.state % (IPT_LAST + 1);
view->needs_update |= view->frame != ui->latest_plane[index];
@@ -928,36 +950,46 @@ view_update(BeamformerUI *ui, BeamformerFrameView *view)
return (view->ctx->updated || view->needs_update) && view->frame;
}
-static void
-update_frame_views(BeamformerUI *ui)
+function void
+update_frame_views(BeamformerUI *ui, Rect window)
{
+ b32 fbo_bound = 0;
for (BeamformerFrameView *view = ui->views; view; view = view->next) {
if (view_update(ui, view)) {
- BeamformFrame *frame = view->frame;
- BeginTextureMode(view->rendered_view);
- ClearBackground(PINK);
- BeginShaderMode(view->ctx->shader);
- glUseProgram(view->ctx->shader.id);
- glBindTextureUnit(0, frame->texture);
- glUniform1f(view->ctx->db_cutoff_id, view->dynamic_range.u.f32);
- glUniform1f(view->ctx->threshold_id, view->threshold.u.f32);
- glUniform1f(view->ctx->gamma_id, view->gamma.u.scaled_f32.val);
- glUniform1ui(view->ctx->log_scale_id, view->log_scale->u.b32);
- DrawTexture(view->rendered_view.texture, 0, 0, WHITE);
- EndShaderMode();
- EndTextureMode();
- glGenerateTextureMipmap(view->rendered_view.texture.id);
+ if (!fbo_bound) {
+ fbo_bound = 1;
+ glBindFramebuffer(GL_FRAMEBUFFER, view->ctx->framebuffer);
+ glUseProgram(view->ctx->shader);
+ glBindVertexArray(view->ctx->vao);
+ glViewport(0, 0, view->texture_dim.w, view->texture_dim.h);
+ glClearColor(0.79, 0.46, 0.77, 1);
+ }
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, view->texture, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glBindTextureUnit(0, view->frame->texture);
+ glUniform1f(FRAME_VIEW_RENDER_DYNAMIC_RANGE_LOC, view->dynamic_range.u.f32);
+ glUniform1f(FRAME_VIEW_RENDER_THRESHOLD_LOC, view->threshold.u.f32);
+ glUniform1f(FRAME_VIEW_RENDER_GAMMA_LOC, view->gamma.u.scaled_f32.val);
+ glUniform1ui(FRAME_VIEW_RENDER_LOG_SCALE_LOC, view->log_scale->u.b32);
+
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+ glGenerateTextureMipmap(view->texture);
view->needs_update = 0;
}
}
+ if (fbo_bound) {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glViewport(window.pos.x, window.pos.y, window.size.w, window.size.h);
+ /* NOTE(rnp): I don't trust raylib to not mess with us */
+ glBindVertexArray(0);
+ }
}
-static b32
+function b32
frame_view_ready_to_present(BeamformerFrameView *view)
{
- uv2 render_size = {.w = view->rendered_view.texture.width,
- .h = view->rendered_view.texture.height};
- return !uv2_equal((uv2){0}, render_size) && view->frame;
+ return !uv2_equal((uv2){0}, view->texture_dim) && view->frame;
}
static Color
@@ -1449,10 +1481,9 @@ draw_beamformer_frame_view(BeamformerUI *ui, Arena a, Variable *var, Rect displa
.y = frame->max_coordinate.z - frame->min_coordinate.z,
};
- Texture *output = &view->rendered_view.texture;
v2 pixels_per_meter = {
- .w = (f32)output->width / output_dim.w,
- .h = (f32)output->height / output_dim.h,
+ .w = (f32)view->texture_dim.w / output_dim.w,
+ .h = (f32)view->texture_dim.h / output_dim.h,
};
v2 texture_points = mul_v2(pixels_per_meter, requested_dim);
@@ -1464,7 +1495,7 @@ draw_beamformer_frame_view(BeamformerUI *ui, Arena a, Variable *var, Rect displa
Rectangle tex_r = {texture_start.x, texture_start.y, texture_points.x, -texture_points.y};
NPatchInfo tex_np = { tex_r, 0, 0, 0, 0, NPATCH_NINE_PATCH };
- DrawTextureNPatch(*output, tex_np, vr.rl, (Vector2){0}, 0, WHITE);
+ DrawTextureNPatch(make_raylib_texture(view), tex_np, vr.rl, (Vector2){0}, 0, WHITE);
v2 start_pos = vr.pos;
start_pos.y += vr.size.y;
@@ -2506,16 +2537,16 @@ ui_init(BeamformerCtx *ctx, Arena store)
UnloadFont(ui->small_font);
for (BeamformerFrameView *view = ui->views; view; view = view->next)
- if (view->rendered_view.id)
- UnloadRenderTexture(view->rendered_view);
+ if (view->texture)
+ glDeleteTextures(1, &view->texture);
}
DEBUG_DECL(u8 *arena_start = store.beg);
ui = ctx->ui = push_struct(&store, typeof(*ui));
ui->os = &ctx->os;
- ui->fsctx = &ctx->fsctx;
ui->arena = store;
+ ui->frame_view_render_context = &ctx->frame_view_render_context;
/* TODO: build these into the binary */
/* TODO(rnp): better font, this one is jank at small sizes */
@@ -2599,18 +2630,16 @@ draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformFrame *frame_to_draw
}
/* NOTE(rnp): can't render to a different framebuffer in the middle of BeginDrawing()... */
- update_frame_views(ui);
+ Rect window_rect = {.size = {.w = ctx->window_size.w, .h = ctx->window_size.h}};
+ update_frame_views(ui, window_rect);
BeginDrawing();
ClearBackground(colour_from_normalized(BG_COLOUR));
- v2 mouse = input->mouse;
- Rect window_rect = {.size = {.w = ctx->window_size.w, .h = ctx->window_size.h}};
-
- draw_ui_regions(ui, window_rect, mouse);
+ draw_ui_regions(ui, window_rect, input->mouse);
if (ui->interaction.type == IT_TEXT)
draw_active_text_box(ui, ui->interaction.active);
if (ui->interaction.type == IT_MENU)
- draw_active_menu(ui, ui->arena, ui->interaction.active, mouse, window_rect);
+ draw_active_menu(ui, ui->arena, ui->interaction.active, input->mouse, window_rect);
EndDrawing();
}
diff --git a/util.c b/util.c
@@ -807,3 +807,82 @@ fill_hadamard_transpose(i32 *out, i32 *tmp, u32 dim)
kronecker_product(out, tmp, (uv2){.x = dim, .y = dim}, hadamard_12_12_transpose,
(uv2){.x = 12, .y = 12});
}
+
+function u32
+compile_shader(OS *os, Arena a, u32 type, s8 shader, s8 name)
+{
+ 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_FALSE) {
+ Stream buf = arena_stream(&a);
+ stream_append_s8(&buf, name);
+ stream_append_s8(&buf, s8(": failed to compile\n"));
+
+ i32 len = 0, out_len = 0;
+ glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &len);
+ glGetShaderInfoLog(sid, len, &out_len, (char *)(buf.data + buf.widx));
+ stream_commit(&buf, out_len);
+ glDeleteShader(sid);
+ os->write_file(os->stderr, stream_to_s8(&buf));
+
+ sid = 0;
+ }
+
+ return sid;
+}
+
+function u32
+link_program(OS *os, Arena a, u32 *shader_ids, u32 shader_id_count)
+{
+ i32 success = 0;
+ u32 result = glCreateProgram();
+ for (u32 i = 0; i < shader_id_count; i++)
+ glAttachShader(result, shader_ids[i]);
+ glLinkProgram(result);
+ glGetProgramiv(result, GL_LINK_STATUS, &success);
+ if (success == GL_FALSE) {
+ i32 len = 0;
+ Stream buf = arena_stream(&a);
+ stream_append_s8(&buf, s8("shader link error: "));
+ glGetProgramInfoLog(result, buf.cap - buf.widx, &len, (c8 *)(buf.data + buf.widx));
+ stream_reset(&buf, len);
+ stream_append_byte(&buf, '\n');
+ os->write_file(os->stderr, stream_to_s8(&buf));
+ glDeleteProgram(result);
+ result = 0;
+ }
+ return result;
+}
+
+function u32
+load_shader(OS *os, Arena arena, b32 compute, s8 vs_text, s8 fs_text, s8 cs_text, s8 info_name, s8 label)
+{
+ u32 result = 0;
+ if (compute) {
+ u32 shader_id = compile_shader(os, arena, GL_COMPUTE_SHADER, cs_text, info_name);
+ if (shader_id) result = link_program(os, arena, (u32 []){shader_id}, 1);
+ glDeleteShader(shader_id);
+ } else {
+ u32 fs_id = compile_shader(os, arena, GL_FRAGMENT_SHADER, fs_text, info_name);
+ u32 vs_id = compile_shader(os, arena, GL_VERTEX_SHADER, vs_text, info_name);
+ if (fs_id && vs_id) result = link_program(os, arena, (u32 []){vs_id, fs_id}, 2);
+ glDeleteShader(fs_id);
+ glDeleteShader(vs_id);
+ }
+
+ if (result) {
+ Stream buf = arena_stream(&arena);
+ stream_append_s8(&buf, s8("loaded: "));
+ stream_append_s8(&buf, info_name);
+ stream_append_byte(&buf, '\n');
+ os->write_file(os->stderr, stream_to_s8(&buf));
+ LABEL_GL_OBJECT(GL_PROGRAM, result, label);
+ }
+
+ return result;
+}
diff --git a/util.h b/util.h
@@ -311,6 +311,8 @@ struct OS {
DEBUG_DECL(renderdoc_end_frame_capture_fn *end_frame_capture);
};
+#define LABEL_GL_OBJECT(type, id, s) {s8 _s = (s); glObjectLabel(type, id, _s.len, (c8 *)_s.data);}
+
#include "util.c"
#endif /* _UTIL_H_ */