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