ogl_beamforming

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

Commit: f24f6aa5a0d8c2378b76d9a0d5efd0c5f9f9bf86
Parent: 11b1ae5152036e9c03936d5b4316a5a8a2f0129e
Author: Randy Palamar
Date:   Tue, 17 Jun 2025 06:25:46 -0600

core: work around pebkac when beamformer is closed during live imaging

Diffstat:
Mbeamformer_work_queue.h | 4++++
Mhelpers/ogl_beamformer_lib.c | 14+++++++++-----
Mhelpers/ogl_beamformer_lib_base.h | 19++++++++++---------
Mmain_linux.c | 2++
Mmain_w32.c | 2++
Mstatic.c | 19+++++++++++++++++++
6 files changed, 46 insertions(+), 14 deletions(-)

diff --git a/beamformer_work_queue.h b/beamformer_work_queue.h @@ -88,6 +88,10 @@ typedef BEAMFORM_WORK_QUEUE_PUSH_COMMIT_FN(beamform_work_queue_push_commit_fn); typedef align_as(64) struct { u32 version; + /* NOTE(rnp): causes future library calls to fail. + * see note in beamformer_invalidate_shared_memory() */ + b32 invalid; + /* NOTE(rnp): not used for locking on w32 but we can use these to peek at the status of * the lock without leaving userspace. also this struct needs a bunch of padding */ i32 locks[BeamformerSharedMemoryLockKind_Count]; diff --git a/helpers/ogl_beamformer_lib.c b/helpers/ogl_beamformer_lib.c @@ -177,13 +177,17 @@ check_shared_memory(void) if (!g_shared_memory.region) { g_shared_memory = os_open_shared_memory_area(OS_SHARED_MEMORY_NAME); if (!g_shared_memory.region) { - result = 0; g_lib_last_error = BF_LIB_ERR_KIND_SHARED_MEMORY; + result = 0; + } else if (((BeamformerSharedMemory *)g_shared_memory.region)->version != + BEAMFORMER_SHARED_MEMORY_VERSION) + { + g_lib_last_error = BF_LIB_ERR_KIND_VERSION_MISMATCH; + result = 0; } - } else if (((BeamformerSharedMemory *)g_shared_memory.region)->version - != BEAMFORMER_SHARED_MEMORY_VERSION) - { - g_lib_last_error = BF_LIB_ERR_KIND_VERSION_MISMATCH; + } + if (result && ((BeamformerSharedMemory *)g_shared_memory.region)->invalid) { + g_lib_last_error = BF_LIB_ERR_KIND_INVALID_ACCESS; result = 0; } if (result) g_bp = g_shared_memory.region; diff --git a/helpers/ogl_beamformer_lib_base.h b/helpers/ogl_beamformer_lib_base.h @@ -8,15 +8,16 @@ #define BEAMFORMER_LIB_ERRORS \ X(NONE, 0, "None") \ X(VERSION_MISMATCH, 1, "host-library version mismatch") \ - X(COMPUTE_STAGE_OVERFLOW, 2, "compute stage overflow: maximum stages: " str(MAX_COMPUTE_SHADER_STAGES)) \ - X(INVALID_COMPUTE_STAGE, 3, "invalid compute shader stage") \ - X(INVALID_IMAGE_PLANE, 4, "invalid image plane") \ - X(BUFFER_OVERFLOW, 5, "passed buffer size exceeds available space") \ - X(WORK_QUEUE_FULL, 6, "work queue full") \ - X(OPEN_EXPORT_PIPE, 7, "failed to open export pipe") \ - X(READ_EXPORT_PIPE, 8, "failed to read full export data from pipe") \ - X(SHARED_MEMORY, 9, "failed to open shared memory region") \ - X(SYNC_VARIABLE, 10, "failed to acquire lock within timeout period") + X(INVALID_ACCESS, 2, "library in invalid state") \ + X(COMPUTE_STAGE_OVERFLOW, 3, "compute stage overflow: maximum stages: " str(MAX_COMPUTE_SHADER_STAGES)) \ + X(INVALID_COMPUTE_STAGE, 4, "invalid compute shader stage") \ + X(INVALID_IMAGE_PLANE, 5, "invalid image plane") \ + X(BUFFER_OVERFLOW, 6, "passed buffer size exceeds available space") \ + X(WORK_QUEUE_FULL, 7, "work queue full") \ + X(OPEN_EXPORT_PIPE, 8, "failed to open export pipe") \ + X(READ_EXPORT_PIPE, 9, "failed to read full export data from pipe") \ + X(SHARED_MEMORY, 10, "failed to open shared memory region") \ + X(SYNC_VARIABLE, 11, "failed to acquire lock within timeout period") #define X(type, num, string) BF_LIB_ERR_KIND_ ##type = num, typedef enum {BEAMFORMER_LIB_ERRORS} BeamformerLibErrorKind; diff --git a/main_linux.c b/main_linux.c @@ -114,6 +114,8 @@ main(void) input.executable_reloaded = 0; } + beamformer_invalidate_shared_memory(&ctx); + /* NOTE: make sure this will get cleaned up after external * programs release their references */ shm_unlink(OS_SHARED_MEMORY_NAME); diff --git a/main_w32.c b/main_w32.c @@ -140,4 +140,6 @@ main(void) input.executable_reloaded = 0; } + + beamformer_invalidate_shared_memory(&ctx); } diff --git a/static.c b/static.c @@ -397,3 +397,22 @@ setup_beamformer(BeamformerCtx *ctx, BeamformerInput *input, Arena *memory) reload_shader(&ctx->os, render_2d->path, (iptr)render_2d, *memory); os_add_file_watch(&ctx->os, memory, render_2d->path, reload_shader, (iptr)render_2d); } + +function void +beamformer_invalidate_shared_memory(BeamformerCtx *ctx) +{ + /* NOTE(rnp): work around pebkac when the beamformer is closed while we are doing live + * imaging. if the verasonics is blocked in an external function (calling the library + * to start compute) it is impossible for us to get it to properly shut down which + * will sometimes result in us needing to power cycle the system. set the shared memory + * into an error state and release dispatch lock so that future calls will error instead + * of blocking. + */ + BeamformerSharedMemory *sm = ctx->shared_memory.region; + BeamformerSharedMemoryLockKind lock = BeamformerSharedMemoryLockKind_DispatchCompute; + atomic_store_u32(&sm->invalid, 1); + atomic_store_u32(&sm->external_work_queue.ridx, sm->external_work_queue.widx); + DEBUG_DECL(if (sm->locks[lock])) { + os_shared_memory_region_unlock(&ctx->shared_memory, sm->locks, lock); + } +}