beamformer_core.c (78712B)
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 // NOTE(rnp): ensure last node descriptor gets proper values for output data kind 592 if (graph.last->output_data_kind == BeamformerDataKind_Count) 593 graph.last->output_data_kind = graph.last->input_data_kind; 594 595 f32 time_offset = pb->parameters.time_offset; 596 u32 subgroup_size = vk_gpu_info()->subgroup_size; 597 598 cp->first_image_shader_index = 0; 599 cp->pipeline.shader_count = 0; 600 601 for (BeamformerComputeGraphNode *node = root_node->next; node; node = node->next) { 602 assert(node->prev->output_data_kind == node->input_data_kind); 603 assert(bv3_all(iv3_equal(node->prev->output_stride, node->input_stride))); 604 605 BeamformerShaderParameters *sp = 0; 606 if (node->user_pipeline_index >= 0) 607 sp = pb->pipeline.parameters + node->user_pipeline_index; 608 609 if (compute_plan_push_shader(cp, node, sp)) { 610 BeamformerShaderDescriptor *sd = cp->shader_descriptors + cp->pipeline.shader_count - 1; 611 612 switch (node->kind) { 613 case BeamformerShaderKind_Decode:{ 614 BeamformerDecodeBakeParameters *db = &sd->bake.Decode; 615 616 u32 decode_sample_count = input_sample_count; 617 db->DecodeMode = pb->parameters.decode_mode; 618 db->TransmitCount = pb->parameters.acquisition_count; 619 db->ChunkChannelCount = chunk_channel_count; 620 621 // NOTE(rnp): ignored when using coop matrices 622 db->OutputSampleStride = node->output_stride.x; 623 db->OutputChannelStride = node->output_stride.y; 624 db->OutputTransmitStride = node->output_stride.z; 625 626 db->ToProcess = 1; 627 628 b32 use_coop_matrix = vk_gpu_info()->cooperative_matrix && 629 node->input_data_kind == BeamformerDataKind_Float16 && 630 (db->TransmitCount % 16 == 0) && 631 (chunk_channel_count % 16 == 0); 632 if (use_coop_matrix) { 633 // TODO(rnp): shared memory for larger sizes 634 sd->layout = (uv3){{subgroup_size, 1, 1}}; 635 636 if (demodulate) 637 decode_sample_count *= 2; 638 639 sd->compile_flags |= BeamformerDecodeCompileFlags_CooperativeMatrix; 640 db->CooperativeMatrixM = 16; 641 db->CooperativeMatrixN = 16; 642 db->CooperativeMatrixK = 16; 643 644 sd->dispatch.x = db->TransmitCount / db->CooperativeMatrixN; 645 sd->dispatch.y = chunk_channel_count / db->CooperativeMatrixM; 646 sd->dispatch.z = decode_sample_count; 647 } else if (db->TransmitCount > 40) { 648 db->UseSharedMemory = 1; 649 650 if (db->TransmitCount == 48) 651 db->ToProcess = db->TransmitCount / 16; 652 653 b32 use_16x = db->TransmitCount == 48 || db->TransmitCount == 80 || 654 db->TransmitCount == 96 || db->TransmitCount == 160; 655 sd->layout.x = use_16x ? 16 : 32; 656 sd->layout.y = 4; 657 sd->layout.z = 1; 658 659 sd->dispatch.x = (u32)ceil_f32((f32)pb->parameters.acquisition_count / (f32)sd->layout.x / (f32)db->ToProcess); 660 sd->dispatch.y = (u32)ceil_f32((f32)chunk_channel_count / (f32)sd->layout.y); 661 sd->dispatch.z = (u32)ceil_f32((f32)decode_sample_count / (f32)sd->layout.z); 662 } else { 663 /* NOTE(rnp): register caching. using more threads will cause the compiler to do 664 * contortions to avoid spilling registers. using less gives higher performance */ 665 sd->layout = (uv3){{subgroup_size / 2, 1, 1}}; 666 667 sd->dispatch.x = (u32)ceil_f32((f32)decode_sample_count / (f32)sd->layout.x); 668 sd->dispatch.y = (u32)ceil_f32((f32)chunk_channel_count / (f32)sd->layout.y); 669 sd->dispatch.z = 1; 670 } 671 }break; 672 673 case BeamformerShaderKind_Demodulate: 674 case BeamformerShaderKind_Filter: 675 { 676 b32 demod = node->kind == BeamformerShaderKind_Demodulate; 677 BeamformerFilter *f = cp->filters + sp->filter_slot; 678 679 sd->compile_flags |= BeamformerFilterCompileFlags_Demodulate * demod; 680 sd->compile_flags |= BeamformerFilterCompileFlags_ComplexFilter * f->parameters.complex; 681 682 time_offset += f->time_delay; 683 684 BeamformerFilterBakeParameters *fb = &sd->bake.Filter; 685 fb->FilterLength = (u32)f->length; 686 687 fb->SampleCount = input_sample_count; 688 fb->DecimationRate = demod ? decimation_rate : 1; 689 690 b32 deinterleave = beamformer_data_kind_complex[node->input_data_kind] && 691 !beamformer_data_kind_complex[node->output_data_kind]; 692 if (deinterleave) 693 fb->BatchSampleCount = chunk_channel_count * input_sample_count * pb->parameters.acquisition_count; 694 695 fb->OutputSampleStride = node->output_stride.x; 696 fb->OutputChannelStride = node->output_stride.y; 697 fb->OutputTransmitStride = node->output_stride.z; 698 699 fb->InputSampleStride = node->input_stride.x; 700 fb->InputChannelStride = node->input_stride.y; 701 fb->InputTransmitStride = node->input_stride.z; 702 703 /* NOTE(rnp): when we are demodulating we pretend that the sampler was alternating 704 * between sampling the I portion and the Q portion of an IQ signal. Therefore there 705 * is an implicit decimation factor of 2 which must always be included. All code here 706 * assumes that the signal was sampled in such a way that supports this operation. 707 * To recover IQ[n] from the sampled data (RF[n]) we do the following: 708 * I[n] = RF[n] 709 * Q[n] = RF[n + 1] 710 * IQ[n] = I[n] - j*Q[n] 711 */ 712 if (demod) { 713 fb->DemodulationFrequency = pb->parameters.demodulation_frequency; 714 fb->SamplingFrequency = pb->parameters.sampling_frequency / 2; 715 } 716 717 sd->layout = (uv3){{subgroup_size, 1, 1}}; 718 sd->dispatch.x = (u32)ceil_f32((f32)input_sample_count / (f32)sd->layout.x); 719 sd->dispatch.y = (u32)ceil_f32((f32)chunk_channel_count / (f32)sd->layout.y); 720 sd->dispatch.z = (u32)ceil_f32((f32)pb->parameters.acquisition_count / (f32)sd->layout.z); 721 }break; 722 723 case BeamformerShaderKind_DAS:{ 724 cp->first_image_shader_index = cp->pipeline.shader_count; 725 726 BeamformerDASBakeParameters *db = &sd->bake.DAS; 727 db->SamplingFrequency = sampling_frequency; 728 db->DemodulationFrequency = pb->parameters.demodulation_frequency; 729 db->SpeedOfSound = pb->parameters.speed_of_sound; 730 db->TimeOffset = time_offset; 731 db->FNumber = pb->parameters.f_number; 732 db->AcquisitionKind = pb->parameters.acquisition_kind; 733 db->SampleCount = input_sample_count; 734 db->ChannelCount = pb->parameters.channel_count; 735 db->AcquisitionCount = pb->parameters.acquisition_count; 736 db->ChunkChannelCount = chunk_channel_count; 737 db->InterpolationMode = pb->parameters.interpolation_mode; 738 db->TransmitAngle = pb->parameters.focal_vector.E[0]; 739 db->FocusDepth = pb->parameters.focal_vector.E[1]; 740 db->TransmitReceiveOrientation = pb->parameters.transmit_receive_orientation; 741 742 // NOTE(rnp): old gcc will miscompile an assignment 743 memory_copy(cp->xdc_transform.E, pb->parameters.xdc_transform.E, sizeof(cp->xdc_transform)); 744 745 cp->voxel_transform = m4_mul(cp->ui_voxel_transform, pb->parameters.das_voxel_transform); 746 cp->xdc_element_pitch = pb->parameters.xdc_element_pitch; 747 748 memory_copy(cp->das_voxel_transform.E, cp->voxel_transform.E, sizeof(cp->voxel_transform)); 749 750 u32 id = pb->parameters.acquisition_kind; 751 if (id == BeamformerAcquisitionKind_UFORCES || id == BeamformerAcquisitionKind_FORCES) 752 cp->das_voxel_transform = m4_mul(cp->xdc_transform, cp->das_voxel_transform); 753 754 db->Sparse = id == BeamformerAcquisitionKind_UFORCES || id == BeamformerAcquisitionKind_UHERCULES; 755 db->SingleFocus = pb->parameters.single_focus; 756 db->SingleOrientation = pb->parameters.single_orientation; 757 758 sd->compile_flags |= BeamformerDASCompileFlags_CoherencyWeighting * pb->parameters.coherency_weighting; 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_CoherencyWeighting:{ 764 sd->layout = layout_for_output(cp->output_points); 765 sd->dispatch = dispatch_for_output(sd->layout, cp->output_points); 766 }break; 767 768 case BeamformerShaderKind_Reshape:{ 769 BeamformerReshapeBakeParameters *rb = &sd->bake.Reshape; 770 b32 deinterleave = beamformer_data_kind_complex[node->input_data_kind] && 771 !beamformer_data_kind_complex[node->output_data_kind]; 772 b32 interleave = !beamformer_data_kind_complex[node->input_data_kind] && 773 beamformer_data_kind_complex[node->output_data_kind]; 774 assert(interleave == 0 || (interleave != deinterleave)); 775 sd->compile_flags |= BeamformerReshapeCompileFlags_Deinterleave * deinterleave; 776 sd->compile_flags |= BeamformerReshapeCompileFlags_Interleave * interleave; 777 778 rb->InputStrideX = node->input_stride.x; 779 rb->InputStrideY = node->input_stride.y; 780 rb->InputStrideZ = node->input_stride.z; 781 rb->OutputStrideX = node->output_stride.x; 782 rb->OutputStrideY = node->output_stride.y; 783 rb->OutputStrideZ = node->output_stride.z; 784 785 // NOTE(rnp): order doesn't really matter here but it must match the dispatch layout 786 rb->SizeX = input_sample_count; 787 rb->SizeY = chunk_channel_count; 788 rb->SizeZ = acquisition_count; 789 790 sd->layout.x = 1; 791 sd->layout.z = Min(subgroup_size, rb->SizeZ); 792 sd->layout.y = subgroup_size / sd->layout.z; 793 794 sd->dispatch.x = (u32)(ceil_f32((f32)rb->SizeX / sd->layout.x)); 795 sd->dispatch.y = (u32)(ceil_f32((f32)rb->SizeY / sd->layout.y)); 796 sd->dispatch.z = (u32)(ceil_f32((f32)rb->SizeZ / sd->layout.z)); 797 }break; 798 799 default:{}break; 800 801 #if 0 802 case BeamformerShaderKind_Sum:{ 803 sd->bake.data_kind = BeamformerDataKind_Float32; 804 if (cp->iq_pipeline) 805 sd->bake.data_kind = BeamformerDataKind_Float32Complex; 806 807 sd->layout = layout_for_output(cp->output_points); 808 sd->dispatch = dispatch_for_output(sd->layout, cp->output_points); 809 810 commit = 1; 811 }break; 812 #endif 813 814 } 815 } 816 } 817 818 cp->pipeline.data_kind = input_data_kind; 819 820 if (cp->first_image_shader_index == 0) 821 cp->first_image_shader_index = cp->pipeline.shader_count; 822 } 823 824 function void 825 stream_append_shader_header(Stream *s, i32 reloadable_index, BeamformerShaderDescriptor *sd, uv3 layout) 826 { 827 stream_append_str8(s, str8("#version 460 core\n\n" 828 "#extension GL_EXT_buffer_reference : require\n" 829 "#extension GL_EXT_shader_16bit_storage : require\n" 830 "#extension GL_EXT_shader_explicit_arithmetic_types : require\n\n" 831 "#define f32 float32_t\n" 832 "#define f16 float16_t\n" 833 "#define s32 int32_t\n" 834 "#define u64 uint64_t\n" 835 "#define u32 uint32_t\n" 836 "#define s16 int16_t\n" 837 "#define u16 uint16_t\n" 838 "#define s32vec2 i32vec2\n" 839 "#define s16vec2 i16vec2\n" 840 "\n")); 841 842 i32 header_vector_length = beamformer_shader_header_vector_lengths[reloadable_index]; 843 i32 *header_vector = beamformer_shader_header_vectors[reloadable_index]; 844 for (i32 index = 0; index < header_vector_length; index++) 845 stream_append_str8(s, beamformer_shader_global_header_strings[header_vector[index]]); 846 847 if (layout.x != 0) { 848 stream_append_str8(s, str8("layout(local_size_x = ")); 849 stream_append_u64(s, layout.x); 850 stream_append_str8(s, str8(", local_size_y = ")); 851 stream_append_u64(s, layout.y); 852 stream_append_str8(s, str8(", local_size_z = ")); 853 stream_append_u64(s, layout.z); 854 stream_append_str8(s, str8(") in;\n\n")); 855 } 856 857 { 858 u32 max_length = 0; 859 for EachElement(beamformer_data_kind_str8, it) 860 max_length = Max(max_length, (u32)beamformer_data_kind_str8[it].length); 861 862 for EachElement(beamformer_data_kind_str8, it) { 863 stream_append_str8s(s, str8("#define DataKind_"), beamformer_data_kind_str8[it]); 864 stream_pad(s, ' ', max_length - beamformer_data_kind_str8[it].length + 1); 865 stream_append_u64(s, it); 866 stream_append_byte(s, '\n'); 867 } 868 stream_append_byte(s, '\n'); 869 } 870 871 if (sd) { 872 BeamformerDataKind data_kinds[] = {sd->input_data_kind, sd->output_data_kind}; 873 str8 line_prefixes[] = {str8_comp("Input"), str8_comp("Output")}; 874 for EachElement(data_kinds, it) { 875 if (data_kinds[it] != BeamformerDataKind_Count) { 876 stream_append_str8s(s, str8("#define "), line_prefixes[it], str8("DataType "), 877 beamformer_data_kind_glsl_type[data_kinds[it]], 878 str8("\n#define "), line_prefixes[it], str8("DataKind DataKind_"), 879 beamformer_data_kind_str8[data_kinds[it]], 880 str8("\n#define "), line_prefixes[it], str8("DataKindByteSize ")); 881 stream_append_u64(s, beamformer_data_kind_byte_size[data_kinds[it]]); 882 stream_append_byte(s, '\n'); 883 } 884 } 885 stream_append_byte(s, '\n'); 886 887 stream_append_str8(s, str8("#define CompileFlags (0x")); 888 stream_append_hex_u64_width(s, sd->compile_flags, 8); 889 stream_append_str8(s, str8(")\n")); 890 891 i32 struct_id = beamformer_base_shader_to_bake_struct_id[reloadable_index]; 892 if (struct_id != -1) { 893 str8 *names = meta_struct_member_names_by_id[struct_id]; 894 MetaStructInfo *si = meta_struct_info_by_id + struct_id; 895 MetaStructMember *sm = meta_struct_members_by_id[struct_id]; 896 for (u32 index = 0; index < si->member_count; index++) { 897 str8 type = meta_kind_glsl_types[sm[index].type_id]; 898 stream_append_str8(s, str8("layout(constant_id = ")); 899 stream_append_u64(s, index); 900 stream_append_str8s(s, str8(") const "), type, str8(" "), names[index], str8(" = "), type, str8("(1);\n")); 901 } 902 } 903 } 904 905 if (!renderdoc_attached()) 906 stream_append_str8(s, str8("\n\n#line 1\n")); 907 } 908 909 function void 910 beamformer_reload_pipeline(VulkanHandle *pipeline, BeamformerShaderReloadInfo *sris, u32 count, Arena arena) 911 { 912 assume(count <= 2); 913 str8 paths[2]; 914 VulkanPipelineCreateInfo infos[2]; 915 916 if (!BakeShaders) { 917 for (u32 i = 0; i < count; i++) 918 paths[i] = push_str8_from_parts(&arena, os_path_separator(), str8("shaders"), sris[i].filename_or_data); 919 } 920 921 u32 push_constants_size = 0; 922 for (u32 i = 0; i < count; i++) { 923 Stream shader_stream = arena_stream(arena); 924 i32 reloadable_index = beamformer_shader_reloadable_index_by_shader[sris[i].shader]; 925 if (i == 0) push_constants_size = beamformer_shader_push_constant_sizes[reloadable_index]; 926 else assert(push_constants_size == beamformer_shader_push_constant_sizes[reloadable_index]); 927 928 stream_append_shader_header(&shader_stream, reloadable_index, sris[i].shader_descriptor, sris[i].layout); 929 930 if (BakeShaders) { 931 stream_append_str8(&shader_stream, sris[i].filename_or_data); 932 } else { 933 shader_stream.widx += os_read_entire_file((c8 *)paths[i].data, 934 shader_stream.data + shader_stream.widx, 935 shader_stream.cap - shader_stream.widx); 936 } 937 938 infos[i].kind = sris[i].shader_kind; 939 infos[i].text = arena_stream_commit_zero(&arena, &shader_stream); 940 infos[i].name = beamformer_shader_names[sris[i].shader]; 941 infos[i].specialization_data = sris[i].shader_descriptor ? &sris[i].shader_descriptor->bake : 0; 942 infos[i].specialization_struct_id = beamformer_base_shader_to_bake_struct_id[reloadable_index]; 943 944 //str8 line = str8("---------------\n"); 945 //str8 nl = str8("\n"); 946 //os_console_log(line.data, line.length); 947 //os_console_log(infos[i].name.data, infos[i].name.length); 948 //os_console_log(nl.data, nl.length); 949 //os_console_log(line.data, line.length); 950 //os_console_log(infos[i].text.data, infos[i].text.length); 951 //os_console_log(line.data, line.length); 952 } 953 954 vk_pipeline_release(*pipeline); 955 *pipeline = vk_pipeline(infos, count, push_constants_size); 956 } 957 958 function void 959 beamformer_reload_render_pipeline(VulkanHandle *pipeline, BeamformerShaderKind shader, Arena arena) 960 { 961 i32 index = beamformer_shader_reloadable_index_by_shader[shader]; 962 BeamformerShaderReloadInfo infos[2] = { 963 { 964 .shader = shader, 965 .shader_kind = beamformer_shader_primitive_is_vertex[index] ? VulkanShaderKind_Vertex : VulkanShaderKind_Mesh, 966 .filename_or_data = BakeShaders ? beamformer_shader_data[index][0] 967 : beamformer_reloadable_shader_files[index][0], 968 }, 969 { 970 .shader = shader, 971 .shader_kind = VulkanShaderKind_Fragment, 972 .filename_or_data = BakeShaders ? beamformer_shader_data[index][1] 973 : beamformer_reloadable_shader_files[index][1], 974 }, 975 }; 976 beamformer_reload_pipeline(pipeline, infos, countof(infos), arena); 977 } 978 979 function void 980 beamformer_reload_compute_pipeline(VulkanHandle *pipeline, BeamformerShaderKind shader, 981 BeamformerShaderDescriptor *shader_descriptor, Arena arena) 982 { 983 i32 index = beamformer_shader_reloadable_index_by_shader[shader]; 984 uv3 layout = shader_descriptor ? shader_descriptor->layout : (uv3){{vk_gpu_info()->subgroup_size, 1, 1}}; 985 BeamformerShaderReloadInfo info = { 986 .shader = shader, 987 .shader_kind = VulkanShaderKind_Compute, 988 .shader_descriptor = shader_descriptor, 989 .filename_or_data = BakeShaders ? beamformer_shader_data[index][0] 990 : beamformer_reloadable_shader_files[index][0], 991 .layout = layout, 992 }; 993 beamformer_reload_pipeline(pipeline, &info, 1, arena); 994 } 995 996 function void 997 beamformer_commit_parameter_block(BeamformerCtx *ctx, BeamformerComputePlan *cp, u32 block, Arena arena) 998 { 999 BeamformerParameterBlock *pb; 1000 DeferLoop(pb = beamformer_parameter_block_lock(ctx->shared_memory, block, -1), 1001 beamformer_parameter_block_unlock(ctx->shared_memory, block)) 1002 for EachBit(pb->region_update_flags, region) 1003 { 1004 pb->region_update_flags &= ~(1ul << region); 1005 switch (region) { 1006 case BeamformerParameterRegionFlag_NotifyUI:{ 1007 atomic_store_u32(&ctx->ui_dirty_parameter_blocks, 1u << block); 1008 }break; 1009 1010 case BeamformerParameterRegionFlag_ComputePipeline: 1011 case BeamformerParameterRegionFlag_Parameters: 1012 { 1013 cp->output_points = das_valid_points(pb->parameters.output_points.xyz); 1014 cp->average_frames = pb->parameters.output_points.E[3]; 1015 1016 plan_compute_pipeline(cp, pb, arena); 1017 1018 /* NOTE(rnp): these are both handled by plan_compute_pipeline() */ 1019 u32 mask = 1 << BeamformerParameterBlockRegion_ComputePipeline | 1020 1 << BeamformerParameterBlockRegion_Parameters; 1021 pb->region_update_flags &= ~mask; 1022 1023 for (u32 shader_slot = 0; shader_slot < cp->pipeline.shader_count; shader_slot++) { 1024 u128 hash = u128_hash_from_data(cp->shader_descriptors + shader_slot, sizeof(BeamformerShaderDescriptor)); 1025 if (!u128_equal(hash, cp->shader_hashes[shader_slot])) 1026 cp->dirty_programs |= 1 << shader_slot; 1027 cp->shader_hashes[shader_slot] = hash; 1028 } 1029 1030 cp->acquisition_count = pb->parameters.acquisition_count; 1031 cp->acquisition_kind = pb->parameters.acquisition_kind; 1032 cp->contrast_mode = pb->parameters.contrast_mode; 1033 1034 i64 buffer_size = PING_PONG_BUFFER_SLOTS * round_up_to(cp->rf_size, 64); 1035 if (ctx->compute_context.ping_pong_buffer.size < buffer_size) { 1036 b32 cuda = cuda_supported(); 1037 GPUBufferAllocateInfo allocate_info = { 1038 .size = buffer_size, 1039 .export = cuda ? &ctx->compute_context.ping_pong_export_handle : 0, 1040 .label = str8("PingPongBuffer"), 1041 }; 1042 vk_buffer_allocate(&ctx->compute_context.ping_pong_buffer, &allocate_info); 1043 1044 BeamformerShaderResourceInfo shader_resource_infos[] = { 1045 { 1046 .kind = BeamformerShaderResourceKind_Buffer, 1047 .handle = ctx->compute_context.ping_pong_buffer.handle, 1048 .slot = BeamformerShaderBufferSlot_PingPong, 1049 }, 1050 }; 1051 vk_bind_shader_resources(shader_resource_infos, countof(shader_resource_infos)); 1052 1053 // TODO(rnp): figure out how to share with CUDA 1054 // IMPORTANT: on linux the handle is returned to os and should be cleared after import 1055 // see usage of glImportMemoryFdEXT and surrounding code in ui.c for examples 1056 if (cuda) { 1057 } 1058 } 1059 1060 if (pb->parameters.decode_mode != BeamformerDecodeMode_None && 1061 cp->hadamard_order != (i32)cp->acquisition_count) 1062 { 1063 beamformer_update_hadamard(cp, (i32)cp->acquisition_count, vk_gpu_info()->cooperative_matrix, arena); 1064 } 1065 }break; 1066 1067 case BeamformerParameterBlockRegion_ChannelMapping:{ 1068 cuda_set_channel_mapping(pb->channel_mapping); 1069 }break; 1070 case BeamformerParameterRegionFlag_TransmitReceiveOrientations:{ 1071 GPUBuffer *b = &cp->array_parameters; 1072 u32 kind = BeamformerComputeArrayParameterKind_TransmitReceiveOrientations; 1073 u64 offset = beamformer_compute_array_parameter_offsets[kind]; 1074 u64 size = beamformer_compute_array_parameter_sizes[kind]; 1075 { 1076 Arena scratch = arena; 1077 u16 *u16s = push_array(&scratch, u16, countof(pb->transmit_receive_orientations)); 1078 for (u32 i = 0; i < countof(pb->transmit_receive_orientations); i++) 1079 u16s[i] = pb->transmit_receive_orientations[i]; 1080 1081 vk_buffer_range_upload(b, u16s, offset, size, 0); 1082 } 1083 }break; 1084 case BeamformerParameterRegionFlag_FocalVectors: 1085 case BeamformerParameterRegionFlag_SparseElements: 1086 { 1087 u32 kind = BeamformerComputeArrayParameterKind_Count; 1088 switch (region) { 1089 case BeamformerParameterBlockRegion_FocalVectors:{ 1090 kind = BeamformerComputeArrayParameterKind_FocalVectors; 1091 }break; 1092 case BeamformerParameterBlockRegion_SparseElements:{ 1093 kind = BeamformerComputeArrayParameterKind_SparseElements; 1094 }break; 1095 InvalidDefaultCase; 1096 } 1097 1098 if (kind != BeamformerComputeArrayParameterKind_Count) { 1099 GPUBuffer *b = &cp->array_parameters; 1100 u64 offset = beamformer_compute_array_parameter_offsets[kind]; 1101 u64 size = beamformer_compute_array_parameter_sizes[kind]; 1102 vk_buffer_range_upload(b, (u8 *)pb + BeamformerParameterBlockRegionOffsets[region], offset, size, 0); 1103 } 1104 }break; 1105 } 1106 } 1107 } 1108 1109 function void 1110 do_compute_shader(BeamformerCtx *ctx, VulkanHandle cmd, BeamformerComputePlan *cp, BeamformerFrame *frame, 1111 u32 shader_slot, u32 channel_offset, u64 rf_pointer, Arena arena) 1112 { 1113 BeamformerComputeContext *cc = &ctx->compute_context; 1114 1115 u32 output_index = !cc->ping_pong_input_index; 1116 u32 input_index = cc->ping_pong_input_index; 1117 u32 das_output_index = PING_PONG_BUFFER_SLOTS - 1; 1118 1119 u64 pp_size = cc->ping_pong_buffer.size / PING_PONG_BUFFER_SLOTS; 1120 u64 pp_input_pointer = cc->ping_pong_buffer.gpu_pointer + input_index * pp_size; 1121 u64 pp_output_pointer = cc->ping_pong_buffer.gpu_pointer + output_index * pp_size; 1122 u64 pp_das_pointer = cc->ping_pong_buffer.gpu_pointer + das_output_index * pp_size; 1123 1124 u32 das_index = cp->first_image_shader_index - 1; 1125 1126 uv3 dispatch = cp->shader_descriptors[shader_slot].dispatch; 1127 1128 vk_command_bind_pipeline(cmd, cp->vulkan_pipelines[shader_slot]); 1129 1130 switch (cp->pipeline.shaders[shader_slot]) { 1131 1132 case BeamformerShaderKind_Decode:{ 1133 BeamformerDecodePushConstants pc = { 1134 .hadamard_buffer = cp->array_parameters.gpu_pointer + offsetof(BeamformerComputeArrayParameters, Hadamard), 1135 .rf_buffer = pp_input_pointer, 1136 }; 1137 1138 if ((shader_slot + 1) == das_index) pc.output_buffer = pp_das_pointer; 1139 else pc.output_buffer = pp_output_pointer; 1140 1141 GPUMemoryBarrierInfo memory_barriers[]= { 1142 // NOTE(rnp): first pass or last stage output 1143 { 1144 .gpu_buffer = &cc->ping_pong_buffer, 1145 .offset = pp_input_pointer - cc->ping_pong_buffer.gpu_pointer, 1146 .size = pp_size, 1147 }, 1148 // NOTE(rnp): output for DAS 1149 { 1150 .gpu_buffer = &cc->ping_pong_buffer, 1151 .offset = pp_das_pointer - cc->ping_pong_buffer.gpu_pointer, 1152 .size = pp_size, 1153 }, 1154 }; 1155 1156 u32 barrier_count = 1; 1157 if (shader_slot + 1 == das_index) 1158 barrier_count++; 1159 1160 vk_command_buffer_memory_barriers(cmd, memory_barriers, barrier_count); 1161 vk_command_push_constants(cmd, 0, sizeof(pc), &pc); 1162 vk_command_dispatch_compute(cmd, dispatch); 1163 1164 cc->ping_pong_input_index = !cc->ping_pong_input_index; 1165 }break; 1166 1167 case BeamformerShaderKind_Hilbert:{ 1168 cuda_hilbert(input_index, output_index); 1169 cc->ping_pong_input_index = !cc->ping_pong_input_index; 1170 }break; 1171 1172 case BeamformerShaderKind_Filter: 1173 case BeamformerShaderKind_Demodulate: 1174 { 1175 BeamformerDataKind output_data_kind = cp->shader_descriptors[shader_slot].output_data_kind; 1176 1177 u64 element_size = beamformer_data_kind_byte_size[output_data_kind]; 1178 u32 filter_slot = cp->pipeline.parameters[shader_slot].filter_slot; 1179 BeamformerFilterPushConstants pc = { 1180 .filter_coefficients = cp->filters[filter_slot].buffer.gpu_pointer, 1181 .input_data = shader_slot == 0 ? rf_pointer : pp_input_pointer, 1182 .output_element_offset = output_index * pp_size / element_size, 1183 }; 1184 1185 if ((shader_slot + 1) == das_index) 1186 pc.output_element_offset = das_output_index * pp_size / element_size; 1187 1188 GPUMemoryBarrierInfo memory_barriers[] = { 1189 // NOTE(rnp): last stage output 1190 { 1191 .gpu_buffer = &cc->ping_pong_buffer, 1192 .offset = pp_input_pointer - cc->ping_pong_buffer.gpu_pointer, 1193 .size = pp_size, 1194 }, 1195 // NOTE(rnp): output for DAS 1196 { 1197 .gpu_buffer = &cc->ping_pong_buffer, 1198 .offset = pp_das_pointer - cc->ping_pong_buffer.gpu_pointer, 1199 .size = pp_size, 1200 }, 1201 }; 1202 GPUMemoryBarrierInfo *barriers = memory_barriers; 1203 1204 u32 barrier_count = 2; 1205 if (shader_slot == 0) { 1206 barriers++; 1207 barrier_count--; 1208 } 1209 1210 if ((shader_slot + 1) != das_index) 1211 barrier_count--; 1212 1213 if (barrier_count) 1214 vk_command_buffer_memory_barriers(cmd, barriers, barrier_count); 1215 1216 vk_command_push_constants(cmd, 0, sizeof(pc), &pc); 1217 vk_command_dispatch_compute(cmd, dispatch); 1218 1219 cc->ping_pong_input_index = !cc->ping_pong_input_index; 1220 }break; 1221 1222 case BeamformerShaderKind_DAS:{ 1223 local_persist u32 das_cycle_t = 0; 1224 1225 GPUBuffer *b = cc->backlog.buffer; 1226 1227 u64 frame_size = beamformer_frame_byte_size(frame->points, frame->data_kind); 1228 u64 iframe_size = frame_size / beamformer_data_kind_element_count[frame->data_kind]; 1229 u64 element_size = beamformer_data_kind_byte_size[cp->shader_descriptors[shader_slot].input_data_kind]; 1230 1231 BeamformerDASPushConstants pc = { 1232 .xdc_element_pitch = cp->xdc_element_pitch, 1233 .rf_element_offset = das_output_index * pp_size / element_size, 1234 .output_frame = b->gpu_pointer + frame->buffer_offset, 1235 .incoherent_frame = b->gpu_pointer + b->size - iframe_size, 1236 .output_size_x = cp->output_points.x, 1237 .output_size_y = cp->output_points.y, 1238 .output_size_z = cp->output_points.z, 1239 .cycle_t = das_cycle_t++, 1240 .channel_offset = channel_offset, 1241 .array_parameters = cp->array_parameters.gpu_pointer + offsetof(BeamformerComputeArrayParameters, FocalVectors), 1242 }; 1243 memory_copy(pc.voxel_transform.E, cp->das_voxel_transform.E, sizeof(pc.voxel_transform)); 1244 memory_copy(pc.xdc_transform.E, cp->xdc_transform.E, sizeof(pc.xdc_transform)); 1245 1246 b32 coherent = (cp->shader_descriptors[shader_slot].compile_flags & BeamformerDASCompileFlags_CoherencyWeighting) != 0; 1247 1248 GPUMemoryBarrierInfo memory_barriers[] = { 1249 // NOTE(rnp): last stage data output barrier 1250 { 1251 .gpu_buffer = &cc->ping_pong_buffer, 1252 .offset = pp_das_pointer - cc->ping_pong_buffer.gpu_pointer, 1253 .size = pp_size, 1254 }, 1255 // NOTE(rnp): output clearing pipeline barriers or last DAS pipeline write barriers 1256 { 1257 .gpu_buffer = b, 1258 .offset = frame->buffer_offset, 1259 .size = frame_size, 1260 }, 1261 { 1262 .gpu_buffer = b, 1263 .offset = pc.incoherent_frame - b->gpu_pointer, 1264 .size = iframe_size, 1265 }, 1266 }; 1267 1268 u32 barrier_count = countof(memory_barriers); 1269 if (!coherent) barrier_count--; 1270 1271 vk_command_buffer_memory_barriers(cmd, memory_barriers, barrier_count); 1272 vk_command_push_constants(cmd, 0, sizeof(pc), &pc); 1273 vk_command_dispatch_compute(cmd, dispatch); 1274 }break; 1275 1276 case BeamformerShaderKind_CoherencyWeighting:{ 1277 GPUBuffer *b = cc->backlog.buffer; 1278 1279 u64 frame_size = beamformer_frame_byte_size(frame->points, frame->data_kind); 1280 u64 iframe_size = frame_size / beamformer_data_kind_element_count[frame->data_kind]; 1281 1282 BeamformerCoherencyWeightingPushConstants pc = { 1283 .left_side_buffer = b->gpu_pointer + frame->buffer_offset, 1284 .right_side_buffer = b->gpu_pointer + b->size - iframe_size, 1285 .scale = 1.0f, 1286 .output_size_x = cp->output_points.x, 1287 .output_size_y = cp->output_points.y, 1288 .output_size_z = cp->output_points.z, 1289 }; 1290 1291 GPUMemoryBarrierInfo memory_barriers[] = { 1292 { 1293 .gpu_buffer = b, 1294 .offset = frame->buffer_offset, 1295 .size = frame_size, 1296 }, 1297 { 1298 .gpu_buffer = b, 1299 .offset = pc.right_side_buffer - b->gpu_pointer, 1300 .size = iframe_size, 1301 }, 1302 }; 1303 1304 vk_command_buffer_memory_barriers(cmd, memory_barriers, countof(memory_barriers)); 1305 vk_command_push_constants(cmd, 0, sizeof(pc), &pc); 1306 vk_command_dispatch_compute(cmd, dispatch); 1307 }break; 1308 1309 case BeamformerShaderKind_Reshape:{ 1310 BeamformerDataKind input_data_kind = cp->shader_descriptors[shader_slot].input_data_kind; 1311 BeamformerReshapeBakeParameters *rb = &cp->shader_descriptors[shader_slot].bake.Reshape; 1312 u64 input_pointer = shader_slot == 0 ? rf_pointer : pp_input_pointer; 1313 BeamformerReshapePushConstants pc = { 1314 .left_input_buffer = input_pointer, 1315 .right_input_buffer = input_pointer + rb->SizeX * rb->SizeY * rb->SizeZ 1316 * beamformer_data_kind_byte_size[input_data_kind], 1317 }; 1318 1319 if ((shader_slot + 1) == das_index) pc.output_buffer = pp_das_pointer; 1320 else pc.output_buffer = pp_output_pointer; 1321 1322 GPUMemoryBarrierInfo memory_barriers[]= { 1323 // NOTE(rnp): first pass or last stage output 1324 { 1325 .gpu_buffer = &cc->ping_pong_buffer, 1326 .offset = pp_input_pointer - cc->ping_pong_buffer.gpu_pointer, 1327 .size = pp_size, 1328 }, 1329 // NOTE(rnp): output for DAS 1330 { 1331 .gpu_buffer = &cc->ping_pong_buffer, 1332 .offset = pp_das_pointer - cc->ping_pong_buffer.gpu_pointer, 1333 .size = pp_size, 1334 }, 1335 }; 1336 1337 u32 barrier_count = 1; 1338 if (shader_slot + 1 == das_index) 1339 barrier_count++; 1340 1341 vk_command_buffer_memory_barriers(cmd, memory_barriers, barrier_count); 1342 vk_command_push_constants(cmd, 0, sizeof(pc), &pc); 1343 vk_command_dispatch_compute(cmd, dispatch); 1344 1345 cc->ping_pong_input_index = !cc->ping_pong_input_index; 1346 }break; 1347 1348 // NOTE(rnp): invalid stages should be filtered in planning phase 1349 InvalidDefaultCase; 1350 } 1351 1352 #if 0 1353 switch (shader) { 1354 case BeamformerShaderKind_MinMax:{ 1355 for (u32 i = 1; i < frame->image.mip_map_levels; i++) { 1356 glBindImageTexture(0, frame->texture, i - 1, GL_TRUE, 0, GL_READ_ONLY, GL_RG32F); 1357 glBindImageTexture(1, frame->texture, i - 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RG32F); 1358 glProgramUniform1i(program, MIN_MAX_MIPS_LEVEL_UNIFORM_LOC, i); 1359 1360 u32 width = (u32)frame->dim.x >> i; 1361 u32 height = (u32)frame->dim.y >> i; 1362 u32 depth = (u32)frame->dim.z >> i; 1363 glDispatchCompute(ORONE(width / 32), ORONE(height), ORONE(depth / 32)); 1364 glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); 1365 } 1366 }break; 1367 case BeamformerShaderKind_Sum:{ 1368 u32 aframe_index = ctx->averaged_frame_index % countof(ctx->averaged_frames); 1369 BeamformerFrame *aframe = ctx->averaged_frames + aframe_index; 1370 aframe->id = ctx->averaged_frame_index; 1371 atomic_store_u32(&aframe->ready_to_present, 0); 1372 /* TODO(rnp): hack we need a better way of specifying which frames to sum; 1373 * this is fine for rolling averaging but what if we want to do something else */ 1374 assert(frame >= ctx->beamform_frames); 1375 assert(frame < ctx->beamform_frames + countof(ctx->beamform_frames)); 1376 u32 base_index = (u32)(frame - ctx->beamform_frames); 1377 u32 to_average = (u32)cp->average_frames; 1378 u32 frame_count = 0; 1379 u32 *in_textures = push_array(&arena, u32, BeamformerMaxBacklogFrames); 1380 ComputeFrameIterator cfi = compute_frame_iterator(ctx, 1 + base_index - to_average, to_average); 1381 for (BeamformerFrame *it = frame_next(&cfi); it; it = frame_next(&cfi)) 1382 in_textures[frame_count++] = it->texture; 1383 1384 assert(to_average == frame_count); 1385 1386 glProgramUniform1f(program, SUM_PRESCALE_UNIFORM_LOC, 1 / (f32)frame_count); 1387 /* NOTE: zero output before summing */ 1388 glClearTexImage(aframe->texture, 0, GL_RED, GL_FLOAT, 0); 1389 glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); 1390 1391 glBindImageTexture(0, out_texture, 0, GL_TRUE, 0, GL_READ_WRITE, GL_RG32F); 1392 for (u32 i = 0; i < in_texture_count; i++) { 1393 glBindImageTexture(1, in_textures[i], 0, GL_TRUE, 0, GL_READ_ONLY, GL_RG32F); 1394 glDispatchCompute(dispatch.x, dispatch.y, dispatch.z); 1395 glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); 1396 } 1397 1398 memory_copy(aframe->voxel_transform.E, frame->voxel_transform.E, sizeof(frame->voxel_transform)); 1399 aframe->compound_count = frame->compound_count; 1400 aframe->acquisition_kind = frame->acquisition_kind; 1401 }break; 1402 } 1403 #endif 1404 } 1405 1406 function void 1407 complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena *arena) 1408 { 1409 BeamformerComputeContext * cs = &ctx->compute_context; 1410 BeamformerSharedMemory * sm = ctx->shared_memory; 1411 1412 for (BeamformWork *work = beamform_work_queue_pop(q); 1413 work; 1414 beamform_work_queue_pop_commit(q), work = beamform_work_queue_pop(q)) 1415 { 1416 switch (work->kind) { 1417 1418 case BeamformerWorkKind_ExportBuffer:{ 1419 /* TODO(rnp): better way of handling DispatchCompute barrier */ 1420 post_sync_barrier(ctx->shared_memory, BeamformerSharedMemoryLockKind_DispatchCompute); 1421 beamformer_shared_memory_take_lock(ctx->shared_memory, (i32)work->lock, (u32)-1); 1422 BeamformerExportContext *ec = &work->export_context; 1423 switch (ec->kind) { 1424 case BeamformerExportKind_BeamformedData:{ 1425 BeamformerFrame *f = ctx->latest_frame; 1426 if (f) { 1427 u64 frame_size = beamformer_frame_byte_size(f->points, f->data_kind); 1428 assert((frame_size & 63) == 0); 1429 if (frame_size <= ec->size) { 1430 vk_host_wait_timeline(VulkanTimeline_Compute, f->timeline_valid_value, -1ULL); 1431 vk_buffer_range_download(beamformer_shared_memory_scratch_arena(sm, ctx->shared_memory_size).beg, 1432 ctx->compute_context.backlog.buffer, f->buffer_offset, 1433 frame_size, 1); 1434 } 1435 } 1436 }break; 1437 case BeamformerExportKind_Stats:{ 1438 ComputeTimingTable *table = ctx->compute_timing_table; 1439 /* NOTE(rnp): do a little spin to let this finish updating */ 1440 spin_wait(table->write_index != atomic_load_u32(&table->read_index)); 1441 ComputeShaderStats *stats = ctx->compute_shader_stats; 1442 if (sizeof(stats->table) <= ec->size) 1443 memory_copy(beamformer_shared_memory_scratch_arena(sm, ctx->shared_memory_size).beg, 1444 &stats->table, sizeof(stats->table)); 1445 }break; 1446 InvalidDefaultCase; 1447 } 1448 beamformer_shared_memory_release_lock(ctx->shared_memory, work->lock); 1449 post_sync_barrier(ctx->shared_memory, BeamformerSharedMemoryLockKind_ExportSync); 1450 }break; 1451 1452 case BeamformerWorkKind_CreateFilter:{ 1453 /* TODO(rnp): this should probably get deleted and moved to lazy loading */ 1454 BeamformerCreateFilterContext *fctx = &work->create_filter_context; 1455 u32 block = fctx->parameter_block; 1456 u32 slot = fctx->filter_slot; 1457 BeamformerComputePlan *cp = beamformer_compute_plan_for_block(cs, block, arena); 1458 beamformer_filter_update(cp->filters + slot, fctx->parameters, block, slot, *arena); 1459 }break; 1460 1461 case BeamformerWorkKind_ComputeIndirect: 1462 case BeamformerWorkKind_Compute: 1463 { 1464 push_compute_timing_info(ctx->compute_timing_table, 1465 (ComputeTimingInfo){.kind = ComputeTimingInfoKind_ComputeFrameBegin}); 1466 1467 BeamformerComputePlan *cp = beamformer_compute_plan_for_block(cs, work->compute_context.parameter_block, arena); 1468 if unlikely(beamformer_parameter_block_dirty(sm, work->compute_context.parameter_block)) { 1469 u32 block = work->compute_context.parameter_block; 1470 beamformer_commit_parameter_block(ctx, cp, block, *arena); 1471 } 1472 1473 post_sync_barrier(ctx->shared_memory, BeamformerSharedMemoryLockKind_DispatchCompute); 1474 1475 u32 dirty_programs = atomic_swap_u32(&cp->dirty_programs, 0); 1476 static_assert(BeamformerMaxComputeShaderStages <= 32, ""); 1477 if unlikely(dirty_programs) { 1478 for EachBit(dirty_programs, slot) { 1479 assert(slot < BeamformerMaxComputeShaderStages); 1480 beamformer_reload_compute_pipeline(cp->vulkan_pipelines + slot, 1481 cp->pipeline.shaders[slot], 1482 cp->shader_descriptors + slot, *arena); 1483 } 1484 } 1485 1486 atomic_store_u32(&cs->processing_compute, 1); 1487 1488 start_renderdoc_capture(); 1489 1490 i32 das_index = -1; 1491 b32 has_sum = 0; 1492 for (u32 i = 0; i < cp->pipeline.shader_count; i++) { 1493 has_sum |= cp->pipeline.shaders[i] == BeamformerShaderKind_Sum; 1494 if (cp->pipeline.shaders[i] == BeamformerShaderKind_DAS) 1495 das_index = (i32)i; 1496 } 1497 1498 b32 das_coherent = das_index >= 0 && 1499 (cp->shader_descriptors[das_index].compile_flags & 1500 BeamformerDASCompileFlags_CoherencyWeighting) != 0; 1501 u64 reserved_frame_size = 0; 1502 1503 if (has_sum) 1504 reserved_frame_size += beamformer_frame_byte_size(cp->output_points, cp->iq_pipeline ? 1505 BeamformerDataKind_Float32Complex : 1506 BeamformerDataKind_Float32); 1507 1508 // TODO(rnp): incoherent sum for different data kinds 1509 if (das_coherent) 1510 reserved_frame_size += beamformer_frame_byte_size(cp->output_points, BeamformerDataKind_Float32); 1511 1512 BeamformerFrame *frame = beamformer_frame_next(cs, cp->output_points, cp->iq_pipeline, reserved_frame_size); 1513 frame->acquisition_kind = cp->acquisition_kind; 1514 frame->contrast_mode = cp->contrast_mode; 1515 frame->compound_count = cp->acquisition_count; 1516 frame->parameter_block = work->compute_context.parameter_block; 1517 frame->view_plane_tag = work->compute_context.view_plane; 1518 memory_copy(frame->voxel_transform.E, cp->voxel_transform.E, sizeof(cp->voxel_transform)); 1519 1520 VulkanHandle cmd = vk_command_begin(VulkanTimeline_Compute); 1521 vk_command_timestamp(cmd); 1522 1523 if (das_index >= 0) { 1524 u64 frame_size = beamformer_frame_byte_size(frame->points, frame->data_kind); 1525 GPUBuffer *backlog = cs->backlog.buffer; 1526 1527 vk_command_clear_buffer(cmd, backlog, frame->buffer_offset, frame_size, 0); 1528 if (das_coherent) { 1529 u64 coherent_size = frame_size / beamformer_data_kind_element_count[frame->data_kind]; 1530 vk_command_clear_buffer(cmd, backlog, backlog->size - coherent_size, coherent_size, 0); 1531 } 1532 } 1533 1534 BeamformerRFBuffer *rf = &cs->rf_buffer; 1535 u32 compute_index = rf->compute_index; 1536 u32 slot = compute_index % countof(rf->upload_complete_values); 1537 1538 if (work->kind == BeamformerWorkKind_ComputeIndirect) { 1539 // TODO(rnp): this shouldn't be necessary, there should be a way of communicating 1540 // what the value will be so that the only the command wait is needed. 1541 spin_wait(atomic_load_u64(&rf->insertion_index) <= compute_index); 1542 1543 /* NOTE(rnp): if the GPU supports BAR there may be no need to synchronize 1544 * other than the above spin */ 1545 if (vk_buffer_needs_sync(&rf->buffer)) 1546 vk_command_wait_timeline(cmd, VulkanTimeline_Transfer, rf->upload_complete_values[slot]); 1547 } else { 1548 slot = (rf->compute_index - 1) % countof(rf->upload_complete_values); 1549 } 1550 1551 for (u32 channel_offset = 0; 1552 channel_offset < cp->channel_count; 1553 channel_offset += BeamformerChunkChannelCount) 1554 { 1555 u64 rf_pointer = rf->buffer.gpu_pointer + slot * rf->active_rf_size; 1556 rf_pointer += cp->raw_channel_byte_stride * channel_offset; 1557 for (u32 i = 0; i < cp->first_image_shader_index; i++) { 1558 do_compute_shader(ctx, cmd, cp, frame, i, channel_offset, rf_pointer, *arena); 1559 vk_command_timestamp(cmd); 1560 } 1561 } 1562 1563 for (u32 i = cp->first_image_shader_index; i < cp->pipeline.shader_count; i++) { 1564 do_compute_shader(ctx, cmd, cp, frame, i, 0, 0, *arena); 1565 vk_command_timestamp(cmd); 1566 } 1567 1568 u64 end_timeline_value = vk_command_end(cmd, (VulkanHandle){0}, (VulkanHandle){0}); 1569 if (work->kind == BeamformerWorkKind_ComputeIndirect) { 1570 atomic_store_u64(rf->compute_complete_values + slot, end_timeline_value); 1571 atomic_add_u64(&rf->compute_index, 1); 1572 } 1573 1574 atomic_store_u64(&frame->timeline_valid_value, end_timeline_value); 1575 1576 { 1577 Arena scratch = *arena; 1578 /* NOTE(rnp): this blocks until work completes */ 1579 u64 *timestamps = vk_command_read_timestamps(VulkanTimeline_Compute, &scratch); 1580 1581 i32 steps = ((i32)cp->channel_count / BeamformerChunkChannelCount) - 1; 1582 i32 step = 0; 1583 u32 shader_index = 0; 1584 u64 last_time = timestamps[0] > 0 ? timestamps[1] : 0; 1585 1586 for (u64 i = 2; i < timestamps[0] + 1; i++) { 1587 push_compute_timing_info(ctx->compute_timing_table, (ComputeTimingInfo){ 1588 .kind = ComputeTimingInfoKind_Shader, 1589 .shader = cp->pipeline.shaders[shader_index], 1590 .shader_slot = shader_index, 1591 .timer_count = timestamps[i] - last_time, 1592 }); 1593 last_time = timestamps[i]; 1594 1595 shader_index++; 1596 if (shader_index == cp->first_image_shader_index && step < steps) { 1597 shader_index = 0; 1598 step++; 1599 } 1600 } 1601 } 1602 1603 cs->processing_progress = 1; 1604 1605 if (has_sum) { 1606 #if 0 1607 u32 aframe_index = ((ctx->averaged_frame_index++) % countof(ctx->averaged_frames)); 1608 ctx->averaged_frames[aframe_index].view_plane_tag = frame->view_plane_tag; 1609 ctx->averaged_frames[aframe_index].ready_to_present = 1; 1610 atomic_store_u64((u64 *)&ctx->latest_frame, (u64)(ctx->averaged_frames + aframe_index)); 1611 #endif 1612 } else { 1613 atomic_store_u64((u64 *)&ctx->latest_frame, (u64)frame); 1614 } 1615 1616 atomic_store_u32(&cs->processing_compute, 0); 1617 1618 push_compute_timing_info(ctx->compute_timing_table, 1619 (ComputeTimingInfo){.kind = ComputeTimingInfoKind_ComputeFrameEnd}); 1620 1621 end_renderdoc_capture(); 1622 }break; 1623 InvalidDefaultCase; 1624 } 1625 } 1626 } 1627 1628 function void 1629 coalesce_timing_table(ComputeTimingTable *t, ComputeShaderStats *stats) 1630 { 1631 /* TODO(rnp): we do not currently do anything to handle the potential for a half written 1632 * info item. this could result in garbage entries but they shouldn't really matter */ 1633 1634 u32 target = atomic_load_u32(&t->write_index); 1635 u32 stats_index = stats->latest_frame_index; 1636 1637 b32 has_rf = 0; 1638 f32 gpu_clocks_to_nano = 1.0e-9f * vk_gpu_info()->timestamp_period_ns; 1639 1640 // NOTE(rnp): not equal (the index may wrap) 1641 while (t->read_index != target) { 1642 ComputeTimingInfo info = t->buffer[t->read_index % countof(t->buffer)]; 1643 switch (info.kind) { 1644 1645 case ComputeTimingInfoKind_ComputeFrameBegin:{ 1646 assert(t->compute_frame_active == 0); 1647 t->compute_frame_active = 1; 1648 /* NOTE(rnp): allow multiple instances of same shader to accumulate */ 1649 t->in_flight_shader_count = 0; 1650 memory_clear(t->in_flight_shader_ids, 0, sizeof(t->in_flight_shader_ids)); 1651 memory_clear(stats->table.times[stats_index], 0, sizeof(stats->table.times[stats_index])); 1652 }break; 1653 1654 case ComputeTimingInfoKind_ComputeFrameEnd:{ 1655 assert(t->compute_frame_active == 1); 1656 t->compute_frame_active = 0; 1657 stats_index = stats->latest_frame_index = (stats_index + 1) % countof(stats->table.times); 1658 stats->table.shader_count = t->in_flight_shader_count; 1659 memory_copy(stats->table.shader_ids, t->in_flight_shader_ids, sizeof(t->in_flight_shader_ids)); 1660 }break; 1661 1662 case ComputeTimingInfoKind_Shader:{ 1663 t->in_flight_shader_count = Max(t->in_flight_shader_count, info.shader_slot + 1u); 1664 t->in_flight_shader_ids[info.shader_slot] = info.shader; 1665 stats->table.times[stats_index][info.shader_slot] += info.timer_count * gpu_clocks_to_nano; 1666 }break; 1667 1668 case ComputeTimingInfoKind_RF_Data:{ 1669 stats->latest_rf_index = (stats->latest_rf_index + 1) % countof(stats->table.rf_time_deltas); 1670 f32 delta = info.timer_count / (f32)os_system_info()->timer_frequency; 1671 stats->table.rf_time_deltas[stats->latest_rf_index] = delta; 1672 has_rf = 1; 1673 }break; 1674 } 1675 /* NOTE(rnp): do this at the end so that stats table is always in a consistent state */ 1676 t->read_index++; 1677 } 1678 1679 for (u32 i = 0; i < stats->table.shader_count; i++) { 1680 f32 sum = 0; 1681 for EachElement(stats->table.times, it) 1682 sum += stats->table.times[it][i]; 1683 stats->average_times[i] = sum / countof(stats->table.times); 1684 } 1685 1686 if (has_rf) { 1687 f32 sum = 0; 1688 for EachElement(stats->table.rf_time_deltas, i) 1689 sum += stats->table.rf_time_deltas[i]; 1690 stats->rf_time_delta_average = sum / countof(stats->table.rf_time_deltas); 1691 } 1692 } 1693 1694 DEBUG_EXPORT BEAMFORMER_COMPLETE_COMPUTE_FN(beamformer_complete_compute) 1695 { 1696 BeamformerSharedMemory *sm = ctx->shared_memory; 1697 complete_queue(ctx, &sm->external_work_queue, arena); 1698 complete_queue(ctx, ctx->beamform_work_queue, arena); 1699 } 1700 1701 DEBUG_EXPORT BEAMFORMER_RF_UPLOAD_FN(beamformer_rf_upload) 1702 { 1703 BeamformerSharedMemory *sm = ctx->shared_memory; 1704 BeamformerSharedMemoryLockKind scratch_lock = BeamformerSharedMemoryLockKind_ScratchSpace; 1705 BeamformerSharedMemoryLockKind upload_lock = BeamformerSharedMemoryLockKind_UploadRF; 1706 1707 u64 rf_block_rf_size; 1708 if (atomic_load_u32(sm->locks + upload_lock) && 1709 (rf_block_rf_size = atomic_swap_u64(&sm->rf_block_rf_size, 0))) 1710 { 1711 beamformer_shared_memory_take_lock(ctx->shared_memory, (i32)scratch_lock, (u32)-1); 1712 1713 BeamformerRFBuffer *rf = ctx->rf_buffer; 1714 1715 rf->active_rf_size = vk_round_up_to_sync_size(rf_block_rf_size & 0xFFFFFFFFULL, 64); 1716 if unlikely(rf->buffer.size < countof(rf->upload_complete_values) * rf->active_rf_size) { 1717 GPUBufferAllocateInfo allocate_info = { 1718 .size = countof(rf->upload_complete_values) * rf->active_rf_size, 1719 .flags = VulkanUsageFlag_HostReadWrite, 1720 .label = str8("RawRFBuffer"), 1721 }; 1722 vk_buffer_allocate(&rf->buffer, &allocate_info); 1723 } 1724 1725 u64 slot = rf->insertion_index % countof(rf->upload_complete_values); 1726 1727 /* NOTE(rnp): don't overwrite slot if the compute thread hasn't processed it */ 1728 spin_wait(atomic_load_u64(&rf->compute_index) < rf->insertion_index); 1729 vk_host_wait_timeline(VulkanTimeline_Compute, rf->compute_complete_values[slot], -1ULL); 1730 1731 vk_buffer_range_upload(&rf->buffer, beamformer_shared_memory_scratch_arena(sm, ctx->shared_memory_size).beg, 1732 slot * rf->active_rf_size, rf->active_rf_size, 1); 1733 store_fence(); 1734 1735 beamformer_shared_memory_release_lock(ctx->shared_memory, (i32)scratch_lock); 1736 post_sync_barrier(ctx->shared_memory, upload_lock); 1737 1738 atomic_store_u64(rf->upload_complete_values + slot, vk_host_signal_timeline(VulkanTimeline_Transfer)); 1739 atomic_add_u64(&rf->insertion_index, 1); 1740 1741 os_wake_all_waiters(ctx->compute_worker_sync); 1742 1743 u64 current_time = os_timer_count(); 1744 push_compute_timing_info(ctx->compute_timing_table, (ComputeTimingInfo){ 1745 .kind = ComputeTimingInfoKind_RF_Data, 1746 .timer_count = current_time - rf->timestamp, 1747 }); 1748 rf->timestamp = current_time; 1749 } 1750 } 1751 1752 function void 1753 beamformer_queue_compute(BeamformerCtx *ctx, BeamformerFrame *frame, u32 parameter_block) 1754 { 1755 BeamformerSharedMemory *sm = ctx->shared_memory; 1756 BeamformerSharedMemoryLockKind dispatch_lock = BeamformerSharedMemoryLockKind_DispatchCompute; 1757 if (!sm->live_imaging_parameters.active && beamformer_shared_memory_take_lock(sm, (i32)dispatch_lock, 0)) 1758 { 1759 BeamformWork *work = beamform_work_queue_push(ctx->beamform_work_queue); 1760 if (work) { 1761 work->kind = BeamformerWorkKind_Compute; 1762 work->compute_context.view_plane = frame ? frame->view_plane_tag : 0; 1763 work->compute_context.parameter_block = parameter_block; 1764 beamform_work_queue_push_commit(ctx->beamform_work_queue); 1765 } 1766 } 1767 os_wake_all_waiters(&ctx->compute_worker.sync_variable); 1768 } 1769 1770 #include "ui.c" 1771 1772 function void 1773 beamformer_process_input_events(BeamformerCtx *ctx, BeamformerInput *input, 1774 BeamformerInputEvent *events, u32 event_count) 1775 { 1776 for (u32 index = 0; index < event_count; index++) { 1777 BeamformerInputEvent *event = events + index; 1778 switch (event->kind) { 1779 1780 // NOTE(rnp): ui will handle these 1781 case BeamformerInputEventKind_ButtonPress: 1782 case BeamformerInputEventKind_ButtonRelease: 1783 case BeamformerInputEventKind_MouseScroll: 1784 case BeamformerInputEventKind_WindowResize: 1785 {}break; 1786 1787 case BeamformerInputEventKind_ExecutableReload:{ 1788 ui_init(ctx, ctx->ui_backing_store); 1789 }break; 1790 1791 case BeamformerInputEventKind_FileEvent:{ 1792 BeamformerFileReloadContext *frc = event->file_watch_user_context; 1793 switch (frc->kind) { 1794 case BeamformerFileReloadKind_ComputeInternalShader:{ 1795 // TODO(rnp): this could stall, better to push it onto compute once queue is better 1796 beamformer_reload_compute_pipeline(frc->shader_reload.pipeline, frc->shader_reload.shader, 0, ctx->arena); 1797 }break; 1798 1799 case BeamformerFileReloadKind_ComputeShader:{ 1800 for EachElement(ctx->compute_context.compute_plans, block) { 1801 BeamformerComputePlan *cp = ctx->compute_context.compute_plans[block]; 1802 for (u32 slot = 0; cp && slot < cp->pipeline.shader_count; slot++) { 1803 i32 shader_index = beamformer_shader_reloadable_index_by_shader[cp->pipeline.shaders[slot]]; 1804 if (beamformer_reloadable_shader_kinds[shader_index] == frc->shader_reload.shader) 1805 atomic_or_u32(&cp->dirty_programs, 1 << slot); 1806 } 1807 } 1808 1809 // TODO(rnp): track latest parameter block 1810 if (ctx->latest_frame) 1811 beamformer_queue_compute(ctx, ctx->latest_frame, 0); 1812 }break; 1813 1814 case BeamformerFileReloadKind_RenderShader:{ 1815 beamformer_reload_render_pipeline(frc->shader_reload.pipeline, frc->shader_reload.shader, ctx->arena); 1816 ctx->render_shader_updated = 1; 1817 }break; 1818 1819 InvalidDefaultCase; 1820 } 1821 }break; 1822 1823 InvalidDefaultCase; 1824 } 1825 } 1826 } 1827 1828 function void 1829 beamformer_panel_group_insert_at(BeamformerUIPanel *group, BeamformerUIPanel *tab, u64 new_child_index) 1830 { 1831 if (tab->parent) beamformer_ui_panel_unlink(tab); 1832 new_child_index = Min(new_child_index, group->child_count); 1833 1834 tab->parent = group; 1835 group->child_count++; 1836 if (group->kind == BeamformerPanelKind_TabGroup) group->u.tab_focus = tab; 1837 1838 BeamformerUIPanel *previous_sibling = new_child_index == 0 ? 0 : group->first_child; 1839 for (u64 child_index = 1; child_index < new_child_index; child_index++) 1840 previous_sibling = previous_sibling->next_sibling; 1841 1842 if (previous_sibling) { 1843 tab->previous_sibling = previous_sibling; 1844 tab->next_sibling = previous_sibling->next_sibling; 1845 if (tab->next_sibling) tab->next_sibling->previous_sibling = tab; 1846 previous_sibling->next_sibling = tab; 1847 if (previous_sibling == group->last_child) group->last_child = tab; 1848 } else { 1849 DLLInsertFirst(0, group->first_child, group->last_child, tab, next_sibling, previous_sibling); 1850 } 1851 } 1852 1853 BEAMFORMER_EXPORT void 1854 beamformer_frame_step(BeamformerInput *input) 1855 { 1856 BeamformerCtx *ctx = beamformer_context = BeamformerContextMemory(input->memory); 1857 beamformer_input = input; 1858 1859 u64 current_time = os_timer_count(); 1860 dt_for_frame = (f64)(current_time - ctx->frame_timestamp) / os_system_info()->timer_frequency; 1861 ctx->frame_timestamp = current_time; 1862 ctx->frame_index++; 1863 1864 coalesce_timing_table(ctx->compute_timing_table, ctx->compute_shader_stats); 1865 1866 // NOTE(rnp): reset frame state 1867 { 1868 ctx->registers = &ctx->base_registers; 1869 swap(ctx->command_queues[0], ctx->command_queues[1]); 1870 zero_struct(ctx->command_queues + 0); 1871 //zero_struct(ctx->registers); 1872 end_temp_arena(ctx->frame_arena_savepoints[ctx->frame_index % countof(ctx->frame_arenas)]); 1873 } 1874 1875 beamformer_process_input_events(ctx, input, input->event_queue, input->event_count); 1876 1877 BeamformerSharedMemory *sm = ctx->shared_memory; 1878 u32 live_imaging_active = atomic_load_u32(&sm->live_imaging_parameters.active); 1879 if (live_imaging_active != ctx->live_imaging_active) { 1880 if (ctx->live_imaging_active) { 1881 BeamformerUIPanel *parent = ctx->auto_live_control_panel->parent; 1882 beamformer_command(beamformer_command_infos[BeamformerCommandKind_CloseTab].string, .tree_node = (u64)ctx->auto_live_control_panel); 1883 if (parent->child_count == 1) 1884 beamformer_command(beamformer_command_infos[BeamformerCommandKind_CloseTab].string, .tree_node = (u64)parent); 1885 ctx->auto_live_control_panel = 0; 1886 } else { 1887 ctx->live_imaging_active_frame = ctx->frame_index; 1888 ctx->auto_live_control_panel = beamformer_ui_push_panel(0, BeamformerPanelKind_LiveImagingControls); 1889 beamformer_command(beamformer_command_infos[BeamformerCommandKind_SplitTree].string, 1890 .tree_node = (u64)ctx->auto_live_control_panel, 1891 .split_axis = Axis2_X, 1892 .split_left_tree = (u64)ui_context->tree, 1893 .split_right_tree = 0, 1894 .drop_target_tree = (u64)ui_context->tree); 1895 } 1896 ctx->live_imaging_active = live_imaging_active; 1897 } 1898 1899 if (atomic_load_u32(sm->locks + BeamformerSharedMemoryLockKind_UploadRF)) 1900 os_wake_all_waiters(&ctx->upload_worker.sync_variable); 1901 if (atomic_load_u32(sm->locks + BeamformerSharedMemoryLockKind_DispatchCompute)) 1902 os_wake_all_waiters(&ctx->compute_worker.sync_variable); 1903 1904 beamformer_registers()->frame = (u64)(ctx->latest_frame - ctx->compute_context.backlog.frames); 1905 1906 beamformer_ui_frame(); 1907 1908 // NOTE(rnp): execute commands 1909 for (BeamformerCommandNode *node = ctx->command_queues[0].first; 1910 node; 1911 node = node == node->next ? 0 : node->next) 1912 { 1913 BeamformerRegistersScope() 1914 { 1915 memory_copy(beamformer_registers(), node->command.registers, sizeof(*node->command.registers)); 1916 BeamformerCommandKind kind = beamformer_command_kind_from_string(node->command.name); 1917 switch (kind) { 1918 InvalidDefaultCase; 1919 case BeamformerCommandKind_CloseTab:{ 1920 BeamformerUIPanel *tab = (BeamformerUIPanel *)beamformer_registers()->tree_node; 1921 ui_kill_panel(tab); 1922 }break; 1923 1924 case BeamformerCommandKind_FocusTab:{ 1925 BeamformerUIPanel *tab = (BeamformerUIPanel *)beamformer_registers()->tree_node; 1926 assert(tab->parent->kind == BeamformerPanelKind_TabGroup); 1927 tab->parent->u.tab_focus = tab; 1928 }break; 1929 1930 case BeamformerCommandKind_MoveTab:{ 1931 BeamformerUIPanel *move = (BeamformerUIPanel *)beamformer_registers()->tree_node; 1932 BeamformerUIPanel *group = (BeamformerUIPanel *)beamformer_registers()->drop_target_tree; 1933 u64 new_child_index = beamformer_registers()->drop_child_index; 1934 beamformer_panel_group_insert_at(group, move, new_child_index); 1935 }break; 1936 1937 case BeamformerCommandKind_OpenTab:{ 1938 BeamformerUIPanel *panel = (BeamformerUIPanel *)beamformer_registers()->tree_node; 1939 assert(panel->kind == BeamformerPanelKind_TabGroup); 1940 1941 BeamformerPanelKind new_panel_kind = beamformer_panel_kind_from_string(beamformer_registers()->string); 1942 beamformer_ui_push_panel(panel, new_panel_kind); 1943 }break; 1944 1945 case BeamformerCommandKind_SplitTree:{ 1946 BeamformerUIPanel *drag = (BeamformerUIPanel *)beamformer_registers()->tree_node; 1947 BeamformerUIPanel *left = (BeamformerUIPanel *)beamformer_registers()->split_left_tree; 1948 BeamformerUIPanel *right = (BeamformerUIPanel *)beamformer_registers()->split_right_tree; 1949 Axis2 axis = beamformer_registers()->split_axis; 1950 1951 BeamformerUIPanel *new_split = beamformer_ui_push_panel(0, BeamformerPanelKind_Split); 1952 BeamformerUIPanel *new_tab_group = beamformer_ui_push_panel(0, BeamformerPanelKind_TabGroup); 1953 beamformer_panel_group_insert_at(new_tab_group, drag, 0); 1954 1955 BeamformerUIPanel *target = 0; 1956 u32 target_child_index = 0; 1957 f32 new_split_pct = 0.5f; 1958 1959 if (left == 0 || right == 0) { 1960 // NOTE(rnp): split on edge of window 1961 target = left ? left : right; 1962 target_child_index = left ? 0 : 1; 1963 1964 if (target->kind == BeamformerPanelKind_TabGroup) { 1965 new_split->kind = BeamformerPanelKind_TabGroup; 1966 new_split->u.tab_focus = target->u.tab_focus; 1967 } 1968 1969 for (BeamformerUIPanel *child = target->last_child, *next; child; child = next) { 1970 next = child->previous_sibling; 1971 beamformer_panel_group_insert_at(new_split, child, 0); 1972 } 1973 1974 beamformer_panel_group_insert_at(target, new_tab_group, 0); 1975 } else if (((drag == left) && right->kind == BeamformerPanelKind_Split) || 1976 ((drag == right) && left->kind == BeamformerPanelKind_Split)) 1977 { 1978 // NOTE(rnp): split on internal split 1979 target = left == drag ? right : left; 1980 target_child_index = 1; 1981 new_split_pct = 1.f / 3.f; 1982 beamformer_panel_group_insert_at(new_split, new_tab_group, 0); 1983 beamformer_panel_group_insert_at(new_split, target->last_child, 1); 1984 } else { 1985 // NOTE(rnp): TabGroup Split 1986 target = left == drag ? right : left; 1987 target_child_index = left == drag ? 1 : 0; 1988 assert(target->kind == BeamformerPanelKind_TabGroup); 1989 1990 new_split->kind = BeamformerPanelKind_TabGroup; 1991 new_split->u.tab_focus = target->u.tab_focus; 1992 for (BeamformerUIPanel *child = target->last_child, *next; child; child = next) { 1993 next = child->previous_sibling; 1994 beamformer_panel_group_insert_at(new_split, child, 0); 1995 } 1996 1997 beamformer_panel_group_insert_at(target, new_tab_group, 0); 1998 } 1999 2000 beamformer_panel_group_insert_at(target, new_split, target_child_index); 2001 if (target->kind == BeamformerPanelKind_Split) { 2002 new_split->u.split.axis = target->u.split.axis; 2003 new_split->u.split.fraction = target->u.split.fraction; 2004 } 2005 target->kind = BeamformerPanelKind_Split; 2006 target->u.split.axis = axis; 2007 target->u.split.fraction = new_split_pct; 2008 }break; 2009 2010 } 2011 } 2012 } 2013 2014 ctx->render_shader_updated = 0; 2015 }