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, ¤t, 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 }