ogl_beamforming

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

Commit: 1e76faef3c5047f8eb14f7fadc64af28f775850a
Parent: f60c3ba8127f72cfee8d55a130526dd73ecebb3d
Author: Randy Palamar
Date:   Thu, 10 Apr 2025 05:33:34 -0600

core/lib: collapse upload commands into single method

Diffstat:
Mbeamformer.c | 151++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mbeamformer.h | 11++++++-----
Mbeamformer_parameters.h | 6++++++
Mbeamformer_work_queue.h | 27+++++++++++++++++----------
Mhelpers/ogl_beamformer_lib.c | 93+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mstatic.c | 8+++-----
Mui.c | 10++++++----
7 files changed, 171 insertions(+), 135 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -1,10 +1,15 @@ /* See LICENSE for license details. */ /* TODO(rnp): - * - make channel_mapping, sparse_elements, focal_vectors into buffer backed textures. - * this way they can all use the same UPLOAD_SUBBUFFER command - * - bake compute shader uniform indices (use push_compute_shader_header) - * - reinvestigate ring buffer raw_data_ssbo ? - * - START_COMPUTE command ? + * [ ]: bake compute shader uniform indices (use push_compute_shader_header) + * [ ]: refactor: BeamformGPUComputeContext + * [ ]: refactor: compute shader timers should be generated based on the pipeline stage limit + * [ ]: reinvestigate ring buffer raw_data_ssbo + * - to minimize latency the main thread should manage the subbuffer upload so that the + * compute thread can just keep computing. This way we can keep the copmute thread busy + * with work while we image. + * - In particular we will potentially need multiple GPUComputeContexts so that we + * can overwrite one while the other is in use. + * - make use of glFenceSync to guard buffer uploads */ #include "beamformer.h" @@ -31,14 +36,6 @@ typedef struct { u32 needed_frames; } ComputeFrameIterator; -static iz -decoded_data_size(ComputeShaderCtx *cs) -{ - uv4 dim = cs->dec_data_dim; - iz result = 2 * sizeof(f32) * dim.x * dim.y * dim.z; - return result; -} - static uv3 make_valid_test_dim(uv3 in) { @@ -110,14 +107,13 @@ alloc_beamform_frame(GLParams *gp, BeamformFrame *out, ComputeShaderStats *out_s } } -static void -alloc_shader_storage(BeamformerCtx *ctx, Arena a) +function void +alloc_shader_storage(BeamformerCtx *ctx, u32 rf_raw_size, Arena a) { - ComputeShaderCtx *cs = &ctx->csctx; + ComputeShaderCtx *cs = &ctx->csctx; BeamformerParameters *bp = &ctx->shared_memory->parameters; uv4 dec_data_dim = bp->dec_data_dim; - u32 rf_raw_size = ctx->shared_memory->raw_data_size; cs->dec_data_dim = dec_data_dim; cs->rf_raw_size = rf_raw_size; @@ -130,7 +126,7 @@ alloc_shader_storage(BeamformerCtx *ctx, Arena a) glNamedBufferStorage(cs->raw_data_ssbo, rf_raw_size, 0, storage_flags); LABEL_GL_OBJECT(GL_BUFFER, cs->raw_data_ssbo, s8("Raw_RF_SSBO")); - iz rf_decoded_size = decoded_data_size(cs); + iz rf_decoded_size = 2 * sizeof(f32) * cs->dec_data_dim.x * cs->dec_data_dim.y * cs->dec_data_dim.z; Stream label = stream_alloc(&a, 256); stream_append_s8(&label, s8("Decoded_RF_SSBO_")); u32 s_widx = label.widx; @@ -575,64 +571,55 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena arena, iptr gl_co #undef X } } break; - case BW_UPLOAD_CHANNEL_MAPPING: { - ASSERT(!atomic_load(&ctx->shared_memory->channel_mapping_sync)); - if (!cs->channel_mapping_texture) { - glCreateTextures(GL_TEXTURE_1D, 1, &cs->channel_mapping_texture); - glTextureStorage1D(cs->channel_mapping_texture, 1, GL_R16I, - ARRAY_COUNT(sm->channel_mapping)); - LABEL_GL_OBJECT(GL_TEXTURE, cs->channel_mapping_texture, s8("Channel_Mapping")); - } - glTextureSubImage1D(cs->channel_mapping_texture, 0, 0, - ARRAY_COUNT(sm->channel_mapping), GL_RED_INTEGER, - GL_SHORT, sm->channel_mapping); - } break; - case BW_UPLOAD_FOCAL_VECTORS: { - ASSERT(!atomic_load(&ctx->shared_memory->focal_vectors_sync)); - if (!cs->focal_vectors_texture) { - glCreateTextures(GL_TEXTURE_1D, 1, &cs->focal_vectors_texture); - glTextureStorage1D(cs->focal_vectors_texture, 1, GL_RG32F, - ARRAY_COUNT(sm->focal_vectors)); - LABEL_GL_OBJECT(GL_TEXTURE, cs->focal_vectors_texture, s8("Focal_Vectors")); - } - glTextureSubImage1D(cs->focal_vectors_texture, 0, 0, - ARRAY_COUNT(sm->focal_vectors), GL_RG, - GL_FLOAT, sm->focal_vectors); - } break; - case BW_UPLOAD_PARAMETERS: - case BW_UPLOAD_PARAMETERS_HEAD: - case BW_UPLOAD_PARAMETERS_UI: { + case BW_UPLOAD_BUFFER: { ASSERT(!atomic_load((i32 *)(barrier_offset + work->completion_barrier))); - glNamedBufferSubData(cs->shared_ubo, 0, sizeof(ctx->shared_memory->parameters), - &ctx->shared_memory->parameters); - ctx->ui_read_params = work->type != BW_UPLOAD_PARAMETERS_HEAD && !work->generic; - } break; - case BW_UPLOAD_RF_DATA: { - ASSERT(!atomic_load(&ctx->shared_memory->raw_data_sync)); + BeamformerUploadContext *uc = &work->upload_context; + u32 tex_type, tex_format, tex_element_count, tex_1d = 0, buffer = 0; + switch (uc->kind) { + case BU_KIND_CHANNEL_MAPPING: { + tex_1d = cs->channel_mapping_texture; + tex_type = GL_SHORT; + tex_format = GL_RED_INTEGER; + tex_element_count = ARRAY_COUNT(sm->channel_mapping); + } break; + case BU_KIND_FOCAL_VECTORS: { + tex_1d = cs->focal_vectors_texture; + tex_type = GL_FLOAT; + tex_format = GL_RG; + tex_element_count = ARRAY_COUNT(sm->focal_vectors); + } break; + case BU_KIND_SPARSE_ELEMENTS: { + tex_1d = cs->sparse_elements_texture; + tex_type = GL_SHORT; + tex_format = GL_RED_INTEGER; + tex_element_count = ARRAY_COUNT(sm->sparse_elements); + } break; + case BU_KIND_PARAMETERS: { + ctx->ui_read_params = barrier_offset != 0; + buffer = cs->shared_ubo; + } break; + case BU_KIND_RF_DATA: { + if (cs->rf_raw_size != uc->size || + !uv4_equal(cs->dec_data_dim, bp->dec_data_dim)) + { + alloc_shader_storage(ctx, uc->size, arena); + } + buffer = cs->raw_data_ssbo; + } break; + default: INVALID_CODE_PATH; break; + } - if (cs->rf_raw_size != ctx->shared_memory->raw_data_size || - !uv4_equal(cs->dec_data_dim, bp->dec_data_dim)) - { - alloc_shader_storage(ctx, arena); + if (tex_1d) { + glTextureSubImage1D(tex_1d, 0, 0, tex_element_count, tex_format, + tex_type, (u8 *)sm + uc->shared_memory_offset); } - glNamedBufferSubData(cs->raw_data_ssbo, 0, cs->rf_raw_size, - (u8 *)ctx->shared_memory + BEAMFORMER_RF_DATA_OFF); - } break; - case BW_UPLOAD_SPARSE_ELEMENTS: { - ASSERT(!atomic_load(&ctx->shared_memory->sparse_elements_sync)); - if (!cs->sparse_elements_texture) { - glCreateTextures(GL_TEXTURE_1D, 1, &cs->sparse_elements_texture); - glTextureStorage1D(cs->sparse_elements_texture, 1, GL_R16I, - ARRAY_COUNT(sm->sparse_elements)); - LABEL_GL_OBJECT(GL_TEXTURE, cs->sparse_elements_texture, s8("Sparse_Elements")); + if (buffer) { + glNamedBufferSubData(buffer, 0, uc->size, + (u8 *)sm + uc->shared_memory_offset); } - glTextureSubImage1D(cs->sparse_elements_texture, 0, 0, - ARRAY_COUNT(sm->sparse_elements), GL_RED_INTEGER, - GL_SHORT, sm->sparse_elements); } break; case BW_COMPUTE: { - BeamformerParameters *bp = &ctx->shared_memory->parameters; atomic_store(&cs->processing_compute, 1); start_renderdoc_capture(gl_context); @@ -660,8 +647,8 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena arena, iptr gl_co frame->frame.compound_count = bp->dec_data_dim.z; b32 did_sum_shader = 0; - u32 stage_count = ctx->shared_memory->compute_stages_count; - ComputeShaderID *stages = ctx->shared_memory->compute_stages; + u32 stage_count = sm->compute_stages_count; + ComputeShaderID *stages = sm->compute_stages; for (u32 i = 0; i < stage_count; i++) { did_sum_shader |= stages[i] == CS_SUM; frame->stats.timer_active[stages[i]] = 1; @@ -721,6 +708,28 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena arena, iptr gl_co } } +DEBUG_EXPORT BEAMFORMER_COMPUTE_SETUP_FN(beamformer_compute_setup) +{ + BeamformerCtx *ctx = (BeamformerCtx *)user_context; + BeamformerSharedMemory *sm = ctx->shared_memory; + ComputeShaderCtx *cs = &ctx->csctx; + + glCreateBuffers(1, &cs->shared_ubo); + glNamedBufferStorage(cs->shared_ubo, sizeof(sm->parameters), 0, GL_DYNAMIC_STORAGE_BIT); + + glCreateTextures(GL_TEXTURE_1D, 1, &cs->channel_mapping_texture); + glCreateTextures(GL_TEXTURE_1D, 1, &cs->sparse_elements_texture); + glCreateTextures(GL_TEXTURE_1D, 1, &cs->focal_vectors_texture); + glTextureStorage1D(cs->channel_mapping_texture, 1, GL_R16I, ARRAY_COUNT(sm->channel_mapping)); + glTextureStorage1D(cs->sparse_elements_texture, 1, GL_R16I, ARRAY_COUNT(sm->sparse_elements)); + glTextureStorage1D(cs->focal_vectors_texture, 1, GL_RG32F, ARRAY_COUNT(sm->focal_vectors)); + + LABEL_GL_OBJECT(GL_TEXTURE, cs->channel_mapping_texture, s8("Channel_Mapping")); + LABEL_GL_OBJECT(GL_TEXTURE, cs->focal_vectors_texture, s8("Focal_Vectors")); + LABEL_GL_OBJECT(GL_TEXTURE, cs->sparse_elements_texture, s8("Sparse_Elements")); + LABEL_GL_OBJECT(GL_BUFFER, cs->shared_ubo, s8("Beamformer_Parameters")); +} + DEBUG_EXPORT BEAMFORMER_COMPLETE_COMPUTE_FN(beamformer_complete_compute) { BeamformerCtx *ctx = (BeamformerCtx *)user_context; diff --git a/beamformer.h b/beamformer.h @@ -61,6 +61,7 @@ typedef struct { } FragmentShaderCtx; #include "beamformer_parameters.h" +#include "beamformer_work_queue.h" #define CS_UNIFORMS \ X(CS_MIN_MAX, mips_level) \ @@ -69,19 +70,18 @@ typedef struct { typedef struct { u32 programs[CS_LAST]; - u32 raw_data_ssbo; - /* NOTE: Decoded data is only relevant in the context of a single frame. We use two * buffers so that they can be swapped when chaining multiple compute stages */ u32 rf_data_ssbos[2]; u32 last_output_ssbo_index; - u32 hadamard_texture; + u32 raw_data_ssbo; u32 shared_ubo; u32 channel_mapping_texture; u32 sparse_elements_texture; u32 focal_vectors_texture; + u32 hadamard_texture; f32 processing_progress; b32 processing_compute; @@ -135,8 +135,6 @@ struct BeamformComputeFrame { b32 ready_to_present; }; -#include "beamformer_work_queue.h" - #define GL_PARAMETERS \ X(MAJOR_VERSION, version_major, "") \ X(MINOR_VERSION, version_minor, "") \ @@ -203,6 +201,9 @@ struct ComputeShaderReloadContext { BeamformerInput *input) typedef BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step_fn); +#define BEAMFORMER_COMPUTE_SETUP_FN(name) void name(iptr user_context, Arena arena, iptr gl_context) +typedef BEAMFORMER_COMPUTE_SETUP_FN(beamformer_compute_setup_fn); + #define BEAMFORMER_COMPLETE_COMPUTE_FN(name) void name(iptr user_context, Arena arena, iptr gl_context) typedef BEAMFORMER_COMPLETE_COMPUTE_FN(beamformer_complete_compute_fn); diff --git a/beamformer_parameters.h b/beamformer_parameters.h @@ -1,5 +1,11 @@ /* See LICENSE for license details. */ +/* TODO(rnp): + * [ ]: Have a method for the library caller to take ownership of a "compute context" + * [ ]: Upload previously exported data for display. maybe this is a UI thing but doing it + * programatically would be nice. + */ + /* X(enumarant, number, shader file name, needs header, pretty name) */ #define COMPUTE_SHADERS \ X(CUDA_DECODE, 0, "", 0, "CUDA Decoding") \ diff --git a/beamformer_work_queue.h b/beamformer_work_queue.h @@ -10,15 +10,24 @@ typedef enum { BW_RELOAD_SHADER, BW_SAVE_FRAME, BW_SEND_FRAME, - BW_UPLOAD_CHANNEL_MAPPING, - BW_UPLOAD_FOCAL_VECTORS, - BW_UPLOAD_PARAMETERS, - BW_UPLOAD_PARAMETERS_HEAD, - BW_UPLOAD_PARAMETERS_UI, - BW_UPLOAD_RF_DATA, - BW_UPLOAD_SPARSE_ELEMENTS, + BW_UPLOAD_BUFFER, } BeamformWorkType; +typedef enum { + BU_KIND_CHANNEL_MAPPING, + BU_KIND_FOCAL_VECTORS, + BU_KIND_PARAMETERS, + BU_KIND_RF_DATA, + BU_KIND_SPARSE_ELEMENTS, + BU_KIND_LAST, +} BeamformerUploadKind; + +typedef struct { + i32 size; + i32 shared_memory_offset; + BeamformerUploadKind kind; +} BeamformerUploadContext; + typedef struct { BeamformComputeFrame *frame; iptr file_handle; @@ -28,6 +37,7 @@ typedef struct { typedef struct { union { BeamformComputeFrame *frame; + BeamformerUploadContext upload_context; BeamformOutputFrameContext output_frame_ctx; ComputeShaderReloadContext *reload_shader_ctx; void *generic; @@ -80,13 +90,10 @@ typedef struct { i32 parameters_sync; i32 parameters_head_sync; i32 parameters_ui_sync; - i32 focal_vectors_sync; i32 channel_mapping_sync; i32 sparse_elements_sync; - i32 raw_data_sync; - u32 raw_data_size; i32 dispatch_compute_sync; ImagePlaneTag current_image_plane; diff --git a/helpers/ogl_beamformer_lib.c b/helpers/ogl_beamformer_lib.c @@ -260,6 +260,28 @@ beamformer_start_compute(char *shm_name, u32 image_plane_tag) return result; } +function b32 +beamformer_upload_buffer(char *shm_name, void *data, u32 size, i32 store_offset, i32 sync_offset, + BeamformerUploadKind kind, i32 timeout_ms) +{ + b32 result = check_shared_memory(shm_name); + if (result) { + BeamformWork *work = beamform_work_queue_push(&g_bp->external_work_queue); + result = work && try_wait_sync((i32 *)((u8 *)g_bp + sync_offset), timeout_ms, os_wait_on_value); + if (result) { + BeamformerUploadContext *uc = &work->upload_context; + uc->shared_memory_offset = store_offset; + uc->size = size; + uc->kind = kind; + work->type = BW_UPLOAD_BUFFER; + work->completion_barrier = sync_offset; + mem_copy((u8 *)g_bp + store_offset, data, size); + beamform_work_queue_push_commit(&g_bp->external_work_queue); + } + } + return result; +} + #define BEAMFORMER_UPLOAD_FNS \ X(channel_mapping, i16, CHANNEL_MAPPING) \ X(sparse_elements, i16, SPARSE_ELEMENTS) \ @@ -267,68 +289,55 @@ beamformer_start_compute(char *shm_name, u32 image_plane_tag) #define X(name, dtype, command) \ b32 beamformer_push_##name (char *shm_id, dtype *data, u32 count, i32 timeout_ms) { \ - b32 result = check_shared_memory(shm_id) && count <= ARRAY_COUNT(g_bp->name); \ - if (result) { \ - BeamformWork *work = beamform_work_queue_push(&g_bp->external_work_queue); \ - result = work && try_wait_sync(&g_bp->name##_sync, timeout_ms, os_wait_on_value); \ - if (result) { \ - work->type = BW_UPLOAD_##command; \ - work->completion_barrier = offsetof(BeamformerSharedMemory, name##_sync); \ - mem_copy(g_bp->name, data, count * sizeof(*g_bp->name)); \ - beamform_work_queue_push_commit(&g_bp->external_work_queue); \ - } \ - } \ - return result; \ + b32 result = count <= ARRAY_COUNT(g_bp->name); \ + if (result) { \ + result = beamformer_upload_buffer(shm_id, data, count * sizeof(dtype), \ + offsetof(BeamformerSharedMemory, name), \ + offsetof(BeamformerSharedMemory, name##_sync), \ + BU_KIND_##command, timeout_ms); \ + } \ + return result; \ } BEAMFORMER_UPLOAD_FNS #undef X b32 -beamformer_push_data(char *shm_name, void *data, u32 data_size, i32 timeout_ms) +beamformer_push_parameters(char *shm_name, BeamformerParameters *bp, i32 timeout_ms) { - b32 result = data_size <= BEAMFORMER_MAX_RF_DATA_SIZE && check_shared_memory(shm_name); - if (result) { - BeamformWork *work = beamform_work_queue_push(&g_bp->external_work_queue); - result = work && try_wait_sync(&g_bp->raw_data_sync, timeout_ms, os_wait_on_value); - if (result) { - work->type = BW_UPLOAD_RF_DATA; - work->completion_barrier = offsetof(BeamformerSharedMemory, raw_data_sync); - mem_copy((u8 *)g_bp + BEAMFORMER_RF_DATA_OFF, data, data_size); - g_bp->raw_data_size = data_size; - beamform_work_queue_push_commit(&g_bp->external_work_queue); - } - } + b32 result = beamformer_upload_buffer(shm_name, bp, sizeof(*bp), + offsetof(BeamformerSharedMemory, parameters), + offsetof(BeamformerSharedMemory, parameters_sync), + BU_KIND_PARAMETERS, timeout_ms); return result; } b32 -beamformer_push_parameters(char *shm_name, BeamformerParameters *bp, i32 timeout_ms) +beamformer_push_data(char *shm_name, void *data, u32 data_size, i32 timeout_ms) { - b32 result = check_shared_memory(shm_name); + b32 result = data_size <= BEAMFORMER_MAX_RF_DATA_SIZE; if (result) { - BeamformWork *work = beamform_work_queue_push(&g_bp->external_work_queue); - result = work && try_wait_sync(&g_bp->parameters_sync, timeout_ms, os_wait_on_value); - if (result) { - work->type = BW_UPLOAD_PARAMETERS; - work->completion_barrier = offsetof(BeamformerSharedMemory, parameters_sync); - mem_copy(&g_bp->parameters, bp, sizeof(g_bp->parameters)); - beamform_work_queue_push_commit(&g_bp->external_work_queue); - } + result = beamformer_upload_buffer(shm_name, data, data_size, BEAMFORMER_RF_DATA_OFF, + offsetof(BeamformerSharedMemory, raw_data_sync), + BU_KIND_RF_DATA, timeout_ms); } return result; } b32 -beamformer_push_ui_parameters(char *shm_name, BeamformerUIParameters *bp, i32 timeout_ms) +beamformer_push_parameters_ui(char *shm_name, BeamformerUIParameters *bp, i32 timeout_ms) { b32 result = check_shared_memory(shm_name); if (result) { BeamformWork *work = beamform_work_queue_push(&g_bp->external_work_queue); result = work && try_wait_sync(&g_bp->parameters_ui_sync, timeout_ms, os_wait_on_value); if (result) { - work->type = BW_UPLOAD_PARAMETERS_UI; + BeamformerUploadContext *uc = &work->upload_context; + uc->shared_memory_offset = offsetof(BeamformerSharedMemory, parameters); + uc->size = sizeof(g_bp->parameters); + uc->kind = BU_KIND_PARAMETERS; + work->type = BW_UPLOAD_BUFFER; work->completion_barrier = offsetof(BeamformerSharedMemory, parameters_ui_sync); - mem_copy(&g_bp->parameters_ui, bp, sizeof(g_bp->parameters_ui)); + mem_copy(&g_bp->parameters_ui, bp, sizeof(*bp)); beamform_work_queue_push_commit(&g_bp->external_work_queue); } } @@ -343,9 +352,13 @@ beamformer_push_parameters_head(char *shm_name, BeamformerParametersHead *bp, i3 BeamformWork *work = beamform_work_queue_push(&g_bp->external_work_queue); result = work && try_wait_sync(&g_bp->parameters_head_sync, timeout_ms, os_wait_on_value); if (result) { - work->type = BW_UPLOAD_PARAMETERS_HEAD; + BeamformerUploadContext *uc = &work->upload_context; + uc->shared_memory_offset = offsetof(BeamformerSharedMemory, parameters); + uc->size = sizeof(g_bp->parameters); + uc->kind = BU_KIND_PARAMETERS; + work->type = BW_UPLOAD_BUFFER; work->completion_barrier = offsetof(BeamformerSharedMemory, parameters_head_sync); - mem_copy(&g_bp->parameters_head, bp, sizeof(g_bp->parameters_head)); + mem_copy(&g_bp->parameters_head, bp, sizeof(*bp)); beamform_work_queue_push_commit(&g_bp->external_work_queue); } } diff --git a/static.c b/static.c @@ -10,6 +10,7 @@ static void *debug_lib; #define DEBUG_ENTRY_POINTS \ X(beamformer_frame_step) \ X(beamformer_complete_compute) \ + X(beamformer_compute_setup) \ X(beamform_work_queue_push) \ X(beamform_work_queue_push_commit) @@ -252,6 +253,8 @@ static OS_THREAD_ENTRY_POINT_FN(compute_worker_thread_entry_point) glfwMakeContextCurrent(ctx->window_handle); ctx->gl_context = os_get_native_gl_context(ctx->window_handle); + beamformer_compute_setup(ctx->user_context, ctx->arena, ctx->gl_context); + for (;;) { for (;;) { i32 current = atomic_load(&ctx->sync_variable); @@ -336,11 +339,6 @@ setup_beamformer(BeamformerCtx *ctx, Arena *memory) glEnable(GL_DEBUG_OUTPUT); #endif - /* NOTE: allocate space for Uniform Buffer but don't send anything yet */ - glCreateBuffers(1, &ctx->csctx.shared_ubo); - glNamedBufferStorage(ctx->csctx.shared_ubo, sizeof(BeamformerParameters), 0, GL_DYNAMIC_STORAGE_BIT); - LABEL_GL_OBJECT(GL_BUFFER, ctx->csctx.shared_ubo, s8("Beamformer_Parameters")); - #define X(e, sn, f, nh, pretty_name) do if (s8(f).len > 0) { \ ComputeShaderReloadContext *csr = push_struct(memory, typeof(*csr)); \ csr->beamformer_ctx = ctx; \ diff --git a/ui.c b/ui.c @@ -2449,11 +2449,13 @@ draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformFrame *frame_to_draw validate_ui_parameters(&ui->params); BeamformWork *work = beamform_work_queue_push(ctx->beamform_work_queue); if (work && try_wait_sync(&ctx->shared_memory->parameters_sync, 0, ctx->os.wait_on_value)) { - work->generic = (void *)1; - work->type = BW_UPLOAD_PARAMETERS; + BeamformerUploadContext *uc = &work->upload_context; + uc->shared_memory_offset = offsetof(BeamformerSharedMemory, parameters); + uc->size = sizeof(ctx->shared_memory->parameters); + uc->kind = BU_KIND_PARAMETERS; + work->type = BW_UPLOAD_BUFFER; work->completion_barrier = (iptr)&ctx->shared_memory->parameters_sync; - mem_copy(&ctx->shared_memory->parameters.output_min_coordinate, - &ui->params, sizeof(ui->params)); + mem_copy(&ctx->shared_memory->parameters_ui, &ui->params, sizeof(ui->params)); beamform_work_queue_push_commit(ctx->beamform_work_queue); ui->flush_params = 0; ctx->start_compute = 1;