ogl_beamforming

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

Commit: 9fca71254e69efeddbcba0524a4662d6bcfbb446
Parent: 6b005a1202c2747d9f037a063a81ca69f75739bb
Author: Randy Palamar
Date:   Mon, 20 Apr 2026 20:36:11 -0600

beamformer_shared_memory: extend dirty_regions to region_update_flags

Now arbitrary extra flags can also be included. This includes a
flag which is set in the library indicating that the UI should be
informed about a parameter set upload. This is the correct place
to decide when the UI should be informed, not in random places
based on broken heuristics.

Diffstat:
Mbeamformer_core.c | 21++++++++++++---------
Mbeamformer_shared_memory.c | 23+++++++++++++++++------
Mlib/ogl_beamformer_lib.c | 4++++
Mui.c | 54++++++++++++++++++++----------------------------------
4 files changed, 53 insertions(+), 49 deletions(-)

diff --git a/beamformer_core.c b/beamformer_core.c @@ -753,10 +753,14 @@ function void beamformer_commit_parameter_block(BeamformerCtx *ctx, BeamformerComputePlan *cp, u32 block, Arena arena) { BeamformerParameterBlock *pb = beamformer_parameter_block_lock(ctx->shared_memory, block, -1); - for EachBit(pb->dirty_regions, region) { + for EachBit(pb->region_update_flags, region) { switch (region) { - case BeamformerParameterBlockRegion_ComputePipeline: - case BeamformerParameterBlockRegion_Parameters: + case BeamformerParameterRegionFlag_NotifyUI:{ + atomic_store_u32(&ctx->ui_dirty_parameter_blocks, 1u << block); + }break; + + case BeamformerParameterRegionFlag_ComputePipeline: + case BeamformerParameterRegionFlag_Parameters: { cp->output_points = das_valid_points(pb->parameters.output_points.xyz); cp->average_frames = pb->parameters.output_points.E[3]; @@ -766,7 +770,7 @@ beamformer_commit_parameter_block(BeamformerCtx *ctx, BeamformerComputePlan *cp, /* NOTE(rnp): these are both handled by plan_compute_pipeline() */ u32 mask = 1 << BeamformerParameterBlockRegion_ComputePipeline | 1 << BeamformerParameterBlockRegion_Parameters; - pb->dirty_regions &= ~mask; + pb->region_update_flags &= ~mask; for (u32 shader_slot = 0; shader_slot < cp->pipeline.shader_count; shader_slot++) { u128 hash = u128_hash_from_data(cp->shader_descriptors + shader_slot, sizeof(BeamformerShaderDescriptor)); @@ -801,15 +805,15 @@ beamformer_commit_parameter_block(BeamformerCtx *ctx, BeamformerComputePlan *cp, case BeamformerParameterBlockRegion_ChannelMapping:{ cuda_set_channel_mapping(pb->channel_mapping); }break; - case BeamformerParameterBlockRegion_FocalVectors: - case BeamformerParameterBlockRegion_SparseElements: - case BeamformerParameterBlockRegion_TransmitReceiveOrientations: + case BeamformerParameterRegionFlag_FocalVectors: + case BeamformerParameterRegionFlag_SparseElements: + case BeamformerParameterRegionFlag_TransmitReceiveOrientations: { BeamformerComputeTextureKind texture_kind = 0; u32 pixel_type = 0, texture_format = 0; switch (region) { #define X(kind, _gl, tf, pt, ...) \ - case BeamformerParameterBlockRegion_## kind:{ \ + case BeamformerParameterRegionFlag_##kind:{ \ texture_kind = BeamformerComputeTextureKind_## kind; \ texture_format = tf; \ pixel_type = pt; \ @@ -1109,7 +1113,6 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena *arena, iptr gl_c if (beamformer_parameter_block_dirty(sm, work->compute_context.parameter_block)) { u32 block = work->compute_context.parameter_block; beamformer_commit_parameter_block(ctx, cp, block, *arena); - atomic_store_u32(&ctx->ui_dirty_parameter_blocks, (u32)(ctx->beamform_work_queue != q) << block); } post_sync_barrier(ctx->shared_memory, work->lock); diff --git a/beamformer_shared_memory.c b/beamformer_shared_memory.c @@ -1,5 +1,5 @@ /* See LICENSE for license details. */ -#define BEAMFORMER_SHARED_MEMORY_VERSION (25UL) +#define BEAMFORMER_SHARED_MEMORY_VERSION (26UL) typedef struct BeamformerFrame BeamformerFrame; @@ -80,7 +80,11 @@ typedef enum {BEAMFORMER_LIVE_IMAGING_DIRTY_FLAG_LIST} BeamformerLiveImagingDirt X(FocalVectors, focal_vectors) \ X(Parameters, parameters) \ X(SparseElements, sparse_elements) \ - X(TransmitReceiveOrientations, transmit_receive_orientations) + X(TransmitReceiveOrientations, transmit_receive_orientations) \ + +#define BEAMFORMER_PARAMETER_BLOCK_REGION_FLAG_LIST \ + BEAMFORMER_PARAMETER_BLOCK_REGION_LIST \ + X(NotifyUI) \ typedef enum { #define X(k, ...) BeamformerParameterBlockRegion_##k, @@ -89,6 +93,13 @@ typedef enum { BeamformerParameterBlockRegion_Count } BeamformerParameterBlockRegions; +typedef enum { + #define X(k, ...) BeamformerParameterRegionFlag_##k, + BEAMFORMER_PARAMETER_BLOCK_REGION_FLAG_LIST + #undef X + BeamformerParameterRegionFlag_Count, +} BeamformerParameterRegionFlags; + typedef union { u8 filter_slot; } BeamformerShaderParameters; @@ -111,8 +122,8 @@ typedef struct { }; /* NOTE(rnp): signals to the beamformer that a subregion of a block has been updated */ - u32 dirty_regions; - static_assert(BeamformerParameterBlockRegion_Count <= 32, "only 32 parameter block regions supported"); + u32 region_update_flags; + static_assert(BeamformerParameterRegionFlag_Count <= 32, ""); BeamformerComputePipeline pipeline; @@ -247,7 +258,7 @@ beamformer_parameter_block(BeamformerSharedMemory *sm, u32 block) function b32 beamformer_parameter_block_dirty(BeamformerSharedMemory *sm, u32 block) { - b32 result = beamformer_parameter_block(sm, block)->dirty_regions != 0; + b32 result = beamformer_parameter_block(sm, block)->region_update_flags != 0; return result; } @@ -282,7 +293,7 @@ function void mark_parameter_block_region_dirty(BeamformerSharedMemory *sm, u32 block, BeamformerParameterBlockRegions region) { BeamformerParameterBlock *pb = beamformer_parameter_block(sm, block); - atomic_or_u32(&pb->dirty_regions, 1u << region); + atomic_or_u32(&pb->region_update_flags, 1u << region); } function void diff --git a/lib/ogl_beamformer_lib.c b/lib/ogl_beamformer_lib.c @@ -488,6 +488,10 @@ beamformer_push_parameters_at(BeamformerParameters *bp, u32 block) BeamformerParameterBlockRegion_Parameters, offsetof(BeamformerParameterBlock, parameters), g_beamformer_library_context.timeout_ms); + if (result) { + BeamformerParameterBlock *pb = beamformer_parameter_block(g_beamformer_library_context.bp, block); + atomic_or_u32(&pb->region_update_flags, 1u << BeamformerParameterRegionFlag_NotifyUI); + } } return result; } diff --git a/ui.c b/ui.c @@ -434,8 +434,6 @@ 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; @@ -4109,42 +4107,32 @@ 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); - u64 timestamp = os_timer_count(); - f64 timestamp_delta = (f64)(timestamp - ui->parameters_update_timestamp) - / os_system_info()->timer_frequency; - ui->parameters_update_timestamp = timestamp; + 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 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)); - - if (recompute) { - mark_parameter_block_region_dirty(ui->shared_memory, selected_block, - BeamformerParameterBlockRegion_Parameters); - beamformer_queue_compute(ctx, frame_to_draw, selected_block); - } + 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); + 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); } } @@ -4202,8 +4190,6 @@ 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); }