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