beamformer_core.c (84732B)
1 /* See LICENSE for license details. */ 2 /* TODO(rnp): 3 * [ ]: backtrace dumping on SIGSEGV 4 * [ ]: cooperative shared memory loading in decode shader 5 * [ ]: refactor: save filter parameters with rest of parameters, whole slot thing is dumb 6 * [ ]: upload previously exported data for display. maybe this is a UI thing but doing it 7 * programatically would be nice. 8 * [ ]: Add interface for multi frame upload. RF upload already uses an offset into SM so 9 * that part works fine. We just need a way of specify a multi frame upload. (Data must 10 * be organized for simple offset access per frame). 11 * [ ]: refactor: do_compute should build its own "command graph" which tracks 12 * dependencies better. It is very important that unnecessary barriers are 13 * not placed between compute stages which requires knowledge of the entire 14 * graph. 15 * [ ]: refactor: replace UploadRF with just the scratch_rf_size variable, 16 * use below to spin wait in library 17 * [ ]: utilize umonitor/umwait (intel), monitorx/mwaitx (amd), and wfe/sev (aarch64) 18 * for power efficient low latency waiting 19 * [ ]: BeamformWorkQueue -> BeamformerWorkQueue 20 * [ ]: refactor: work queue needs a cleanup, we should only have a single one 21 * - that queue isn't really considered hot so a lock is probably fine 22 * [ ]: bug: reinit cuda on hot-reload 23 * 24 * [ ]: export for special frames/data 25 * - Color Map Data 26 * - Recursive Imaging Result 27 * - Incoherent sum 28 * 29 * [ ]: Tiled Array Handling 30 * [ ]: add tile count uv2 31 * [ ]: make xdc_transform an array 32 * [ ]: modify CPU DAS dispatch code to lookup xdc_transform based on tile index 33 * [ ]: modify CPU DAS dispatch code to set rf_element_offset based on tile index 34 * - need to check if this works for HERCULES or if the shader just needs to 35 * know about the extra tiles 36 * [ ]: write simple backpropagation code to optimize 2D tile position and 2D tilt 37 * (4 variables per tile). 38 * - two tests: 39 * 1. Maximize value of wire target 40 * 2. Maximize value of single cyst contrast 41 * 42 * [ ]: Recursive Imaging Handling 43 * [ ]: add array of image offsets to Compute Array Parameters 44 * [ ]: add array of weighting coeffiecents to Compute Array Parameters 45 * [ ]: Update 2D sum shader to use these 46 * 47 * [ ]: Power Doppler 48 * [ ]: also needs array of image offsets 49 * [ ]: make modified filter that grabs each sample from a different image offset 50 * - NOTE: this can't use existing striding mechanism because one image may 51 * be at the start of the ring buffer and another at the end of the 52 * ring buffer and the image size may not cleanly divide the ring buffer size. 53 * - Probably want this shader to just output the estimated power map directly. 54 * [ ]: make a modified render shader that takes two images: one structural and one that 55 * is used to index into a color map. (or second render pass that applies color overlay) 56 */ 57 58 #include "base_platform.h" 59 60 #if defined(BEAMFORMER_DEBUG) && !defined(BEAMFORMER_EXPORT) && OS_WINDOWS 61 #define BEAMFORMER_EXPORT __declspec(dllexport) 62 #endif 63 64 #include "beamformer_internal.h" 65 66 #define GPU_RESOURCE_HASH_TABLE_COUNT 256 67 68 typedef struct GPUResource GPUResource; 69 struct GPUResource { 70 str8 name; 71 u64 size; 72 u64 offset; 73 u64 alignment; 74 75 void *data; 76 77 u64 hash; 78 79 GPUResource *next; 80 GPUResource *hash_next, *hash_prev; 81 }; 82 typedef struct {GPUResource *first, *last;} GPUResourceHashBucket; 83 84 typedef struct { 85 Arena *arena; 86 u64 position; 87 88 GPUResource *resource_list; 89 GPUResourceHashBucket hash_table[GPU_RESOURCE_HASH_TABLE_COUNT]; 90 } GPUResourceBuilder; 91 92 read_only BeamformerFrame beamformer_nil_frame; 93 read_only BeamformerComputePlan beamformer_nil_compute_plan; 94 95 global BeamformerCtx *beamformer_context; 96 global BeamformerInput *beamformer_input; 97 global f32 dt_for_frame; 98 99 #define beamformer_frame_arena() (beamformer_context->frame_arenas[beamformer_context->frame_index % countof(beamformer_context->frame_arenas)]) 100 #define beamformer_registers() (&beamformer_context->registers->v) 101 #define beamformer_push_registers(...) beamformer_push_registers_(&(BeamformerRegisters){beamformer_registers_init_literal __VA_ARGS__}) 102 #define BeamformerRegistersScope(...) DeferLoop(beamformer_push_registers(__VA_ARGS__), beamformer_pop_registers()) 103 #define beamformer_command(name, ...) beamformer_push_command(name, &(BeamformerRegisters){beamformer_registers_init_literal __VA_ARGS__}) 104 105 function BeamformerRegisters * 106 beamformer_pop_registers(void) 107 { 108 BeamformerRegisters *result = &beamformer_context->registers->v; 109 SLLStackPop(beamformer_context->registers, next); 110 if (beamformer_context->registers == 0) 111 beamformer_context->registers = &beamformer_context->base_registers; 112 return result; 113 } 114 115 function BeamformerRegisters * 116 beamformer_push_registers_(BeamformerRegisters *registers) 117 { 118 BeamformerRegistersNode *node = push_struct(beamformer_frame_arena(), BeamformerRegistersNode); 119 BeamformerRegisters *result = &node->v; 120 memory_copy(result, registers, sizeof(node->v)); 121 SLLStackPush(beamformer_context->registers, node, next); 122 return result; 123 } 124 125 function void 126 beamformer_command_list_push_new(Arena *arena, BeamformerCommandList *commands, str8 name, BeamformerRegisters *registers) 127 { 128 BeamformerCommandNode *node = push_struct(arena, BeamformerCommandNode); 129 node->command.registers = push_struct_no_zero(arena, BeamformerRegisters); 130 node->command.name = push_str8(arena, name); 131 memory_copy(node->command.registers, registers, sizeof(*registers)); 132 DLLInsertLast(0, commands->first, commands->last, node, next, prev); 133 commands->count += 1; 134 } 135 136 function void 137 beamformer_push_command(str8 name, BeamformerRegisters *registers) 138 { 139 beamformer_command_list_push_new(beamformer_frame_arena(), beamformer_context->command_queues + 0, 140 name, registers); 141 } 142 143 function BeamformerCommandKind 144 beamformer_command_kind_from_string(str8 s) 145 { 146 BeamformerCommandKind result = BeamformerCommandKind_Nil; 147 for EachElement(beamformer_command_infos, it) { 148 if (str8_equal(beamformer_command_infos[it].string, s)) { 149 result = (BeamformerCommandKind)it; 150 break; 151 } 152 } 153 return result; 154 } 155 156 function BeamformerPanelKind 157 beamformer_panel_kind_from_string(str8 s) 158 { 159 BeamformerPanelKind result = BeamformerPanelKind_Nil; 160 for EachElement(beamformer_panel_infos, it) { 161 if (str8_equal(beamformer_panel_infos[it].string, s)) { 162 result = (BeamformerPanelKind)it; 163 break; 164 } 165 } 166 return result; 167 } 168 169 function BeamformerFrame * 170 beamformer_frame_from_index(u64 index) 171 { 172 BeamformerFrame *result = (BeamformerFrame *)&beamformer_nil_frame; 173 if (index < countof(beamformer_context->compute_context.backlog.frames)) { 174 BeamformerFrame *frame = beamformer_context->compute_context.backlog.frames + index; 175 if (frame->timeline_valid_value != 0) 176 result = frame; 177 } 178 return result; 179 } 180 181 function b32 182 beamformer_frame_valid(u64 index) 183 { 184 b32 result = beamformer_frame_from_index(index) != &beamformer_nil_frame; 185 return result; 186 } 187 188 function void 189 beamformer_compute_plan_release(BeamformerComputeContext *cc, u32 block) 190 { 191 assert(block < countof(cc->compute_plans)); 192 BeamformerComputePlan *cp = cc->compute_plans[block]; 193 if (cp) { 194 gpu_buffer_release(&cp->gpu_temp_arena); 195 cc->compute_plans[block] = 0; 196 SLLPushFreelist(cp, cc->compute_plan_freelist); 197 } 198 } 199 200 function GPUResource * 201 gpu_resource_from_hash(GPUResourceBuilder *rb, u64 hash) 202 { 203 GPUResource *result = 0; 204 205 GPUResourceHashBucket *hb = rb->hash_table + (hash % GPU_RESOURCE_HASH_TABLE_COUNT); 206 for (GPUResource *r = hb->first; r; r = r->hash_next) { 207 if (hash == r->hash) { 208 result = r; 209 break; 210 } 211 } 212 213 return result; 214 } 215 216 typedef struct { 217 str8 name; 218 u64 align; 219 u64 size; 220 void *data; 221 } GPUResourcePushInfo; 222 #define gpu_resource_push(rb, t, count, ...) gpu_resource_push_(rb, (GPUResourcePushInfo){\ 223 .align = Max(alignof(t), 16), \ 224 .size = sizeof(t) * count, \ 225 __VA_ARGS__}) 226 227 function u32 228 gpu_resource_push_(GPUResourceBuilder *rb, GPUResourcePushInfo info) 229 { 230 assert(info.size > 0 && info.name.length > 0 && IsPowerOfTwo(info.align)); 231 232 u64 hash = u64_hash_from_str8(info.name); 233 GPUResource *r = gpu_resource_from_hash(rb, hash); 234 if (!r) { 235 r = push_struct(rb->arena, GPUResource); 236 GPUResourceHashBucket *hb = rb->hash_table + (hash % GPU_RESOURCE_HASH_TABLE_COUNT); 237 DLLInsert(0, hb->first, hb->last, r, hash_next, hash_prev); 238 SLLStackPush(rb->resource_list, r, next); 239 240 r->hash = hash; 241 r->name = info.name; 242 r->alignment = Max(16, info.align); 243 r->offset = AlignUpPowerOfTwo(rb->position, r->alignment); 244 r->size = info.size; 245 rb->position = r->offset + r->size; 246 } 247 248 u32 result = r->offset; 249 250 // NOTE(rnp): if this is a new resource and no data is provided it is likely 251 // a temporary GPU side buffer. if this is not a new resource and no data 252 // is provided then maybe it is shared and someone else will provide it 253 if (info.data) r->data = info.data; 254 255 return result; 256 } 257 258 function GPUResourceBuilder * 259 gpu_resource_build_begin(Arena *arena) 260 { 261 GPUResourceBuilder *result = push_struct(arena, GPUResourceBuilder); 262 result->arena = arena; 263 return result; 264 } 265 266 function void 267 gpu_resource_build_end(GPUResourceBuilder *rb, GPUBuffer *buffer) 268 { 269 u64 size = gpu_round_up_to_sync_size(rb->position, 64); 270 if (size != (u64)buffer->size) { 271 gpu_buffer_allocate(buffer, (GPUBufferAllocateInfo){ 272 .size = size, 273 .flags = GPUUsageFlag_HostWrite, 274 .label = push_str8_f(rb->arena, "GPU Temp Arena [%p]", buffer), 275 .timelines_used = (GPUTimeline []){GPUTimeline_Compute}, 276 .timeline_count = 1, 277 }); 278 } 279 280 ////////////////////////////////////// 281 // NOTE(rnp): upload data 282 u64 last_wait_value = 0; 283 for (GPUResource *r = rb->resource_list; r; r = r->next) 284 if (r->data) 285 last_wait_value = gpu_buffer_range_upload(buffer, r->data, r->offset, r->size, 0); 286 287 // TODO(rnp): cleanup this pointless stall 288 if (gpu_buffer_needs_sync(buffer)) 289 gpu_host_wait_timeline(GPUTimeline_Transfer, last_wait_value, -1ULL); 290 } 291 292 function BeamformerComputePlan * 293 beamformer_compute_plan_for_block(BeamformerComputeContext *cc, u32 block, Arena *arena) 294 { 295 assert(block < countof(cc->compute_plans)); 296 BeamformerComputePlan *result = cc->compute_plans[block]; 297 if (!result) { 298 result = SLLPopFreelist(cc->compute_plan_freelist); 299 if (!result) result = push_struct_no_zero(arena, BeamformerComputePlan); 300 zero_struct(result); 301 cc->compute_plans[block] = result; 302 303 result->ui_voxel_transform = m4_identity(); 304 } 305 return result; 306 } 307 308 function BeamformerFilter * 309 beamformer_filter_create(Arena *arena, BeamformerFilterParameters fp) 310 { 311 BeamformerFilter *result = push_struct(arena, BeamformerFilter); 312 switch (fp.kind) { 313 case BeamformerFilterKind_Kaiser:{ 314 /* TODO(rnp): this should also support complex */ 315 /* TODO(rnp): implement this as an IFIR filter instead to reduce computation */ 316 result->data = kaiser_low_pass_filter(arena, fp.kaiser.cutoff_frequency, fp.sampling_frequency, 317 fp.kaiser.beta, (i32)fp.kaiser.length); 318 result->length = (i32)fp.kaiser.length; 319 result->time_delay = (f32)result->length / 2.0f / fp.sampling_frequency; 320 }break; 321 322 case BeamformerFilterKind_MatchedChirp:{ 323 typeof(fp.matched_chirp) *mc = &fp.matched_chirp; 324 f32 fs = fp.sampling_frequency; 325 result->length = (i32)(mc->duration * fs); 326 if (fp.complex) { 327 result->data = baseband_chirp(arena, mc->min_frequency, mc->max_frequency, fs, result->length, 1, 0.5f); 328 result->time_delay = complex_filter_first_moment(result->data, result->length, fs); 329 } else { 330 result->data = rf_chirp(arena, mc->min_frequency, mc->max_frequency, fs, result->length, 1); 331 result->time_delay = real_filter_first_moment(result->data, result->length, fs); 332 } 333 }break; 334 335 InvalidDefaultCase; 336 } 337 338 result->parameters = fp; 339 return result; 340 } 341 342 function iv3 343 das_valid_points(iv3 points) 344 { 345 iv3 result; 346 result.x = Max(points.x, 1); 347 result.y = Max(points.y, 1); 348 result.z = Max(points.z, 1); 349 return result; 350 } 351 352 function GPUBuffer * 353 beamformer_gpu_buffer_from_frame(BeamformerFrame *frame) 354 { 355 GPUBuffer *result = beamformer_context->compute_context.backlog.buffer; 356 if (!Between(frame->gpu_pointer, result->gpu_pointer, result->gpu_pointer + result->size)) { 357 result = 0; 358 BeamformerComputePlan *cp = beamformer_context->compute_context.compute_plans[frame->parameter_block]; 359 if (cp) { 360 result = &cp->gpu_temp_arena; 361 assert(Between(frame->gpu_pointer, result->gpu_pointer, result->gpu_pointer + result->size)); 362 } 363 } 364 return result; 365 } 366 367 function u64 368 beamformer_frame_byte_size(iv3 points, BeamformerDataKind kind) 369 { 370 u64 result = points.x * points.y * points.z * beamformer_data_kind_byte_size[kind]; 371 result = round_up_to(result, 64); 372 return result; 373 } 374 375 function u64 376 beamformer_incoherent_frame_byte_size(iv3 points, BeamformerDataKind kind) 377 { 378 u64 result = beamformer_frame_byte_size(points, kind) / beamformer_data_kind_element_count[kind]; 379 return result; 380 } 381 382 function BeamformerFrame * 383 beamformer_frame_next(BeamformerComputeContext *cc, iv3 output_points, b32 complex) 384 { 385 BeamformerFrameBacklog *bl = &cc->backlog; 386 387 BeamformerDataKind kind = complex ? BeamformerDataKind_Float32Complex : BeamformerDataKind_Float32; 388 u64 frame_size = beamformer_frame_byte_size(output_points, kind); 389 390 // TODO(rnp): handle this somewhat gracefully (even it produces garbled output) 391 assert(frame_size <= (u64)bl->buffer->size); 392 393 if (bl->next_offset > (u64)bl->buffer->size - frame_size) 394 bl->next_offset = 0; 395 396 u64 id = bl->counter++; 397 398 BeamformerFrame *result = bl->frames + (id % countof(bl->frames)); 399 atomic_store_u64(&result->timeline_valid_value, -1ULL); 400 result->id = id & U32_MAX; 401 result->gpu_pointer = bl->buffer->gpu_pointer + bl->next_offset; 402 result->points = output_points; 403 result->data_kind = kind; 404 405 bl->next_offset += frame_size; 406 407 return result; 408 } 409 410 function void 411 push_compute_timing_info(ComputeTimingTable *t, ComputeTimingInfo info) 412 { 413 u32 index = atomic_add_u32(&t->write_index, 1) % countof(t->buffer); 414 t->buffer[index] = info; 415 } 416 417 function uv3 418 layout_for_output(iv3 points) 419 { 420 uv3 result = {{1, 1, 1}}; 421 422 b32 has_x = points.x > 1; 423 b32 has_y = points.y > 1; 424 b32 has_z = points.z > 1; 425 426 u32 subgroup_size = gpu_info()->subgroup_size; 427 u32 grid_3d_z_size = Max(1, subgroup_size / (4 * 4)); 428 u32 grid_2d_y_size = Max(1, subgroup_size / 8); 429 430 switch (iv3_dimension(points)) { 431 case 1:{ 432 if (has_x) result.x = subgroup_size; 433 if (has_y) result.y = subgroup_size; 434 if (has_z) result.z = subgroup_size; 435 }break; 436 437 case 2:{ 438 if (has_x && has_y) {result.x = 8; result.y = grid_2d_y_size;} 439 if (has_x && has_z) {result.x = 8; result.z = grid_2d_y_size;} 440 if (has_y && has_z) {result.y = 8; result.z = grid_2d_y_size;} 441 }break; 442 443 case 3:{result = (uv3){{4, 4, grid_3d_z_size}};}break; 444 445 InvalidDefaultCase; 446 } 447 448 return result; 449 } 450 451 function uv3 452 dispatch_for_output(uv3 layout, iv3 points) 453 { 454 uv3 result; 455 result.x = ceil_div_u32(points.x, layout.x); 456 result.y = ceil_div_u32(points.y, layout.y); 457 result.z = ceil_div_u32(points.z, layout.z); 458 return result; 459 } 460 461 typedef struct BeamformerComputeGraphNode BeamformerComputeGraphNode; 462 struct BeamformerComputeGraphNode { 463 // NOTE(rnp): will be BeamformerShaderKind_Count for root node 464 BeamformerShaderKind kind; 465 466 // NOTE(rnp): when any of input or output stride is assigned it is assumed that 467 // the shader requires a fixed layout for input, output, or both. When two adjacent 468 // nodes require incompatible layouts the second pass over the graph will insert 469 // Reshape shaders in between. 470 BeamformerDataKind input_data_kind; 471 BeamformerDataLayout input_data_layout; 472 473 BeamformerDataKind output_data_kind; 474 BeamformerDataLayout output_data_layout; 475 476 uv3 rf_dimensions; 477 478 b32 requires_fp_input; 479 480 i32 user_pipeline_index; 481 482 BeamformerComputeGraphNode *prev; 483 BeamformerComputeGraphNode *next; 484 }; 485 486 typedef struct { 487 BeamformerComputeGraphNode *first; 488 BeamformerComputeGraphNode *last; 489 u64 count; 490 } BeamformerComputeGraph; 491 492 function b32 493 compute_plan_push_shader(BeamformerComputePlan *p, BeamformerComputeGraphNode *node, BeamformerShaderParameters *sp) 494 { 495 b32 result = 0; 496 if (p->pipeline.shader_count < countof(p->pipeline.shaders)) { 497 u32 index = p->pipeline.shader_count++; 498 p->pipeline.shaders[index] = node->kind; 499 zero_struct(p->shader_descriptors + index); 500 p->pipeline.parameters[index] = sp ? *sp : (BeamformerShaderParameters){0}; 501 502 p->shader_descriptors[index].input_data_kind = node->input_data_kind; 503 p->shader_descriptors[index].output_data_kind = node->output_data_kind; 504 505 result = 1; 506 } 507 return result; 508 } 509 510 function BeamformerComputeGraphNode * 511 push_compute_graph_node(BeamformerComputeGraph *graph, BeamformerShaderKind kind, Arena *arena) 512 { 513 BeamformerComputeGraphNode *result = push_struct(arena, BeamformerComputeGraphNode); 514 if (graph) { 515 if (graph->last) result->rf_dimensions = graph->last->rf_dimensions; 516 DLLInsertLast(0, graph->first, graph->last, result, next, prev); 517 graph->count++; 518 } 519 result->kind = kind; 520 result->user_pipeline_index = -1; 521 // NOTE(rnp): initially don't care data parameters 522 result->input_data_kind = BeamformerDataKind_Count; 523 result->output_data_kind = BeamformerDataKind_Count; 524 result->input_data_layout = BeamformerDataLayout_Count; 525 result->output_data_layout = BeamformerDataLayout_Count; 526 return result; 527 } 528 529 function uv3 530 beamformer_data_strides(BeamformerDataLayout layout, uv3 rf_dimensions) 531 { 532 assert(layout >= 0 && layout < BeamformerDataLayout_Count); 533 uv3 result = {0}; 534 BeamformerRFDimension x = beamformer_data_stride_indices[layout][0]; 535 BeamformerRFDimension y = beamformer_data_stride_indices[layout][1]; 536 BeamformerRFDimension z = beamformer_data_stride_indices[layout][2]; 537 result.E[z] = 1; 538 result.E[y] = rf_dimensions.E[z]; 539 result.E[x] = rf_dimensions.E[z] * rf_dimensions.E[y]; 540 return result; 541 } 542 543 function void 544 plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, Arena *scratch) 545 { 546 b32 run_hilbert = 0; 547 b32 demodulate = 0; 548 549 for (u32 i = 0; i < pb->pipeline.shader_count; i++) { 550 switch (pb->pipeline.shaders[i]) { 551 case BeamformerShaderKind_Hilbert:{run_hilbert = 1;}break; 552 case BeamformerShaderKind_Demodulate:{demodulate = 1;}break; 553 default:{}break; 554 } 555 } 556 557 if (demodulate) run_hilbert = 0; 558 559 f32 sampling_frequency = pb->parameters.sampling_frequency; 560 u32 input_sample_count = pb->parameters.sample_count; 561 u32 acquisition_count = pb->parameters.acquisition_count; 562 u32 decimation_rate = Max(pb->parameters.decimation_rate, 1); 563 564 cp->raw_channel_byte_stride = pb->parameters.sample_count * pb->parameters.acquisition_count 565 * beamformer_data_kind_byte_size[pb->pipeline.data_kind]; 566 567 BeamformerDataKind input_data_kind = pb->pipeline.data_kind; 568 if (demodulate) { 569 switch (input_data_kind) { 570 case BeamformerDataKind_Int16:{ input_data_kind = BeamformerDataKind_Int16Complex; }break; 571 case BeamformerDataKind_Float16:{input_data_kind = BeamformerDataKind_Float16Complex;}break; 572 case BeamformerDataKind_Float32:{input_data_kind = BeamformerDataKind_Float32Complex;}break; 573 default:{}break; 574 } 575 input_sample_count /= (2 * decimation_rate); 576 sampling_frequency /= (2 * decimation_rate); 577 } 578 579 cp->iq_pipeline = beamformer_data_kind_complex[input_data_kind] || run_hilbert; 580 581 BeamformerDataKind das_data_kind = cp->iq_pipeline ? BeamformerDataKind_Float32Complex 582 : BeamformerDataKind_Float32; 583 584 cp->channel_count = pb->parameters.channel_count; 585 u32 chunk_channel_count = Min(cp->channel_count, BeamformerChunkChannelCount); 586 587 cp->rf_size = input_sample_count * pb->parameters.acquisition_count * chunk_channel_count 588 * beamformer_data_kind_byte_size[das_data_kind]; 589 590 i64 buffer_size = PING_PONG_BUFFER_SLOTS * round_up_to(cp->rf_size, 64); 591 if (beamformer_context->compute_context.ping_pong_buffer.size < buffer_size) { 592 b32 cuda = cuda_supported(); 593 GPUBufferAllocateInfo allocate_info = { 594 .size = buffer_size, 595 .export = cuda ? &beamformer_context->compute_context.ping_pong_export_handle : 0, 596 .label = str8("PingPongBuffer"), 597 }; 598 gpu_buffer_allocate(&beamformer_context->compute_context.ping_pong_buffer, allocate_info); 599 600 // TODO(rnp): figure out how to share with CUDA 601 // IMPORTANT: on linux the handle is returned to os and should be cleared after import 602 // see usage of glImportMemoryFdEXT and surrounding code in ui.c for examples 603 if (cuda) { 604 } 605 } 606 607 read_only BeamformerDataKind data_kind_to_element_kind[] = { 608 [BeamformerDataKind_Int16] = BeamformerDataKind_Float16, 609 [BeamformerDataKind_Float16] = BeamformerDataKind_Float16, 610 [BeamformerDataKind_Float32] = BeamformerDataKind_Float32, 611 [BeamformerDataKind_Int16Complex] = BeamformerDataKind_Float16, 612 [BeamformerDataKind_Float16Complex] = BeamformerDataKind_Float16, 613 [BeamformerDataKind_Float32Complex] = BeamformerDataKind_Float32, 614 }; 615 616 read_only BeamformerDataKind data_kind_to_fp_kind[] = { 617 [BeamformerDataKind_Int16] = BeamformerDataKind_Float16, 618 [BeamformerDataKind_Float16] = BeamformerDataKind_Float16, 619 [BeamformerDataKind_Float32] = BeamformerDataKind_Float32, 620 [BeamformerDataKind_Int16Complex] = BeamformerDataKind_Float16Complex, 621 [BeamformerDataKind_Float16Complex] = BeamformerDataKind_Float16Complex, 622 [BeamformerDataKind_Float32Complex] = BeamformerDataKind_Float32Complex, 623 }; 624 625 ////////////////////////////////////// 626 // NOTE(rnp): First Pass: build initial graph and insert hard layout constraints 627 BeamformerComputeGraph graph = {0}; 628 BeamformerComputeGraphNode *root_node = push_compute_graph_node(&graph, BeamformerShaderKind_Count, scratch); 629 root_node->input_data_kind = input_data_kind; 630 root_node->input_data_layout = BeamformerDataLayout_ChannelEventSample; 631 root_node->output_data_kind = input_data_kind; 632 root_node->output_data_layout = BeamformerDataLayout_ChannelEventSample; 633 634 root_node->rf_dimensions.E[BeamformerRFDimension_Channels] = chunk_channel_count; 635 root_node->rf_dimensions.E[BeamformerRFDimension_Events] = pb->parameters.acquisition_count; 636 root_node->rf_dimensions.E[BeamformerRFDimension_Samples] = pb->parameters.sample_count; 637 638 for EachIndex(pb->pipeline.shader_count, it) { 639 // NOTE(rnp): skip unnecessary shaders 640 switch (pb->pipeline.shaders[it]) { 641 case BeamformerShaderKind_Hilbert:{if (!run_hilbert) continue;}break; 642 643 case BeamformerShaderKind_Decode:{ 644 if (pb->parameters.decode_mode == BeamformerDecodeMode_None) 645 continue; 646 }break; 647 648 case BeamformerShaderKind_Sum: 649 case BeamformerShaderKind_MinMax: 650 { 651 // NOTE(rnp): currently unsupported 652 continue; 653 }break; 654 655 default:{}break; 656 } 657 658 BeamformerComputeGraphNode *node = push_compute_graph_node(&graph, pb->pipeline.shaders[it], scratch); 659 node->user_pipeline_index = (i32)it; 660 switch (pb->pipeline.shaders[it]) { 661 case BeamformerShaderKind_Demodulate:{ 662 node->rf_dimensions.E[BeamformerRFDimension_Samples] /= (2 * decimation_rate); 663 }break; 664 665 case BeamformerShaderKind_Decode:{ 666 b32 low_precision = beamformer_data_kind_element_size[input_data_kind] < 4; 667 b32 use_coop_matrix = gpu_info()->cooperative_matrix && 668 low_precision && 669 (acquisition_count % 16 == 0) && 670 (chunk_channel_count % 16 == 0); 671 672 node->requires_fp_input = 1; 673 674 // NOTE(rnp): fixed input layout required for reasonable performance 675 node->input_data_layout = BeamformerDataLayout_SampleChannelEvent; 676 677 if (use_coop_matrix) { 678 node->input_data_kind = BeamformerDataKind_Float16; 679 node->output_data_kind = data_kind_to_element_kind[das_data_kind]; 680 node->output_data_layout = node->input_data_layout; 681 } 682 }break; 683 684 case BeamformerShaderKind_DAS:{ 685 // NOTE(rnp): if we aren't decoding we can let the previous stage's 686 // data kind pass through as long as it is floating point 687 node->requires_fp_input = 1; 688 if (pb->parameters.decode_mode != BeamformerDecodeMode_None) 689 node->input_data_kind = das_data_kind; 690 691 node->input_data_layout = BeamformerDataLayout_ChannelEventSample; 692 if (pb->parameters.acquisition_kind == BeamformerAcquisitionKind_RCA_TPW || 693 pb->parameters.acquisition_kind == BeamformerAcquisitionKind_RCA_VLS) 694 { 695 node->input_data_layout = BeamformerDataLayout_EventChannelSample; 696 } 697 698 node->output_data_layout = BeamformerDataLayout_Image; 699 node->output_data_kind = das_data_kind; 700 701 // NOTE(rnp): insert implicit CoherencyWeighting node 702 if (pb->parameters.coherency_weighting) 703 node = push_compute_graph_node(&graph, BeamformerShaderKind_CoherencyWeighting, scratch); 704 }break; 705 706 default:{}break; 707 } 708 } 709 710 ////////////////////////////////////// 711 // NOTE(rnp): Second Pass: resolve layout constraints 712 for (BeamformerComputeGraphNode *node = root_node->next; node; node = node->next) { 713 b32 needs_reshape = 0; 714 715 // NOTE(rnp): data strides 716 { 717 b32 input_dont_care = node->input_data_layout == BeamformerDataLayout_Count; 718 b32 prev_output_dont_care = node->prev->output_data_layout == BeamformerDataLayout_Count; 719 720 if (prev_output_dont_care && !input_dont_care) 721 node->prev->output_data_layout = node->input_data_layout; 722 723 if (!prev_output_dont_care && input_dont_care) 724 node->input_data_layout = node->prev->output_data_layout; 725 726 if (prev_output_dont_care && input_dont_care) 727 node->input_data_layout = node->prev->output_data_layout = node->prev->input_data_layout; 728 729 needs_reshape |= node->input_data_layout != node->prev->output_data_layout; 730 } 731 732 // NOTE(rnp): data kinds 733 { 734 b32 input_dont_care = node->input_data_kind == BeamformerDataKind_Count; 735 b32 prev_output_dont_care = node->prev->output_data_kind == BeamformerDataKind_Count; 736 737 if (prev_output_dont_care && !input_dont_care) 738 node->prev->output_data_kind = node->input_data_kind; 739 740 if (!prev_output_dont_care && input_dont_care) 741 node->input_data_kind = node->prev->output_data_kind; 742 743 if (prev_output_dont_care && input_dont_care) 744 node->input_data_kind = node->prev->output_data_kind = node->prev->input_data_kind; 745 746 if (node->requires_fp_input) { 747 node->input_data_kind = data_kind_to_fp_kind[node->input_data_kind]; 748 if (prev_output_dont_care) 749 node->prev->output_data_kind = node->input_data_kind; 750 } 751 752 needs_reshape |= node->input_data_kind != node->prev->output_data_kind; 753 } 754 755 // NOTE(rnp): insert reshape if needed 756 if (needs_reshape) { 757 BeamformerComputeGraphNode *new = push_compute_graph_node(0, BeamformerShaderKind_Reshape, scratch); 758 BeamformerComputeGraphNode *last = node->prev; 759 DLLInsertLast(0, node, last, new, next, prev); 760 graph.count++; 761 new->rf_dimensions = new->prev->rf_dimensions; 762 new->input_data_kind = new->prev->output_data_kind; 763 new->input_data_layout = new->prev->output_data_layout; 764 new->output_data_kind = new->next->input_data_kind; 765 new->output_data_layout = new->next->input_data_layout; 766 } 767 } 768 769 // NOTE(rnp): ensure last node descriptor gets proper values for output data kind 770 if (graph.last->output_data_kind == BeamformerDataKind_Count) 771 graph.last->output_data_kind = graph.last->input_data_kind; 772 773 f32 time_offset = pb->parameters.time_offset; 774 u32 subgroup_size = gpu_info()->subgroup_size; 775 776 cp->first_image_shader_index = 0; 777 cp->pipeline.shader_count = 0; 778 779 GPUResourceBuilder *resource_builder = gpu_resource_build_begin(scratch); 780 for (BeamformerComputeGraphNode *node = root_node->next; node; node = node->next) { 781 assert(node->prev->output_data_kind == node->input_data_kind); 782 assert(node->prev->output_data_layout == node->input_data_layout); 783 784 BeamformerShaderParameters *sp = 0; 785 if (node->user_pipeline_index >= 0) 786 sp = pb->pipeline.parameters + node->user_pipeline_index; 787 788 if (compute_plan_push_shader(cp, node, sp)) { 789 BeamformerShaderDescriptor *sd = cp->shader_descriptors + cp->pipeline.shader_count - 1; 790 791 uv3 output_stride = beamformer_data_strides(node->output_data_layout, node->rf_dimensions); 792 uv3 input_stride = beamformer_data_strides(node->input_data_layout, node->prev->rf_dimensions); 793 794 switch (node->kind) { 795 case BeamformerShaderKind_Decode:{ 796 BeamformerDecodeBakeParameters *db = &sd->bake.Decode; 797 798 u32 decode_sample_count = node->rf_dimensions.E[BeamformerRFDimension_Samples]; 799 db->DecodeMode = pb->parameters.decode_mode; 800 db->TransmitCount = node->rf_dimensions.E[BeamformerRFDimension_Events]; 801 db->ChunkChannelCount = node->rf_dimensions.E[BeamformerRFDimension_Channels]; 802 803 // NOTE(rnp): ignored when using coop matrices 804 db->OutputSampleStride = output_stride.E[BeamformerRFDimension_Samples]; 805 db->OutputChannelStride = output_stride.E[BeamformerRFDimension_Channels]; 806 db->OutputTransmitStride = output_stride.E[BeamformerRFDimension_Events]; 807 808 db->ToProcess = 1; 809 810 b32 use_coop_matrix = gpu_info()->cooperative_matrix && 811 node->input_data_kind == BeamformerDataKind_Float16 && 812 (db->TransmitCount % 16 == 0) && 813 (chunk_channel_count % 16 == 0); 814 if (use_coop_matrix) { 815 // TODO(rnp): shared memory for larger sizes 816 sd->layout = (uv3){{subgroup_size, 1, 1}}; 817 818 if (demodulate) 819 decode_sample_count *= 2; 820 821 sd->compile_flags |= BeamformerDecodeCompileFlags_CooperativeMatrix; 822 db->CooperativeMatrixM = 16; 823 db->CooperativeMatrixN = 16; 824 db->CooperativeMatrixK = 16; 825 826 sd->dispatch.x = db->TransmitCount / db->CooperativeMatrixN; 827 sd->dispatch.y = chunk_channel_count / db->CooperativeMatrixM; 828 sd->dispatch.z = decode_sample_count; 829 } else if (db->TransmitCount > 40) { 830 sd->compile_flags |= BeamformerDecodeCompileFlags_UseSharedMemory; 831 832 if (db->TransmitCount == 48) 833 db->ToProcess = db->TransmitCount / 16; 834 835 b32 use_16x = db->TransmitCount == 48 || db->TransmitCount == 80 || 836 db->TransmitCount == 96 || db->TransmitCount == 160; 837 sd->layout.x = use_16x ? 16 : 32; 838 sd->layout.y = 4; 839 sd->layout.z = 1; 840 841 sd->dispatch.x = ceil_div_u32(db->TransmitCount / db->ToProcess, sd->layout.x); 842 sd->dispatch.y = ceil_div_u32(db->ChunkChannelCount, sd->layout.y); 843 sd->dispatch.z = ceil_div_u32(decode_sample_count, sd->layout.z); 844 } else { 845 /* NOTE(rnp): register caching. using more threads will cause the compiler to do 846 * contortions to avoid spilling registers. using less gives higher performance */ 847 sd->layout = (uv3){{subgroup_size / 2, 1, 1}}; 848 849 sd->dispatch.x = ceil_div_u32(decode_sample_count, sd->layout.x); 850 sd->dispatch.y = ceil_div_u32(db->ChunkChannelCount, sd->layout.y); 851 sd->dispatch.z = 1; 852 } 853 854 sd->uses_heap = 1; 855 u32 order = db->TransmitCount; 856 db->Hadamard = gpu_resource_push(resource_builder, f16, order * order, 857 .data = make_hadamard_transpose(scratch, order, use_coop_matrix), 858 .name = str8("hadamard")); 859 }break; 860 861 case BeamformerShaderKind_Demodulate: 862 case BeamformerShaderKind_Filter: 863 { 864 b32 demod = node->kind == BeamformerShaderKind_Demodulate; 865 BeamformerFilter *f = beamformer_filter_create(scratch, cp->filter_parameters[sp->filter_slot]); 866 867 sd->compile_flags |= BeamformerFilterCompileFlags_Demodulate * demod; 868 sd->compile_flags |= BeamformerFilterCompileFlags_ComplexFilter * f->parameters.complex; 869 870 time_offset += f->time_delay; 871 872 BeamformerFilterBakeParameters *fb = &sd->bake.Filter; 873 874 sd->uses_heap = 1; 875 fb->FilterLength = (u32)f->length; 876 fb->FilterCoefficients = gpu_resource_push(resource_builder, f32, f->length * (f->parameters.complex ? 2 : 1), 877 .data = f->data, 878 .name = push_str8_f(scratch, "filter_%u", sp->filter_slot)); 879 880 fb->SampleCount = node->rf_dimensions.E[BeamformerRFDimension_Samples]; 881 fb->DecimationRate = demod ? decimation_rate : 1; 882 883 b32 deinterleave = beamformer_data_kind_complex[node->input_data_kind] && 884 !beamformer_data_kind_complex[node->output_data_kind]; 885 if (deinterleave) 886 fb->BatchSampleCount = node->rf_dimensions.x * node->rf_dimensions.y * node->rf_dimensions.z; 887 888 fb->OutputSampleStride = output_stride.E[BeamformerRFDimension_Samples]; 889 fb->OutputChannelStride = output_stride.E[BeamformerRFDimension_Channels]; 890 fb->OutputTransmitStride = output_stride.E[BeamformerRFDimension_Events]; 891 892 fb->InputSampleStride = input_stride.E[BeamformerRFDimension_Samples]; 893 fb->InputChannelStride = input_stride.E[BeamformerRFDimension_Channels]; 894 fb->InputTransmitStride = input_stride.E[BeamformerRFDimension_Events]; 895 896 /* NOTE(rnp): when we are demodulating we pretend that the sampler was alternating 897 * between sampling the I portion and the Q portion of an IQ signal. Therefore there 898 * is an implicit decimation factor of 2 which must always be included. All code here 899 * assumes that the signal was sampled in such a way that supports this operation. 900 * To recover IQ[n] from the sampled data (RF[n]) we do the following: 901 * I[n] = RF[n] 902 * Q[n] = RF[n + 1] 903 * IQ[n] = I[n] - j*Q[n] 904 */ 905 if (demod) { 906 fb->DemodulationFrequency = pb->parameters.demodulation_frequency; 907 fb->SamplingFrequency = pb->parameters.sampling_frequency / 2; 908 } 909 910 sd->layout = (uv3){{subgroup_size, 1, 1}}; 911 sd->dispatch.x = ceil_div_u32(node->rf_dimensions.E[BeamformerRFDimension_Samples], sd->layout.x); 912 sd->dispatch.y = ceil_div_u32(node->rf_dimensions.E[BeamformerRFDimension_Channels], sd->layout.y); 913 sd->dispatch.z = ceil_div_u32(node->rf_dimensions.E[BeamformerRFDimension_Events], sd->layout.z); 914 }break; 915 916 case BeamformerShaderKind_DAS:{ 917 cp->first_image_shader_index = cp->pipeline.shader_count; 918 919 BeamformerDASBakeParameters *db = &sd->bake.DAS; 920 db->SamplingFrequency = sampling_frequency; 921 db->DemodulationFrequency = pb->parameters.demodulation_frequency; 922 db->SpeedOfSound = pb->parameters.speed_of_sound; 923 db->TimeOffset = time_offset; 924 db->FNumber = pb->parameters.f_number; 925 db->AcquisitionKind = pb->parameters.acquisition_kind; 926 db->SampleCount = node->rf_dimensions.E[BeamformerRFDimension_Samples]; 927 db->AcquisitionCount = node->rf_dimensions.E[BeamformerRFDimension_Events]; 928 db->ChunkChannelCount = node->rf_dimensions.E[BeamformerRFDimension_Channels]; 929 db->ChannelByteStride = input_stride.E[BeamformerRFDimension_Channels] 930 * beamformer_data_kind_byte_size[node->input_data_kind]; 931 db->AcquisitionByteStride = input_stride.E[BeamformerRFDimension_Events] 932 * beamformer_data_kind_byte_size[node->input_data_kind]; 933 934 // TODO(rnp): pass these in 935 db->ReceiveChannelCount = pb->parameters.channel_count; 936 db->TransmitChannelCount = pb->parameters.channel_count; 937 db->ReceiveTileCount = 1; 938 db->TransmitTileCount = 1; 939 940 db->InterpolationMode = pb->parameters.interpolation_mode; 941 db->ReadiGroupCount = pb->parameters.readi_group_count; 942 db->OutputSizeX = cp->output_points.x; 943 db->OutputSizeY = cp->output_points.y; 944 db->OutputSizeZ = cp->output_points.z; 945 db->TransmitReceiveOrientation = pb->parameters.transmit_receive_orientation; 946 947 cp->xdc_element_pitch = pb->parameters.xdc_element_pitch; 948 949 u32 id = pb->parameters.acquisition_kind; 950 951 u32 acquisition_kind_flags = beamformer_acquisition_kind_flags[id]; 952 b32 sparse = (acquisition_kind_flags & BeamformerAcquisitionKindFlags_Sparse) != 0; 953 b32 single_focus = pb->parameters.single_focus != 0; 954 b32 single_orientation = pb->parameters.single_orientation != 0; 955 956 sd->compile_flags |= BeamformerDASCompileFlags_CoherencyWeighting * (pb->parameters.coherency_weighting != 0); 957 sd->compile_flags |= BeamformerDASCompileFlags_Sparse * sparse; 958 959 sd->layout = layout_for_output(cp->output_points); 960 sd->dispatch = dispatch_for_output(sd->layout, cp->output_points); 961 962 sd->uses_heap = 1; 963 db->TransducerTransforms = gpu_resource_push(resource_builder, m4, db->TransmitTileCount * db->ReceiveTileCount, 964 .data = &pb->parameters.xdc_transform, 965 .name = str8("transducer_transforms")); 966 967 if (!single_focus) { 968 db->FocalVectors = gpu_resource_push(resource_builder, v2, db->AcquisitionCount, 969 .data = pb->focal_vectors, 970 .name = str8("focal_vectors")); 971 } 972 973 if (!single_orientation) { 974 db->TransmitReceiveOrientations = gpu_resource_push(resource_builder, u8, db->AcquisitionCount, 975 .data = pb->transmit_receive_orientations, 976 .name = str8("transmit_receive_orientations")); 977 } 978 979 if (sparse) { 980 db->SparseElements = gpu_resource_push(resource_builder, i16, db->AcquisitionCount, 981 .data = pb->sparse_elements, 982 .name = str8("sparse_elements")); 983 } 984 985 if (pb->parameters.coherency_weighting) { 986 db->IncoherentFrame = gpu_resource_push(resource_builder, u32, 0, 987 .size = beamformer_incoherent_frame_byte_size(cp->output_points, das_data_kind), 988 .name = str8("incoherent_buffer")); 989 } 990 991 cp->readi_group = pb->parameters.readi_group; 992 if (db->ReadiGroupCount > 1) { 993 u32 order = db->ReadiGroupCount; 994 db->Hadamard = gpu_resource_push(resource_builder, f16, order * order, 995 .data = make_hadamard_transpose(scratch, order, 0), 996 .name = str8("readi_hadamard")); 997 } 998 }break; 999 1000 case BeamformerShaderKind_CoherencyWeighting:{ 1001 // NOTE(rnp): beamformed data is stored in linear order; making the layout 2D or 3D 1002 // here is just slower 1003 sd->layout = (uv3){{subgroup_size, 1, 1}}; 1004 sd->dispatch = dispatch_for_output(sd->layout, cp->output_points); 1005 1006 BeamformerCoherencyWeightingBakeParameters *cw = &sd->bake.CoherencyWeighting; 1007 cw->Scale = 1.f; 1008 cw->OutputVoxels = cp->output_points.x * cp->output_points.y * cp->output_points.z; 1009 cw->IncoherentSum = gpu_resource_push(resource_builder, u32, 0, 1010 .size = beamformer_incoherent_frame_byte_size(cp->output_points, das_data_kind), 1011 .name = str8("incoherent_buffer")); 1012 sd->uses_heap = 1; 1013 }break; 1014 1015 case BeamformerShaderKind_Reshape:{ 1016 BeamformerReshapeBakeParameters *rb = &sd->bake.Reshape; 1017 b32 deinterleave = beamformer_data_kind_complex[node->input_data_kind] && 1018 !beamformer_data_kind_complex[node->output_data_kind]; 1019 b32 interleave = !beamformer_data_kind_complex[node->input_data_kind] && 1020 beamformer_data_kind_complex[node->output_data_kind]; 1021 assert(interleave == 0 || (interleave != deinterleave)); 1022 sd->compile_flags |= BeamformerReshapeCompileFlags_Deinterleave * deinterleave; 1023 sd->compile_flags |= BeamformerReshapeCompileFlags_Interleave * interleave; 1024 1025 rb->InputStrideX = input_stride.x; 1026 rb->InputStrideY = input_stride.y; 1027 rb->InputStrideZ = input_stride.z; 1028 rb->OutputStrideX = output_stride.x; 1029 rb->OutputStrideY = output_stride.y; 1030 rb->OutputStrideZ = output_stride.z; 1031 1032 // NOTE(rnp): order doesn't really matter here but it must match the dispatch layout 1033 rb->SizeX = node->rf_dimensions.x; 1034 rb->SizeY = node->rf_dimensions.y; 1035 rb->SizeZ = node->rf_dimensions.z; 1036 1037 sd->layout.x = 1; 1038 sd->layout.z = Min(subgroup_size, rb->SizeZ); 1039 sd->layout.y = subgroup_size / sd->layout.z; 1040 1041 sd->dispatch.x = ceil_div_u32(rb->SizeX, sd->layout.x); 1042 sd->dispatch.y = ceil_div_u32(rb->SizeY, sd->layout.y); 1043 sd->dispatch.z = ceil_div_u32(rb->SizeZ, sd->layout.z); 1044 }break; 1045 1046 default:{}break; 1047 1048 #if 0 1049 case BeamformerShaderKind_Sum:{ 1050 sd->bake.data_kind = BeamformerDataKind_Float32; 1051 if (cp->iq_pipeline) 1052 sd->bake.data_kind = BeamformerDataKind_Float32Complex; 1053 1054 sd->layout = layout_for_output(cp->output_points); 1055 sd->dispatch = dispatch_for_output(sd->layout, cp->output_points); 1056 1057 commit = 1; 1058 }break; 1059 #endif 1060 1061 } 1062 } 1063 } 1064 1065 cp->pipeline.data_kind = input_data_kind; 1066 1067 if (cp->first_image_shader_index == 0) 1068 cp->first_image_shader_index = cp->pipeline.shader_count; 1069 1070 gpu_resource_build_end(resource_builder, &cp->gpu_temp_arena); 1071 } 1072 1073 function void 1074 stream_append_shader_header(Stream *s, i32 reloadable_index, u64 gpu_heap_pointer, BeamformerShaderDescriptor *sd, uv3 layout) 1075 { 1076 stream_append_str8(s, str8("#version 460 core\n\n" 1077 "#extension GL_EXT_buffer_reference : require\n" 1078 "#extension GL_EXT_shader_16bit_storage : require\n" 1079 "#extension GL_EXT_shader_explicit_arithmetic_types : require\n\n" 1080 "#define f32 float32_t\n" 1081 "#define f16 float16_t\n" 1082 "#define s32 int32_t\n" 1083 "#define u64 uint64_t\n" 1084 "#define u32 uint32_t\n" 1085 "#define s16 int16_t\n" 1086 "#define u16 uint16_t\n" 1087 "#define u8 uint8_t\n" 1088 "#define s32vec2 i32vec2\n" 1089 "#define s16vec2 i16vec2\n" 1090 "\n")); 1091 1092 i32 header_vector_length = beamformer_shader_header_vector_lengths[reloadable_index]; 1093 i32 *header_vector = (i32 *)beamformer_shader_header_vectors[reloadable_index]; 1094 for (i32 index = 0; index < header_vector_length; index++) 1095 stream_append_str8(s, beamformer_shader_global_header_strings[header_vector[index]]); 1096 1097 if (layout.x != 0) { 1098 stream_append_str8(s, str8("layout(local_size_x = ")); 1099 stream_append_u64(s, layout.x); 1100 stream_append_str8(s, str8(", local_size_y = ")); 1101 stream_append_u64(s, layout.y); 1102 stream_append_str8(s, str8(", local_size_z = ")); 1103 stream_append_u64(s, layout.z); 1104 stream_append_str8(s, str8(") in;\n\n")); 1105 } 1106 1107 { 1108 u32 max_length = 0; 1109 for EachElement(beamformer_data_kind_str8, it) 1110 max_length = Max(max_length, (u32)beamformer_data_kind_str8[it].length); 1111 1112 for EachElement(beamformer_data_kind_str8, it) { 1113 stream_append_str8s(s, str8("#define DataKind_"), beamformer_data_kind_str8[it]); 1114 stream_pad(s, ' ', max_length - beamformer_data_kind_str8[it].length + 1); 1115 stream_append_u64(s, it); 1116 stream_append_byte(s, '\n'); 1117 } 1118 stream_append_byte(s, '\n'); 1119 } 1120 1121 if (gpu_heap_pointer) { 1122 stream_append_str8(s, str8("#define HeapBase u64(0x")); 1123 stream_append_hex_u64(s, gpu_heap_pointer); 1124 stream_append_str8(s, str8("ul)\n")); 1125 } 1126 1127 if (sd) { 1128 BeamformerDataKind data_kinds[] = {sd->input_data_kind, sd->output_data_kind}; 1129 str8 line_prefixes[] = {str8_comp("Input"), str8_comp("Output")}; 1130 for EachElement(data_kinds, it) { 1131 if (data_kinds[it] != BeamformerDataKind_Count) { 1132 stream_append_str8s(s, str8("#define "), line_prefixes[it], str8("DataType "), 1133 beamformer_data_kind_glsl_type[data_kinds[it]], 1134 str8("\n#define "), line_prefixes[it], str8("DataKind DataKind_"), 1135 beamformer_data_kind_str8[data_kinds[it]], 1136 str8("\n#define "), line_prefixes[it], str8("DataKindByteSize ")); 1137 stream_append_u64(s, beamformer_data_kind_byte_size[data_kinds[it]]); 1138 stream_append_byte(s, '\n'); 1139 } 1140 } 1141 stream_append_byte(s, '\n'); 1142 1143 stream_append_str8(s, str8("#define CompileFlags (0x")); 1144 stream_append_hex_u64_width(s, sd->compile_flags, 8); 1145 stream_append_str8(s, str8(")\n")); 1146 1147 i32 struct_id = beamformer_base_shader_to_bake_struct_id[reloadable_index]; 1148 if (struct_id != -1) { 1149 const str8 *names = meta_struct_member_names_by_id[struct_id]; 1150 const MetaStructInfo *si = meta_struct_info_by_id + struct_id; 1151 const MetaStructMember *sm = meta_struct_members_by_id[struct_id]; 1152 for (u32 index = 0; index < si->member_count; index++) { 1153 str8 type = meta_kind_glsl_types[sm[index].type_id]; 1154 stream_append_str8(s, str8("layout(constant_id = ")); 1155 stream_append_u64(s, index); 1156 stream_append_str8s(s, str8(") const "), type, str8(" "), names[index], str8(" = "), type, str8("(1);\n")); 1157 } 1158 } 1159 } 1160 1161 if (!renderdoc_attached()) 1162 stream_append_str8(s, str8("\n\n#line 1\n")); 1163 } 1164 1165 function void 1166 beamformer_reload_pipeline(VulkanHandle *pipeline, BeamformerShaderReloadInfo *sris, u32 count, Arena *scratch) 1167 { 1168 assume(count <= 2); 1169 str8 paths[2]; 1170 VulkanPipelineCreateInfo infos[2]; 1171 1172 if (!BakeShaders) { 1173 for (u32 i = 0; i < count; i++) 1174 paths[i] = push_str8_from_parts(scratch, os_path_separator(), str8("shaders"), sris[i].filename_or_data); 1175 } 1176 1177 u32 push_constants_size = 0; 1178 for (u32 i = 0; i < count; i++) { 1179 Stream shader_stream = arena_stream(scratch); 1180 i32 reloadable_index = beamformer_shader_reloadable_index_by_shader[sris[i].shader]; 1181 if (i == 0) push_constants_size = beamformer_shader_push_constant_sizes[reloadable_index]; 1182 else assert(push_constants_size == beamformer_shader_push_constant_sizes[reloadable_index]); 1183 1184 stream_append_shader_header(&shader_stream, reloadable_index, sris[i].gpu_heap_pointer, 1185 sris[i].shader_descriptor, sris[i].layout); 1186 1187 str8 shader_text; 1188 if (BakeShaders) { 1189 stream_append_str8(&shader_stream, sris[i].filename_or_data); 1190 shader_text = arena_stream_commit_zero(scratch, &shader_stream); 1191 } else { 1192 str8 stream_data = arena_stream_commit(scratch, &shader_stream); 1193 str8 shader_data = os_read_entire_file(scratch, (c8 *)paths[i].data); 1194 // NOTE(rnp): kinda sucky but need to make sure these are a contiguous string 1195 shader_text = push_str8_from_parts(scratch, str8(""), stream_data, shader_data); 1196 } 1197 1198 infos[i].kind = sris[i].shader_kind; 1199 infos[i].text = shader_text; 1200 infos[i].name = beamformer_shader_names[sris[i].shader]; 1201 infos[i].specialization_data = sris[i].shader_descriptor ? &sris[i].shader_descriptor->bake : 0; 1202 infos[i].specialization_struct_id = beamformer_base_shader_to_bake_struct_id[reloadable_index]; 1203 1204 //str8 line = str8("---------------\n"); 1205 //str8 nl = str8("\n"); 1206 //os_console_log(line.data, line.length); 1207 //os_console_log(infos[i].name.data, infos[i].name.length); 1208 //os_console_log(nl.data, nl.length); 1209 //os_console_log(line.data, line.length); 1210 //os_console_log(infos[i].text.data, infos[i].text.length); 1211 //os_console_log(line.data, line.length); 1212 } 1213 1214 vk_pipeline_release(*pipeline); 1215 *pipeline = vk_pipeline(infos, count, push_constants_size); 1216 } 1217 1218 function void 1219 beamformer_reload_render_pipeline(VulkanHandle *pipeline, BeamformerShaderKind shader, Arena *scratch) 1220 { 1221 i32 index = beamformer_shader_reloadable_index_by_shader[shader]; 1222 BeamformerShaderReloadInfo infos[2] = { 1223 { 1224 .shader = shader, 1225 .shader_kind = beamformer_shader_primitive_is_vertex[index] ? VulkanShaderKind_Vertex : VulkanShaderKind_Mesh, 1226 .filename_or_data = BakeShaders ? beamformer_shader_data[index][0] 1227 : beamformer_reloadable_shader_files[index][0], 1228 }, 1229 { 1230 .shader = shader, 1231 .shader_kind = VulkanShaderKind_Fragment, 1232 .filename_or_data = BakeShaders ? beamformer_shader_data[index][1] 1233 : beamformer_reloadable_shader_files[index][1], 1234 }, 1235 }; 1236 beamformer_reload_pipeline(pipeline, infos, countof(infos), scratch); 1237 } 1238 1239 function void 1240 beamformer_commit_parameter_block(BeamformerCtx *ctx, BeamformerComputePlan *cp, u32 block, Arena *scratch) 1241 { 1242 BeamformerParameterBlock *pb; 1243 DeferLoop(pb = beamformer_parameter_block_lock(ctx->shared_memory, block, -1), 1244 beamformer_parameter_block_unlock(ctx->shared_memory, block)) 1245 for EachBit(pb->region_update_flags, region) 1246 { 1247 pb->region_update_flags &= ~(1ul << region); 1248 switch (region) { 1249 case BeamformerParameterDirtyFlag_NotifyUI:{ 1250 atomic_store_u32(&ctx->ui_dirty_parameter_blocks, 1u << block); 1251 cp->ui_voxel_transform = m4_identity(); 1252 }break; 1253 1254 case BeamformerParameterDirtyFlag_Parameters:{ 1255 cp->output_points = das_valid_points(pb->parameters.output_points.xyz); 1256 cp->average_frames = pb->parameters.output_points.E[3]; 1257 1258 plan_compute_pipeline(cp, pb, scratch); 1259 1260 b32 heap_pointer_updated = cp->last_gpu_heap_pointer != cp->gpu_temp_arena.gpu_pointer; 1261 cp->last_gpu_heap_pointer = cp->gpu_temp_arena.gpu_pointer; 1262 1263 for (u32 shader_slot = 0; shader_slot < cp->pipeline.shader_count; shader_slot++) { 1264 u128 hash = u128_hash_from_data(cp->shader_descriptors + shader_slot, sizeof(BeamformerShaderDescriptor)); 1265 if (!u128_equal(hash, cp->shader_hashes[shader_slot]) || 1266 (cp->shader_descriptors[shader_slot].uses_heap && heap_pointer_updated)) 1267 { 1268 cp->dirty_programs |= 1 << shader_slot; 1269 } 1270 cp->shader_hashes[shader_slot] = hash; 1271 } 1272 1273 cp->acquisition_count = pb->parameters.acquisition_count; 1274 cp->acquisition_kind = pb->parameters.acquisition_kind; 1275 cp->contrast_mode = pb->parameters.contrast_mode; 1276 }break; 1277 } 1278 } 1279 } 1280 1281 function void 1282 do_compute_shader(GPUCommandList cmd, BeamformerComputePlan *cp, BeamformerFrame *frame, 1283 u64 input_pointer, u64 output_pointer, u32 shader_slot, u32 channel_offset) 1284 { 1285 BeamformerComputeContext *cc = &beamformer_context->compute_context; 1286 1287 uv3 dispatch = cp->shader_descriptors[shader_slot].dispatch; 1288 1289 gpu_command_bind_pipeline(cmd, cp->vulkan_pipelines[shader_slot]); 1290 1291 switch (cp->pipeline.shaders[shader_slot]) { 1292 1293 case BeamformerShaderKind_Decode:{ 1294 BeamformerDecodePushConstants pc = { 1295 .rf_buffer = input_pointer, 1296 .output_buffer = output_pointer, 1297 }; 1298 1299 gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); 1300 gpu_command_dispatch_compute(cmd, dispatch); 1301 1302 cc->ping_pong_input_index = !cc->ping_pong_input_index; 1303 }break; 1304 1305 case BeamformerShaderKind_Hilbert:{ 1306 //cuda_hilbert(input_index, output_index); 1307 //cc->ping_pong_input_index = !cc->ping_pong_input_index; 1308 }break; 1309 1310 case BeamformerShaderKind_Filter: 1311 case BeamformerShaderKind_Demodulate: 1312 { 1313 BeamformerFilterPushConstants pc = { 1314 .input_buffer = input_pointer, 1315 .output_buffer = output_pointer, 1316 }; 1317 1318 gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); 1319 gpu_command_dispatch_compute(cmd, dispatch); 1320 1321 cc->ping_pong_input_index = !cc->ping_pong_input_index; 1322 }break; 1323 1324 case BeamformerShaderKind_DAS:{ 1325 BeamformerDASPushConstants pc = { 1326 .xdc_element_pitch = cp->xdc_element_pitch, 1327 .rf_data = input_pointer, 1328 .output_frame = frame->gpu_pointer, 1329 .channel_offset = channel_offset, 1330 .focus_depth = frame->focus_depth, 1331 .steering_angle = frame->steering_angle, 1332 .tx_rx_orientation = frame->transmit_receive_orientation, 1333 .readi_group = cp->readi_group, 1334 }; 1335 memory_copy(pc.voxel_transform.E, frame->voxel_transform.E, sizeof(pc.voxel_transform)); 1336 1337 gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); 1338 gpu_command_dispatch_compute(cmd, dispatch); 1339 }break; 1340 1341 case BeamformerShaderKind_CoherencyWeighting:{ 1342 BeamformerCoherencyWeightingPushConstants pc = {.coherent_sum = frame->gpu_pointer}; 1343 gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); 1344 gpu_command_dispatch_compute(cmd, dispatch); 1345 }break; 1346 1347 case BeamformerShaderKind_Reshape:{ 1348 BeamformerDataKind input_data_kind = cp->shader_descriptors[shader_slot].input_data_kind; 1349 BeamformerReshapeBakeParameters *rb = &cp->shader_descriptors[shader_slot].bake.Reshape; 1350 BeamformerReshapePushConstants pc = { 1351 .left_input_buffer = input_pointer, 1352 .right_input_buffer = input_pointer + rb->SizeX * rb->SizeY * rb->SizeZ 1353 * beamformer_data_kind_byte_size[input_data_kind], 1354 .output_buffer = output_pointer, 1355 }; 1356 1357 gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); 1358 gpu_command_dispatch_compute(cmd, dispatch); 1359 1360 cc->ping_pong_input_index = !cc->ping_pong_input_index; 1361 }break; 1362 1363 // NOTE(rnp): invalid stages should be filtered in planning phase 1364 InvalidDefaultCase; 1365 } 1366 1367 #if 0 1368 switch (shader) { 1369 case BeamformerShaderKind_MinMax:{ 1370 for (u32 i = 1; i < frame->image.mip_map_levels; i++) { 1371 glBindImageTexture(0, frame->texture, i - 1, GL_TRUE, 0, GL_READ_ONLY, GL_RG32F); 1372 glBindImageTexture(1, frame->texture, i - 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RG32F); 1373 glProgramUniform1i(program, MIN_MAX_MIPS_LEVEL_UNIFORM_LOC, i); 1374 1375 u32 width = (u32)frame->dim.x >> i; 1376 u32 height = (u32)frame->dim.y >> i; 1377 u32 depth = (u32)frame->dim.z >> i; 1378 glDispatchCompute(ORONE(width / 32), ORONE(height), ORONE(depth / 32)); 1379 glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); 1380 } 1381 }break; 1382 case BeamformerShaderKind_Sum:{ 1383 u32 aframe_index = ctx->averaged_frame_index % countof(ctx->averaged_frames); 1384 BeamformerFrame *aframe = ctx->averaged_frames + aframe_index; 1385 aframe->id = ctx->averaged_frame_index; 1386 atomic_store_u32(&aframe->ready_to_present, 0); 1387 /* TODO(rnp): hack we need a better way of specifying which frames to sum; 1388 * this is fine for rolling averaging but what if we want to do something else */ 1389 assert(frame >= ctx->beamform_frames); 1390 assert(frame < ctx->beamform_frames + countof(ctx->beamform_frames)); 1391 u32 base_index = (u32)(frame - ctx->beamform_frames); 1392 u32 to_average = (u32)cp->average_frames; 1393 u32 frame_count = 0; 1394 u32 *in_textures = push_array(&arena, u32, BeamformerMaxBacklogFrames); 1395 ComputeFrameIterator cfi = compute_frame_iterator(ctx, 1 + base_index - to_average, to_average); 1396 for (BeamformerFrame *it = frame_next(&cfi); it; it = frame_next(&cfi)) 1397 in_textures[frame_count++] = it->texture; 1398 1399 assert(to_average == frame_count); 1400 1401 glProgramUniform1f(program, SUM_PRESCALE_UNIFORM_LOC, 1 / (f32)frame_count); 1402 /* NOTE: zero output before summing */ 1403 glClearTexImage(aframe->texture, 0, GL_RED, GL_FLOAT, 0); 1404 glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); 1405 1406 glBindImageTexture(0, out_texture, 0, GL_TRUE, 0, GL_READ_WRITE, GL_RG32F); 1407 for (u32 i = 0; i < in_texture_count; i++) { 1408 glBindImageTexture(1, in_textures[i], 0, GL_TRUE, 0, GL_READ_ONLY, GL_RG32F); 1409 glDispatchCompute(dispatch.x, dispatch.y, dispatch.z); 1410 glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); 1411 } 1412 1413 memory_copy(aframe->voxel_transform.E, frame->voxel_transform.E, sizeof(frame->voxel_transform)); 1414 aframe->compound_count = frame->compound_count; 1415 aframe->acquisition_kind = frame->acquisition_kind; 1416 }break; 1417 } 1418 #endif 1419 } 1420 1421 function void 1422 complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena *arena) 1423 { 1424 BeamformerComputeContext * cs = &ctx->compute_context; 1425 BeamformerSharedMemory * sm = ctx->shared_memory; 1426 1427 for (BeamformWork *work = beamform_work_queue_pop(q); 1428 work; 1429 beamform_work_queue_pop_commit(q), work = beamform_work_queue_pop(q)) 1430 { 1431 switch (work->kind) { 1432 1433 case BeamformerWorkKind_ExportBuffer:{ 1434 /* TODO(rnp): better way of handling DispatchCompute barrier */ 1435 post_sync_barrier(ctx->shared_memory, BeamformerSharedMemoryLockKind_DispatchCompute); 1436 beamformer_shared_memory_take_lock(ctx->shared_memory, (i32)work->lock, (u32)-1); 1437 BeamformerExportContext *ec = &work->export_context; 1438 switch (ec->kind) { 1439 case BeamformerExportKind_BeamformedData:{ 1440 BeamformerFrameBacklog *bl = &ctx->compute_context.backlog; 1441 u32 req_count = Clamp(ec->count, 1, bl->counter); 1442 u32 frame_idx = bl->counter - req_count; 1443 u8 *sm_output = beamformer_shared_memory_data_pointer(sm, ctx->shared_memory_size); 1444 u64 exported_size = 0; 1445 for (u32 export_count = 0; export_count < req_count; export_count++, frame_idx++) { 1446 BeamformerFrame *f = bl->frames + frame_idx % countof(bl->frames); 1447 u64 frame_size = beamformer_frame_byte_size(f->points, f->data_kind); 1448 assert((frame_size & 63) == 0); 1449 // NOTE(tkh) we don't want to assume that all req_count frames are the same size, 1450 // so we either need to count the total size of all requested frames first or 1451 // just fill up as much as possible. 1452 if (exported_size + frame_size <= ec->size) { 1453 u64 offset = f->gpu_pointer - bl->buffer->gpu_pointer; 1454 gpu_host_wait_timeline(GPUTimeline_Compute, f->timeline_valid_value, -1ULL); 1455 gpu_buffer_range_download(sm_output + exported_size, bl->buffer, offset, frame_size, 1); 1456 exported_size += frame_size; 1457 } 1458 } 1459 }break; 1460 1461 case BeamformerExportKind_Stats:{ 1462 ComputeTimingTable *table = ctx->compute_timing_table; 1463 /* NOTE(rnp): do a little spin to let this finish updating */ 1464 spin_wait(table->write_index != atomic_load_u32(&table->read_index)); 1465 ComputeShaderStats *stats = ctx->compute_shader_stats; 1466 if (sizeof(stats->table) <= ec->size) 1467 memory_copy(beamformer_shared_memory_data_pointer(sm, ctx->shared_memory_size), 1468 &stats->table, sizeof(stats->table)); 1469 }break; 1470 InvalidDefaultCase; 1471 } 1472 beamformer_shared_memory_release_lock(ctx->shared_memory, work->lock); 1473 post_sync_barrier(ctx->shared_memory, BeamformerSharedMemoryLockKind_ExportSync); 1474 }break; 1475 1476 case BeamformerWorkKind_CreateFilter:{ 1477 BeamformerCreateFilterContext *fctx = &work->create_filter_context; 1478 u32 block = fctx->parameter_block; 1479 u32 slot = fctx->filter_slot; 1480 BeamformerComputePlan *cp = beamformer_compute_plan_for_block(cs, block, arena); 1481 cp->filter_parameters[slot] = fctx->parameters; 1482 }break; 1483 1484 case BeamformerWorkKind_WaitThenCompute: 1485 case BeamformerWorkKind_Compute: 1486 { 1487 push_compute_timing_info(ctx->compute_timing_table, 1488 (ComputeTimingInfo){.kind = ComputeTimingInfoKind_ComputeFrameBegin}); 1489 1490 BeamformerComputePlan *cp = beamformer_compute_plan_for_block(cs, work->compute_context.parameter_set, arena); 1491 if unlikely(beamformer_parameter_block_dirty(sm, work->compute_context.parameter_set)) { 1492 u32 set = work->compute_context.parameter_set; 1493 Temp scratch = temp_begin(arena); 1494 beamformer_commit_parameter_block(ctx, cp, set, arena); 1495 temp_end(scratch); 1496 } 1497 1498 post_sync_barrier(ctx->shared_memory, BeamformerSharedMemoryLockKind_DispatchCompute); 1499 1500 u32 dirty_programs = atomic_swap_u32(&cp->dirty_programs, 0); 1501 static_assert(BeamformerMaxComputeShaderStages <= 32, ""); 1502 if unlikely(dirty_programs) { 1503 for EachBit(dirty_programs, slot) { 1504 assert(slot < BeamformerMaxComputeShaderStages); 1505 Temp scratch; 1506 DeferLoop(scratch = temp_begin(arena), temp_end(scratch)) 1507 { 1508 u64 gpu_heap_pointer = cp->gpu_temp_arena.gpu_pointer; 1509 i32 index = beamformer_shader_reloadable_index_by_shader[cp->pipeline.shaders[slot]]; 1510 BeamformerShaderReloadInfo info = { 1511 .shader = cp->pipeline.shaders[slot], 1512 .shader_kind = VulkanShaderKind_Compute, 1513 .shader_descriptor = cp->shader_descriptors + slot, 1514 .gpu_heap_pointer = cp->shader_descriptors[slot].uses_heap ? gpu_heap_pointer : 0, 1515 .filename_or_data = BakeShaders ? beamformer_shader_data[index][0] 1516 : beamformer_reloadable_shader_files[index][0], 1517 .layout = cp->shader_descriptors[slot].layout, 1518 }; 1519 beamformer_reload_pipeline(cp->vulkan_pipelines + slot, &info, 1, arena); 1520 } 1521 } 1522 } 1523 1524 atomic_store_u32(&cs->processing_compute, 1); 1525 1526 start_renderdoc_capture(); 1527 1528 i32 das_index = -1; 1529 i32 coherency_weighting = -1; 1530 for (u32 i = 0; i < cp->pipeline.shader_count; i++) { 1531 if (cp->pipeline.shaders[i] == BeamformerShaderKind_CoherencyWeighting) 1532 coherency_weighting = (i32)i; 1533 1534 if (cp->pipeline.shaders[i] == BeamformerShaderKind_DAS) 1535 das_index = (i32)i; 1536 } 1537 1538 BeamformerFrame *frame = beamformer_frame_next(cs, cp->output_points, cp->iq_pipeline); 1539 frame->acquisition_kind = cp->acquisition_kind; 1540 frame->contrast_mode = cp->contrast_mode; 1541 frame->compound_count = cp->acquisition_count; 1542 frame->parameter_block = work->compute_context.parameter_set; 1543 frame->view_plane_tag = work->compute_context.view_plane; 1544 frame->focus_depth = work->compute_context.focus_depth; 1545 frame->steering_angle = work->compute_context.steering_angle; 1546 frame->transmit_receive_orientation = work->compute_context.tx_rx_orientation; 1547 1548 b32 transform_changed = !m4_equal(cp->last_voxel_transform, work->compute_context.voxel_transform); 1549 memory_copy(&cp->last_voxel_transform, &work->compute_context.voxel_transform, sizeof(cp->last_voxel_transform)); 1550 memory_copy(&frame->voxel_transform, &cp->last_voxel_transform, sizeof(frame->voxel_transform)); 1551 1552 if (transform_changed) { 1553 atomic_store_u32(&beamformer_context->ui_dirty_parameter_blocks, 1u << work->compute_context.parameter_set); 1554 cp->ui_voxel_transform = m4_identity(); 1555 } else if (u64_hash_from_str8(str8_struct(&cp->last_voxel_transform)) == cp->ui_last_voxel_transform_expected_hash) { 1556 frame->voxel_transform = m4_mul(cp->ui_voxel_transform, frame->voxel_transform); 1557 } 1558 1559 GPUCommandList cmd = gpu_command_list_begin(GPUTimeline_Compute); 1560 gpu_command_timestamp(cmd); 1561 1562 if (das_index >= 0) { 1563 GPUBuffer *backlog = cs->backlog.buffer; 1564 u64 frame_size = beamformer_frame_byte_size(frame->points, frame->data_kind); 1565 u64 offset = frame->gpu_pointer - backlog->gpu_pointer; 1566 gpu_command_clear_buffer(cmd, backlog, offset, frame_size, 0); 1567 } 1568 1569 if (coherency_weighting >= 0) { 1570 BeamformerCoherencyWeightingBakeParameters *cw = &cp->shader_descriptors[coherency_weighting].bake.CoherencyWeighting; 1571 GPUBuffer *gpu_arena = &cp->gpu_temp_arena; 1572 u64 coherent_size = beamformer_incoherent_frame_byte_size(frame->points, frame->data_kind); 1573 gpu_command_clear_buffer(cmd, gpu_arena, cw->IncoherentSum, coherent_size, 0); 1574 } 1575 1576 BeamformerRFBuffer *rf = &cs->rf_buffer; 1577 u64 compute_index = rf->compute_index; 1578 u64 slot = compute_index % countof(rf->upload_complete_values); 1579 1580 // NOTE(rnp): the library/ui will ensure that any time a new dataset is uploaded 1581 // the next time beamforming is issued it will be tagged as WaitThenCompute. 1582 if (work->kind != BeamformerWorkKind_WaitThenCompute) 1583 slot = (compute_index - 1) % countof(rf->upload_complete_values); 1584 1585 // NOTE(rnp): nvidia needs a memory barrier between pipeline stages 1586 // for correct output. It doesn't seem to effect performance on nvidia cards. 1587 // It does have a very minor performance decrease on AMD, but technically its 1588 // against vulkan spec to skip them so for simplicity we will default to for now 1589 b32 pipeline_memory_barrier = 1; //gpu_info()->vendor == GPUVendor_NVIDIA; 1590 1591 for (u32 channel_offset = 0; 1592 channel_offset < cp->channel_count; 1593 channel_offset += BeamformerChunkChannelCount) 1594 { 1595 u64 rf_pointer = rf->buffer.gpu_pointer + slot * rf->active_rf_size; 1596 rf_pointer += cp->raw_channel_byte_stride * channel_offset; 1597 1598 // NOTE(rnp): handle the case where the channel count is not a multiple of 1599 // BeamformerChunkChannelCount. We do not want to have to modify every shader 1600 // that might run to care about this so here we run a copy/clear to pad the 1601 // data for the last iteration. 1602 b32 special_handling = (cp->channel_count - channel_offset) < BeamformerChunkChannelCount; 1603 if unlikely(special_handling) { 1604 u32 channel_pad_count = BeamformerChunkChannelCount - (cp->channel_count - channel_offset); 1605 u32 input_index = cs->ping_pong_input_index; 1606 u64 pp_size = cs->ping_pong_buffer.size / PING_PONG_BUFFER_SLOTS; 1607 u64 copy_size = cp->raw_channel_byte_stride * (cp->channel_count - channel_offset); 1608 u64 copy_offset = rf_pointer - rf->buffer.gpu_pointer; 1609 u64 clear_size = cp->raw_channel_byte_stride * channel_pad_count; 1610 1611 gpu_command_copy_buffer(cmd, &cs->ping_pong_buffer, input_index * pp_size, &rf->buffer, copy_offset, copy_size); 1612 gpu_command_clear_buffer(cmd, &cs->ping_pong_buffer, input_index * pp_size + copy_size, clear_size, 0); 1613 // TODO(rnp): technically this should be a transfer to compute barrier 1614 // but a full memory barrier will also do the trick 1615 gpu_command_pipeline_barrier(cmd, 1); 1616 } 1617 1618 for (u32 i = 0; i < cp->first_image_shader_index; i++) { 1619 u32 output_index = !cs->ping_pong_input_index; 1620 u32 input_index = cs->ping_pong_input_index; 1621 1622 u64 pp_size = cs->ping_pong_buffer.size / PING_PONG_BUFFER_SLOTS; 1623 u64 pp_input_pointer = cs->ping_pong_buffer.gpu_pointer + input_index * pp_size; 1624 u64 pp_output_pointer = cs->ping_pong_buffer.gpu_pointer + output_index * pp_size; 1625 1626 u64 output_pointer = pp_output_pointer; 1627 u64 input_pointer = (i == 0 && !special_handling) ? rf_pointer : pp_input_pointer; 1628 1629 if (i != 0) gpu_command_pipeline_barrier(cmd, pipeline_memory_barrier); 1630 do_compute_shader(cmd, cp, frame, input_pointer, output_pointer, i, channel_offset); 1631 gpu_command_timestamp(cmd); 1632 } 1633 } 1634 1635 for (u32 i = cp->first_image_shader_index; i < cp->pipeline.shader_count; i++) { 1636 gpu_command_pipeline_barrier(cmd, pipeline_memory_barrier); 1637 do_compute_shader(cmd, cp, frame, 0, 0, i, 0); 1638 gpu_command_timestamp(cmd); 1639 } 1640 1641 u32 wait_info_count = 0; 1642 GPUSemaphoreSignalInfo wait_info = {.semaphore = rf->upload_semaphore}; 1643 if (work->kind == BeamformerWorkKind_WaitThenCompute) { 1644 spin_wait(atomic_load_u64(&rf->insertion_index) <= compute_index); 1645 wait_info.value = atomic_load_u64(rf->upload_complete_values + slot); 1646 wait_info_count = 1; 1647 } 1648 1649 u64 end_timeline_value = gpu_command_list_end(cmd, &wait_info, wait_info_count, 0, 0); 1650 atomic_store_u64(rf->compute_complete_values + slot, end_timeline_value); 1651 1652 if (work->kind == BeamformerWorkKind_WaitThenCompute) 1653 atomic_add_u64(&rf->compute_index, 1); 1654 1655 atomic_store_u64(&frame->timeline_valid_value, end_timeline_value); 1656 1657 Temp scratch; 1658 DeferLoop(scratch = temp_begin(arena), temp_end(scratch)) 1659 { 1660 /* NOTE(rnp): this blocks until work completes */ 1661 u64 count = 0; 1662 u64 *timestamps = gpu_read_timestamps(GPUTimeline_Compute, &count, arena); 1663 1664 i32 steps = ((i32)cp->channel_count / BeamformerChunkChannelCount) - 1; 1665 i32 step = 0; 1666 u32 shader_index = 0; 1667 u64 last_time = count > 0 ? timestamps[0] : 0; 1668 1669 for (u64 i = 1; i < count; i++) { 1670 push_compute_timing_info(ctx->compute_timing_table, (ComputeTimingInfo){ 1671 .kind = ComputeTimingInfoKind_Shader, 1672 .shader = cp->pipeline.shaders[shader_index], 1673 .shader_slot = shader_index, 1674 .timer_count = timestamps[i] - last_time, 1675 }); 1676 last_time = timestamps[i]; 1677 1678 shader_index++; 1679 if (shader_index == cp->first_image_shader_index && step < steps) { 1680 shader_index = 0; 1681 step++; 1682 } 1683 } 1684 } 1685 1686 cs->processing_progress = 1; 1687 1688 //if (has_sum) { 1689 if (0) { 1690 #if 0 1691 u32 aframe_index = ((ctx->averaged_frame_index++) % countof(ctx->averaged_frames)); 1692 ctx->averaged_frames[aframe_index].view_plane_tag = frame->view_plane_tag; 1693 ctx->averaged_frames[aframe_index].ready_to_present = 1; 1694 atomic_store_u64((u64 *)&ctx->latest_frame, (u64)(ctx->averaged_frames + aframe_index)); 1695 #endif 1696 } else { 1697 atomic_store_u64((u64 *)&ctx->latest_frame, (u64)frame); 1698 } 1699 1700 atomic_store_u32(&cs->processing_compute, 0); 1701 1702 push_compute_timing_info(ctx->compute_timing_table, 1703 (ComputeTimingInfo){.kind = ComputeTimingInfoKind_ComputeFrameEnd}); 1704 1705 end_renderdoc_capture(); 1706 }break; 1707 InvalidDefaultCase; 1708 } 1709 } 1710 } 1711 1712 function void 1713 coalesce_timing_table(ComputeTimingTable *t, ComputeShaderStats *stats) 1714 { 1715 /* TODO(rnp): we do not currently do anything to handle the potential for a half written 1716 * info item. this could result in garbage entries but they shouldn't really matter */ 1717 1718 u32 target = atomic_load_u32(&t->write_index); 1719 u32 stats_index = stats->latest_frame_index; 1720 1721 b32 has_rf = 0; 1722 f32 gpu_clocks_to_nano = 1.0e-9f * gpu_info()->timestamp_period_ns; 1723 1724 // NOTE(rnp): not equal (the index may wrap) 1725 while (t->read_index != target) { 1726 ComputeTimingInfo info = t->buffer[t->read_index % countof(t->buffer)]; 1727 switch (info.kind) { 1728 1729 case ComputeTimingInfoKind_ComputeFrameBegin:{ 1730 assert(t->compute_frame_active == 0); 1731 t->compute_frame_active = 1; 1732 /* NOTE(rnp): allow multiple instances of same shader to accumulate */ 1733 t->in_flight_shader_count = 0; 1734 memory_clear(t->in_flight_shader_ids, 0, sizeof(t->in_flight_shader_ids)); 1735 memory_clear(stats->table.times[stats_index], 0, sizeof(stats->table.times[stats_index])); 1736 }break; 1737 1738 case ComputeTimingInfoKind_ComputeFrameEnd:{ 1739 assert(t->compute_frame_active == 1); 1740 t->compute_frame_active = 0; 1741 stats_index = stats->latest_frame_index = (stats_index + 1) % countof(stats->table.times); 1742 stats->table.shader_count = t->in_flight_shader_count; 1743 memory_copy(stats->table.shader_ids, t->in_flight_shader_ids, sizeof(t->in_flight_shader_ids)); 1744 }break; 1745 1746 case ComputeTimingInfoKind_Shader:{ 1747 t->in_flight_shader_count = Max(t->in_flight_shader_count, info.shader_slot + 1u); 1748 t->in_flight_shader_ids[info.shader_slot] = info.shader; 1749 stats->table.times[stats_index][info.shader_slot] += info.timer_count * gpu_clocks_to_nano; 1750 }break; 1751 1752 case ComputeTimingInfoKind_RF_Data:{ 1753 stats->latest_rf_index = (stats->latest_rf_index + 1) % countof(stats->table.rf_time_deltas); 1754 f32 delta = info.timer_count / (f32)os_system_info()->timer_frequency; 1755 stats->table.rf_time_deltas[stats->latest_rf_index] = delta; 1756 has_rf = 1; 1757 }break; 1758 } 1759 /* NOTE(rnp): do this at the end so that stats table is always in a consistent state */ 1760 t->read_index++; 1761 } 1762 1763 for (u32 i = 0; i < stats->table.shader_count; i++) { 1764 f32 sum = 0; 1765 for EachElement(stats->table.times, it) 1766 sum += stats->table.times[it][i]; 1767 stats->average_times[i] = sum / countof(stats->table.times); 1768 } 1769 1770 if (has_rf) { 1771 f32 sum = 0; 1772 for EachElement(stats->table.rf_time_deltas, i) 1773 sum += stats->table.rf_time_deltas[i]; 1774 stats->rf_time_delta_average = sum / countof(stats->table.rf_time_deltas); 1775 } 1776 } 1777 1778 DEBUG_EXPORT BEAMFORMER_COMPLETE_COMPUTE_FN(beamformer_complete_compute) 1779 { 1780 BeamformerSharedMemory *sm = ctx->shared_memory; 1781 complete_queue(ctx, &sm->external_work_queue, arena); 1782 complete_queue(ctx, ctx->beamform_work_queue, arena); 1783 } 1784 1785 DEBUG_EXPORT BEAMFORMER_RF_UPLOAD_FN(beamformer_rf_upload) 1786 { 1787 BeamformerSharedMemory *sm = ctx->shared_memory; 1788 BeamformerSharedMemoryLockKind scratch_lock = BeamformerSharedMemoryLockKind_ScratchSpace; 1789 BeamformerSharedMemoryLockKind upload_lock = BeamformerSharedMemoryLockKind_UploadRF; 1790 1791 u64 rf_block_rf_size; 1792 if (atomic_load_u32(sm->locks + upload_lock) && 1793 (rf_block_rf_size = atomic_swap_u64(&sm->rf_block_rf_size, 0))) 1794 { 1795 beamformer_shared_memory_take_lock(ctx->shared_memory, (i32)scratch_lock, (u32)-1); 1796 1797 BeamformerRFBuffer *rf = ctx->rf_buffer; 1798 1799 rf->active_rf_size = gpu_round_up_to_sync_size(rf_block_rf_size & 0x00FFFFFFFFFFFFFFull, 64); 1800 if unlikely((u64)rf->buffer.size < countof(rf->upload_complete_values) * rf->active_rf_size) { 1801 gpu_buffer_allocate(&rf->buffer, (GPUBufferAllocateInfo){ 1802 .size = countof(rf->upload_complete_values) * rf->active_rf_size, 1803 .flags = GPUUsageFlag_HostWrite, 1804 .label = str8("RawRFBuffer"), 1805 .timelines_used = (GPUTimeline []){GPUTimeline_Compute}, 1806 .timeline_count = 1, 1807 }); 1808 } 1809 1810 u64 slot = rf->insertion_index % countof(rf->upload_complete_values); 1811 1812 /* NOTE(rnp): don't overwrite slot if the compute thread hasn't processed it */ 1813 spin_wait(rf->insertion_index - atomic_load_u64(&rf->compute_index) >= countof(rf->upload_complete_values)); 1814 gpu_host_wait_timeline(GPUTimeline_Compute, rf->compute_complete_values[slot], -1ULL); 1815 1816 assert((ctx->shared_memory_size % os_system_info()->page_size) == 0 && 1817 (os_system_info()->page_size % gpu_round_up_to_sync_size(1, 64)) == 0); 1818 1819 u64 wait_value = gpu_semaphore_value(rf->upload_semaphore) + 1; 1820 atomic_store_u64(rf->upload_complete_values + slot, wait_value); 1821 atomic_add_u64(&rf->insertion_index, 1); 1822 1823 void *host_pointer = gpu_buffer_host_pointer(&rf->buffer); 1824 memory_copy_non_temporal((u8 *)host_pointer + slot * rf->active_rf_size, 1825 beamformer_shared_memory_data_pointer(sm, ctx->shared_memory_size), 1826 rf->active_rf_size); 1827 store_fence(); 1828 1829 GPUSemaphoreSignalInfo signal_info = {.semaphore = rf->upload_semaphore, .value = wait_value}; 1830 gpu_buffer_make_visible(&rf->buffer, slot * rf->active_rf_size, rf->active_rf_size, &signal_info, 1); 1831 1832 beamformer_shared_memory_release_lock(ctx->shared_memory, (i32)scratch_lock); 1833 post_sync_barrier(ctx->shared_memory, upload_lock); 1834 os_wake_all_waiters(ctx->compute_worker_sync); 1835 1836 u64 current_time = os_timer_count(); 1837 push_compute_timing_info(ctx->compute_timing_table, (ComputeTimingInfo){ 1838 .kind = ComputeTimingInfoKind_RF_Data, 1839 .timer_count = current_time - rf->timestamp, 1840 }); 1841 rf->timestamp = current_time; 1842 } 1843 } 1844 1845 function void 1846 beamformer_queue_compute(BeamformerCtx *ctx, BeamformerComputeWorkContext cw) 1847 { 1848 BeamformerSharedMemory *sm = ctx->shared_memory; 1849 BeamformerSharedMemoryLockKind dispatch_lock = BeamformerSharedMemoryLockKind_DispatchCompute; 1850 if (!sm->live_imaging_parameters.active && beamformer_shared_memory_take_lock(sm, (i32)dispatch_lock, 0)) 1851 { 1852 BeamformWork *work = beamform_work_queue_push(ctx->beamform_work_queue); 1853 if (work) { 1854 work->kind = BeamformerWorkKind_Compute; 1855 memory_copy(&work->compute_context, &cw, sizeof(cw)); 1856 beamform_work_queue_push_commit(ctx->beamform_work_queue); 1857 } 1858 } 1859 os_wake_all_waiters(&ctx->compute_worker.sync_variable); 1860 } 1861 1862 #include "ui.c" 1863 1864 function void 1865 beamformer_process_input_events(BeamformerCtx *ctx, BeamformerInput *input, 1866 BeamformerInputEvent *events, u32 event_count) 1867 { 1868 for (u32 index = 0; index < event_count; index++) { 1869 BeamformerInputEvent *event = events + index; 1870 switch (event->kind) { 1871 1872 // NOTE(rnp): ui will handle these 1873 case BeamformerInputEventKind_ButtonPress: 1874 case BeamformerInputEventKind_ButtonRelease: 1875 case BeamformerInputEventKind_MouseScroll: 1876 case BeamformerInputEventKind_WindowResize: 1877 {}break; 1878 1879 case BeamformerInputEventKind_ExecutableReload:{ 1880 ui_init(ctx, ctx->ui_arena); 1881 }break; 1882 1883 case BeamformerInputEventKind_FileEvent:{ 1884 BeamformerFileReloadContext *frc = event->file_watch_user_context; 1885 switch (frc->kind) { 1886 case BeamformerFileReloadKind_ComputeShader:{ 1887 for EachElement(ctx->compute_context.compute_plans, block) { 1888 BeamformerComputePlan *cp = ctx->compute_context.compute_plans[block]; 1889 for (u32 slot = 0; cp && slot < cp->pipeline.shader_count; slot++) { 1890 i32 shader_index = beamformer_shader_reloadable_index_by_shader[cp->pipeline.shaders[slot]]; 1891 if (beamformer_reloadable_shader_kinds[shader_index] == frc->shader_reload.shader) 1892 atomic_or_u32(&cp->dirty_programs, 1 << slot); 1893 } 1894 } 1895 1896 // TODO(rnp): track latest parameter block 1897 if (ctx->latest_frame) { 1898 BeamformerFrame *f = ctx->latest_frame; 1899 beamformer_queue_compute(ctx, (BeamformerComputeWorkContext){ 1900 .voxel_transform = f->voxel_transform, 1901 .rf_offset = 0, 1902 .focus_depth = f->focus_depth, 1903 .steering_angle = f->steering_angle, 1904 .tx_rx_orientation = f->transmit_receive_orientation, 1905 .parameter_set = 0, 1906 .view_plane = f->view_plane_tag, 1907 }); 1908 } 1909 }break; 1910 1911 case BeamformerFileReloadKind_RenderShader:{ 1912 beamformer_reload_render_pipeline(frc->shader_reload.pipeline, frc->shader_reload.shader, ctx->arena); 1913 ctx->render_shader_updated = 1; 1914 }break; 1915 1916 InvalidDefaultCase; 1917 } 1918 }break; 1919 1920 InvalidDefaultCase; 1921 } 1922 } 1923 } 1924 1925 function void 1926 beamformer_panel_group_insert_at(BeamformerUIPanel *group, BeamformerUIPanel *tab, u64 new_child_index) 1927 { 1928 if (tab->parent) beamformer_ui_panel_unlink(tab); 1929 new_child_index = Min(new_child_index, group->child_count); 1930 1931 tab->parent = group; 1932 group->child_count++; 1933 if (group->kind == BeamformerPanelKind_TabGroup) group->u.tab_focus = tab; 1934 1935 BeamformerUIPanel *previous_sibling = new_child_index == 0 ? 0 : group->first_child; 1936 for (u64 child_index = 1; child_index < new_child_index; child_index++) 1937 previous_sibling = previous_sibling->next_sibling; 1938 1939 if (previous_sibling) { 1940 tab->previous_sibling = previous_sibling; 1941 tab->next_sibling = previous_sibling->next_sibling; 1942 if (tab->next_sibling) tab->next_sibling->previous_sibling = tab; 1943 previous_sibling->next_sibling = tab; 1944 if (previous_sibling == group->last_child) group->last_child = tab; 1945 } else { 1946 DLLInsertFirst(0, group->first_child, group->last_child, tab, next_sibling, previous_sibling); 1947 } 1948 } 1949 1950 BEAMFORMER_EXPORT void 1951 beamformer_frame_step(void *memory, BeamformerInput *input) 1952 { 1953 BeamformerCtx *ctx = beamformer_context = memory; 1954 beamformer_input = input; 1955 1956 u64 current_time = os_timer_count(); 1957 dt_for_frame = (f64)(current_time - ctx->frame_timestamp) / os_system_info()->timer_frequency; 1958 ctx->frame_timestamp = current_time; 1959 ctx->frame_index++; 1960 1961 coalesce_timing_table(ctx->compute_timing_table, ctx->compute_shader_stats); 1962 1963 // NOTE(rnp): reset frame state 1964 { 1965 ctx->registers = &ctx->base_registers; 1966 swap(ctx->command_queues[0], ctx->command_queues[1]); 1967 zero_struct(ctx->command_queues + 0); 1968 //zero_struct(ctx->registers); 1969 arena_clear(beamformer_frame_arena()); 1970 } 1971 1972 beamformer_process_input_events(ctx, input, input->event_queue, input->event_count); 1973 1974 BeamformerSharedMemory *sm = ctx->shared_memory; 1975 u32 live_imaging_active = atomic_load_u32(&sm->live_imaging_parameters.active); 1976 if (live_imaging_active != ctx->live_imaging_active) { 1977 if (ctx->live_imaging_active) { 1978 if (ctx->auto_live_control_panel) { 1979 BeamformerUIPanel *parent = ctx->auto_live_control_panel->parent; 1980 beamformer_command(beamformer_command_infos[BeamformerCommandKind_CloseTab].string, .tree_node = (u64)ctx->auto_live_control_panel); 1981 if (parent->child_count == 1) 1982 beamformer_command(beamformer_command_infos[BeamformerCommandKind_CloseTab].string, .tree_node = (u64)parent); 1983 } 1984 } else { 1985 if (beamformer_registers()->live_controls) { 1986 beamformer_command(beamformer_command_infos[BeamformerCommandKind_FocusTab].string, 1987 .tree_node = beamformer_registers()->live_controls); 1988 } else { 1989 ctx->auto_live_control_panel = beamformer_ui_push_panel(0, BeamformerPanelKind_LiveImagingControls); 1990 beamformer_command(beamformer_command_infos[BeamformerCommandKind_SplitTree].string, 1991 .tree_node = (u64)ctx->auto_live_control_panel, 1992 .split_axis = Axis2_X, 1993 .split_left_tree = (u64)ui_context->tree, 1994 .split_right_tree = 0, 1995 .drop_target_tree = (u64)ui_context->tree); 1996 } 1997 ctx->live_imaging_active_frame = ctx->frame_index; 1998 } 1999 ctx->live_imaging_active = live_imaging_active; 2000 } 2001 2002 if (atomic_load_u32(sm->locks + BeamformerSharedMemoryLockKind_UploadRF)) 2003 os_wake_all_waiters(&ctx->upload_worker.sync_variable); 2004 if (atomic_load_u32(sm->locks + BeamformerSharedMemoryLockKind_DispatchCompute)) 2005 os_wake_all_waiters(&ctx->compute_worker.sync_variable); 2006 2007 beamformer_registers()->frame = (u64)(ctx->latest_frame - ctx->compute_context.backlog.frames); 2008 2009 beamformer_ui_frame(); 2010 2011 // NOTE(rnp): execute commands 2012 for (BeamformerCommandNode *node = ctx->command_queues[0].first; 2013 node; 2014 node = node == node->next ? 0 : node->next) 2015 { 2016 BeamformerRegistersScope() 2017 { 2018 memory_copy(beamformer_registers(), node->command.registers, sizeof(*node->command.registers)); 2019 BeamformerCommandKind kind = beamformer_command_kind_from_string(node->command.name); 2020 switch (kind) { 2021 InvalidDefaultCase; 2022 case BeamformerCommandKind_CloseTab:{ 2023 BeamformerUIPanel *tab = (BeamformerUIPanel *)beamformer_registers()->tree_node; 2024 ui_kill_panel(tab); 2025 }break; 2026 2027 case BeamformerCommandKind_FocusTab:{ 2028 BeamformerUIPanel *tab = (BeamformerUIPanel *)beamformer_registers()->tree_node; 2029 assert(tab->parent->kind == BeamformerPanelKind_TabGroup); 2030 tab->parent->u.tab_focus = tab; 2031 }break; 2032 2033 case BeamformerCommandKind_MoveTab:{ 2034 BeamformerUIPanel *move = (BeamformerUIPanel *)beamformer_registers()->tree_node; 2035 BeamformerUIPanel *group = (BeamformerUIPanel *)beamformer_registers()->drop_target_tree; 2036 BeamformerUIPanel *parent = move->parent; 2037 u64 new_child_index = beamformer_registers()->drop_child_index; 2038 beamformer_panel_group_insert_at(group, move, new_child_index); 2039 2040 if (move->kind == BeamformerPanelKind_LiveImagingControls) { 2041 beamformer_context->base_registers.v.live_controls = (u64)move; 2042 if (move == ctx->auto_live_control_panel) 2043 ctx->auto_live_control_panel = 0; 2044 } 2045 2046 if (parent->child_count == 0) 2047 beamformer_command(beamformer_command_infos[BeamformerCommandKind_CloseTab].string, .tree_node = (u64)parent); 2048 }break; 2049 2050 case BeamformerCommandKind_OpenTab:{ 2051 BeamformerUIPanel *panel = (BeamformerUIPanel *)beamformer_registers()->tree_node; 2052 assert(panel->kind == BeamformerPanelKind_TabGroup); 2053 2054 BeamformerPanelKind new_panel_kind = beamformer_panel_kind_from_string(beamformer_registers()->string); 2055 beamformer_ui_push_panel(panel, new_panel_kind); 2056 }break; 2057 2058 case BeamformerCommandKind_SplitTree:{ 2059 BeamformerUIPanel *drag = (BeamformerUIPanel *)beamformer_registers()->tree_node; 2060 BeamformerUIPanel *left = (BeamformerUIPanel *)beamformer_registers()->split_left_tree; 2061 BeamformerUIPanel *right = (BeamformerUIPanel *)beamformer_registers()->split_right_tree; 2062 Axis2 axis = beamformer_registers()->split_axis; 2063 2064 BeamformerUIPanel *new_split = beamformer_ui_push_panel(0, BeamformerPanelKind_Split); 2065 BeamformerUIPanel *new_tab_group = beamformer_ui_push_panel(0, BeamformerPanelKind_TabGroup); 2066 beamformer_panel_group_insert_at(new_tab_group, drag, 0); 2067 2068 BeamformerUIPanel *target = 0; 2069 u32 target_child_index = 0; 2070 f32 new_split_pct = 0.5f; 2071 2072 if (left == 0 || right == 0) { 2073 // NOTE(rnp): split on edge of window 2074 target = left ? left : right; 2075 target_child_index = left ? 0 : 1; 2076 2077 if (target->kind == BeamformerPanelKind_TabGroup) { 2078 new_split->kind = BeamformerPanelKind_TabGroup; 2079 new_split->u.tab_focus = target->u.tab_focus; 2080 } 2081 2082 for (BeamformerUIPanel *child = target->last_child, *next; child; child = next) { 2083 next = child->previous_sibling; 2084 beamformer_panel_group_insert_at(new_split, child, 0); 2085 } 2086 2087 beamformer_panel_group_insert_at(target, new_tab_group, 0); 2088 } else if (((drag == left) && right->kind == BeamformerPanelKind_Split) || 2089 ((drag == right) && left->kind == BeamformerPanelKind_Split)) 2090 { 2091 // NOTE(rnp): split on internal split 2092 target = left == drag ? right : left; 2093 target_child_index = 1; 2094 new_split_pct = 1.f / 3.f; 2095 beamformer_panel_group_insert_at(new_split, new_tab_group, 0); 2096 beamformer_panel_group_insert_at(new_split, target->last_child, 1); 2097 } else { 2098 // NOTE(rnp): TabGroup Split 2099 target = left == drag ? right : left; 2100 target_child_index = left == drag ? 1 : 0; 2101 assert(target->kind == BeamformerPanelKind_TabGroup); 2102 2103 BeamformerUIPanel *focus = target->u.tab_focus; 2104 new_split->kind = BeamformerPanelKind_TabGroup; 2105 for (BeamformerUIPanel *child = target->last_child, *next; child; child = next) { 2106 next = child->previous_sibling; 2107 beamformer_panel_group_insert_at(new_split, child, 0); 2108 } 2109 new_split->u.tab_focus = focus; 2110 2111 beamformer_panel_group_insert_at(target, new_tab_group, 0); 2112 } 2113 2114 beamformer_panel_group_insert_at(target, new_split, target_child_index); 2115 if (target->kind == BeamformerPanelKind_Split) { 2116 new_split->u.split.axis = target->u.split.axis; 2117 new_split->u.split.fraction = target->u.split.fraction; 2118 } 2119 target->kind = BeamformerPanelKind_Split; 2120 target->u.split.axis = axis; 2121 target->u.split.fraction = new_split_pct; 2122 }break; 2123 2124 } 2125 } 2126 } 2127 2128 ctx->render_shader_updated = 0; 2129 }