ogl_beamformer_lib.c (21750B)
1 /* See LICENSE for license details. */ 2 #include "../compiler.h" 3 4 #define BEAMFORMER_IMPORT static 5 6 #include "../beamformer.h" 7 8 #include "../util.h" 9 10 #include "../generated/beamformer.meta.c" 11 #include "../beamformer_parameters.h" 12 #include "ogl_beamformer_lib_base.h" 13 14 #if OS_LINUX 15 #include "../os_linux.c" 16 #elif OS_WINDOWS 17 #include "../os_win32.c" 18 19 W32(iptr) OpenFileMappingA(u32, b32, c8 *); 20 21 #else 22 #error Unsupported Platform 23 #endif 24 25 #include "../util_os.c" 26 #include "../beamformer_shared_memory.c" 27 28 global struct { 29 BeamformerSharedMemory *bp; 30 i32 timeout_ms; 31 BeamformerLibErrorKind last_error; 32 } g_beamformer_library_context; 33 34 #if OS_LINUX 35 36 function void * 37 os_open_shared_memory_area(char *name) 38 { 39 void *result = 0; 40 i32 fd = shm_open(name, O_RDWR, S_IRUSR|S_IWUSR); 41 if (fd > 0) { 42 void *new = mmap(0, BEAMFORMER_SHARED_MEMORY_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 43 if (new != MAP_FAILED) result = new; 44 close(fd); 45 } 46 return result; 47 } 48 49 function void 50 os_close_shared_memory_area(void *memory) 51 { 52 munmap(memory, BEAMFORMER_SHARED_MEMORY_SIZE); 53 } 54 55 #elif OS_WINDOWS 56 57 W32(b32) UnmapViewOfFile(void *); 58 59 function b32 60 os_reserve_region_locks(void) 61 { 62 u8 buffer[1024]; 63 Stream sb = {.data = buffer, .cap = countof(buffer)}; 64 stream_append_s8(&sb, s8(OS_SHARED_MEMORY_NAME "_lock_")); 65 66 i32 start_index = sb.widx; 67 u32 reserved_count = 0; 68 for EachElement(os_w32_shared_memory_semaphores, it) { 69 stream_reset(&sb, start_index); 70 stream_append_u64(&sb, it); 71 stream_append_byte(&sb, 0); 72 os_w32_shared_memory_semaphores[it] = os_w32_create_semaphore((c8 *)sb.data, 1, 1); 73 if InvalidHandle(os_w32_shared_memory_semaphores[it]) 74 break; 75 reserved_count++; 76 } 77 78 b32 result = reserved_count == countof(os_w32_shared_memory_semaphores); 79 if (!result) { 80 for (u32 i = 0; i < reserved_count; i++) 81 CloseHandle(os_w32_shared_memory_semaphores[i].value[0]); 82 } 83 84 return result; 85 } 86 87 function void * 88 os_open_shared_memory_area(char *name) 89 { 90 iptr h = OpenFileMappingA(FILE_MAP_ALL_ACCESS, 0, name); 91 void *result = 0; 92 if (h != INVALID_FILE) { 93 void *new = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, BEAMFORMER_SHARED_MEMORY_SIZE); 94 if (new && os_reserve_region_locks()) 95 result = new; 96 if (new && !result) 97 UnmapViewOfFile(new); 98 CloseHandle(h); 99 } 100 return result; 101 } 102 103 function void 104 os_close_shared_memory_area(void *memory) 105 { 106 UnmapViewOfFile(memory); 107 } 108 109 #endif 110 111 #define lib_error_check(c, e) lib_error_check_(c, BeamformerLibErrorKind_##e) 112 function b32 113 lib_error_check_(b32 condition, BeamformerLibErrorKind error_kind) 114 { 115 b32 result = condition; 116 if (!result) g_beamformer_library_context.last_error = error_kind; 117 assert(result); 118 return result; 119 } 120 121 function b32 122 check_shared_memory(void) 123 { 124 b32 result = g_beamformer_library_context.bp != 0; 125 if unlikely(!g_beamformer_library_context.bp) { 126 BeamformerSharedMemory *bp = os_open_shared_memory_area(OS_SHARED_MEMORY_NAME); 127 if (lib_error_check(bp != 0, SharedMemory)) { 128 result = lib_error_check(bp->version == BEAMFORMER_SHARED_MEMORY_VERSION, VersionMismatch); 129 if (result) g_beamformer_library_context.bp = bp; 130 else os_close_shared_memory_area(bp); 131 } 132 } 133 134 if likely(g_beamformer_library_context.bp) 135 result = lib_error_check(likely(!g_beamformer_library_context.bp->invalid), InvalidAccess); 136 return result; 137 } 138 139 function b32 140 valid_parameter_block(u32 block) 141 { 142 b32 result = check_shared_memory(); 143 if (result) { 144 result = lib_error_check(block < g_beamformer_library_context.bp->reserved_parameter_blocks, 145 ParameterBlockUnallocated); 146 } 147 return result; 148 } 149 150 function BeamformWork * 151 try_push_work_queue(void) 152 { 153 BeamformWork *result = beamform_work_queue_push(&g_beamformer_library_context.bp->external_work_queue); 154 lib_error_check(result != 0, WorkQueueFull); 155 return result; 156 } 157 158 function b32 159 lib_try_lock(i32 lock, i32 timeout_ms) 160 { 161 b32 result = beamformer_shared_memory_take_lock(g_beamformer_library_context.bp, lock, (u32)timeout_ms); 162 lib_error_check(result, SyncVariable); 163 return result; 164 } 165 166 function void 167 lib_release_lock(i32 lock) 168 { 169 beamformer_shared_memory_release_lock(g_beamformer_library_context.bp, lock); 170 } 171 172 u32 173 beamformer_get_api_version(void) 174 { 175 return BEAMFORMER_SHARED_MEMORY_VERSION; 176 } 177 178 const char * 179 beamformer_error_string(BeamformerLibErrorKind kind) 180 { 181 #define X(type, num, string) string, 182 local_persist const char *error_string_table[] = {BEAMFORMER_LIB_ERRORS "invalid error kind"}; 183 #undef X 184 return error_string_table[MIN(kind, countof(error_string_table) - 1)]; 185 } 186 187 BeamformerLibErrorKind 188 beamformer_get_last_error(void) 189 { 190 return g_beamformer_library_context.last_error; 191 } 192 193 const char * 194 beamformer_get_last_error_string(void) 195 { 196 return beamformer_error_string(beamformer_get_last_error()); 197 } 198 199 void 200 beamformer_set_global_timeout(u32 timeout_ms) 201 { 202 g_beamformer_library_context.timeout_ms = timeout_ms; 203 } 204 205 b32 206 beamformer_reserve_parameter_blocks(uint32_t count) 207 { 208 b32 result = 0; 209 if (check_shared_memory() && 210 lib_error_check(count <= BeamformerMaxParameterBlockSlots, ParameterBlockOverflow)) 211 { 212 g_beamformer_library_context.bp->reserved_parameter_blocks = count; 213 result = 1; 214 } 215 return result; 216 } 217 218 function b32 219 validate_pipeline(i32 *shaders, u32 shader_count, BeamformerDataKind data_kind) 220 { 221 b32 result = lib_error_check(shader_count <= BeamformerMaxComputeShaderStages, ComputeStageOverflow); 222 if (result) { 223 for (u32 i = 0; i < shader_count; i++) 224 result &= BETWEEN(shaders[i], BeamformerShaderKind_ComputeFirst, BeamformerShaderKind_ComputeLast); 225 if (!result) { 226 g_beamformer_library_context.last_error = BeamformerLibErrorKind_InvalidComputeStage; 227 } else if (shaders[0] != BeamformerShaderKind_Demodulate && 228 shaders[0] != BeamformerShaderKind_Decode) 229 { 230 g_beamformer_library_context.last_error = BeamformerLibErrorKind_InvalidStartShader; 231 result = 0; 232 } else if (shaders[0] == BeamformerShaderKind_Demodulate && 233 !(data_kind == BeamformerDataKind_Int16 || data_kind == BeamformerDataKind_Float32)) 234 { 235 g_beamformer_library_context.last_error = BeamformerLibErrorKind_InvalidDemodulationDataKind; 236 result = 0; 237 } 238 } 239 return result; 240 } 241 242 function b32 243 validate_simple_parameters(BeamformerSimpleParameters *bp) 244 { 245 b32 result = check_shared_memory(); 246 if (result) { 247 result &= bp->channel_count <= BeamformerMaxChannelCount; 248 if (!result) 249 g_beamformer_library_context.last_error = BeamformerLibErrorKind_InvalidSimpleParameters; 250 } 251 return result; 252 } 253 254 function b32 255 parameter_block_region_upload(void *data, u32 size, u32 block, BeamformerParameterBlockRegions region_id, 256 u32 block_offset, i32 timeout_ms) 257 { 258 i32 lock = BeamformerSharedMemoryLockKind_Count + (i32)block; 259 b32 result = valid_parameter_block(block) && lib_try_lock(lock, timeout_ms); 260 if (result) { 261 mem_copy((u8 *)beamformer_parameter_block(g_beamformer_library_context.bp, block) + block_offset, 262 data, size); 263 mark_parameter_block_region_dirty(g_beamformer_library_context.bp, block, region_id); 264 lib_release_lock(lock); 265 } 266 return result; 267 } 268 269 b32 270 beamformer_set_pipeline_stage_parameters_at(u32 stage_index, i32 parameter, u32 block) 271 { 272 u32 offset = BeamformerParameterBlockRegionOffsets[BeamformerParameterBlockRegion_ComputePipeline]; 273 offset += offsetof(BeamformerComputePipeline, parameters); 274 offset += (stage_index % BeamformerMaxComputeShaderStages) * sizeof(BeamformerShaderParameters); 275 b32 result = parameter_block_region_upload(¶meter, sizeof(BeamformerShaderParameters), block, 276 BeamformerParameterBlockRegion_ComputePipeline, offset, 277 g_beamformer_library_context.timeout_ms); 278 return result; 279 } 280 281 b32 282 beamformer_set_pipeline_stage_parameters(u32 stage_index, i32 parameter) 283 { 284 b32 result = beamformer_set_pipeline_stage_parameters_at(stage_index, parameter, 0); 285 return result; 286 } 287 288 b32 289 beamformer_push_pipeline_at(i32 *shaders, u32 shader_count, BeamformerDataKind data_kind, u32 block) 290 { 291 b32 result = 0; 292 if (check_shared_memory() && validate_pipeline(shaders, shader_count, data_kind)) { 293 i32 lock = BeamformerSharedMemoryLockKind_Count + (i32)block; 294 if (valid_parameter_block(block) && lib_try_lock(lock, g_beamformer_library_context.timeout_ms)) { 295 BeamformerParameterBlock *b = beamformer_parameter_block(g_beamformer_library_context.bp, block); 296 mem_copy(&b->pipeline.shaders, shaders, shader_count * sizeof(*shaders)); 297 mark_parameter_block_region_dirty(g_beamformer_library_context.bp, block, 298 BeamformerParameterBlockRegion_ComputePipeline); 299 b->pipeline.shader_count = shader_count; 300 b->pipeline.data_kind = data_kind; 301 lib_release_lock(lock); 302 result = 1; 303 } 304 } 305 return result; 306 } 307 308 b32 309 beamformer_push_pipeline(i32 *shaders, u32 shader_count, BeamformerDataKind data_kind) 310 { 311 b32 result = beamformer_push_pipeline_at(shaders, shader_count, data_kind, 0); 312 return result; 313 } 314 315 function b32 316 beamformer_create_filter_base(BeamformerFilterParameters params, u8 filter_slot, u8 parameter_block) 317 { 318 b32 result = 0; 319 if (check_shared_memory()) { 320 BeamformWork *work = try_push_work_queue(); 321 if (work) { 322 BeamformerCreateFilterContext *ctx = &work->create_filter_context; 323 work->kind = BeamformerWorkKind_CreateFilter; 324 ctx->parameters = params; 325 ctx->filter_slot = filter_slot % BeamformerFilterSlots; 326 ctx->parameter_block = parameter_block % BeamformerMaxParameterBlockSlots; 327 beamform_work_queue_push_commit(&g_beamformer_library_context.bp->external_work_queue); 328 result = 1; 329 } 330 } 331 return result; 332 } 333 334 b32 335 beamformer_create_filter(BeamformerFilterKind kind, void *filter_parameters, u32 filter_size, 336 f32 sampling_frequency, b32 complex, u8 filter_slot, u8 parameter_block) 337 { 338 b32 result = 0; 339 if (lib_error_check(kind >= 0 && kind < BeamformerFilterKind_Count, InvalidFilterKind)) { 340 BeamformerFilterParameters fp = {0}; 341 /* NOTE(rnp): any parameter struct works as base offset */ 342 filter_size = MIN(filter_size, sizeof(fp) - offsetof(BeamformerFilterParameters, kaiser)); 343 mem_copy(&fp.kaiser, filter_parameters, filter_size); 344 fp.kind = kind; 345 fp.complex = complex != 0; 346 fp.sampling_frequency = sampling_frequency; 347 result = beamformer_create_filter_base(fp, filter_slot, parameter_block); 348 } 349 return result; 350 } 351 352 function void 353 beamformer_flush_commands(void) 354 { 355 i32 lock = BeamformerSharedMemoryLockKind_DispatchCompute; 356 beamformer_shared_memory_take_lock(g_beamformer_library_context.bp, lock, 0); 357 } 358 359 #define BEAMFORMER_UPLOAD_FNS \ 360 X(channel_mapping, i16, 1, ChannelMapping) \ 361 X(focal_vectors, f32, 2, FocalVectors) \ 362 X(sparse_elements, i16, 1, SparseElements) \ 363 X(transmit_receive_orientations, u8, 1, TransmitReceiveOrientations) 364 365 #define X(name, dtype, elements, region_name) \ 366 b32 beamformer_push_##name ##_at(dtype *data, u32 count, u32 block) { \ 367 b32 result = 0; \ 368 if (lib_error_check(count <= countof(((BeamformerParameterBlock *)0)->name), BufferOverflow)) { \ 369 result = parameter_block_region_upload(data, count * elements * sizeof(dtype), block, \ 370 BeamformerParameterBlockRegion_##region_name, \ 371 offsetof(BeamformerParameterBlock, name), \ 372 g_beamformer_library_context.timeout_ms); \ 373 } \ 374 return result; \ 375 } 376 BEAMFORMER_UPLOAD_FNS 377 #undef X 378 379 #define X(name, dtype, ...) \ 380 b32 beamformer_push_##name (dtype *data, u32 count) { \ 381 b32 result = beamformer_push_##name ##_at(data, count, 0); \ 382 return result; \ 383 } 384 BEAMFORMER_UPLOAD_FNS 385 #undef X 386 387 function b32 388 beamformer_push_data_base(void *data, u32 data_size, i32 timeout_ms, u32 block) 389 { 390 b32 result = 0; 391 Arena scratch = beamformer_shared_memory_scratch_arena(g_beamformer_library_context.bp); 392 BeamformerParameterBlock *b = beamformer_parameter_block(g_beamformer_library_context.bp, block); 393 BeamformerParameters *bp = &b->parameters; 394 BeamformerDataKind data_kind = b->pipeline.data_kind; 395 396 u32 size = bp->acquisition_count * bp->sample_count * bp->channel_count * beamformer_data_kind_byte_size[data_kind]; 397 u32 raw_size = bp->raw_data_dimensions.x * bp->raw_data_dimensions.y * beamformer_data_kind_byte_size[data_kind]; 398 399 if (lib_error_check(size <= arena_capacity(&scratch, u8), BufferOverflow) && 400 lib_error_check(size <= data_size && data_size == raw_size, DataSizeMismatch)) 401 { 402 if (lib_try_lock(BeamformerSharedMemoryLockKind_UploadRF, timeout_ms)) { 403 if (lib_try_lock(BeamformerSharedMemoryLockKind_ScratchSpace, 0)) { 404 u32 channel_count = bp->channel_count; 405 u32 out_channel_stride = beamformer_data_kind_byte_size[data_kind] * bp->sample_count * bp->acquisition_count; 406 u32 in_channel_stride = beamformer_data_kind_byte_size[data_kind] * bp->raw_data_dimensions.x; 407 408 for (u32 channel = 0; channel < channel_count; channel++) { 409 u16 data_channel = (u16)b->channel_mapping[channel]; 410 u32 out_off = out_channel_stride * channel; 411 u32 in_off = in_channel_stride * data_channel; 412 /* TODO(rnp): it would be better to do non temporal copy here, but we can't ensure 413 * 64 byte boundaries. */ 414 mem_copy(scratch.beg + out_off, (u8 *)data + in_off, out_channel_stride); 415 } 416 417 lib_release_lock(BeamformerSharedMemoryLockKind_ScratchSpace); 418 /* TODO(rnp): need a better way to communicate this */ 419 u64 rf_block_rf_size = (u64)block << 32ULL | (u64)size; 420 atomic_store_u64(&g_beamformer_library_context.bp->rf_block_rf_size, rf_block_rf_size); 421 result = 1; 422 } 423 } 424 } 425 return result; 426 } 427 428 b32 429 beamformer_push_data_with_compute(void *data, u32 data_size, u32 image_plane_tag, u32 parameter_slot) 430 { 431 b32 result = 0; 432 if (check_shared_memory()) { 433 u32 reserved_blocks = g_beamformer_library_context.bp->reserved_parameter_blocks; 434 if (lib_error_check(image_plane_tag < BeamformerViewPlaneTag_Count, InvalidImagePlane) && 435 lib_error_check(parameter_slot < reserved_blocks, ParameterBlockUnallocated) && 436 beamformer_push_data_base(data, data_size, g_beamformer_library_context.timeout_ms, parameter_slot)) 437 { 438 BeamformWork *work = try_push_work_queue(); 439 if (work) { 440 work->kind = BeamformerWorkKind_ComputeIndirect; 441 work->compute_indirect_context.view_plane = image_plane_tag; 442 work->compute_indirect_context.parameter_block = parameter_slot; 443 beamform_work_queue_push_commit(&g_beamformer_library_context.bp->external_work_queue); 444 beamformer_flush_commands(); 445 result = 1; 446 } 447 } 448 } 449 return result; 450 } 451 452 b32 453 beamformer_push_parameters_at(BeamformerParameters *bp, u32 block) 454 { 455 b32 result = parameter_block_region_upload(bp, sizeof(*bp), block, 456 BeamformerParameterBlockRegion_Parameters, 457 offsetof(BeamformerParameterBlock, parameters), 458 g_beamformer_library_context.timeout_ms); 459 return result; 460 } 461 462 b32 463 beamformer_push_parameters(BeamformerParameters *bp) 464 { 465 b32 result = beamformer_push_parameters_at(bp, 0); 466 return result; 467 } 468 469 b32 470 beamformer_push_simple_parameters_at(BeamformerSimpleParameters *bp, u32 block) 471 { 472 b32 result = validate_simple_parameters(bp); 473 if (result) { 474 alignas(64) v2 focal_vectors[countof(bp->steering_angles)]; 475 for (u32 i = 0; i < countof(bp->steering_angles); i++) 476 focal_vectors[i] = (v2){{bp->steering_angles[i], bp->focal_depths[i]}}; 477 478 result &= beamformer_push_parameters_at((BeamformerParameters *)bp, block); 479 result &= beamformer_push_pipeline_at(bp->compute_stages, bp->compute_stages_count, (BeamformerDataKind)bp->data_kind, block); 480 result &= beamformer_push_channel_mapping_at(bp->channel_mapping, bp->channel_count, block); 481 result &= beamformer_push_focal_vectors_at((f32 *)focal_vectors, countof(focal_vectors), block); 482 result &= beamformer_push_transmit_receive_orientations_at(bp->transmit_receive_orientations, 483 bp->acquisition_count, block); 484 485 if (bp->acquisition_kind == BeamformerAcquisitionKind_UFORCES || 486 bp->acquisition_kind == BeamformerAcquisitionKind_UHERCULES) 487 { 488 result &= beamformer_push_sparse_elements_at(bp->sparse_elements, bp->acquisition_count, block); 489 } 490 491 for (u32 stage = 0; stage < bp->compute_stages_count; stage++) 492 result &= beamformer_set_pipeline_stage_parameters_at(stage, bp->compute_stage_parameters[stage], block); 493 } 494 return result; 495 } 496 497 b32 498 beamformer_push_simple_parameters(BeamformerSimpleParameters *bp) 499 { 500 b32 result = beamformer_push_simple_parameters_at(bp, 0); 501 return result; 502 } 503 504 b32 505 beamformer_push_parameters_ui(BeamformerUIParameters *bp) 506 { 507 b32 result = parameter_block_region_upload(bp, sizeof(*bp), 0, BeamformerParameterBlockRegion_Parameters, 508 offsetof(BeamformerParameterBlock, parameters_ui), 509 g_beamformer_library_context.timeout_ms); 510 return result; 511 } 512 513 b32 514 beamformer_push_parameters_head(BeamformerParametersHead *bp) 515 { 516 b32 result = parameter_block_region_upload(bp, sizeof(*bp), 0, BeamformerParameterBlockRegion_Parameters, 517 offsetof(BeamformerParameterBlock, parameters_head), 518 g_beamformer_library_context.timeout_ms); 519 return result; 520 } 521 522 function b32 523 beamformer_export_buffer(BeamformerExportContext export_context) 524 { 525 BeamformWork *work = try_push_work_queue(); 526 b32 result = work && lib_try_lock(BeamformerSharedMemoryLockKind_ExportSync, 0); 527 if (result) { 528 work->export_context = export_context; 529 work->kind = BeamformerWorkKind_ExportBuffer; 530 work->lock = BeamformerSharedMemoryLockKind_ScratchSpace; 531 beamform_work_queue_push_commit(&g_beamformer_library_context.bp->external_work_queue); 532 } 533 return result; 534 } 535 536 function b32 537 beamformer_export(BeamformerExportContext export, void *out, i32 timeout_ms) 538 { 539 b32 result = 0; 540 if (beamformer_export_buffer(export)) { 541 /* NOTE(rnp): if this fails it just means that the work from push_data hasn't 542 * started yet. This is here to catch the other case where the work started 543 * and finished before we finished queuing the export work item */ 544 beamformer_flush_commands(); 545 546 if (lib_try_lock(BeamformerSharedMemoryLockKind_ExportSync, timeout_ms)) { 547 if (lib_try_lock(BeamformerSharedMemoryLockKind_ScratchSpace, 0)) { 548 Arena scratch = beamformer_shared_memory_scratch_arena(g_beamformer_library_context.bp); 549 mem_copy(out, scratch.beg, export.size); 550 lib_release_lock(BeamformerSharedMemoryLockKind_ScratchSpace); 551 result = 1; 552 } 553 lib_release_lock(BeamformerSharedMemoryLockKind_ExportSync); 554 } 555 } 556 return result; 557 } 558 559 b32 560 beamformer_beamform_data(BeamformerSimpleParameters *bp, void *data, uint32_t data_size, 561 void *out_data, int32_t timeout_ms) 562 { 563 b32 result = validate_simple_parameters(bp); 564 if (result) { 565 bp->output_points.E[0] = MAX(1, bp->output_points.E[0]); 566 bp->output_points.E[1] = MAX(1, bp->output_points.E[1]); 567 bp->output_points.E[2] = MAX(1, bp->output_points.E[2]); 568 569 beamformer_push_simple_parameters(bp); 570 571 b32 complex = 0; 572 for (u32 stage = 0; stage < bp->compute_stages_count; stage++) { 573 BeamformerShaderKind shader = (BeamformerShaderKind)bp->compute_stages[stage]; 574 complex |= shader == BeamformerShaderKind_Demodulate || shader == BeamformerShaderKind_CudaHilbert; 575 } 576 577 iz output_size = bp->output_points.x * bp->output_points.y * bp->output_points.z * (i32)sizeof(f32); 578 if (complex) output_size *= 2; 579 580 Arena scratch = beamformer_shared_memory_scratch_arena(g_beamformer_library_context.bp); 581 if (out_data) result = lib_error_check(output_size <= arena_capacity(&scratch, u8), ExportSpaceOverflow); 582 583 if (result) { 584 result = beamformer_push_data_with_compute(data, data_size, 0, 0); 585 if (result && out_data) { 586 BeamformerExportContext export; 587 export.kind = BeamformerExportKind_BeamformedData; 588 export.size = (u32)output_size; 589 result = beamformer_export(export, out_data, timeout_ms); 590 } 591 } 592 } 593 return result; 594 } 595 596 b32 597 beamformer_compute_timings(BeamformerComputeStatsTable *output, i32 timeout_ms) 598 { 599 static_assert(sizeof(*output) <= BEAMFORMER_SHARED_MEMORY_MAX_SCRATCH_SIZE, 600 "timing table size exceeds scratch space"); 601 602 b32 result = 0; 603 if (check_shared_memory()) { 604 Arena scratch = beamformer_shared_memory_scratch_arena(g_beamformer_library_context.bp); 605 if (lib_error_check((iz)sizeof(*output) <= arena_capacity(&scratch, u8), ExportSpaceOverflow)) { 606 BeamformerExportContext export; 607 export.kind = BeamformerExportKind_Stats; 608 export.size = sizeof(*output); 609 result = beamformer_export(export, output, timeout_ms); 610 } 611 } 612 return result; 613 } 614 615 i32 616 beamformer_live_parameters_get_dirty_flag(void) 617 { 618 i32 result = -1; 619 if (check_shared_memory()) { 620 u32 flag = ctz_u32(g_beamformer_library_context.bp->live_imaging_dirty_flags); 621 if (flag != 32) { 622 atomic_and_u32(&g_beamformer_library_context.bp->live_imaging_dirty_flags, ~(1u << flag)); 623 result = (i32)flag; 624 } 625 } 626 return result; 627 } 628 629 BeamformerLiveImagingParameters * 630 beamformer_get_live_parameters(void) 631 { 632 BeamformerLiveImagingParameters *result = 0; 633 if (check_shared_memory()) result = &g_beamformer_library_context.bp->live_imaging_parameters; 634 return result; 635 } 636 637 b32 638 beamformer_set_live_parameters(BeamformerLiveImagingParameters *new) 639 { 640 b32 result = 0; 641 if (check_shared_memory()) { 642 mem_copy(&g_beamformer_library_context.bp->live_imaging_parameters, new, sizeof(*new)); 643 store_fence(); 644 result = 1; 645 } 646 return result; 647 }