ogl_beamforming

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

ogl_beamformer_lib.c (12785B)


      1 /* See LICENSE for license details. */
      2 #include "../compiler.h"
      3 
      4 #include "../util.h"
      5 #include "../beamformer_parameters.h"
      6 #include "ogl_beamformer_lib_base.h"
      7 #include "../beamformer_work_queue.c"
      8 
      9 #define PIPE_RETRY_PERIOD_MS (100ULL)
     10 
     11 global BeamformerSharedMemory *g_bp;
     12 global BeamformerLibErrorKind  g_lib_last_error;
     13 
     14 #if OS_LINUX
     15 #include "../os_linux.c"
     16 #elif OS_WINDOWS
     17 #include "../os_win32.c"
     18 
     19 #define PIPE_TYPE_BYTE      0x00
     20 #define PIPE_ACCESS_INBOUND 0x01
     21 
     22 #define PIPE_WAIT   0x00
     23 #define PIPE_NOWAIT 0x01
     24 
     25 #define ERROR_NO_DATA            232L
     26 #define ERROR_PIPE_NOT_CONNECTED 233L
     27 #define ERROR_PIPE_LISTENING     536L
     28 
     29 W32(iptr) CreateNamedPipeA(c8 *, u32, u32, u32, u32, u32, u32, void *);
     30 W32(b32)  DisconnectNamedPipe(iptr);
     31 W32(iptr) OpenFileMappingA(u32, b32, c8 *);
     32 W32(void) Sleep(u32);
     33 
     34 #else
     35 #error Unsupported Platform
     36 #endif
     37 
     38 #if OS_LINUX
     39 
     40 function Pipe
     41 os_open_read_pipe(char *name)
     42 {
     43 	mkfifo(name, 0660);
     44 	return (Pipe){.file = open(name, O_RDONLY|O_NONBLOCK), .name = name};
     45 }
     46 
     47 static void
     48 os_disconnect_pipe(Pipe p)
     49 {
     50 }
     51 
     52 static void
     53 os_close_pipe(iptr *file, char *name)
     54 {
     55 	if (file) close(*file);
     56 	if (name) unlink(name);
     57 	*file = INVALID_FILE;
     58 }
     59 
     60 static b32
     61 os_wait_read_pipe(Pipe p, void *buf, iz read_size, u32 timeout_ms)
     62 {
     63 	struct pollfd pfd = {.fd = p.file, .events = POLLIN};
     64 	iz total_read = 0;
     65 	if (poll(&pfd, 1, timeout_ms) > 0) {
     66 		iz r;
     67 		do {
     68 			 r = read(p.file, (u8 *)buf + total_read, read_size - total_read);
     69 			 if (r > 0) total_read += r;
     70 		} while (r != 0);
     71 	}
     72 	return total_read == read_size;
     73 }
     74 
     75 static BeamformerSharedMemory *
     76 os_open_shared_memory_area(char *name)
     77 {
     78 	BeamformerSharedMemory *result = 0;
     79 	i32 fd = shm_open(name, O_RDWR, S_IRUSR|S_IWUSR);
     80 	if (fd > 0) {
     81 		void *new = mmap(0, BEAMFORMER_SHARED_MEMORY_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
     82 		if (new != MAP_FAILED)
     83 			result = new;
     84 		close(fd);
     85 	}
     86 	return result;
     87 }
     88 
     89 #elif OS_WINDOWS
     90 
     91 /* TODO(rnp): temporary workaround */
     92 function OS_WAIT_ON_VALUE_FN(os_wait_on_value_stub)
     93 {
     94 	/* TODO(rnp): this doesn't work across processes on win32 (return 1 to cause a spin wait) */
     95 	return 1;
     96 	return WaitOnAddress(value, &current, sizeof(*value), timeout_ms);
     97 }
     98 #define os_wait_on_value os_wait_on_value_stub
     99 
    100 static Pipe
    101 os_open_read_pipe(char *name)
    102 {
    103 	iptr file = CreateNamedPipeA(name, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE|PIPE_NOWAIT, 1,
    104 	                             0, 1024UL * 1024UL, 0, 0);
    105 	return (Pipe){.file = file, .name = name};
    106 }
    107 
    108 static void
    109 os_disconnect_pipe(Pipe p)
    110 {
    111 	DisconnectNamedPipe(p.file);
    112 }
    113 
    114 static void
    115 os_close_pipe(iptr *file, char *name)
    116 {
    117 	if (file) CloseHandle(*file);
    118 	*file = INVALID_FILE;
    119 }
    120 
    121 static b32
    122 os_wait_read_pipe(Pipe p, void *buf, iz read_size, u32 timeout_ms)
    123 {
    124 	iz elapsed_ms = 0, total_read = 0;
    125 	while (elapsed_ms <= timeout_ms && read_size != total_read) {
    126 		u8 data;
    127 		i32 read;
    128 		b32 result = ReadFile(p.file, &data, 0, &read, 0);
    129 		if (!result) {
    130 			i32 error = GetLastError();
    131 			if (error != ERROR_NO_DATA &&
    132 			    error != ERROR_PIPE_LISTENING &&
    133 			    error != ERROR_PIPE_NOT_CONNECTED)
    134 			{
    135 				/* NOTE: pipe is in a bad state; we will never read anything */
    136 				break;
    137 			}
    138 			Sleep(PIPE_RETRY_PERIOD_MS);
    139 			elapsed_ms += PIPE_RETRY_PERIOD_MS;
    140 		} else {
    141 			ReadFile(p.file, (u8 *)buf + total_read, read_size - total_read, &read, 0);
    142 			total_read += read;
    143 		}
    144 	}
    145 	return total_read == read_size;
    146 }
    147 
    148 function BeamformerSharedMemory *
    149 os_open_shared_memory_area(char *name)
    150 {
    151 	BeamformerSharedMemory *result = 0;
    152 	iptr h = OpenFileMappingA(FILE_MAP_ALL_ACCESS, 0, name);
    153 	if (h != INVALID_FILE) {
    154 		result = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, BEAMFORMER_SHARED_MEMORY_SIZE);
    155 		CloseHandle(h);
    156 	}
    157 
    158 	return result;
    159 }
    160 
    161 #endif
    162 
    163 function b32
    164 check_shared_memory(void)
    165 {
    166 	b32 result = 1;
    167 	if (!g_bp) {
    168 		g_bp = os_open_shared_memory_area(OS_SHARED_MEMORY_NAME);
    169 		if (!g_bp) {
    170 			result = 0;
    171 			g_lib_last_error = BF_LIB_ERR_KIND_SHARED_MEMORY;
    172 		}
    173 	} else if (g_bp->version != BEAMFORMER_PARAMETERS_VERSION) {
    174 		g_lib_last_error = BF_LIB_ERR_KIND_VERSION_MISMATCH;
    175 		result = 0;
    176 	}
    177 	return result;
    178 }
    179 
    180 function BeamformWork *
    181 try_push_work_queue(void)
    182 {
    183 	BeamformWork *result = beamform_work_queue_push(&g_bp->external_work_queue);
    184 	if (!result) g_lib_last_error = BF_LIB_ERR_KIND_WORK_QUEUE_FULL;
    185 	return result;
    186 }
    187 
    188 function b32
    189 lib_try_wait_sync(i32 *sync, i32 timeout_ms, os_wait_on_value_fn *os_wait_on_value)
    190 {
    191 	b32 result = try_wait_sync(sync, timeout_ms, os_wait_on_value);
    192 	if (!result) g_lib_last_error = BF_LIB_ERR_KIND_SYNC_VARIABLE;
    193 	return result;
    194 }
    195 
    196 u32
    197 beamformer_get_api_version(void)
    198 {
    199 	return BEAMFORMER_PARAMETERS_VERSION;
    200 }
    201 
    202 const char *
    203 beamformer_error_string(BeamformerLibErrorKind kind)
    204 {
    205 	#define X(type, num, string) string,
    206 	local_persist const char *error_string_table[] = {BEAMFORMER_LIB_ERRORS "invalid error kind"};
    207 	#undef X
    208 	return error_string_table[MIN(kind, countof(error_string_table) - 1)];
    209 }
    210 
    211 BeamformerLibErrorKind
    212 beamformer_get_last_error(void)
    213 {
    214 	return g_lib_last_error;
    215 }
    216 
    217 const char *
    218 beamformer_get_last_error_string(void)
    219 {
    220 	return beamformer_error_string(beamformer_get_last_error());
    221 }
    222 
    223 b32
    224 set_beamformer_pipeline(i32 *stages, i32 stages_count)
    225 {
    226 	b32 result = 0;
    227 	if (stages_count <= countof(g_bp->compute_stages)) {
    228 		if (check_shared_memory()) {
    229 			g_bp->compute_stages_count = 0;
    230 			for (i32 i = 0; i < stages_count; i++) {
    231 				if (BETWEEN(stages[i], 0, ComputeShaderKind_Count)) {
    232 					g_bp->compute_stages[g_bp->compute_stages_count++] = stages[i];
    233 				}
    234 			}
    235 			result = g_bp->compute_stages_count == stages_count;
    236 			if (!result) {
    237 				g_lib_last_error = BF_LIB_ERR_KIND_INVALID_COMPUTE_STAGE;
    238 				g_bp->compute_stages_count = 0;
    239 			}
    240 		}
    241 	} else {
    242 		g_lib_last_error = BF_LIB_ERR_KIND_COMPUTE_STAGE_OVERFLOW;
    243 	}
    244 	return result;
    245 }
    246 
    247 b32
    248 beamformer_start_compute(u32 image_plane_tag)
    249 {
    250 	b32 result = 0;
    251 	if (image_plane_tag < IPT_LAST) {
    252 		if (check_shared_memory()) {
    253 			if (atomic_load(&g_bp->dispatch_compute_sync) == 0) {
    254 				g_bp->current_image_plane = image_plane_tag;
    255 				atomic_store(&g_bp->dispatch_compute_sync, 1);
    256 				result = 1;
    257 			} else {
    258 				g_lib_last_error = BF_LIB_ERR_KIND_SYNC_VARIABLE;
    259 			}
    260 		}
    261 	} else {
    262 		g_lib_last_error = BF_LIB_ERR_KIND_INVALID_IMAGE_PLANE;
    263 	}
    264 	return result;
    265 }
    266 
    267 function b32
    268 beamformer_upload_buffer(void *data, u32 size, i32 store_offset, i32 sync_offset,
    269                          BeamformerUploadKind kind, i32 timeout_ms)
    270 {
    271 	b32 result = 0;
    272 	if (check_shared_memory()) {
    273 		BeamformWork *work = try_push_work_queue();
    274 		result = work && lib_try_wait_sync((i32 *)((u8 *)g_bp + sync_offset), timeout_ms, os_wait_on_value);
    275 		if (result) {
    276 			BeamformerUploadContext *uc = &work->upload_context;
    277 			uc->shared_memory_offset = store_offset;
    278 			uc->size = size;
    279 			uc->kind = kind;
    280 			work->type = BW_UPLOAD_BUFFER;
    281 			work->completion_barrier = sync_offset;
    282 			mem_copy((u8 *)g_bp + store_offset, data, size);
    283 			beamform_work_queue_push_commit(&g_bp->external_work_queue);
    284 		}
    285 	}
    286 	return result;
    287 }
    288 
    289 #define BEAMFORMER_UPLOAD_FNS \
    290 	X(channel_mapping, i16, 1, CHANNEL_MAPPING) \
    291 	X(sparse_elements, i16, 1, SPARSE_ELEMENTS) \
    292 	X(focal_vectors,   f32, 2, FOCAL_VECTORS)
    293 
    294 #define X(name, dtype, elements, command) \
    295 b32 beamformer_push_##name (dtype *data, u32 count, i32 timeout_ms) { \
    296 	b32 result = 0;                                                                          \
    297 	if (count <= countof(g_bp->name)) {                                                      \
    298 		result = beamformer_upload_buffer(data, count * elements * sizeof(dtype),        \
    299 		                                  offsetof(BeamformerSharedMemory, name),        \
    300 		                                  offsetof(BeamformerSharedMemory, name##_sync), \
    301 		                                  BU_KIND_##command, timeout_ms);                \
    302 	} else {                                                                                 \
    303 		g_lib_last_error = BF_LIB_ERR_KIND_BUFFER_OVERFLOW;                              \
    304 	}                                                                                        \
    305 	return result;                                                                           \
    306 }
    307 BEAMFORMER_UPLOAD_FNS
    308 #undef X
    309 
    310 b32
    311 beamformer_push_parameters(BeamformerParameters *bp, i32 timeout_ms)
    312 {
    313 	b32 result = beamformer_upload_buffer(bp, sizeof(*bp),
    314 	                                      offsetof(BeamformerSharedMemory, parameters),
    315 	                                      offsetof(BeamformerSharedMemory, parameters_sync),
    316 	                                      BU_KIND_PARAMETERS, timeout_ms);
    317 	return result;
    318 }
    319 
    320 b32
    321 beamformer_push_data(void *data, u32 data_size, i32 timeout_ms)
    322 {
    323 	b32 result = 0;
    324 	if (data_size <= BEAMFORMER_MAX_RF_DATA_SIZE) {
    325 		result = beamformer_upload_buffer(data, data_size, BEAMFORMER_RF_DATA_OFF,
    326 		                                  offsetof(BeamformerSharedMemory, raw_data_sync),
    327 		                                  BU_KIND_RF_DATA, timeout_ms);
    328 	} else {
    329 		g_lib_last_error = BF_LIB_ERR_KIND_BUFFER_OVERFLOW;
    330 	}
    331 	return result;
    332 }
    333 
    334 b32
    335 beamformer_push_parameters_ui(BeamformerUIParameters *bp, i32 timeout_ms)
    336 {
    337 	b32 result = 0;
    338 	if (check_shared_memory()) {
    339 		BeamformWork *work = try_push_work_queue();
    340 		result = work && lib_try_wait_sync(&g_bp->parameters_ui_sync, timeout_ms, os_wait_on_value);
    341 		if (result) {
    342 			BeamformerUploadContext *uc = &work->upload_context;
    343 			uc->shared_memory_offset = offsetof(BeamformerSharedMemory, parameters);
    344 			uc->size = sizeof(g_bp->parameters);
    345 			uc->kind = BU_KIND_PARAMETERS;
    346 			work->type = BW_UPLOAD_BUFFER;
    347 			work->completion_barrier = offsetof(BeamformerSharedMemory, parameters_ui_sync);
    348 			mem_copy(&g_bp->parameters_ui, bp, sizeof(*bp));
    349 			beamform_work_queue_push_commit(&g_bp->external_work_queue);
    350 		}
    351 	}
    352 	return result;
    353 }
    354 
    355 b32
    356 beamformer_push_parameters_head(BeamformerParametersHead *bp, i32 timeout_ms)
    357 {
    358 	b32 result = 0;
    359 	if (check_shared_memory()) {
    360 		BeamformWork *work = try_push_work_queue();
    361 		result = work && lib_try_wait_sync(&g_bp->parameters_head_sync, timeout_ms, os_wait_on_value);
    362 		if (result) {
    363 			BeamformerUploadContext *uc = &work->upload_context;
    364 			uc->shared_memory_offset = offsetof(BeamformerSharedMemory, parameters);
    365 			uc->size = sizeof(g_bp->parameters);
    366 			uc->kind = BU_KIND_PARAMETERS;
    367 			work->type = BW_UPLOAD_BUFFER;
    368 			work->completion_barrier = offsetof(BeamformerSharedMemory, parameters_head_sync);
    369 			mem_copy(&g_bp->parameters_head, bp, sizeof(*bp));
    370 			beamform_work_queue_push_commit(&g_bp->external_work_queue);
    371 		}
    372 	}
    373 	return result;
    374 }
    375 
    376 b32
    377 set_beamformer_parameters(BeamformerParametersV0 *new_bp)
    378 {
    379 	b32 result = 0;
    380 	result |= beamformer_push_channel_mapping((i16 *)new_bp->channel_mapping,
    381 	                                          countof(new_bp->channel_mapping), 0);
    382 	result |= beamformer_push_sparse_elements((i16 *)new_bp->uforces_channels,
    383 	                                          countof(new_bp->uforces_channels), 0);
    384 	v2 focal_vectors[256];
    385 	for (u32 i = 0; i < ARRAY_COUNT(focal_vectors); i++)
    386 		focal_vectors[i] = (v2){{new_bp->transmit_angles[i], new_bp->focal_depths[i]}};
    387 	result |= beamformer_push_focal_vectors((f32 *)focal_vectors, countof(focal_vectors), 0);
    388 	result |= beamformer_push_parameters((BeamformerParameters *)&new_bp->xdc_transform, 0);
    389 	return result;
    390 }
    391 
    392 b32
    393 send_data(void *data, u32 data_size)
    394 {
    395 	b32 result = 0;
    396 	if (beamformer_push_data(data, data_size, 0)) {
    397 		result = beamformer_start_compute(0);
    398 		if (result) {
    399 			/* TODO(rnp): should we just set timeout on acquiring the lock instead of this? */
    400 			try_wait_sync(&g_bp->raw_data_sync, -1, os_wait_on_value);
    401 			atomic_store(&g_bp->raw_data_sync, 1);
    402 		}
    403 	}
    404 	return result;
    405 }
    406 
    407 b32
    408 beamform_data_synchronized(void *data, u32 data_size, u32 output_points[3], f32 *out_data, i32 timeout_ms)
    409 {
    410 	b32 result = 0;
    411 	if (check_shared_memory()) {
    412 		output_points[0] = MAX(1, output_points[0]);
    413 		output_points[1] = MAX(1, output_points[1]);
    414 		output_points[2] = MAX(1, output_points[2]);
    415 
    416 		g_bp->parameters.output_points[0] = output_points[0];
    417 		g_bp->parameters.output_points[1] = output_points[1];
    418 		g_bp->parameters.output_points[2] = output_points[2];
    419 		g_bp->export_next_frame = 1;
    420 
    421 		Pipe export_pipe = os_open_read_pipe(OS_EXPORT_PIPE_NAME);
    422 		if (export_pipe.file != INVALID_FILE) {
    423 			if (send_data(data, data_size)) {
    424 				iz output_size = output_points[0] * output_points[1] *
    425 				                 output_points[2] * sizeof(f32) * 2;
    426 				result = os_wait_read_pipe(export_pipe, out_data, output_size, timeout_ms);
    427 				if (!result) g_lib_last_error = BF_LIB_ERR_KIND_READ_EXPORT_PIPE;
    428 			}
    429 
    430 			os_disconnect_pipe(export_pipe);
    431 			os_close_pipe(&export_pipe.file, export_pipe.name);
    432 		} else {
    433 			g_lib_last_error = BF_LIB_ERR_KIND_OPEN_EXPORT_PIPE;
    434 		}
    435 	}
    436 	return result;
    437 }