ogl_beamforming

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

Commit: 67d21780192c5a07d15baeceaa9535746d2e20b8
Parent: 67bd1deee3a5fa0454f0ac8d6361492c2fa9daea
Author: Randy Palamar
Date:   Tue, 12 Nov 2024 16:14:59 -0700

add queued data exporting

For now the export button has been disabled and this is only
method of exporting data.

Diffstat:
Mbeamformer.c | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mbeamformer.h | 31++++++++++++++-----------------
Mhelpers/ogl_beamformer_lib.c | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mhelpers/ogl_beamformer_lib.h | 8++++++++
Mos_unix.c | 16++++++++++++++--
Mos_win32.c | 14++++++++++++--
Mstatic.c | 4+---
Mutil.c | 3++-
Mutil.h | 14+++++++++++++-
9 files changed, 313 insertions(+), 91 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -49,6 +49,7 @@ static void alloc_output_image(BeamformerCtx *ctx, uv4 output_dim) { uv4 try_dim = {.xyz = output_dim.xyz}; + try_dim.w = 1; if (!uv4_equal(try_dim, ctx->averaged_frame.dim)) { alloc_beamform_frame(&ctx->gl, &ctx->averaged_frame, try_dim, 0, s8("Beamformed_Averaged_Data")); @@ -142,12 +143,6 @@ beamform_work_queue_pop(BeamformWorkQueue *q) { BeamformWork *result = q->first; if (result) { - q->first = result->next; - if (result == q->last) { - ASSERT(result->next == 0); - q->last = 0; - } - switch (result->type) { case BW_FULL_COMPUTE: case BW_RECOMPUTE: @@ -160,12 +155,22 @@ beamform_work_queue_pop(BeamformWorkQueue *q) break; } } + /* NOTE: only do this once we have determined if we are doing the work */ + if (result) { + q->first = result->next; + if (result == q->last) { + ASSERT(result->next == 0); + q->last = 0; + } + } return result; } static BeamformWork * beamform_work_queue_push(BeamformerCtx *ctx, Arena *a, enum beamform_work work_type) { + /* TODO: we should have a sub arena specifically for this purpose */ + BeamformWorkQueue *q = &ctx->beamform_work_queue; ComputeShaderCtx *cs = &ctx->csctx; @@ -255,6 +260,20 @@ f32_4_to_v4(f32 *in) } static void +export_frame(BeamformerCtx *ctx, iptr handle, BeamformFrame *frame) +{ + uv3 dim = frame->dim.xyz; + size out_size = dim.x * dim.y * dim.z * 2 * sizeof(f32); + ctx->export_buffer = ctx->platform.alloc_arena(ctx->export_buffer, out_size); + u32 texture = frame->textures[frame->dim.w - 1]; + glGetTextureImage(texture, 0, GL_RG, GL_FLOAT, out_size, ctx->export_buffer.beg); + s8 raw = {.len = out_size, .data = ctx->export_buffer.beg}; + if (!ctx->platform.write_file(handle, raw)) + TraceLog(LOG_WARNING, "failed to export frame\n"); + ctx->platform.close(handle); +} + +static void do_sum_shader(ComputeShaderCtx *cs, u32 *in_textures, u32 in_texture_count, f32 in_scale, u32 out_texture, uv4 out_data_dim) { @@ -274,7 +293,7 @@ do_sum_shader(ComputeShaderCtx *cs, u32 *in_textures, u32 in_texture_count, f32 static void do_beamform_shader(ComputeShaderCtx *cs, BeamformerParameters *bp, BeamformFrame *frame, - u32 rf_ssbo, iv3 compute_dim_offset, i32 compute_pass) + u32 rf_ssbo, iv3 dispatch_dim, iv3 compute_dim_offset, i32 compute_pass) { glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, rf_ssbo); glUniform3iv(cs->volume_export_dim_offset_id, 1, compute_dim_offset.E); @@ -289,8 +308,9 @@ do_beamform_shader(ComputeShaderCtx *cs, BeamformerParameters *bp, BeamformFrame glBindImageTexture(0, texture, 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RG32F); glUniform1i(cs->xdc_index_id, i); glUniformMatrix3fv(cs->xdc_transform_id, 1, GL_FALSE, xdc_transform.E); - glDispatchCompute(ORONE(frame->dim.x / 32), frame->dim.y, - ORONE(frame->dim.z / 32)); + glDispatchCompute(ORONE(dispatch_dim.x / 32), + ORONE(dispatch_dim.y), + ORONE(dispatch_dim.z / 32)); } } @@ -304,11 +324,14 @@ do_partial_compute_step(BeamformerCtx *ctx, BeamformFrame *frame) /* NOTE: we start this elsewhere on the first dispatch so that we can include * times such as decoding/demodulation/etc. */ - if (!(pc->state & PCS_TIMER_ACTIVE)) { + if (!pc->timer_active) { glQueryCounter(pc->timer_ids[0], GL_TIMESTAMP); - pc->state |= PCS_TIMER_ACTIVE; + pc->timer_active = 1; } + glBeginQuery(GL_TIME_ELAPSED, cs->timer_ids[cs->timer_index][pc->shader]); + cs->timer_active[cs->timer_index][pc->shader] = 1; + glUseProgram(cs->programs[pc->shader]); glBindBufferBase(GL_UNIFORM_BUFFER, 0, cs->shared_ubo); @@ -316,16 +339,18 @@ do_partial_compute_step(BeamformerCtx *ctx, BeamformFrame *frame) /* TODO: this could be based on multiple dimensions */ i32 dispatch_count = frame->dim.z / 32; iv3 dim_offset = {.z = !!dispatch_count * 32 * pc->dispatch_index++}; - do_beamform_shader(cs, &ctx->params->raw, frame, pc->rf_data_ssbo, dim_offset, 1); + iv3 dispatch_dim = {.x = frame->dim.x, .y = frame->dim.y, .z = 1}; + do_beamform_shader(cs, &ctx->params->raw, frame, pc->rf_data_ssbo, dispatch_dim, dim_offset, 1); if (pc->dispatch_index >= dispatch_count) { pc->dispatch_index = 0; - pc->state &= ~PCS_COMPUTING; done = 1; } glQueryCounter(pc->timer_ids[1], GL_TIMESTAMP); + glEndQuery(GL_TIME_ELAPSED); + return done; } @@ -338,6 +363,7 @@ do_compute_shader(BeamformerCtx *ctx, BeamformFrame *frame, u32 raw_data_index, size rf_raw_size = rf_raw_dim.x * rf_raw_dim.y * sizeof(i16); glBeginQuery(GL_TIME_ELAPSED, csctx->timer_ids[csctx->timer_index][shader]); + csctx->timer_active[csctx->timer_index][shader] = 1; glUseProgram(csctx->programs[shader]); glBindBufferBase(GL_UNIFORM_BUFFER, 0, csctx->shared_ubo); @@ -391,8 +417,9 @@ do_compute_shader(BeamformerCtx *ctx, BeamformFrame *frame, u32 raw_data_index, } break; case CS_HERCULES: case CS_UFORCES: { - u32 rf_ssbo = csctx->rf_data_ssbos[input_ssbo_idx]; - do_beamform_shader(csctx, &ctx->params->raw, frame, rf_ssbo, (iv3){0}, 0); + u32 rf_ssbo = csctx->rf_data_ssbos[input_ssbo_idx]; + iv3 dispatch_dim = {.x = frame->dim.x, .y = frame->dim.y, .z = frame->dim.z}; + do_beamform_shader(csctx, &ctx->params->raw, frame, rf_ssbo, dispatch_dim, (iv3){0}, 0); if (frame->dim.w > 1) { glUseProgram(csctx->programs[CS_SUM]); glBindBufferBase(GL_UNIFORM_BUFFER, 0, csctx->shared_ubo); @@ -438,6 +465,15 @@ do_beamform_work(BeamformerCtx *ctx, Arena *a) ctx->params->upload = 0; } + PartialComputeCtx *pc = &ctx->partial_compute_ctx; + pc->runtime = 0; + pc->timer_active = 1; + glQueryCounter(pc->timer_ids[0], GL_TIMESTAMP); + glDeleteBuffers(1, &pc->rf_data_ssbo); + glCreateBuffers(1, &pc->rf_data_ssbo); + glNamedBufferStorage(pc->rf_data_ssbo, decoded_data_size(cs), 0, 0); + LABEL_GL_OBJECT(GL_BUFFER, pc->rf_data_ssbo, s8("Volume_RF_SSBO")); + /* TODO: maybe we should have some concept of compute shader * groups, then we could define a group that does the decoding * and filtering and apply that group directly here. For now @@ -455,11 +491,10 @@ do_beamform_work(BeamformerCtx *ctx, Arena *a) work->compute_ctx.raw_data_ssbo_index, stages[i]); } - u32 output_ssbo = ctx->partial_compute_ctx.rf_data_ssbo; - u32 input_ssbo = cs->last_output_ssbo_index; + u32 output_ssbo = pc->rf_data_ssbo; + u32 input_ssbo = cs->rf_data_ssbos[cs->last_output_ssbo_index]; size rf_size = decoded_data_size(cs); - glCopyNamedBufferSubData(cs->rf_data_ssbos[input_ssbo], - output_ssbo, 0, 0, rf_size); + glCopyNamedBufferSubData(input_ssbo, output_ssbo, 0, 0, rf_size); } b32 done = do_partial_compute_step(ctx, frame); @@ -467,18 +502,25 @@ do_beamform_work(BeamformerCtx *ctx, Arena *a) BeamformWork *new; /* NOTE: this push must not fail */ new = beamform_work_queue_push(ctx, a, BW_PARTIAL_COMPUTE); - new->compute_ctx.first_pass = 0; + new->compute_ctx.first_pass = 0; + new->compute_ctx.frame = frame; + new->compute_ctx.export_handle = work->compute_ctx.export_handle; + } else if (work->compute_ctx.export_handle != INVALID_FILE) { + export_frame(ctx, work->compute_ctx.export_handle, frame); + work->compute_ctx.export_handle = INVALID_FILE; + /* NOTE: do not waste a bunch of GPU space holding onto the volume + * texture if it was just for export */ + glDeleteTextures(frame->dim.w, frame->textures); + mem_clear(frame, 0, sizeof(*frame)); } } break; case BW_FULL_COMPUTE: case BW_RECOMPUTE: { 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; - } + if (ctx->params->upload) { + glNamedBufferSubData(cs->shared_ubo, 0, sizeof(*bp), bp); + ctx->params->upload = 0; } u32 stage_count = ctx->params->compute_stages_count; @@ -486,6 +528,12 @@ do_beamform_work(BeamformerCtx *ctx, Arena *a) for (u32 i = 0; i < stage_count; i++) do_compute_shader(ctx, frame, work->compute_ctx.raw_data_ssbo_index, stages[i]); + + if (work->compute_ctx.export_handle != INVALID_FILE) { + export_frame(ctx, work->compute_ctx.export_handle, frame); + work->compute_ctx.export_handle = INVALID_FILE; + } + ctx->flags |= GEN_MIPMAPS; } break; } @@ -508,13 +556,13 @@ static void check_compute_timers(ComputeShaderCtx *cs, PartialComputeCtx *pc, BeamformerParametersFull *bp) { /* NOTE: volume generation running timer */ - if (pc->state & PCS_TIMER_ACTIVE) { + if (pc->timer_active) { u64 start_ns = 0, end_ns = 0; glGetQueryObjectui64v(pc->timer_ids[0], GL_QUERY_RESULT, &start_ns); glGetQueryObjectui64v(pc->timer_ids[1], GL_QUERY_RESULT, &end_ns); - u64 elapsed_ns = end_ns - start_ns; - pc->runtime += (f32)elapsed_ns * 1e-9; - pc->state &= ~PCS_TIMER_ACTIVE; + u64 elapsed_ns = end_ns - start_ns; + pc->runtime += (f32)elapsed_ns * 1e-9; + pc->timer_active = 0; } /* NOTE: main timers for display portion of the program */ @@ -531,7 +579,10 @@ check_compute_timers(ComputeShaderCtx *cs, PartialComputeCtx *pc, BeamformerPara for (u32 i = 0; i < bp->compute_stages_count; i++) { u64 ns = 0; i32 idx = bp->compute_stages[i]; - glGetQueryObjectui64v(cs->timer_ids[last_idx][idx], GL_QUERY_RESULT, &ns); + if (cs->timer_active[last_idx][idx]) { + glGetQueryObjectui64v(cs->timer_ids[last_idx][idx], GL_QUERY_RESULT, &ns); + cs->timer_active[last_idx][idx] = 0; + } cs->last_frame_time[idx] = (f32)ns / 1e9; } } @@ -553,6 +604,7 @@ do_beamformer(BeamformerCtx *ctx, Arena *arena) BeamformerParameters *bp = &ctx->params->raw; /* NOTE: Check for and Load RF Data into GPU */ + /* TODO: move pipe polling out of the beamformer */ if (ctx->platform.poll_pipe(ctx->data_pipe)) { BeamformWork *work = beamform_work_queue_push(ctx, arena, BW_FULL_COMPUTE); /* NOTE: we can only read in the new data if we get back a work item. @@ -565,6 +617,27 @@ do_beamformer(BeamformerCtx *ctx, Arena *arena) /* TODO: we may need to invalidate all queue items here */ } + if (ctx->params->export_next_frame) { + /* TODO: we don't really want the beamformer opening/closing files */ + iptr f = ctx->platform.open_for_write(ctx->params->export_pipe_name); + work->compute_ctx.export_handle = f; + ctx->params->export_next_frame = 0; + } else { + work->compute_ctx.export_handle = INVALID_FILE; + } + + b32 output_3d = bp->output_points.x > 1 && bp->output_points.y > 1 && + bp->output_points.z > 1; + + if (output_3d) { + work->type = BW_PARTIAL_COMPUTE; + BeamformFrame *frame = &ctx->partial_compute_ctx.frame; + uv4 out_dim = ctx->params->raw.output_points; + out_dim.w = ctx->params->raw.xdc_count; + alloc_beamform_frame(&ctx->gl, frame, out_dim, 0, s8("Beamformed_Volume")); + work->compute_ctx.frame = frame; + } + u32 raw_index = work->compute_ctx.raw_data_ssbo_index; uv2 rf_raw_dim = cs->rf_raw_dim; size rf_raw_size = rf_raw_dim.x * rf_raw_dim.y * sizeof(i16); diff --git a/beamformer.h b/beamformer.h @@ -109,6 +109,8 @@ typedef struct { enum compute_shaders compute_stages[16]; u32 compute_stages_count; b32 upload; + b32 export_next_frame; + c8 export_pipe_name[1024]; } BeamformerParametersFull; #define CS_UNIFORMS \ @@ -124,6 +126,7 @@ typedef struct { u32 timer_index; u32 timer_ids[MAX_FRAMES_IN_FLIGHT][CS_LAST]; + b32 timer_active[MAX_FRAMES_IN_FLIGHT][CS_LAST]; GLsync timer_fences[MAX_FRAMES_IN_FLIGHT]; f32 last_frame_time[CS_LAST]; @@ -165,11 +168,6 @@ typedef struct { f32 threshold; } FragmentShaderCtx; -enum { - PCS_COMPUTING, - PCS_TIMER_ACTIVE, -}; - typedef struct { /* NOTE: we always have one extra texture to sum into; thus the final output data * is always found in textures[dim.w - 1] */ @@ -179,17 +177,13 @@ typedef struct { } BeamformFrame; typedef struct { - /* TODO: possibly both of these should be stored elsewhere */ - Arena export_buf; - uv4 volume_dim; - BeamformFrame frame; - u32 timer_ids[2]; - f32 runtime; - u32 rf_data_ssbo; - u32 shader; - u32 dispatch_index; - u32 state; + u32 timer_ids[2]; + f32 runtime; + u32 rf_data_ssbo; + u32 shader; + u32 dispatch_index; + b32 timer_active; } PartialComputeCtx; typedef struct { @@ -218,8 +212,9 @@ typedef struct { typedef struct { BeamformFrame *frame; - u32 raw_data_ssbo_index; - b32 first_pass; + iptr export_handle; + u32 raw_data_ssbo_index; + b32 first_pass; } BeamformCompute; typedef struct { @@ -266,6 +261,8 @@ typedef struct BeamformerCtx { FragmentShaderCtx fsctx; PartialComputeCtx partial_compute_ctx; + Arena export_buffer; + Pipe data_pipe; u32 partial_transfer_count; diff --git a/helpers/ogl_beamformer_lib.c b/helpers/ogl_beamformer_lib.c @@ -1,13 +1,25 @@ /* See LICENSE for license details. */ - #include "ogl_beamformer_lib.h" + typedef struct { BeamformerParameters raw; enum compute_shaders compute_stages[16]; u32 compute_stages_count; b32 upload; + b32 export_next_frame; + c8 export_pipe_name[1024]; } BeamformerParametersFull; +typedef struct { + iptr file; + char *name; +} Pipe; + +#define INVALID_FILE (-1) + +static volatile BeamformerParametersFull *g_bp; +static Pipe g_pipe = {.file = INVALID_FILE}; + #define ARRAY_COUNT(a) (sizeof(a) / sizeof(*a)) #if defined(__unix__) @@ -16,48 +28,67 @@ typedef struct { #include <sys/stat.h> #include <unistd.h> -#define OS_INVALID_FILE (-1) -typedef i32 os_file; -typedef struct { - os_file file; - char *name; -} os_pipe; +#define OS_EXPORT_PIPE_NAME "/tmp/beamformer_output_pipe" + #elif defined(_WIN32) + +#define OS_EXPORT_PIPE_NAME "\\\\.\\pipe\\beamformer_output_fifo" + #define OPEN_EXISTING 3 #define GENERIC_WRITE 0x40000000 #define FILE_MAP_ALL_ACCESS 0x000F001F -#define INVALID_HANDLE_VALUE (void *)-1 + +#define PIPE_TYPE_BYTE 0x00 +#define PIPE_ACCESS_INBOUND 0x01 #define W32(r) __declspec(dllimport) r __stdcall -W32(b32) CloseHandle(void *); -W32(void *) CreateFileA(c8 *, u32, u32, void *, u32, u32, void *); -W32(void *) MapViewOfFile(void *, u32, u32, u32, u64); -W32(void *) OpenFileMappingA(u32, b32, c8 *); -W32(b32) WriteFile(void *, u8 *, i32, i32 *, void *); - -#define OS_INVALID_FILE (INVALID_HANDLE_VALUE) -typedef void *os_file; -typedef struct { - os_file file; - char *name; -} os_pipe; +W32(b32) CloseHandle(iptr); +W32(iptr) CreateFileA(c8 *, u32, u32, void *, u32, u32, void *); +W32(iptr) CreateNamedPipeA(c8 *, u32, u32, u32, u32, u32, u32, void *); +W32(iptr) MapViewOfFile(iptr, u32, u32, u32, u64); +W32(iptr) OpenFileMappingA(u32, b32, c8 *); +W32(b32) ReadFile(iptr, u8 *, i32, i32 *, void *); +W32(b32) WriteFile(iptr, u8 *, i32, i32 *, void *); #else #error Unsupported Platform #endif -static volatile BeamformerParametersFull *g_bp; -static os_pipe g_pipe = {.file = OS_INVALID_FILE}; - #if defined(__unix__) -static os_pipe +static Pipe +os_open_read_pipe(char *name) +{ + mkfifo(name, 0660); + return (Pipe){.file = open(name, O_RDONLY|O_NONBLOCK), .name = name}; +} + +static void +os_close_read_pipe(Pipe p) +{ + close(p.file); + unlink(p.name); +} + +static b32 +os_read_pipe(Pipe p, void *buf, size read_size) +{ + size r = 0, total_read = 0; + do { + if (r != -1) + total_read += r; + r = read(p.file, buf + total_read, read_size - total_read); + } while (r); + return total_read == read_size; +} + +static Pipe os_open_named_pipe(char *name) { - return (os_pipe){.file = open(name, O_WRONLY), .name = name}; + return (Pipe){.file = open(name, O_WRONLY), .name = name}; } static size -os_write_to_pipe(os_pipe p, void *data, size len) +os_write_to_pipe(Pipe p, void *data, size len) { size written = 0, w = 0; do { @@ -87,15 +118,37 @@ os_open_shared_memory_area(char *name) #elif defined(_WIN32) -static os_pipe +static Pipe +os_open_read_pipe(char *name) +{ + iptr file = CreateNamedPipeA(name, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE, 1, + 0, 1024UL * 1024UL, 0, 0); + return (Pipe){.file = file, .name = name}; +} + +static void +os_close_read_pipe(Pipe p) +{ + CloseHandle(p.file); +} + +static b32 +os_read_pipe(Pipe p, void *buf, size read_size) +{ + i32 total_read = 0; + ReadFile(p.file, buf, len, &total_read, 0); + return total_read == read_size; +} + +static Pipe os_open_named_pipe(char *name) { - void *pipe = CreateFileA(name, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); - return (os_pipe){.file = pipe, .name = name}; + iptr pipe = CreateFileA(name, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); + return (Pipe){.file = pipe, .name = name}; } static size -os_write_to_pipe(os_pipe p, void *data, size len) +os_write_to_pipe(Pipe p, void *data, size len) { i32 bytes_written; WriteFile(p.file, data, len, &bytes_written, 0); @@ -105,9 +158,9 @@ os_write_to_pipe(os_pipe p, void *data, size len) static BeamformerParametersFull * os_open_shared_memory_area(char *name) { - void *h = OpenFileMappingA(FILE_MAP_ALL_ACCESS, 0, name); - if (h == OS_INVALID_FILE) - return NULL; + iptr h = OpenFileMappingA(FILE_MAP_ALL_ACCESS, 0, name); + if (h == INVALID_FILE) + return 0; BeamformerParametersFull *new; new = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(*new)); @@ -179,9 +232,9 @@ set_beamformer_pipeline(char *shm_name, i32 *stages, i32 stages_count) b32 send_data(char *pipe_name, char *shm_name, i16 *data, uv2 data_dim) { - if (g_pipe.file == OS_INVALID_FILE) { + if (g_pipe.file == INVALID_FILE) { g_pipe = os_open_named_pipe(pipe_name); - if (g_pipe.file == OS_INVALID_FILE) { + if (g_pipe.file == INVALID_FILE) { error_msg("failed to open pipe"); return 0; } @@ -214,3 +267,61 @@ set_beamformer_parameters(char *shm_name, BeamformerParameters *new_bp) return 1; } + +void +beamform_data_synchronized(char *pipe_name, char *shm_name, i16 *data, uv2 data_dim, + uv3 output_points, f32 *out_data) +{ + if (!check_shared_memory(shm_name)) + return; + + if (output_points.x == 0) output_points.x = 1; + if (output_points.y == 0) output_points.y = 1; + if (output_points.z == 0) output_points.z = 1; + + Pipe pipe = os_open_read_pipe(OS_EXPORT_PIPE_NAME); + if (pipe.file == INVALID_FILE) { + error_msg("failed to open export pipe"); + return; + } + + if (g_pipe.file == INVALID_FILE) { + g_pipe = os_open_named_pipe(pipe_name); + if (g_pipe.file == INVALID_FILE) { + error_msg("failed to open data pipe"); + return; + } + } + + g_bp->raw.rf_raw_dim = data_dim; + g_bp->raw.output_points.x = output_points.x; + g_bp->raw.output_points.y = output_points.y; + g_bp->raw.output_points.z = output_points.z; + g_bp->export_next_frame = 1; + + s8 export_name = s8(OS_EXPORT_PIPE_NAME); + if (export_name.len > ARRAY_COUNT(g_bp->export_pipe_name)) { + error_msg("export pipe name too long"); + return; + } + + for (u32 i = 0; i < export_name.len; i++) + g_bp->export_pipe_name[i] = export_name.data[i]; + + g_bp->upload = 1; + + size data_size = data_dim.x * data_dim.y * sizeof(i16); + size written = os_write_to_pipe(g_pipe, data, data_size); + if (written != data_size) { + /* error */ + error_msg("failed to write full data to pipe: wrote: %ld", written); + return; + } + + size output_size = output_points.x * output_points.y * output_points.z * 2 * sizeof(f32); + b32 success = os_read_pipe(pipe, out_data, output_size); + os_close_read_pipe(pipe); + + if (!success) + warning_msg("failed to read full export data from pipe\n"); +} diff --git a/helpers/ogl_beamformer_lib.h b/helpers/ogl_beamformer_lib.h @@ -13,6 +13,7 @@ typedef uint64_t u64; typedef float f32; typedef double f64; typedef ptrdiff_t size; +typedef ptrdiff_t iptr; #define ARRAY_COUNT(a) (sizeof(a) / sizeof(*a)) typedef struct { size len; u8 *data; } s8; @@ -27,6 +28,7 @@ typedef struct { size len; u8 *data; } s8; typedef struct { f32 x, y; } v2; typedef struct { f32 x, y, z, w; } v4; typedef struct { u32 x, y; } uv2; +typedef struct { u32 x, y, z; } uv3; typedef struct { u32 x, y, z, w; } uv4; #include "../beamformer_parameters.h" @@ -34,3 +36,9 @@ typedef struct { u32 x, y, z, w; } uv4; LIB_FN b32 set_beamformer_parameters(char *shm_name, BeamformerParameters *); LIB_FN b32 set_beamformer_pipeline(char *shm_name, i32 *stages, i32 stages_count); LIB_FN b32 send_data(char *pipe_name, char *shm_name, i16 *data, uv2 data_dim); + +/* NOTE: sends data and waits for (complex) beamformed data to be returned. + * out_data: must be allocated by the caller as 2 f32s per output point. */ +LIB_FN void beamform_data_synchronized(char *pipe_name, char *shm_name, + i16 *data, uv2 data_dim, + uv3 output_points, f32 *out_data); diff --git a/os_unix.c b/os_unix.c @@ -8,8 +8,7 @@ #include <sys/stat.h> #include <unistd.h> -static b32 -os_write_file(iptr file, s8 raw) +static PLATFORM_WRITE_FILE_FN(os_write_file) { while (raw.len) { size r = write(file, raw.data, raw.len); @@ -54,6 +53,19 @@ static PLATFORM_ALLOC_ARENA_FN(os_alloc_arena) return result; } +static PLATFORM_CLOSE_FN(os_close) +{ + close(file); +} + +static PLATFORM_OPEN_FOR_WRITE_FN(os_open_for_write) +{ + iptr result = open(fname, O_WRONLY|O_TRUNC); + if (result == -1) + result = INVALID_FILE; + return result; +} + static s8 os_read_file(Arena *a, char *fname, size fsize) { diff --git a/os_win32.c b/os_win32.c @@ -74,8 +74,7 @@ W32(b32) VirtualFree(u8 *, size, u32); static iptr win32_stderr_handle; -static b32 -os_write_file(iptr file, s8 raw) +static PLATFORM_WRITE_FILE_FN(os_write_file) { i32 wlen; WriteFile(file, raw.data, raw.len, &wlen, 0); @@ -121,6 +120,17 @@ static PLATFORM_ALLOC_ARENA_FN(os_alloc_arena) return result; } +static PLATFORM_CLOSE_FN(os_close) +{ + CloseHandle(file); +} + +static PLATFORM_OPEN_FOR_WRITE_FN(os_open_for_write) +{ + iptr result = CreateFileA(name, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); + return result; +} + static s8 os_read_file(Arena *a, char *fname, size fsize) { diff --git a/static.c b/static.c @@ -255,8 +255,6 @@ setup_beamformer(BeamformerCtx *ctx, Arena temp_memory) { ctx->window_size = (uv2){.w = 1280, .h = 840}; - ctx->partial_compute_ctx.volume_dim = (uv4){.x = 1, .y = 1, .z = 1}; - SetConfigFlags(FLAG_VSYNC_HINT); InitWindow(ctx->window_size.w, ctx->window_size.h, "OGL Beamformer"); /* NOTE: do this after initing so that the window starts out floating in tiling wm */ @@ -276,7 +274,7 @@ setup_beamformer(BeamformerCtx *ctx, Arena temp_memory) ctx->fsctx.threshold = 40.0f; ctx->data_pipe = os_open_named_pipe(OS_PIPE_NAME); - ctx->params = os_open_shared_memory_area(OS_SMEM_NAME, sizeof(ctx->params)); + ctx->params = os_open_shared_memory_area(OS_SMEM_NAME, sizeof(*ctx->params)); /* TODO: properly handle this? */ ASSERT(ctx->data_pipe.file != INVALID_FILE); ASSERT(ctx->params); diff --git a/util.c b/util.c @@ -15,8 +15,9 @@ static i32 hadamard_12_12_transpose[] = { }; static void * -mem_clear(u8 *p, u8 c, size len) +mem_clear(void *p_, u8 c, size len) { + u8 *p = p_; while (len) p[--len] = c; return p; } diff --git a/util.h b/util.h @@ -164,6 +164,12 @@ typedef struct { #define PLATFORM_ALLOC_ARENA_FN(name) Arena name(Arena old, size capacity) typedef PLATFORM_ALLOC_ARENA_FN(platform_alloc_arena_fn); +#define PLATFORM_CLOSE_FN(name) void name(iptr file) +typedef PLATFORM_CLOSE_FN(platform_close_fn); + +#define PLATFORM_OPEN_FOR_WRITE_FN(name) iptr name(c8 *fname) +typedef PLATFORM_OPEN_FOR_WRITE_FN(platform_open_for_write_fn); + #define PLATFORM_POLL_PIPE_FN(name) b32 name(Pipe p) typedef PLATFORM_POLL_PIPE_FN(platform_poll_pipe_fn); @@ -173,11 +179,17 @@ typedef PLATFORM_READ_PIPE_FN(platform_read_pipe_fn); #define PLATFORM_WRITE_NEW_FILE_FN(name) b32 name(char *fname, s8 raw) typedef PLATFORM_WRITE_NEW_FILE_FN(platform_write_new_file_fn); +#define PLATFORM_WRITE_FILE_FN(name) b32 name(iptr file, s8 raw) +typedef PLATFORM_WRITE_FILE_FN(platform_write_file_fn); + #define PLATFORM_FNS \ X(alloc_arena) \ + X(close) \ + X(open_for_write) \ X(poll_pipe) \ X(read_pipe) \ - X(write_new_file) + X(write_new_file) \ + X(write_file) #define X(name) platform_ ## name ## _fn *name; typedef struct { PLATFORM_FNS } Platform;