ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: 4309cc50baa49661b39f119fbbb0b3318efad501
Parent: 3520f866131d4d0aaa74ba7babc5e5e5ea58982f
Author: Randy Palamar
Date:   Mon, 27 Jul 2026 06:35:56 -0700

ui: add shader bake parameter context menu to compute stats

Diffstat:
Mbeamformer_internal.h | 1-
Mbuild.c | 2--
Mlib/ogl_beamformer_lib.c | 1-
Mui.c | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mutil.c | 28++++++++++++++++++++++++++++
Mutil.h | 1+
6 files changed, 101 insertions(+), 8 deletions(-)

diff --git a/beamformer_internal.h b/beamformer_internal.h @@ -7,7 +7,6 @@ #include "util.h" #include "opengl.h" -#include "meta.h" #include "generated/beamformer.c" #include "generated/beamformer_core.c" #include "generated/beamformer_shader_data.c" diff --git a/build.c b/build.c @@ -1065,8 +1065,6 @@ typedef enum { MetaEmitLang_Count, } MetaEmitLang; -#include "meta.h" - read_only global u8 meta_kind_elements[] = { #define X(_k, _c, _g, _b, _m, _by, elements, ...) elements, META_KIND_LIST diff --git a/lib/ogl_beamformer_lib.c b/lib/ogl_beamformer_lib.c @@ -6,7 +6,6 @@ #include "../beamformer.h" #include "../util.h" -#include "../meta.h" #include "../generated/beamformer.c" #include "ogl_beamformer_lib_base.h" diff --git a/ui.c b/ui.c @@ -1,5 +1,6 @@ /* See LICENSE for license details. */ /* TODO(rnp): + * [ ]: pretty print compile flags in shader bake parameter lister * [ ]: bug: nil nodes break hot reloading * - only one that matters is ui_node_nil, for now maybe just put it into ui_context (won't be read_only of course) * [ ]: bug: flickering x-scroll bar on switch from ComputeBarGraph to other @@ -3209,7 +3210,7 @@ function UI_CUSTOM_DRAW_FUNCTION(beamformer_ui_custom_draw_compute_bar_graph) } function void -ui_build_compute_stats(BeamformerComputePlan *cp, f32 broken_shader_t) +ui_build_compute_stats(BeamformerComputePlan *cp, f32 broken_shader_t, BeamformerUIPanel *panel) { ComputeShaderStats *stats = beamformer_context->compute_shader_stats; f32 compute_time_sum = 0; @@ -3250,9 +3251,69 @@ ui_build_compute_stats(BeamformerComputePlan *cp, f32 broken_shader_t) str8 shader = beamformer_shader_names[stats->table.shader_ids[it]]; UITextColour(label_colour) - UIParent(label_column) ui_labelf("%.*s:###csl%u", (i32)shader.length, shader.data, (u32)it); UIParent(value_column) ui_labelf("%0.2e###csv%u", stats->average_times[it], (u32)it); UIParent(unit_column) ui_labelf("[s]###csu%u", (u32)it); + UIParent(label_column) + { + i32 reloadable_index = beamformer_shader_reloadable_index_by_shader[stats->table.shader_ids[it]]; + UISignal signal; + UIFlags(reloadable_index >= 0? UINodeFlag_Clickable|UINodeFlag_DrawHotEffects : 0) + signal = ui_labelf("%.*s:###csl%u", (i32)shader.length, shader.data, (u32)it); + + if ui_pressed(signal) + ui_context_menu_open(signal.node->key, panel); + + if (ui_node_key_equal(ui_context->context_menu_anchor_key, signal.node->key)) { + UIParent(ui_context->context_menu_root) + UIChildLayoutAxis(Axis2_X) + UIPrefHeight(ui_children_sum(1.f)) + UIPrefWidth(ui_children_sum(1.f)) + UIParent(ui_spacer(0)) + UIChildLayoutAxis(Axis2_Y) + { + UINode *left, *right; + ui_padw(UI_NODE_PAD); + left = ui_node_from_string(0, str8("###left")); + ui_padw(UI_NODE_PAD * 2.f); + right = ui_node_from_string(0, str8("###right")); + ui_padw(UI_NODE_PAD); + + UIPrefHeight(ui_text_dim(1.1f, 1.f)) + UIPrefWidth(ui_text_dim(1.f, 1.f)) + UIFontSize(24.f) + { + // TODO(rnp): pretty print the extra compile flags field + BeamformerShaderDescriptor *sd = cp->shader_descriptors + it; + UIParent(left) ui_label(str8("Layout")); + UIParent(right) ui_labelf("{%u, %u, %u}###layout", sd->layout.x, sd->layout.y, sd->layout.z); + UIParent(left) ui_label(str8("Dispatch")); + UIParent(right) ui_labelf("{%u, %u, %u}###dispatch", sd->dispatch.x, sd->dispatch.y, sd->dispatch.z); + UIParent(left) ui_label(str8("Input")); + UIParent(right) ui_label(push_str8_from_parts(ui_build_arena(), str8(""), + beamformer_data_kind_str8[sd->input_data_kind], + str8("##input_kind"))); + UIParent(left) ui_label(str8("Output")); + UIParent(right) ui_label(push_str8_from_parts(ui_build_arena(), str8(""), + beamformer_data_kind_str8[sd->output_data_kind], + str8("##output_kind"))); + + i32 struct_id = beamformer_base_shader_to_bake_struct_id[reloadable_index]; + if (struct_id != -1) { + str8 *names = beamformer_shader_bake_parameter_names[reloadable_index]; + MetaStructInfo *si = meta_struct_info_by_id + struct_id; + MetaStructMember *sm = meta_struct_members_by_id[struct_id]; + for EachIndex(si->member_count, member) { + Stream sb = arena_stream(*ui_build_arena()); + stream_append_struct_member(&sb, sm + member, &sd->bake); + stream_append_str8s(&sb, str8("##"), names[member]); + UIParent(left) ui_label(names[member]); + UIParent(right) ui_label(arena_stream_commit(ui_build_arena(), &sb)); + } + } + } + } + } + } } UIParent(label_column) ui_label(str8("Compute Total:")); @@ -4135,7 +4196,7 @@ ui_build_regions(UINode *root_node, BeamformerUIPanel *tree_root) BeamformerComputePlan *cp = beamformer_context->compute_context.compute_plans[selected_plan]; if (!cp) cp = &beamformer_nil_compute_plan; f32 t = beamformer_ui_blinker_update(&panel->u.compute_stats_broken_shader_blinker, BLINK_SPEED); - ui_build_compute_stats(cp, t); + ui_build_compute_stats(cp, t, panel); }break; case BeamformerPanelKind_FrameViewXPlane: @@ -5384,6 +5445,7 @@ beamformer_ui_frame(void) ui_layout_nodes(ui->root_node); + b32 context_menu_ready = 0; if (!ui_node_key_nil(ui->context_menu_anchor_key)) { UIParent(ui->context_menu_root) UIAxisSize(Axis2_X, ui_px(0.f, 0.5f)) ui_padh(0.8f * UI_NODE_PAD); @@ -5391,8 +5453,14 @@ beamformer_ui_frame(void) v2 anchor_p = ui_node_final_position(anchor); ui->context_menu_root->computed_position[Axis2_X] = anchor_p.x; ui->context_menu_root->computed_position[Axis2_Y] = anchor_p.y + anchor->computed_size[Axis2_Y]; + Rect nr = ui_node_rect(ui->context_menu_root); + if (nr.pos.y + nr.size.y > window_rect.size.y) + ui->context_menu_root->computed_position[Axis2_Y] += (window_rect.size.y - (nr.pos.y + nr.size.y)); ui_layout_nodes(ui->context_menu_root); + + nr = ui_node_rect(ui->context_menu_root); + context_menu_ready = (nr.pos.y + nr.size.y <= window_rect.size.y); } BeginDrawing(); @@ -5400,7 +5468,7 @@ beamformer_ui_frame(void) glClearNamedFramebufferfv(0, GL_DEPTH, 0, (f32 []){1}); ui_draw_nodes(ui->root_node, window_rect); - if (!ui_node_key_nil(ui->context_menu_anchor_key)) + if (!ui_node_key_nil(ui->context_menu_anchor_key) && context_menu_ready) ui_draw_nodes(ui->context_menu_root, window_rect); if (ui->drag_root) { diff --git a/util.c b/util.c @@ -534,6 +534,34 @@ stream_append_f64_e(Stream *s, f64 f) stream_append_u64(s, (u64)Abs(scale)); } +function void +stream_append_struct_member(Stream *s, MetaStructMember *m, void *struct_base) +{ + switch (m->type_id) { + InvalidDefaultCase; + case MetaKind_F32:{ + f32 value; + memory_copy(&value, ((u8 *)struct_base + m->offset), sizeof(value)); + stream_append_f64_e(s, value); + }break; + case MetaKind_B32:{ + b32 value; + memory_copy(&value, ((u8 *)struct_base + m->offset), sizeof(value)); + stream_append_str8(s, value ? str8("True") : str8("False")); + }break; + case MetaKind_U32:{ + u32 value; + memory_copy(&value, ((u8 *)struct_base + m->offset), sizeof(value)); + stream_append_u64(s, value); + }break; + case MetaKind_S32:{ + i32 value; + memory_copy(&value, ((u8 *)struct_base + m->offset), sizeof(value)); + stream_append_i64(s, value); + }break; + } +} + function Stream arena_stream(Arena a) { diff --git a/util.h b/util.h @@ -396,6 +396,7 @@ typedef alignas(16) u8 RenderDocAPI[216]; #define RENDERDOC_END_FRAME_CAPTURE(a) (renderdoc_end_frame_capture_fn *) RENDERDOC_API_FN_ADDR(a, 168) #define RENDERDOC_SET_CAPTURE_PATH_TEMPLATE(a) (renderdoc_set_capture_path_template_fn *) RENDERDOC_API_FN_ADDR(a, 184) +#include "meta.h" #include "util.c" #include "math.c"