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