ogl_beamforming

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

ogl_beamformer_lib.c (7730B)


      1 /* See LICENSE for license details. */
      2 #include "ogl_beamformer_lib.h"
      3 
      4 typedef struct {
      5 	BeamformerParameters raw;
      6 	enum compute_shaders compute_stages[16];
      7 	u32                  compute_stages_count;
      8 	b32                  upload;
      9 	b32                  export_next_frame;
     10 	c8                   export_pipe_name[1024];
     11 } BeamformerParametersFull;
     12 
     13 typedef struct {
     14 	iptr  file;
     15 	char *name;
     16 } Pipe;
     17 
     18 #define INVALID_FILE (-1)
     19 
     20 static volatile BeamformerParametersFull *g_bp;
     21 static Pipe g_pipe = {.file = INVALID_FILE};
     22 
     23 #define ARRAY_COUNT(a) (sizeof(a) / sizeof(*a))
     24 
     25 #if defined(__unix__)
     26 #include <fcntl.h>
     27 #include <sys/mman.h>
     28 #include <sys/stat.h>
     29 #include <unistd.h>
     30 
     31 #define OS_EXPORT_PIPE_NAME "/tmp/beamformer_output_pipe"
     32 
     33 #elif defined(_WIN32)
     34 
     35 #define OS_EXPORT_PIPE_NAME "\\\\.\\pipe\\beamformer_output_fifo"
     36 
     37 #define OPEN_EXISTING        3
     38 #define GENERIC_WRITE        0x40000000
     39 #define FILE_MAP_ALL_ACCESS  0x000F001F
     40 
     41 #define PIPE_TYPE_BYTE      0x00
     42 #define PIPE_ACCESS_INBOUND 0x01
     43 
     44 #define W32(r) __declspec(dllimport) r __stdcall
     45 W32(b32)  CloseHandle(iptr);
     46 W32(iptr) CreateFileA(c8 *, u32, u32, void *, u32, u32, void *);
     47 W32(iptr) CreateNamedPipeA(c8 *, u32, u32, u32, u32, u32, u32, void *);
     48 W32(iptr) MapViewOfFile(iptr, u32, u32, u32, u64);
     49 W32(iptr) OpenFileMappingA(u32, b32, c8 *);
     50 W32(b32)  ReadFile(iptr, u8 *, i32, i32 *, void *);
     51 W32(b32)  WriteFile(iptr, u8 *, i32, i32 *, void *);
     52 
     53 #else
     54 #error Unsupported Platform
     55 #endif
     56 
     57 #if defined(__unix__)
     58 static Pipe
     59 os_open_read_pipe(char *name)
     60 {
     61 	mkfifo(name, 0660);
     62 	return (Pipe){.file = open(name, O_RDONLY|O_NONBLOCK), .name = name};
     63 }
     64 
     65 static void
     66 os_close_read_pipe(Pipe p)
     67 {
     68 	close(p.file);
     69 	unlink(p.name);
     70 }
     71 
     72 static b32
     73 os_read_pipe(Pipe p, void *buf, size read_size)
     74 {
     75 	size r = 0, total_read = 0;
     76 	do {
     77 		if (r != -1)
     78 			total_read += r;
     79 		r = read(p.file, buf + total_read, read_size - total_read);
     80 	} while (r);
     81 	return total_read == read_size;
     82 }
     83 
     84 static Pipe
     85 os_open_named_pipe(char *name)
     86 {
     87 	return (Pipe){.file = open(name, O_WRONLY), .name = name};
     88 }
     89 
     90 static size
     91 os_write_to_pipe(Pipe p, void *data, size len)
     92 {
     93 	size written = 0, w = 0;
     94 	do {
     95 		if (w != -1)
     96 			written += w;
     97 		w = write(p.file, data + written, len - written);
     98 	} while(written != len && w != 0);
     99 	return written;
    100 }
    101 
    102 static BeamformerParametersFull *
    103 os_open_shared_memory_area(char *name)
    104 {
    105 	i32 fd = shm_open(name, O_RDWR, S_IRUSR|S_IWUSR);
    106 	if (fd == -1)
    107 		return NULL;
    108 
    109 	BeamformerParametersFull *new;
    110 	new = mmap(NULL, sizeof(*new), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    111 	close(fd);
    112 
    113 	if (new == MAP_FAILED)
    114 		return NULL;
    115 
    116 	return new;
    117 }
    118 
    119 #elif defined(_WIN32)
    120 
    121 static Pipe
    122 os_open_read_pipe(char *name)
    123 {
    124 	iptr file = CreateNamedPipeA(name, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE, 1,
    125 	                             0, 1024UL * 1024UL, 0, 0);
    126 	return (Pipe){.file = file, .name = name};
    127 }
    128 
    129 static void
    130 os_close_read_pipe(Pipe p)
    131 {
    132 	CloseHandle(p.file);
    133 }
    134 
    135 static b32
    136 os_read_pipe(Pipe p, void *buf, size read_size)
    137 {
    138 	i32 total_read = 0;
    139 	ReadFile(p.file, buf, read_size, &total_read, 0);
    140 	return total_read == read_size;
    141 }
    142 
    143 static Pipe
    144 os_open_named_pipe(char *name)
    145 {
    146 	iptr pipe = CreateFileA(name, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
    147 	return (Pipe){.file = pipe, .name = name};
    148 }
    149 
    150 static size
    151 os_write_to_pipe(Pipe p, void *data, size len)
    152 {
    153 	i32 bytes_written;
    154 	WriteFile(p.file, data, len, &bytes_written, 0);
    155 	return bytes_written;
    156 }
    157 
    158 static BeamformerParametersFull *
    159 os_open_shared_memory_area(char *name)
    160 {
    161 	iptr h = OpenFileMappingA(FILE_MAP_ALL_ACCESS, 0, name);
    162 	if (h == INVALID_FILE)
    163 		return 0;
    164 
    165 	BeamformerParametersFull *new;
    166 	iptr view = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(*new));
    167 	new = (BeamformerParametersFull *)view;
    168 	CloseHandle(h);
    169 
    170 	return new;
    171 }
    172 #endif
    173 
    174 #if defined(MATLAB_CONSOLE)
    175 #define mexErrMsgIdAndTxt  mexErrMsgIdAndTxt_800
    176 #define mexWarnMsgIdAndTxt mexWarnMsgIdAndTxt_800
    177 void mexErrMsgIdAndTxt(const c8 *, c8 *, ...);
    178 void mexWarnMsgIdAndTxt(const c8 *, c8 *, ...);
    179 #define error_tag "ogl_beamformer_lib:error"
    180 #define error_msg(...)   mexErrMsgIdAndTxt(error_tag, __VA_ARGS__)
    181 #define warning_msg(...) mexWarnMsgIdAndTxt(error_tag, __VA_ARGS__)
    182 #else
    183 #define error_msg(...)
    184 #define warning_msg(...)
    185 #endif
    186 
    187 static b32
    188 check_shared_memory(char *name)
    189 {
    190 	if (!g_bp) {
    191 		g_bp = os_open_shared_memory_area(name);
    192 		if (!g_bp) {
    193 			error_msg("failed to open shared memory area");
    194 			return 0;
    195 		}
    196 	}
    197 	return 1;
    198 }
    199 
    200 b32
    201 set_beamformer_pipeline(char *shm_name, i32 *stages, i32 stages_count)
    202 {
    203 	if (stages_count > ARRAY_COUNT(g_bp->compute_stages)) {
    204 		error_msg("maximum stage count is %u", ARRAY_COUNT(g_bp->compute_stages));
    205 		return 0;
    206 	}
    207 
    208 	if (!check_shared_memory(shm_name))
    209 		return 0;
    210 
    211 	for (i32 i = 0; i < stages_count; i++) {
    212 		switch (stages[i]) {
    213 		case CS_CUDA_DECODE:
    214 		case CS_CUDA_HILBERT:
    215 		case CS_DAS:
    216 		case CS_DEMOD:
    217 		case CS_HADAMARD:
    218 		case CS_MIN_MAX:
    219 		case CS_SUM:
    220 			g_bp->compute_stages[i] = stages[i];
    221 			break;
    222 		default:
    223 			error_msg("invalid shader stage: %d", stages[i]);
    224 			return 0;
    225 		}
    226 	}
    227 	g_bp->compute_stages_count = stages_count;
    228 
    229 	return 1;
    230 }
    231 
    232 b32
    233 send_data(char *pipe_name, char *shm_name, i16 *data, uv2 data_dim)
    234 {
    235 	if (g_pipe.file == INVALID_FILE) {
    236 		g_pipe = os_open_named_pipe(pipe_name);
    237 		if (g_pipe.file == INVALID_FILE) {
    238 			error_msg("failed to open pipe");
    239 			return 0;
    240 		}
    241 	}
    242 
    243 	if (!check_shared_memory(shm_name))
    244 		return 0;
    245 
    246 	/* TODO: this probably needs a mutex around it if we want to change it here */
    247 	g_bp->raw.rf_raw_dim = data_dim;
    248 	size data_size       = data_dim.x * data_dim.y * sizeof(i16);
    249 	size written         = os_write_to_pipe(g_pipe, data, data_size);
    250 	if (written != data_size)
    251 		warning_msg("failed to write full data to pipe: wrote: %ld", written);
    252 	g_bp->upload = 1;
    253 
    254 	return 1;
    255 }
    256 
    257 b32
    258 set_beamformer_parameters(char *shm_name, BeamformerParameters *new_bp)
    259 {
    260 	if (!check_shared_memory(shm_name))
    261 		return 0;
    262 
    263 	u8 *src = (u8 *)new_bp, *dest = (u8 *)&g_bp->raw;
    264 	for (size i = 0; i < sizeof(BeamformerParameters); i++)
    265 		dest[i] = src[i];
    266 	g_bp->upload = 1;
    267 
    268 	return 1;
    269 }
    270 
    271 void
    272 beamform_data_synchronized(char *pipe_name, char *shm_name, i16 *data, uv2 data_dim,
    273                            uv3 output_points, f32 *out_data)
    274 {
    275 	if (!check_shared_memory(shm_name))
    276 		return;
    277 
    278 	if (output_points.x == 0) output_points.x = 1;
    279 	if (output_points.y == 0) output_points.y = 1;
    280 	if (output_points.z == 0) output_points.z = 1;
    281 
    282 	Pipe pipe = os_open_read_pipe(OS_EXPORT_PIPE_NAME);
    283 	if (pipe.file == INVALID_FILE) {
    284 		error_msg("failed to open export pipe");
    285 		return;
    286 	}
    287 
    288 	if (g_pipe.file == INVALID_FILE) {
    289 		g_pipe = os_open_named_pipe(pipe_name);
    290 		if (g_pipe.file == INVALID_FILE) {
    291 			error_msg("failed to open data pipe");
    292 			return;
    293 		}
    294 	}
    295 
    296 	g_bp->raw.rf_raw_dim      = data_dim;
    297 	g_bp->raw.output_points.x = output_points.x;
    298 	g_bp->raw.output_points.y = output_points.y;
    299 	g_bp->raw.output_points.z = output_points.z;
    300 	g_bp->export_next_frame   = 1;
    301 
    302 	s8 export_name = s8(OS_EXPORT_PIPE_NAME);
    303 	if (export_name.len > ARRAY_COUNT(g_bp->export_pipe_name)) {
    304 		error_msg("export pipe name too long");
    305 		return;
    306 	}
    307 
    308 	for (u32 i = 0; i < export_name.len; i++)
    309 		g_bp->export_pipe_name[i] = export_name.data[i];
    310 
    311 	g_bp->upload = 1;
    312 
    313 	size data_size = data_dim.x * data_dim.y * sizeof(i16);
    314 	size written   = os_write_to_pipe(g_pipe, data, data_size);
    315 	if (written != data_size) {
    316 		/* error */
    317 		error_msg("failed to write full data to pipe: wrote: %ld", written);
    318 		return;
    319 	}
    320 
    321 	size output_size = output_points.x * output_points.y * output_points.z * 2 * sizeof(f32);
    322 	b32 success = os_read_pipe(pipe, out_data, output_size);
    323 	os_close_read_pipe(pipe);
    324 
    325 	if (!success)
    326 		warning_msg("failed to read full export data from pipe\n");
    327 }