ogl_beamforming

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

Commit: 41ef29023e2589452bcaf9f7a38475bf54ddd78e
Parent: 85eb7561effdd6cbe0af2a1b9b8a66ae7bf98a33
Author: Randy Palamar
Date:   Sun, 23 Mar 2025 20:08:53 -0600

core: use a ping-pong buffer for average frames when averaging

The ensures there is no flickering or texture glitches when
drawing and gives us a frame-change-edge to rerender a frame view on.

closes: #28

Diffstat:
Mbeamformer.c | 71++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mbeamformer.h | 5+++--
2 files changed, 45 insertions(+), 31 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -61,6 +61,8 @@ static void alloc_beamform_frame(GLParams *gp, BeamformFrame *out, ComputeShaderStats *out_stats, uv3 out_dim, u32 frame_index, s8 name) { + out->ready_to_present = 0; + out->dim.x = CLAMP(round_down_power_of_2(ORONE(out_dim.x)), 1, gp->max_3d_texture_dim); out->dim.y = CLAMP(round_down_power_of_2(ORONE(out_dim.y)), 1, gp->max_3d_texture_dim); out->dim.z = CLAMP(round_down_power_of_2(ORONE(out_dim.z)), 1, gp->max_3d_texture_dim); @@ -88,17 +90,6 @@ alloc_beamform_frame(GLParams *gp, BeamformFrame *out, ComputeShaderStats *out_s } static void -alloc_output_image(BeamformerCtx *ctx, uv3 output_dim) -{ - uv3 try_dim = make_valid_test_dim(output_dim); - if (!uv3_equal(try_dim, ctx->averaged_frame.dim)) { - alloc_beamform_frame(&ctx->gl, &ctx->averaged_frame, - &ctx->averaged_frame_compute_stats, try_dim, 0, - s8("Beamformed_Averaged_Data")); - } -} - -static void alloc_shader_storage(BeamformerCtx *ctx, Arena a) { ComputeShaderCtx *cs = &ctx->csctx; @@ -419,22 +410,26 @@ do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformFrame *frame, Compute #endif } break; case CS_SUM: { - ctx->averaged_frame.ready_to_present = 0; + u32 aframe_index = atomic_inc(&ctx->averaged_frame_index, 1); + aframe_index %= ARRAY_COUNT(ctx->averaged_frames); + BeamformFrame *aframe = ctx->averaged_frames + aframe_index; + aframe->ready_to_present = 0; /* TODO(rnp): hack we need a better way of specifying which frames to sum; * this is fine for rolling averaging but what if we want to do something else */ u32 base_index = CLAMP(frame - ctx->beamform_frames, 0, ARRAY_COUNT(ctx->beamform_frames)); + u32 to_average = ctx->params->raw.output_points.w; u32 frame_count = 0; u32 *in_textures = alloc(&arena, u32, MAX_BEAMFORMED_SAVED_FRAMES); - BeamformFrameIterator bfi = beamform_frame_iterator(ctx, 1 + base_index - ctx->params->raw.output_points.w, - ctx->params->raw.output_points.w); + BeamformFrameIterator bfi = beamform_frame_iterator(ctx, 1 + base_index - to_average, + to_average); for (BeamformFrame *it = frame_next(&bfi); it; it = frame_next(&bfi)) in_textures[frame_count++] = it->texture; do_sum_shader(csctx, in_textures, frame_count, 1 / (f32)frame_count, - ctx->averaged_frame.texture, ctx->averaged_frame.dim); - ctx->averaged_frame.min_coordinate = frame->min_coordinate; - ctx->averaged_frame.max_coordinate = frame->max_coordinate; - ctx->averaged_frame.compound_count = frame->compound_count; - ctx->averaged_frame.das_shader_id = frame->das_shader_id; + aframe->texture, aframe->dim); + aframe->min_coordinate = frame->min_coordinate; + aframe->max_coordinate = frame->max_coordinate; + aframe->compound_count = frame->compound_count; + aframe->das_shader_id = frame->das_shader_id; } break; default: ASSERT(0); } @@ -642,13 +637,26 @@ DEBUG_EXPORT BEAMFORMER_COMPLETE_COMPUTE_FN(beamformer_complete_compute) if (cs->programs[CS_DAS]) glProgramUniform1ui(cs->programs[CS_DAS], cs->cycle_t_id, cycle_t++); - uv3 try_dim = ctx->params->raw.output_points.xyz; + uv3 try_dim = make_valid_test_dim(ctx->params->raw.output_points.xyz); if (!uv3_equal(try_dim, frame->store->dim)) { iz frame_index = frame->store - ctx->beamform_frames; alloc_beamform_frame(&ctx->gl, frame->store, frame->stats, try_dim, frame_index, s8("Beamformed_Data")); } + if (ctx->params->raw.output_points.w > 1) { + if (!uv3_equal(try_dim, ctx->averaged_frames[0].dim)) { + alloc_beamform_frame(&ctx->gl, ctx->averaged_frames + 0, + ctx->averaged_frame_compute_stats + 0, + try_dim, 0, + s8("Beamformed_Averaged_Data")); + alloc_beamform_frame(&ctx->gl, ctx->averaged_frames + 1, + ctx->averaged_frame_compute_stats + 1, + try_dim, 1, + s8("Beamformed_Averaged_Data")); + } + } + frame->store->in_flight = 1; frame->store->min_coordinate = ctx->params->raw.output_min_coordinate; frame->store->max_coordinate = ctx->params->raw.output_max_coordinate; @@ -680,10 +688,12 @@ DEBUG_EXPORT BEAMFORMER_COMPLETE_COMPUTE_FN(beamformer_complete_compute) } if (did_sum_shader) { - ctx->averaged_frame.ready_to_present = 1; + u32 aframe_index = !(ctx->averaged_frame_index % + ARRAY_COUNT(ctx->averaged_frames)); + ctx->averaged_frames[aframe_index].ready_to_present = 1; /* TODO(rnp): not really sure what to do here */ - mem_copy(ctx->averaged_frame_compute_stats.times, frame->stats->times, - sizeof(frame->stats->times)); + mem_copy(ctx->averaged_frame_compute_stats[aframe_index].times, + frame->stats->times, sizeof(frame->stats->times)); } frame->store->ready_to_present = 1; cs->processing_compute = 0; @@ -755,8 +765,12 @@ DEBUG_EXPORT BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step) export->type = BW_SAVE_FRAME; export->output_frame_ctx.file_handle = f; if (ctx->params->raw.output_points.w > 1) { - export->output_frame_ctx.frame.store = &ctx->averaged_frame; - export->output_frame_ctx.frame.stats = &ctx->averaged_frame_compute_stats; + u32 a_index = ctx->averaged_frame_index % + ARRAY_COUNT(ctx->averaged_frames); + BeamformFrame *aframe = ctx->averaged_frames + a_index; + ComputeShaderStats *astats = ctx->averaged_frame_compute_stats + a_index; + export->output_frame_ctx.frame.store = aframe; + export->output_frame_ctx.frame.stats = astats; } else { export->output_frame_ctx.frame = compute->frame; } @@ -769,8 +783,6 @@ DEBUG_EXPORT BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step) /* TODO(rnp): clean this up */ ctx->ui_read_params = 1; } - - alloc_output_image(ctx, bp->output_points.xyz); } } @@ -791,8 +803,9 @@ DEBUG_EXPORT BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step) BeamformFrame *frame_to_draw; ComputeShaderStats *frame_compute_stats; if (bp->output_points.w > 1) { - frame_to_draw = &ctx->averaged_frame; - frame_compute_stats = &ctx->averaged_frame_compute_stats; + u32 a_index = ctx->averaged_frame_index % ARRAY_COUNT(ctx->averaged_frames); + frame_to_draw = ctx->averaged_frames + a_index; + frame_compute_stats = ctx->averaged_frame_compute_stats + a_index; } else { frame_to_draw = ctx->beamform_frames + ctx->display_frame_index; frame_compute_stats = ctx->beamform_frame_compute_stats + ctx->display_frame_index; diff --git a/beamformer.h b/beamformer.h @@ -227,8 +227,9 @@ typedef struct BeamformerCtx { u32 display_frame_index; /* NOTE: this will only be used when we are averaging */ - BeamformFrame averaged_frame; - ComputeShaderStats averaged_frame_compute_stats; + u32 averaged_frame_index; + BeamformFrame averaged_frames[2]; + ComputeShaderStats averaged_frame_compute_stats[2]; ComputeShaderCtx csctx; FragmentShaderCtx fsctx;