ogl_beamforming

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

Commit: ce873093639fe84a5045638e41680d8b575729a8
Parent: b8477f3fa4abb28270edb66c0026f8da72fb8724
Author: Randy Palamar
Date:   Mon, 20 Apr 2026 12:26:09 -0600

ui: fix region updates during live imaging

the ui parameters still get marked as out of date when the region
is updated during live imaging. this meant that the UI would
assume the DAS transform had changed and therefore the UI's
internal state should be updated. There is no easy way around this
so instead the UI now stores a hash of the API provided DAS
transform and uses that to determine when the UI state needs to be
reset. A timestamp is included as a basic detection of when the
user stopped and restarted imaging (in this case we also want to
reset the UI state).

Diffstat:
Mui.c | 54++++++++++++++++++++++++++++++++++--------------------
Mutil.h | 1+
2 files changed, 35 insertions(+), 20 deletions(-)

diff --git a/ui.c b/ui.c @@ -434,6 +434,8 @@ struct BeamformerUI { b32 flush_params; u32 selected_parameter_block; + u64 parameters_update_timestamp; + u64 das_transform_hash; v2 min_coordinate; v2 max_coordinate; f32 off_axis_position; @@ -4107,32 +4109,42 @@ draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformerFrame *frame_to_dr atomic_and_u32(&ctx->ui_dirty_parameter_blocks, ~selected_mask); beamformer_parameter_block_unlock(ui->shared_memory, selected_block); - BeamformerComputePlan *cp = ui->beamformer_context->compute_context.compute_plans[selected_block]; - m4 identity = m4_identity(); - b32 recompute = !m4_equal(identity, cp->ui_voxel_transform); - mem_copy(cp->ui_voxel_transform.E, identity.E, sizeof(identity)); + u64 timestamp = os_timer_count(); + f64 timestamp_delta = (f64)(timestamp - ui->parameters_update_timestamp) + / os_system_info()->timer_frequency; + ui->parameters_update_timestamp = timestamp; - if (recompute) { - mark_parameter_block_region_dirty(ui->shared_memory, selected_block, - BeamformerParameterBlockRegion_Parameters); - beamformer_queue_compute(ctx, frame_to_draw, selected_block); - } + u64 das_transform_hash = u64_hash_from_s8(s8_struct(&das_transform)); + if (das_transform_hash != ui->das_transform_hash || timestamp_delta > 2.0) { + ui->das_transform_hash = das_transform_hash; + + BeamformerComputePlan *cp = ui->beamformer_context->compute_context.compute_plans[selected_block]; + m4 identity = m4_identity(); + b32 recompute = !m4_equal(identity, cp->ui_voxel_transform); + mem_copy(cp->ui_voxel_transform.E, identity.E, sizeof(identity)); - v3 U = v3_normalize(das_transform.c[0].xyz); - v3 V = v3_normalize(das_transform.c[1].xyz); - v3 N = cross(V, U); + if (recompute) { + mark_parameter_block_region_dirty(ui->shared_memory, selected_block, + BeamformerParameterBlockRegion_Parameters); + beamformer_queue_compute(ctx, frame_to_draw, selected_block); + } + + v3 U = v3_normalize(das_transform.c[0].xyz); + v3 V = v3_normalize(das_transform.c[1].xyz); + v3 N = cross(V, U); - ui->off_axis_position = v3_dot(N, das_transform.c[3].xyz); - ui->beamform_plane = 0; + ui->off_axis_position = v3_dot(N, das_transform.c[3].xyz); + ui->beamform_plane = 0; - v3 min_coordinate = m4_mul_v3(das_transform, (v3){{0.0f, 0.0f, 0.0f}}); - v3 max_coordinate = m4_mul_v3(das_transform, (v3){{1.0f, 1.0f, 1.0f}}); + v3 min_coordinate = m4_mul_v3(das_transform, (v3){{0.0f, 0.0f, 0.0f}}); + v3 max_coordinate = m4_mul_v3(das_transform, (v3){{1.0f, 1.0f, 1.0f}}); - ui->min_coordinate.x = v3_dot(U, min_coordinate); - ui->min_coordinate.y = v3_dot(V, min_coordinate); + ui->min_coordinate.x = v3_dot(U, min_coordinate); + ui->min_coordinate.y = v3_dot(V, min_coordinate); - ui->max_coordinate.x = v3_dot(U, max_coordinate); - ui->max_coordinate.y = v3_dot(V, max_coordinate); + ui->max_coordinate.x = v3_dot(U, max_coordinate); + ui->max_coordinate.y = v3_dot(V, max_coordinate); + } } } @@ -4190,6 +4202,8 @@ draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformerFrame *frame_to_dr BeamformerParameterBlockRegion_Parameters); beamformer_parameter_block_unlock(ui->shared_memory, selected_block); + ui->parameters_update_timestamp = os_timer_count(); + if (recompute) beamformer_queue_compute(ctx, frame_to_draw, selected_block); } diff --git a/util.h b/util.h @@ -213,6 +213,7 @@ typedef struct { Arena *arena, original_arena; } TempArena; typedef struct { iz len; u8 *data; } s8; #define s8(s) (s8){.len = countof(s) - 1, .data = (u8 *)s} #define s8_comp(s) {sizeof(s) - 1, (u8 *)s} +#define s8_struct(v) (s8){.len = sizeof(*v), .data = (u8 *)v} typedef struct { iz len; u16 *data; } s16;