ogl_beamforming

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

Commit: 004752ed6527fcbbec1a09443e69c35aa71c9f23
Parent: e06bc2e345da76ce0c8ca5b1723a8ff76710e5f4
Author: Randy Palamar
Date:   Tue,  9 Jul 2024 14:01:35 -0600

modify helper to be a callable lib

This also adds a shared memory region for sending imaging
parameters in from an external process.

Diffstat:
Mbeamformer.h | 36++++++++++++++++++++++++------------
Mbuild.sh | 1-
Ahelpers/ogl_beamformer_lib.c | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahelpers/ogl_beamformer_lib.h | 36++++++++++++++++++++++++++++++++++++
Dhelpers/ogl_beamformer_pipe.c | 122-------------------------------------------------------------------------------
Mmain.c | 6++++++
Mos_unix.c | 28++++++++++++++++++++++++++++
7 files changed, 225 insertions(+), 135 deletions(-)

diff --git a/beamformer.h b/beamformer.h @@ -48,18 +48,6 @@ enum program_flags { RELOAD_SHADERS = 1 << 0, }; -#if defined(__unix__) - #define GL_GLEXT_PROTOTYPES 1 - #include <GL/glcorearb.h> - #include <GL/glext.h> - #include "os_unix.c" -#elif defined(_WIN32) - #include <glad.h> - #include "os_win32.c" -#else - #error Unsupported Platform! -#endif - typedef struct { u32 programs[CS_LAST]; @@ -84,6 +72,28 @@ typedef struct { } FragmentShaderCtx; typedef struct { + i16 channel_row_mapping[128]; + i16 channel_column_mapping[128]; + i16 uforces_channels[128]; + u32 channel_data_stride; + f32 speed_of_sound; + f32 sampling_frequency; + uv3 output_points; +} BeamformerParameters; + +#if defined(__unix__) + #define GL_GLEXT_PROTOTYPES 1 + #include <GL/glcorearb.h> + #include <GL/glext.h> + #include "os_unix.c" +#elif defined(_WIN32) + #include <glad.h> + #include "os_win32.c" +#else + #error Unsupported Platform! +#endif + +typedef struct { uv2 window_size; u32 flags; @@ -101,6 +111,8 @@ typedef struct { os_pipe data_pipe; u32 partial_transfer_count; + + BeamformerParameters *params; } BeamformerCtx; #endif /*_BEAMFORMER_H_ */ diff --git a/build.sh b/build.sh @@ -20,5 +20,4 @@ case "$1" in ;; esac - cc $cflags -o ogl main.c $ldflags diff --git a/helpers/ogl_beamformer_lib.c b/helpers/ogl_beamformer_lib.c @@ -0,0 +1,131 @@ +#include "ogl_beamformer_lib.h" + +#if defined(__unix__) +#include <fcntl.h> +#include <sys/mman.h> +#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; +#elif defined(_WIN32) +#include <windows.h> + +#define OS_INVALID_FILE (INVALID_HANDLE_VALUE) +typedef HANDLE os_file; +typedef struct { + os_file file; + char *name; +} os_pipe; + +#else +#error Unsupported Platform +#endif + +static BeamformerParameters *bp; +static char *shm_name = "/ogl_beamformer_parameters"; +static os_pipe g_pipe = {.file = OS_INVALID_FILE}; + +#if defined(__unix__) +static os_pipe +os_open_named_pipe(char *name) +{ + return (os_pipe){.file = open(name, O_WRONLY), .name = name}; +} + +static size +os_write_to_pipe(os_pipe p, void *data, size len) +{ + size written = 0, w = 0; + do { + written += w; + w = write(p.file, data, len); + } while(written != len && w != 0); + return written; +} + +static void +os_close_pipe(void) +{ + close(g_pipe.file); +} + +static BeamformerParameters * +os_open_shared_memory_area(char *name) +{ + i32 fd = shm_open(name, O_RDWR, S_IRUSR|S_IWUSR); + if (fd == -1) + return NULL; + + BeamformerParameters *new; + new = mmap(NULL, sizeof(BeamformerParameters), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + close(fd); + + if (new == MAP_FAILED) + return NULL; + + return new; +} + +#elif defined(_WIN32) + +static os_pipe +os_open_named_pipe(char *name) +{ + HANDLE pipe = CreateFileA(name, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); + return (os_pipe){.file = pipe, .name = name}; +} + +static size +os_write_to_pipe(os_pipe p, void *data, size len) +{ + DWORD bytes_written; + WriteFile(p.file, data, len, &bytes_written, 0); + return bytes_written; +} + +static void +os_close_pipe(void) +{ + CloseHandle(g_pipe.file); +} +#endif + +void +send_data(char *pipe_name, i16 *data, uv3 data_dim) +{ + if (g_pipe.file == OS_INVALID_FILE) { + g_pipe = os_open_named_pipe(pipe_name); + if (g_pipe.file == OS_INVALID_FILE) { + mexErrMsgIdAndTxt("ogl_beamformer:pipe_error", "failed to open pipe"); + return; + } + } + + size data_size = data_dim.x * data_dim.y * data_dim.z * sizeof(i16); + size written = os_write_to_pipe(g_pipe, data, data_size); + if (written != data_size) + mexWarnMsgIdAndTxt("ogl_beamformer:write_error", + "failed to write full data to pipe: wrote: %ld", written); +} + +void +set_beamformer_parameters(BeamformerParameters *new_bp) +{ + if (bp == NULL) { + bp = os_open_shared_memory_area(shm_name); + if (bp == NULL) { + mexErrMsgIdAndTxt("ogl_beamformer:shared_memory", + "failed to open shared memory area"); + return; + } + } + + u8 *src = (u8 *)new_bp, *dest = (u8 *)bp; + for (size i = 0; i < sizeof(BeamformerParameters); i++) + dest[i] = src[i]; +} diff --git a/helpers/ogl_beamformer_lib.h b/helpers/ogl_beamformer_lib.h @@ -0,0 +1,36 @@ +/* NOTE: mex.h can still be used to get access to functions that print to the matlab console */ +#include <mex.h> + +#include <stddef.h> +#include <stdint.h> + +typedef uint8_t u8; +typedef int16_t i16; +typedef int32_t i32; +typedef uint32_t u32; +typedef uint32_t b32; +typedef float f32; +typedef double f64; +typedef ptrdiff_t size; + +#if defined(_WIN32) +#define LIB_FN __declspec(dllexport) +#else +#define LIB_FN +#endif + +typedef struct { u32 x, y, z; } uv3; +typedef struct { f32 x, y, z; } v3; + +typedef struct { + i16 channel_row_mapping[128]; + i16 channel_column_mapping[128]; + i16 uforces_channels[128]; + u32 channel_data_stride; + f32 speed_of_sound; + f32 sampling_frequency; + uv3 output_points; +} BeamformerParameters; + +LIB_FN void set_beamformer_parameters(BeamformerParameters *); +LIB_FN void send_data(char *, i16 *, uv3 data_dim); diff --git a/helpers/ogl_beamformer_pipe.c b/helpers/ogl_beamformer_pipe.c @@ -1,122 +0,0 @@ -#include <mex.h> - -#include <stddef.h> -#include <stdint.h> -typedef int16_t i16; -typedef int32_t i32; -typedef ptrdiff_t size; - -#if defined(__unix__) -#include <fcntl.h> -#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; -#elif defined(_WIN32) -#include <windows.h> - -#define OS_INVALID_FILE (INVALID_HANDLE_VALUE) -typedef HANDLE os_file; -typedef struct { - os_file file; - char *name; -} os_pipe; - -#else -#error Unsupported Platform -#endif - -/* NOTE: the mexAtExit function is poorly designed and doesn't - * take a context pointer so this must be a global */ -static os_pipe g_pipe = {.file = OS_INVALID_FILE}; - -#if defined(__unix__) -static os_pipe -os_open_named_pipe(char *name) -{ - return (os_pipe){.file = open(name, O_WRONLY), .name = name}; -} - -static size -os_write_to_pipe(os_pipe p, void *data, size len) -{ - size written = 0, w = 0; - do { - written += w; - w = write(p.file, data, len); - } while(written != len && w != 0); - return written; -} - -static void -os_close_pipe(void) -{ - close(g_pipe.file); - mxFree(g_pipe.name); -} - -#elif defined(_WIN32) - -static os_pipe -os_open_named_pipe(char *name) -{ - HANDLE pipe = CreateFileA(name, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); - return (os_pipe){.file = pipe, .name = name}; -} - -static size -os_write_to_pipe(os_pipe p, void *data, size len) -{ - DWORD bytes_written; - WriteFile(p.file, data, len, &bytes_written, 0); - return bytes_written; -} - -static void -os_close_pipe(void) -{ - CloseHandle(g_pipe.file); - mxFree(g_pipe.name); -} -#endif - -/* NOTE: usage: pipe_data_to_beamformer(pipe_name, data) */ -void -mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) -{ - if (nrhs != 2) { - mexErrMsgIdAndTxt("ogl_beamformer:wrong_input", - "usage: ogl_beamformer_pipe(pipe_name, data)"); - return; - } - - if (g_pipe.file == OS_INVALID_FILE) { - char *pipe_name = mxArrayToString(prhs[0]); - g_pipe = os_open_named_pipe(pipe_name); - if (g_pipe.file == OS_INVALID_FILE) { - mexErrMsgIdAndTxt("ogl_beamformer:pipe_error", "failed to open pipe"); - mxFree(pipe_name); - return; - } - mexAtExit(os_close_pipe); - } - - const mxArray *mxdata = prhs[1]; - if (!mxIsInt16(mxdata)) { - mexErrMsgIdAndTxt("ogl_beamformer:invalid_type", - "invalid data type; only int16 is supported"); - return; - } - - void *data = mxGetPr(mxdata); - size data_size = mxGetNumberOfElements(mxdata) * sizeof(i16); - size written = os_write_to_pipe(g_pipe, data, data_size); - if (written != data_size) - mexWarnMsgIdAndTxt("ogl_beamformer:write_error", - "failed to write full data to pipe: wrote: %ld", written); -} diff --git a/main.c b/main.c @@ -213,8 +213,10 @@ main(void) init_fragment_shader_ctx(&ctx.fsctx, ctx.out_data_dim); ctx.data_pipe = os_open_named_pipe("/tmp/beamformer_data_fifo"); + ctx.params = os_open_shared_memory_area("/ogl_beamformer_parameters"); /* TODO: properly handle this? */ ASSERT(ctx.data_pipe.file != OS_INVALID_FILE); + ASSERT(ctx.params); ctx.flags |= RELOAD_SHADERS; @@ -229,6 +231,10 @@ main(void) do_beamformer(&ctx, temp_memory); } + /* NOTE: make sure this will get cleaned up after external + * programs release their references */ + os_remove_shared_memory("/ogl_beamformer_parameters"); + /* NOTE: garbage code needed for Linux */ os_close_named_pipe(ctx.data_pipe); } diff --git a/os_unix.c b/os_unix.c @@ -101,3 +101,31 @@ os_read_pipe_data(os_pipe p, void *buf, size len) } while (r); return total_read; } + +static BeamformerParameters * +os_open_shared_memory_area(char *name) +{ + i32 fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); + if (fd == -1) + return NULL; + + if (ftruncate(fd, sizeof(BeamformerParameters)) == -1) { + close(fd); + return NULL; + } + + BeamformerParameters *new; + new = mmap(NULL, sizeof(BeamformerParameters), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + close(fd); + + if (new == MAP_FAILED) + return NULL; + + return new; +} + +static void +os_remove_shared_memory(char *name) +{ + shm_unlink(name); +}