ogl_beamforming

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

Commit: 5d25b3e6c3e82a494d8711fcf387fab9fe9e8fb7
Parent: a4c7561750ffdfaf52ef28da28e44f3756d8b254
Author: Tyler Henry
Date:   Fri, 31 Jul 2026 14:13:02 -0600

lib: add bulk frame export

closes #49

Diffstat:
Mbeamformer_core.c | 20++++++++++++++------
Mbeamformer_shared_memory.c | 5+++--
Mlib/ogl_beamformer_lib.c | 21++++++++++++++-------
Mlib/ogl_beamformer_lib_base.h | 17+++++++++++++++++
4 files changed, 48 insertions(+), 15 deletions(-)

diff --git a/beamformer_core.c b/beamformer_core.c @@ -1432,18 +1432,26 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena *arena) BeamformerExportContext *ec = &work->export_context; switch (ec->kind) { case BeamformerExportKind_BeamformedData:{ - BeamformerFrame *f = ctx->latest_frame; - if (f) { + BeamformerFrameBacklog *bl = &ctx->compute_context.backlog; + u32 req_count = Clamp(ec->count, 1, bl->counter); + u32 frame_idx = bl->counter - req_count; + u8 *sm_output = beamformer_shared_memory_scratch_arena(sm, ctx->shared_memory_size).beg; + u64 exported_size = 0; + for (u32 export_count = 0; export_count < req_count; export_count++, frame_idx++) { + BeamformerFrame *f = bl->frames + frame_idx % countof(bl->frames); u64 frame_size = beamformer_frame_byte_size(f->points, f->data_kind); assert((frame_size & 63) == 0); - if (frame_size <= ec->size) { + // NOTE(tkh) we don't want to assume that all req_count frames are the same size, + // so we either need to count the total size of all requested frames first or + // just fill up as much as possible. + if (exported_size + frame_size <= ec->size) { vk_host_wait_timeline(VulkanTimeline_Compute, f->timeline_valid_value, -1ULL); - vk_buffer_range_download(beamformer_shared_memory_scratch_arena(sm, ctx->shared_memory_size).beg, - ctx->compute_context.backlog.buffer, f->buffer_offset, - frame_size, 1); + vk_buffer_range_download(sm_output + exported_size, bl->buffer, f->buffer_offset, frame_size, 1); + exported_size += frame_size; } } }break; + case BeamformerExportKind_Stats:{ ComputeTimingTable *table = ctx->compute_timing_table; /* NOTE(rnp): do a little spin to let this finish updating */ 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 (32UL) +#define BEAMFORMER_SHARED_MEMORY_VERSION (33UL) typedef enum { BeamformerWorkKind_Compute, @@ -23,7 +23,8 @@ typedef enum { typedef struct { BeamformerExportKind kind; - u32 size; + u32 count; /* Number of items to export */ + u64 size; /* Total expected size of the exported data */ } BeamformerExportContext; #define BEAMFORMER_SHARED_MEMORY_LOCKS \ diff --git a/lib/ogl_beamformer_lib.c b/lib/ogl_beamformer_lib.c @@ -689,6 +689,17 @@ beamformer_export(BeamformerExportContext export, void *out, i32 timeout_ms) return result; } +BEAMFORMER_LIB_EXPORT b32 +beamformer_get_last_frames(void *out_data, u64 out_data_size, u32 count) +{ + BeamformerExportContext export = {0}; + export.kind = BeamformerExportKind_BeamformedData; + export.count = count; + export.size = out_data_size; + b32 result = out_data && out_data_size && count && beamformer_export(export, out_data, g_beamformer_library_context.timeout_ms); + return result; +} + b32 beamformer_beamform_data(BeamformerSimpleParameters *bp, void *data, uint32_t data_size, void *out_data, int32_t timeout_ms) @@ -715,12 +726,8 @@ beamformer_beamform_data(BeamformerSimpleParameters *bp, void *data, uint32_t da if (result) { result = beamformer_push_data_with_compute(data, data_size, 0, 0); - if (result && out_data) { - BeamformerExportContext export; - export.kind = BeamformerExportKind_BeamformedData; - export.size = (u32)output_size; - result = beamformer_export(export, out_data, timeout_ms); - } + if (result && out_data) + result = beamformer_get_last_frames(out_data, output_size, 1); } } return result; @@ -734,7 +741,7 @@ beamformer_compute_timings(BeamformerComputeStatsTable *output, i32 timeout_ms) Arena scratch = beamformer_shared_memory_scratch_arena(g_beamformer_library_context.bp, g_beamformer_library_context.shared_memory_size); if (lib_error_check((i64)sizeof(*output) <= arena_capacity(&scratch, u8), ExportSpaceOverflow)) { - BeamformerExportContext export; + BeamformerExportContext export = {0}; export.kind = BeamformerExportKind_Stats; export.size = sizeof(*output); result = beamformer_export(export, output, timeout_ms); diff --git a/lib/ogl_beamformer_lib_base.h b/lib/ogl_beamformer_lib_base.h @@ -85,6 +85,23 @@ BEAMFORMER_LIB_EXPORT uint32_t beamformer_push_data_with_compute(void *data, uin uint32_t image_plane_tag, uint32_t parameter_slot); + +/* Returns the last N beamformed frames, ordered from oldest to newest. + * out_data: Preallocated output buffer. + * out_data_size: Total output buffer size. + * count: Number of frames to return. + * + * NOTE: + * - Individual frame sizes are rounded up to 64 byte alignment prior to export + * + * - Frame size is not guarenteed to be consistent between frames, + * it is the callers responsibility to know the correct dimensions of each frame. + * + * - The buffer will be filled one frame at a time from oldest to newest, + * if the output buffer is too small the most recent frames will be dropped. + */ +BEAMFORMER_LIB_EXPORT uint32_t beamformer_get_last_frames(void *out_data, uint64_t out_data_size, uint32_t count); + /////////////////////////// // Parameter Configuration BEAMFORMER_LIB_EXPORT uint32_t beamformer_reserve_parameter_blocks(uint32_t count);