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