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 (12759B)


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