ogl_beamforming

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

Commit: f3fd61ef5a82e14f936b0fc854da1f7606e5213d
Parent: 20ab88cdec5da5c429ac14b297e54076564a6692
Author: Randy Palamar
Date:   Fri,  4 Jul 2025 09:02:45 -0600

core/lib: add live imaging parameters sharing

Diffstat:
Mbeamformer_parameters.h | 11+++++++++++
Mbeamformer_work_queue.h | 7+++++++
Mhelpers/ogl_beamformer_lib.c | 37+++++++++++++++++++++++++++++++++++--
Mhelpers/ogl_beamformer_lib_base.h | 6++++++
Mintrinsics.c | 4++++
Mui.c | 69+++++++++++++++++++++++++++++++++++++++++++++++----------------------
6 files changed, 110 insertions(+), 24 deletions(-)

diff --git a/beamformer_parameters.h b/beamformer_parameters.h @@ -161,3 +161,14 @@ _Static_assert((offsetof(BeamformerParameters, output_min_coordinate) & 15) == 0 _Static_assert((sizeof(BeamformerParameters) & 15) == 0, "sizeof(BeamformerParameters) must be a multiple of 16"); #endif + +#define BEAMFORMER_LIVE_IMAGING_DIRTY_FLAG_LIST \ + X(ImagePlaneOffsets, 0) \ + X(TransmitPower, 1) +/* NOTE(rnp): if this exceeds 32 you need to fix the flag handling code */ + +typedef struct { + uint32_t active; + float transmit_power; + float image_plane_offsets[BeamformerViewPlaneTag_Count]; +} BeamformerLiveImagingParameters; diff --git a/beamformer_work_queue.h b/beamformer_work_queue.h @@ -89,6 +89,10 @@ typedef BEAMFORM_WORK_QUEUE_PUSH_COMMIT_FN(beamform_work_queue_push_commit_fn); #define BEAMFORMER_SCRATCH_SIZE (BEAMFORMER_SHARED_MEMORY_SIZE - BEAMFORMER_SCRATCH_OFF) #define BEAMFORMER_MAX_RF_DATA_SIZE (BEAMFORMER_SCRATCH_SIZE) +#define X(name, id) BeamformerLiveImagingDirtyFlags_##name = (1 << id), +typedef enum {BEAMFORMER_LIVE_IMAGING_DIRTY_FLAG_LIST} BeamformerLiveImagingDirtyFlags; +#undef X + typedef struct { u32 version; @@ -128,6 +132,9 @@ typedef struct { /* TODO(rnp): this shouldn't be needed */ b32 export_next_frame; + BeamformerLiveImagingParameters live_imaging_parameters; + BeamformerLiveImagingDirtyFlags live_imaging_dirty_flags; + BeamformWorkQueue external_work_queue; } BeamformerSharedMemory; diff --git a/helpers/ogl_beamformer_lib.c b/helpers/ogl_beamformer_lib.c @@ -44,8 +44,7 @@ os_open_shared_memory_area(char *name) SharedMemoryRegion result = {0}; iptr h = OpenFileMappingA(FILE_MAP_ALL_ACCESS, 0, name); if (h != INVALID_FILE) { - void *new = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, - os_round_up_to_page_size(BEAMFORMER_SHARED_MEMORY_SIZE)); + void *new = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, BEAMFORMER_SHARED_MEMORY_SIZE); if (new) { u8 buffer[1024]; Stream sb = {.data = buffer, .cap = 1024}; @@ -422,3 +421,37 @@ beamformer_compute_timings(BeamformerComputeStatsTable *output, i32 timeout_ms) } return result; } + +i32 +beamformer_live_parameters_get_dirty_flag(void) +{ + i32 result = -1; + if (check_shared_memory()) { + u32 flag = ctz_u32(g_bp->live_imaging_dirty_flags); + if (flag != 32) { + atomic_and_u32(&g_bp->live_imaging_dirty_flags, ~(1 << flag)); + result = flag; + } + } + return result; +} + +BeamformerLiveImagingParameters * +beamformer_get_live_parameters(void) +{ + BeamformerLiveImagingParameters *result = 0; + if (check_shared_memory()) result = &g_bp->live_imaging_parameters; + return result; +} + +b32 +beamformer_set_live_parameters(BeamformerLiveImagingParameters *new) +{ + b32 result = 0; + if (check_shared_memory()) { + mem_copy(&g_bp->live_imaging_parameters, new, sizeof(*new)); + memory_write_barrier(); + result = 1; + } + return result; +} diff --git a/helpers/ogl_beamformer_lib_base.h b/helpers/ogl_beamformer_lib_base.h @@ -56,3 +56,9 @@ LIB_FN uint32_t beamformer_push_focal_vectors(float *vectors, uint32_t coun LIB_FN uint32_t beamformer_push_parameters(BeamformerParameters *, int32_t timeout_ms); LIB_FN uint32_t beamformer_push_parameters_ui(BeamformerUIParameters *, int32_t timeout_ms); LIB_FN uint32_t beamformer_push_parameters_head(BeamformerParametersHead *, int32_t timeout_ms); + +////////////////////////// +// Live Imaging Controls +LIB_FN int32_t beamformer_live_parameters_get_dirty_flag(void); +LIB_FN uint32_t beamformer_set_live_parameters(BeamformerLiveImagingParameters *); +LIB_FN BeamformerLiveImagingParameters *beamformer_get_live_parameters(void); diff --git a/intrinsics.c b/intrinsics.c @@ -26,6 +26,8 @@ #define debugbreak() __debugbreak() #define unreachable() __assume(0) + #define memory_write_barrier() _WriteBarrier() + #define atomic_store_u64(ptr, n) *((volatile u64 *)(ptr)) = (n) #define atomic_store_u32(ptr, n) *((volatile u32 *)(ptr)) = (n) #define atomic_load_u64(ptr) *((volatile u64 *)(ptr)) @@ -58,6 +60,8 @@ #endif #define unreachable() __builtin_unreachable() + #define memory_write_barrier() asm volatile ("" ::: "memory") + #define atomic_store_u64(ptr, n) __atomic_store_n(ptr, n, __ATOMIC_RELEASE) #define atomic_load_u64(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE) #define atomic_add_u64(ptr, n) __atomic_fetch_add(ptr, n, __ATOMIC_ACQ_REL) diff --git a/ui.c b/ui.c @@ -237,7 +237,6 @@ typedef enum { V_INPUT = 1 << 0, V_TEXT = 1 << 1, V_RADIO_BUTTON = 1 << 2, - V_IMAGING_PARAM = 1 << 28, V_CAUSES_COMPUTE = 1 << 29, V_UPDATE_VIEW = 1 << 30, } VariableFlags; @@ -377,6 +376,10 @@ struct BeamformerUI { b32 flush_params; FrameViewRenderContext *frame_view_render_context; + + /* TODO(rnp): hack? */ + SharedMemoryRegion shared_memory; + OS *os; }; @@ -1096,10 +1099,8 @@ add_beamformer_frame_view(BeamformerUI *ui, Variable *parent, Arena *arena, resize_frame_view(bv, (uv2){{FRAME_VIEW_RENDER_TARGET_SIZE}}, 0); glTextureParameteri(bv->textures[0], GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTextureParameteri(bv->textures[0], GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - fill_variable(bv->x_plane_shifts + 0, var, s8("XZ Shift"), V_INPUT|V_IMAGING_PARAM, - VT_X_PLANE_SHIFT, ui->small_font); - fill_variable(bv->x_plane_shifts + 1, var, s8("YZ Shift"), V_INPUT|V_IMAGING_PARAM, - VT_X_PLANE_SHIFT, ui->small_font); + fill_variable(bv->x_plane_shifts + 0, var, s8("XZ Shift"), V_INPUT, VT_X_PLANE_SHIFT, ui->small_font); + fill_variable(bv->x_plane_shifts + 1, var, s8("YZ Shift"), V_INPUT, VT_X_PLANE_SHIFT, ui->small_font); bv->demo = add_variable(ui, menu, arena, s8("Demo Mode"), V_INPUT|V_UPDATE_VIEW|V_RADIO_BUTTON, VT_B32, ui->small_font); }break; @@ -1294,6 +1295,15 @@ x_plane_size(BeamformerUI *ui) return result; } +function f32 +x_plane_rotation_for_view_plane(BeamformerFrameView *view, BeamformerViewPlaneTag tag) +{ + f32 result = view->rotation; + if (tag == BeamformerViewPlaneTag_YZ) + result += 0.25f; + return result; +} + function v2 normalized_p_in_rect(Rect r, v2 p, b32 invert_y) { @@ -1313,6 +1323,18 @@ x_plane_position(BeamformerUI *ui) } function v3 +offset_x_plane_position(BeamformerUI *ui, BeamformerFrameView *view, BeamformerViewPlaneTag tag) +{ + BeamformerSharedMemory *sm = ui->shared_memory.region; + BeamformerLiveImagingParameters *li = &sm->live_imaging_parameters; + m4 x_rotation = m4_rotation_about_y(x_plane_rotation_for_view_plane(view, tag)); + v3 Z = x_rotation.c[2].xyz; + v3 offset = v3_scale(Z, li->image_plane_offsets[tag]); + v3 result = v3_add(x_plane_position(ui), offset); + return result; +} + +function v3 camera_for_x_plane_view(BeamformerUI *ui, BeamformerFrameView *view) { v3 size = x_plane_size(ui); @@ -1370,15 +1392,6 @@ view_plane_tag_from_x_plane_shift(BeamformerFrameView *view, Variable *x_plane_s return result; } -function f32 -x_plane_rotation_for_view_plane(BeamformerFrameView *view, BeamformerViewPlaneTag tag) -{ - f32 result = view->rotation; - if (tag == BeamformerViewPlaneTag_YZ) - result += 0.25f; - return result; -} - function void render_single_xplane(BeamformerUI *ui, BeamformerFrameView *view, Variable *x_plane_shift, u32 program, f32 rotation_turns, v3 translate, BeamformerViewPlaneTag tag) @@ -1433,11 +1446,14 @@ render_3D_xplane(BeamformerUI *ui, BeamformerFrameView *view, u32 program) glProgramUniformMatrix4fv(program, FRAME_VIEW_VIEW_MATRIX_LOC, 1, 0, view_m.E); glProgramUniformMatrix4fv(program, FRAME_VIEW_PROJ_MATRIX_LOC, 1, 0, projection.E); - v3 model_translate = x_plane_position(ui); - render_single_xplane(ui, view, view->x_plane_shifts + 0, program, view->rotation, + v3 model_translate = offset_x_plane_position(ui, view, BeamformerViewPlaneTag_XZ); + render_single_xplane(ui, view, view->x_plane_shifts + 0, program, + x_plane_rotation_for_view_plane(view, BeamformerViewPlaneTag_XZ), model_translate, BeamformerViewPlaneTag_XZ); + model_translate = offset_x_plane_position(ui, view, BeamformerViewPlaneTag_YZ); model_translate.y -= 0.0001; - render_single_xplane(ui, view, view->x_plane_shifts + 1, program, view->rotation + 0.25f, + render_single_xplane(ui, view, view->x_plane_shifts + 1, program, + x_plane_rotation_for_view_plane(view, BeamformerViewPlaneTag_YZ), model_translate, BeamformerViewPlaneTag_YZ); } @@ -2169,13 +2185,18 @@ draw_3D_xplane_frame_view(BeamformerUI *ui, Arena arena, Variable *var, Rect dis i32 id = -1; if (hover_interaction(ui, mouse, auto_interaction(vr, var))) { ray mouse_ray = ray_for_x_plane_view(ui, view, normalized_p_in_rect(vr, mouse, 0)); - v3 x_position = x_plane_position(ui); v3 x_size = v3_scale(x_plane_size(ui), 0.5f); - m4 x_rotation = m4_rotation_about_y(view->rotation); + + f32 rotation = x_plane_rotation_for_view_plane(view, BeamformerViewPlaneTag_XZ); + m4 x_rotation = m4_rotation_about_y(rotation); + v3 x_position = offset_x_plane_position(ui, view, BeamformerViewPlaneTag_XZ); f32 test[2] = {0}; test[0] = obb_raycast(x_rotation, x_size, x_position, mouse_ray); - x_rotation = m4_rotation_about_y(view->rotation + 0.25); + + x_position = offset_x_plane_position(ui, view, BeamformerViewPlaneTag_YZ); + rotation = x_plane_rotation_for_view_plane(view, BeamformerViewPlaneTag_YZ); + x_rotation = m4_rotation_about_y(rotation); test[1] = obb_raycast(x_rotation, x_size, x_position, mouse_ray); if (test[0] >= 0 && test[1] >= 0) id = test[1] < test[0]? 1 : 0; @@ -3259,9 +3280,12 @@ ui_end_interact(BeamformerUI *ui, v2 mouse) m4 x_rotation = m4_rotation_about_y(rotation); v3 Z = x_rotation.c[2].xyz; f32 delta = v3_dot(Z, v3_sub(xp->end_point, xp->start_point)); - /* TODO(rnp): commit to image parameters shared memory */ - (void)delta; xp->start_point = xp->end_point; + + BeamformerSharedMemory *sm = ui->shared_memory.region; + BeamformerLiveImagingParameters *li = &sm->live_imaging_parameters; + li->image_plane_offsets[plane] += delta; + atomic_or_u32(&sm->live_imaging_dirty_flags, BeamformerLiveImagingDirtyFlags_ImagePlaneOffsets); }break; default:{}break; } @@ -3454,6 +3478,7 @@ ui_init(BeamformerCtx *ctx, Arena store) ui->arena = store; ui->frame_view_render_context = &ctx->frame_view_render_context; ui->unit_cube_model = ctx->csctx.unit_cube_model; + ui->shared_memory = ctx->shared_memory; /* TODO: build these into the binary */ /* TODO(rnp): better font, this one is jank at small sizes */