ogl_beamforming

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

Commit: 29d0e03ddc27128970bdde7a6890b001e77e59f1
Parent: 9792bfe783355418e55f9b520f5239394fd7bc72
Author: Randy Palamar
Date:   Tue, 14 Jan 2025 08:15:44 -0700

ui: don't stretch the output when changing view region

To aid in this we now store the region used to beamform a
BeamformFrame in the struct. This will have other uses as well.

closes: #10

Diffstat:
Mbeamformer.c | 47+++++++++++++++++++++++++++--------------------
Mbeamformer.h | 6++++++
Mui.c | 40++++++++++++++++++++++++++++------------
Mutil.c | 11+++++++++++
4 files changed, 72 insertions(+), 32 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -507,13 +507,27 @@ do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformFrame *frame, u32 raw glEndQuery(GL_TIME_ELAPSED); } +static BeamformFrame * +start_beamform_compute_work(BeamformWork *work, ComputeShaderCtx *cs, BeamformerParametersFull *bpf) +{ + BeamformFrame *result = work->compute_ctx.frame; + if (bpf->upload) { + glNamedBufferSubData(cs->shared_ubo, 0, sizeof(bpf->raw), &bpf->raw); + bpf->upload = 0; + } + + result->min_coordinate = bpf->raw.output_min_coordinate; + result->max_coordinate = bpf->raw.output_max_coordinate; + + return result; +} + static void do_beamform_work(BeamformerCtx *ctx, Arena *a) { - BeamformerParameters *bp = &ctx->params->raw; - BeamformWorkQueue *q = &ctx->beamform_work_queue; - BeamformWork *work = beamform_work_queue_pop(q); - ComputeShaderCtx *cs = &ctx->csctx; + BeamformWorkQueue *q = &ctx->beamform_work_queue; + BeamformWork *work = beamform_work_queue_pop(q); + ComputeShaderCtx *cs = &ctx->csctx; while (work) { switch (work->type) { @@ -521,10 +535,7 @@ do_beamform_work(BeamformerCtx *ctx, Arena *a) BeamformFrame *frame = work->compute_ctx.frame; if (work->compute_ctx.first_pass) { - if (ctx->params->upload) { - glNamedBufferSubData(cs->shared_ubo, 0, sizeof(*bp), bp); - ctx->params->upload = 0; - } + start_beamform_compute_work(work, cs, ctx->params); PartialComputeCtx *pc = &ctx->partial_compute_ctx; pc->runtime = 0; @@ -575,12 +586,7 @@ do_beamform_work(BeamformerCtx *ctx, Arena *a) } break; case BW_FULL_COMPUTE: case BW_RECOMPUTE: { - BeamformFrame *frame = work->compute_ctx.frame; - - if (ctx->params->upload) { - glNamedBufferSubData(cs->shared_ubo, 0, sizeof(*bp), bp); - ctx->params->upload = 0; - } + BeamformFrame *frame = start_beamform_compute_work(work, cs, ctx->params); u32 stage_count = ctx->params->compute_stages_count; enum compute_shaders *stages = ctx->params->compute_stages; @@ -737,7 +743,7 @@ DEBUG_EXPORT BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step) do_beamform_work(ctx, arena); /* NOTE: draw output image texture using render fragment shader */ - b32 output_image_drawn; + BeamformFrame *frame_to_draw = 0; BeginTextureMode(ctx->fsctx.output); ClearBackground(PINK); BeginShaderMode(ctx->fsctx.shader); @@ -745,13 +751,14 @@ DEBUG_EXPORT BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step) glUseProgram(fs->shader.id); u32 out_texture = 0; if (bp->output_points.w > 1) { - out_texture = ctx->averaged_frame.textures[0]; + frame_to_draw = &ctx->averaged_frame; + out_texture = ctx->averaged_frame.textures[0]; } else { - BeamformFrame *f = ctx->beamform_frames + ctx->displayed_frame_index; + frame_to_draw = ctx->beamform_frames + ctx->displayed_frame_index; /* NOTE: verify we have actually beamformed something yet */ - if (f->dim.w) out_texture = f->textures[f->dim.w - 1]; + if (frame_to_draw->dim.w) + out_texture = frame_to_draw->textures[frame_to_draw->dim.w - 1]; } - output_image_drawn = out_texture != 0; glBindTextureUnit(0, out_texture); glUniform1f(fs->db_cutoff_id, fs->db); glUniform1f(fs->threshold_id, fs->threshold); @@ -768,7 +775,7 @@ DEBUG_EXPORT BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step) ctx->flags &= ~GEN_MIPMAPS; } - draw_ui(ctx, input, output_image_drawn); + draw_ui(ctx, input, frame_to_draw); if (IsKeyPressed(KEY_R)) { ctx->flags |= RELOAD_SHADERS; diff --git a/beamformer.h b/beamformer.h @@ -228,6 +228,12 @@ typedef struct { * is always found in textures[dim.w - 1] */ u32 textures[MAX_MULTI_XDC_COUNT + 1]; uv4 dim; + + /* NOTE: for use when displaying either prebeamformed frames or on the current frame + * when we intend to recompute on the next frame */ + v4 min_coordinate; + v4 max_coordinate; + u32 mips; } BeamformFrame; diff --git a/ui.c b/ui.c @@ -209,7 +209,7 @@ do_scale_bar(BeamformerUI *ui, Stream *buf, Variable var, v2 mouse, i32 directio } static void -draw_display_overlay(BeamformerCtx *ctx, Arena a, v2 mouse, Rect display_rect) +draw_display_overlay(BeamformerCtx *ctx, Arena a, v2 mouse, Rect display_rect, BeamformFrame *frame) { BeamformerUI *ui = ctx->ui; BeamformerParameters *bp = &ctx->params->raw; @@ -218,12 +218,6 @@ draw_display_overlay(BeamformerCtx *ctx, Arena a, v2 mouse, Rect display_rect) Stream buf = arena_stream(&a); Texture *output = &ctx->fsctx.output.texture; - /* TODO: this depends on the direction being rendered (x vs y) */ - v2 output_dim = { - .x = bp->output_max_coordinate.x - bp->output_min_coordinate.x, - .y = bp->output_max_coordinate.z - bp->output_min_coordinate.z, - }; - v2 txt_s = measure_text(ui->small_font, s8("-288.8 mm")); display_rect.pos.x += 0.02 * display_rect.size.w; @@ -238,7 +232,17 @@ draw_display_overlay(BeamformerCtx *ctx, Arena a, v2 mouse, Rect display_rect) vr.size.h = display_rect.size.h - pad; vr.size.w = display_rect.size.w - pad; - f32 aspect = output_dim.h / output_dim.w; + /* TODO(rnp): make this depend on the requested draw orientation (x-z or y-z or x-y) */ + v2 output_dim = { + .x = frame->max_coordinate.x - frame->min_coordinate.x, + .y = frame->max_coordinate.z - frame->min_coordinate.z, + }; + v2 requested_dim = { + .x = bp->output_max_coordinate.x - bp->output_min_coordinate.x, + .y = bp->output_max_coordinate.z - bp->output_min_coordinate.z, + }; + + f32 aspect = requested_dim.h / requested_dim.w; if (display_rect.size.h < (vr.size.w * aspect) + pad) { vr.size.w = vr.size.h / aspect; } else { @@ -247,7 +251,19 @@ draw_display_overlay(BeamformerCtx *ctx, Arena a, v2 mouse, Rect display_rect) vr.pos.x += (display_rect.size.w - (vr.size.w + pad)) / 2; vr.pos.y += (display_rect.size.h - (vr.size.h + pad)) / 2; - Rectangle tex_r = { 0.0f, 0.0f, (f32)output->width, -(f32)output->height }; + v2 pixels_per_meter = { + .w = (f32)output->width / output_dim.w, + .h = (f32)output->height / output_dim.h, + }; + + v2 texture_points = mul_v2(pixels_per_meter, requested_dim); + /* TODO(rnp): this also depends on x-y, y-z, x-z */ + v2 texture_start = { + .x = pixels_per_meter.x * 0.5 * (output_dim.x - requested_dim.x), + .y = pixels_per_meter.y * (frame->max_coordinate.z - bp->output_max_coordinate.z), + }; + + Rectangle tex_r = {texture_start.x, texture_start.y, texture_points.x, -texture_points.y}; NPatchInfo tex_np = { tex_r, 0, 0, 0, 0, NPATCH_NINE_PATCH }; DrawTextureNPatch(*output, tex_np, vr.rl, (Vector2){0}, 0, WHITE); @@ -1120,7 +1136,7 @@ ui_init(BeamformerCtx *ctx, Arena store) } static void -draw_ui(BeamformerCtx *ctx, BeamformerInput *input, b32 draw_display) +draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformFrame *frame_to_draw) { BeamformerUI *ui = ctx->ui; @@ -1144,8 +1160,8 @@ draw_ui(BeamformerCtx *ctx, BeamformerInput *input, b32 draw_display) rr.pos.x = lr.pos.x + lr.size.w; draw_settings_ui(ctx, lr, mouse); - if (draw_display) - draw_display_overlay(ctx, ui->arena_for_frame, mouse, rr); + if (frame_to_draw->dim.w) + draw_display_overlay(ctx, ui->arena_for_frame, mouse, rr, frame_to_draw); draw_debug_overlay(ctx, ui->arena_for_frame, lr); EndDrawing(); } diff --git a/util.c b/util.c @@ -366,6 +366,17 @@ magnitude_v2(v2 a) return result; } +static v4 +sub_v4(v4 a, v4 b) +{ + v4 result; + result.x = a.x - b.x; + result.y = a.y - b.y; + result.z = a.z - b.z; + result.w = a.w - b.w; + return result; +} + static f64 parse_f64(s8 s) {