ogl_beamforming

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

Commit: 59960f78aa16d60ab2403973aac65236e0c7f9ee
Parent: 324efd5c3a8c7a649ad8671eedead28552475838
Author: Randy Palamar
Date:   Thu, 24 Apr 2025 20:27:42 -0600

ui: UIViews now have a single child and only manage scrolling

Diffstat:
Mui.c | 226+++++++++++++++++++++++++++++++++++++++++--------------------------------------
1 file changed, 116 insertions(+), 110 deletions(-)

diff --git a/ui.c b/ui.c @@ -2,7 +2,6 @@ /* TODO(rnp): * [ ]: 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: split up draw_ui_view function (have a VT_UI_LISTING) * [ ]: refactor: remove all the excessive measure_texts (cell drawing, hover_var in params table) * [ ]: refactor: move remaining fragment shader stuff into ui * [ ]: refactor: re-add next_hot variable. this will simplify the code and number of checks @@ -184,13 +183,12 @@ typedef enum { } UIViewFlags; typedef struct { - /* NOTE(rnp): superset of group, group must come first */ - VariableGroup group; - Variable *close; - Variable *menu; - f32 needed_height; - f32 offset; - UIViewFlags flags; + Variable *child; + Variable *close; + Variable *menu; + f32 needed_height; + f32 offset; + UIViewFlags flags; } UIView; /* X(id, text) */ @@ -751,7 +749,7 @@ ui_variable_free(BeamformerUI *ui, Variable *var) if (var) { var->parent = 0; while (var) { - if (var->type == VT_GROUP || var->type == VT_UI_VIEW) { + if (var->type == VT_GROUP) { var = var->u.group.first; } else { if (var->type == VT_BEAMFORMER_FRAME_VIEW) { @@ -790,10 +788,11 @@ ui_variable_free(BeamformerUI *ui, Variable *var) } } -static void +function void ui_view_free(BeamformerUI *ui, Variable *view) { ASSERT(view->type == VT_UI_VIEW); + ui_variable_free(ui, view->u.view.child); ui_variable_free(ui, view->u.view.close); ui_variable_free(ui, view->u.view.menu); ui_variable_free(ui, view); @@ -808,7 +807,7 @@ fill_variable(Variable *var, Variable *group, s8 name, u32 flags, VariableType t var->parent = group; var->name_width = measure_text(font, name).x; - if (group && (group->type == VT_GROUP || group->type == VT_UI_VIEW)) { + if (group && group->type == VT_GROUP) { if (group->u.group.last) group->u.group.last = group->u.group.last->next = var; else group->u.group.last = group->u.group.first = var; } @@ -887,12 +886,11 @@ add_global_menu(BeamformerUI *ui, Arena *arena, Variable *parent) return result; } -static Variable * +function Variable * add_ui_view(BeamformerUI *ui, Variable *parent, Arena *arena, s8 name, u32 view_flags, b32 closable) { Variable *result = add_variable(ui, parent, arena, name, 0, VT_UI_VIEW, ui->small_font); UIView *view = &result->u.view; - view->group.type = VG_LIST; view->flags = view_flags; view->menu = add_global_menu(ui, arena, result); if (closable) { @@ -932,7 +930,7 @@ add_beamformer_variable_b32(BeamformerUI *ui, Variable *group, Arena *arena, s8 bv->name_table.names[1] = true_text; } -static Variable * +function Variable * add_beamformer_parameters_view(Variable *parent, BeamformerCtx *ctx) { BeamformerUI *ui = ctx->ui; @@ -942,66 +940,68 @@ add_beamformer_parameters_view(Variable *parent, BeamformerCtx *ctx) /* TODO(rnp): this can be closable once we have a way of opening new views */ Variable *result = add_ui_view(ui, parent, &ui->arena, s8("Parameters"), 0, 0); + Variable *group = result->u.view.child = add_variable(ui, result, &ui->arena, s8(""), 0, + VT_GROUP, ui->font); - add_beamformer_variable_f32(ui, result, &ui->arena, s8("Sampling Frequency:"), s8("[MHz]"), + add_beamformer_variable_f32(ui, group, &ui->arena, s8("Sampling Frequency:"), s8("[MHz]"), &bp->sampling_frequency, (v2){0}, 1e-6, 0, 0, ui->font); - add_beamformer_variable_f32(ui, result, &ui->arena, s8("Center Frequency:"), s8("[MHz]"), + add_beamformer_variable_f32(ui, group, &ui->arena, s8("Center Frequency:"), s8("[MHz]"), &bp->center_frequency, (v2){.y = 100e-6}, 1e-6, 1e5, V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font); - add_beamformer_variable_f32(ui, result, &ui->arena, s8("Speed of Sound:"), s8("[m/s]"), + add_beamformer_variable_f32(ui, group, &ui->arena, s8("Speed of Sound:"), s8("[m/s]"), &bp->speed_of_sound, (v2){.y = 1e6}, 1, 10, V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font); - result = add_variable_group(ui, result, &ui->arena, s8("Lateral Extent:"), VG_V2, ui->font); + group = add_variable_group(ui, group, &ui->arena, s8("Lateral Extent:"), VG_V2, ui->font); { - add_beamformer_variable_f32(ui, result, &ui->arena, s8("Min:"), s8("[mm]"), + add_beamformer_variable_f32(ui, group, &ui->arena, s8("Min:"), s8("[mm]"), &bp->output_min_coordinate.x, v2_inf, 1e3, 0.5e-3, V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font); - add_beamformer_variable_f32(ui, result, &ui->arena, s8("Max:"), s8("[mm]"), + add_beamformer_variable_f32(ui, group, &ui->arena, s8("Max:"), s8("[mm]"), &bp->output_max_coordinate.x, v2_inf, 1e3, 0.5e-3, V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font); } - result = end_variable_group(result); + group = end_variable_group(group); - result = add_variable_group(ui, result, &ui->arena, s8("Axial Extent:"), VG_V2, ui->font); + group = add_variable_group(ui, group, &ui->arena, s8("Axial Extent:"), VG_V2, ui->font); { - add_beamformer_variable_f32(ui, result, &ui->arena, s8("Min:"), s8("[mm]"), + add_beamformer_variable_f32(ui, group, &ui->arena, s8("Min:"), s8("[mm]"), &bp->output_min_coordinate.z, v2_inf, 1e3, 0.5e-3, V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font); - add_beamformer_variable_f32(ui, result, &ui->arena, s8("Max:"), s8("[mm]"), + add_beamformer_variable_f32(ui, group, &ui->arena, s8("Max:"), s8("[mm]"), &bp->output_max_coordinate.z, v2_inf, 1e3, 0.5e-3, V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font); } - result = end_variable_group(result); + group = end_variable_group(group); - add_beamformer_variable_f32(ui, result, &ui->arena, s8("Off Axis Position:"), s8("[mm]"), + add_beamformer_variable_f32(ui, group, &ui->arena, s8("Off Axis Position:"), s8("[mm]"), &bp->off_axis_pos, (v2){.x = -1e3, .y = 1e3}, 0.25e3, 0.5e-3, V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font); - add_beamformer_variable_b32(ui, result, &ui->arena, s8("Beamform Plane:"), s8("XZ"), s8("YZ"), + add_beamformer_variable_b32(ui, group, &ui->arena, s8("Beamform Plane:"), s8("XZ"), s8("YZ"), (b32 *)&bp->beamform_plane, V_INPUT|V_CAUSES_COMPUTE, ui->font); - add_beamformer_variable_f32(ui, result, &ui->arena, s8("F#:"), s8(""), &bp->f_number, + add_beamformer_variable_f32(ui, group, &ui->arena, s8("F#:"), s8(""), &bp->f_number, (v2){.y = 1e3}, 1, 0.1, V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font); - add_beamformer_variable_b32(ui, result, &ui->arena, s8("Interpolate:"), s8("False"), s8("True"), + add_beamformer_variable_b32(ui, group, &ui->arena, s8("Interpolate:"), s8("False"), s8("True"), &bp->interpolate, V_INPUT|V_CAUSES_COMPUTE, ui->font); return result; } -static Variable * +function Variable * add_beamformer_frame_view(BeamformerUI *ui, Variable *parent, Arena *arena, BeamformerFrameViewType type, b32 closable) { /* TODO(rnp): this can be always closable once we have a way of opening new views */ Variable *result = add_ui_view(ui, parent, arena, s8(""), UI_VIEW_CUSTOM_TEXT, closable); - Variable *var = add_variable(ui, result, arena, s8(""), 0, VT_BEAMFORMER_FRAME_VIEW, - ui->small_font); + Variable *var = result->u.view.child = add_variable(ui, result, arena, s8(""), 0, + VT_BEAMFORMER_FRAME_VIEW, ui->small_font); BeamformerFrameView *bv = SLLPop(ui->view_freelist); if (bv) zero_struct(bv); @@ -1074,26 +1074,27 @@ add_beamformer_frame_view(BeamformerUI *ui, Variable *parent, Arena *arena, return result; } -static Variable * +function Variable * add_compute_progress_bar(Variable *parent, BeamformerCtx *ctx) { BeamformerUI *ui = ctx->ui; /* TODO(rnp): this can be closable once we have a way of opening new views */ Variable *result = add_ui_view(ui, parent, &ui->arena, s8(""), UI_VIEW_CUSTOM_TEXT, 0); - add_variable(ui, result, &ui->arena, s8(""), 0, VT_COMPUTE_PROGRESS_BAR, ui->small_font); - ComputeProgressBar *bar = &result->u.group.first->u.compute_progress_bar; + result->u.view.child = add_variable(ui, result, &ui->arena, s8(""), 0, + VT_COMPUTE_PROGRESS_BAR, ui->small_font); + ComputeProgressBar *bar = &result->u.view.child->u.compute_progress_bar; bar->progress = &ctx->csctx.processing_progress; bar->processing = &ctx->csctx.processing_compute; return result; } -static Variable * +function Variable * add_compute_stats_view(BeamformerUI *ui, Variable *parent, Arena *arena, VariableType type) { /* TODO(rnp): this can be closable once we have a way of opening new views */ - Variable *result = add_ui_view(ui, parent, arena, s8(""), UI_VIEW_CUSTOM_TEXT, 0); - add_variable(ui, result, &ui->arena, s8(""), 0, type, ui->small_font); + Variable *result = add_ui_view(ui, parent, arena, s8(""), UI_VIEW_CUSTOM_TEXT, 0); + result->u.view.child = add_variable(ui, result, &ui->arena, s8(""), 0, type, ui->small_font); return result; } @@ -2023,25 +2024,12 @@ draw_compute_stats_view(BeamformerCtx *ctx, Arena arena, ComputeShaderStats *sta return draw_table(ui, arena, &table, r, text_spec, (v2){0}); } -function void -draw_ui_view(BeamformerUI *ui, Variable *ui_view, Rect r, v2 mouse, TextSpec text_spec) +function v2 +draw_ui_view_listing(BeamformerUI *ui, Variable *group, Arena arena, Rect r, v2 mouse, TextSpec text_spec) { - ASSERT(ui_view->type == VT_UI_VIEW); - UIView *view = &ui_view->u.view; - - if (view->needed_height - r.size.h < view->offset) - view->offset = view->needed_height - r.size.h; - - if (view->needed_height - r.size.h < 0) - view->offset = 0; - - r.pos.y -= view->offset; - - v2 size = {0}; - Arena arena = ui->arena; - Table table = table_new(&arena, 0, 3, (TextAlignment []){TA_LEFT, TA_LEFT, TA_RIGHT}); - - Variable *var = view->group.first; + ASSERT(group->type == VT_GROUP); + Table table = table_new(&arena, 0, 3, (TextAlignment []){TA_LEFT, TA_LEFT, TA_RIGHT}); + Variable *var = group->u.group.first; while (var) { switch (var->type) { case VT_BEAMFORMER_VARIABLE: { @@ -2058,8 +2046,8 @@ draw_ui_view(BeamformerUI *ui, Variable *ui_view, Rect r, v2 mouse, TextSpec tex case VT_GROUP: { VariableGroup *g = &var->u.group; - TableCell *cells = table_push_row(&table, &arena, TRK_CELLS)->data; - cells[0] = (TableCell){.text = var->name, .kind = TCK_VARIABLE, .var = var}; + TableCell *cells = table_push_row(&table, &arena, TRK_CELLS)->data; + cells[0] = (TableCell){.text = var->name, .kind = TCK_VARIABLE, .var = var}; if (g->expanded) { var = g->first; @@ -2093,71 +2081,89 @@ draw_ui_view(BeamformerUI *ui, Variable *ui_view, Rect r, v2 mouse, TextSpec tex var = var->next; } } break; - case VT_BEAMFORMER_FRAME_VIEW: { - BeamformerFrameView *bv = var->u.generic; - if (frame_view_ready_to_present(bv)) - draw_beamformer_frame_view(ui, ui->arena, var, r, mouse); - var = var->next; - } break; - case VT_COMPUTE_PROGRESS_BAR: { - ComputeProgressBar *bar = &var->u.compute_progress_bar; - size = draw_compute_progress_bar(ui, ui->arena, bar, r); - var = var->next; - } break; - case VT_COMPUTE_LATEST_STATS_VIEW: - case VT_COMPUTE_STATS_VIEW: { - ComputeShaderStats *stats = var->u.compute_stats_view.stats; - if (var->type == VT_COMPUTE_LATEST_STATS_VIEW) - stats = *(ComputeShaderStats **)stats; - size = draw_compute_stats_view(var->u.compute_stats_view.ctx, ui->arena, stats, r); - var = var->next; - } break; default: INVALID_CODE_PATH; } } - if (table.rows) { - text_spec.flags |= TF_LIMITED; - /* NOTE(rnp): minimum width for middle column */ - table.widths[1] = 150; - size = table_extent(&table, arena, text_spec.font); - TableCellIterator it = table_cell_iterator_new(&table, &arena, 0, r.pos, text_spec.font); - for (TableCell *cell = table_cell_iterator_next(&it, &arena); - cell; - cell = table_cell_iterator_next(&it, &arena)) - { - text_spec.limits.size.w = r.size.w - (it.cell_rect.pos.x - it.start_x); - if (cell->kind == TCK_VARIABLE_GROUP) { - Variable *v = cell->var->u.group.first; - v2 at = table_cell_align(cell, it.alignment, it.cell_rect); - text_spec.limits.size.w = r.size.w - (at.x - it.start_x); - f32 dw = draw_text(s8("{"), at, &text_spec).x; - while (v) { + text_spec.flags |= TF_LIMITED; + /* NOTE(rnp): minimum width for middle column */ + table.widths[1] = 150; + v2 result = table_extent(&table, arena, text_spec.font); + TableCellIterator it = table_cell_iterator_new(&table, &arena, 0, r.pos, text_spec.font); + for (TableCell *cell = table_cell_iterator_next(&it, &arena); + cell; + cell = table_cell_iterator_next(&it, &arena)) + { + text_spec.limits.size.w = r.size.w - (it.cell_rect.pos.x - it.start_x); + if (cell->kind == TCK_VARIABLE_GROUP) { + Variable *v = cell->var->u.group.first; + v2 at = table_cell_align(cell, it.alignment, it.cell_rect); + text_spec.limits.size.w = r.size.w - (at.x - it.start_x); + f32 dw = draw_text(s8("{"), at, &text_spec).x; + while (v) { + at.x += dw; + text_spec.limits.size.w -= dw; + dw = draw_variable(ui, arena, v, at, mouse, text_spec.colour, text_spec).x; + + v = v->next; + if (v) { at.x += dw; text_spec.limits.size.w -= dw; - dw = draw_variable(ui, arena, v, at, mouse, - text_spec.colour, text_spec).x; - - v = v->next; - if (v) { - at.x += dw; - text_spec.limits.size.w -= dw; - dw = draw_text(s8(", "), at, &text_spec).x; - } + dw = draw_text(s8(", "), at, &text_spec).x; } - at.x += dw; - text_spec.limits.size.w -= dw; - draw_text(s8("}"), at, &text_spec); - } else { - draw_table_cell(ui, cell, it.cell_rect, it.alignment, text_spec, mouse); } + at.x += dw; + text_spec.limits.size.w -= dw; + draw_text(s8("}"), at, &text_spec); + } else { + draw_table_cell(ui, cell, it.cell_rect, it.alignment, text_spec, mouse); } } + return result; +} + +function void +draw_ui_view(BeamformerUI *ui, Variable *ui_view, Rect r, v2 mouse, TextSpec text_spec) +{ + ASSERT(ui_view->type == VT_UI_VIEW); + UIView *view = &ui_view->u.view; + + if (view->needed_height - r.size.h < view->offset) + view->offset = view->needed_height - r.size.h; + + if (view->needed_height - r.size.h < 0) + view->offset = 0; + + r.pos.y -= view->offset; + + v2 size = {0}; + + Variable *var = view->child; + switch (var->type) { + case VT_GROUP: size = draw_ui_view_listing(ui, var, ui->arena, r, mouse, text_spec); break; + case VT_BEAMFORMER_FRAME_VIEW: { + BeamformerFrameView *bv = var->u.generic; + if (frame_view_ready_to_present(bv)) + draw_beamformer_frame_view(ui, ui->arena, var, r, mouse); + } break; + case VT_COMPUTE_PROGRESS_BAR: { + size = draw_compute_progress_bar(ui, ui->arena, &var->u.compute_progress_bar, r); + } break; + case VT_COMPUTE_LATEST_STATS_VIEW: + case VT_COMPUTE_STATS_VIEW: { + ComputeShaderStats *stats = var->u.compute_stats_view.stats; + if (var->type == VT_COMPUTE_LATEST_STATS_VIEW) + stats = *(ComputeShaderStats **)stats; + size = draw_compute_stats_view(var->u.compute_stats_view.ctx, ui->arena, stats, r); + } break; + default: INVALID_CODE_PATH; + } + view->needed_height = size.y; } -static void +function void draw_active_text_box(BeamformerUI *ui, Variable *var) { InputState *is = &ui->text_input_state; @@ -2834,7 +2840,7 @@ ui_init(BeamformerCtx *ctx, Arena store) RSD_VERTICAL, ui->font); split->u.region_split.right = add_beamformer_frame_view(ui, split, &ui->arena, FVT_LATEST, 0); - ui_fill_live_frame_view(ui, split->u.region_split.right->u.group.first->u.generic); + ui_fill_live_frame_view(ui, split->u.region_split.right->u.view.child->u.generic); split = split->u.region_split.left; split->u.region_split.left = add_beamformer_parameters_view(split, ctx);