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:
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;