ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: 47fbb837a68eb7a2bdeae47cae86b680d999fa74
Parent: 354fdb6b6e626ad3d3df69d902282f7768300a77
Author: Randy Palamar
Date:   Thu, 13 Aug 2026 06:33:59 -0700

core: move some push constant GPU pointers into spec constants

these addresses are fixed at runtime so might as well force them
to be constant data in the compiled shader as well.

Diffstat:
Mbeamformer.meta | 14+++++++-------
Mbeamformer_core.c | 18+++++++++---------
Mbuild.c | 1+
Mgenerated/beamformer.c | 67+++++++++++++++++++++++++++++++++++--------------------------------
Mshaders/das.glsl | 10+++++-----
Mshaders/decode.glsl | 12+++++++-----
Mshaders/filter.glsl | 7++++---
Mutil.c | 6++++++
8 files changed, 74 insertions(+), 61 deletions(-)

diff --git a/beamformer.meta b/beamformer.meta @@ -355,11 +355,12 @@ @Flags { CooperativeMatrix + UseSharedMemory } @Bake { - [UseSharedMemory B32] + [HadamardBuffer U64] [DecodeMode U32] [OutputChannelStride U32] [OutputSampleStride U32] @@ -374,9 +375,8 @@ @PushConstants { - [hadamard_buffer U64] - [rf_buffer U64] - [output_buffer U64] + [rf_buffer U64] + [output_buffer U64] } } @@ -395,10 +395,11 @@ @Bake { + [FilterCoefficients U64] + [FilterLength U32] [SamplingFrequency F32] [DemodulationFrequency F32] [DecimationRate U32] - [FilterLength U32] [SampleCount U32] [BatchSampleCount U32] [InputChannelStride U32] @@ -412,7 +413,6 @@ @PushConstants { [input_data U64] - [filter_coefficients U64] [output_element_offset U32] } } @@ -437,6 +437,7 @@ @Bake { + [ArrayParameters U64] [AcquisitionKind U32] [Sparse B32] [AcquisitionCount S32] @@ -465,7 +466,6 @@ [xdc_transform M4] [voxel_transform M4] [xdc_element_pitch V2] - [array_parameters U64] [output_frame U64] [incoherent_frame U64] [rf_element_offset U32] diff --git a/beamformer_core.c b/beamformer_core.c @@ -604,6 +604,9 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A case BeamformerShaderKind_Decode:{ BeamformerDecodeBakeParameters *db = &sd->bake.Decode; + db->HadamardBuffer = cp->array_parameters.gpu_pointer + + offsetof(BeamformerComputeArrayParameters, decode_hadamard); + u32 decode_sample_count = input_sample_count; db->DecodeMode = pb->parameters.decode_mode; db->TransmitCount = pb->parameters.acquisition_count; @@ -636,7 +639,7 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A sd->dispatch.y = chunk_channel_count / db->CooperativeMatrixM; sd->dispatch.z = decode_sample_count; } else if (db->TransmitCount > 40) { - db->UseSharedMemory = 1; + sd->compile_flags |= BeamformerDecodeCompileFlags_UseSharedMemory; if (db->TransmitCount == 48) db->ToProcess = db->TransmitCount / 16; @@ -673,7 +676,9 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A time_offset += f->time_delay; BeamformerFilterBakeParameters *fb = &sd->bake.Filter; - fb->FilterLength = (u32)f->length; + + fb->FilterCoefficients = f->buffer.gpu_pointer; + fb->FilterLength = (u32)f->length; fb->SampleCount = input_sample_count; fb->DecimationRate = demod ? decimation_rate : 1; @@ -729,6 +734,7 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A db->TransmitAngle = pb->parameters.focal_vector.E[0]; db->FocusDepth = pb->parameters.focal_vector.E[1]; db->ReadiGroupCount = pb->parameters.readi_group_count; + db->ArrayParameters = cp->array_parameters.gpu_pointer; db->TransmitReceiveOrientation = pb->parameters.transmit_receive_orientation; cp->readi_group = pb->parameters.readi_group; @@ -1131,10 +1137,7 @@ do_compute_shader(BeamformerCtx *ctx, GPUCommandList cmd, BeamformerComputePlan switch (cp->pipeline.shaders[shader_slot]) { case BeamformerShaderKind_Decode:{ - BeamformerDecodePushConstants pc = { - .hadamard_buffer = cp->array_parameters.gpu_pointer + offsetof(BeamformerComputeArrayParameters, decode_hadamard), - .rf_buffer = pp_input_pointer, - }; + BeamformerDecodePushConstants pc = {.rf_buffer = pp_input_pointer}; if ((shader_slot + 1) == das_index) pc.output_buffer = pp_das_pointer; else pc.output_buffer = pp_output_pointer; @@ -1157,9 +1160,7 @@ do_compute_shader(BeamformerCtx *ctx, GPUCommandList cmd, BeamformerComputePlan BeamformerDataKind output_data_kind = cp->shader_descriptors[shader_slot].output_data_kind; u64 element_size = beamformer_data_kind_byte_size[output_data_kind]; - u32 filter_slot = cp->pipeline.parameters[shader_slot].filter_slot; BeamformerFilterPushConstants pc = { - .filter_coefficients = cp->filters[filter_slot].buffer.gpu_pointer, .input_data = shader_slot == 0 ? rf_pointer : pp_input_pointer, .output_element_offset = output_index * pp_size / element_size, }; @@ -1193,7 +1194,6 @@ do_compute_shader(BeamformerCtx *ctx, GPUCommandList cmd, BeamformerComputePlan .output_size_z = cp->output_points.z, .channel_offset = channel_offset, .readi_group = cp->readi_group, - .array_parameters = cp->array_parameters.gpu_pointer, }; memory_copy(pc.voxel_transform.E, cp->das_voxel_transform.E, sizeof(pc.voxel_transform)); memory_copy(pc.xdc_transform.E, cp->xdc_transform.E, sizeof(pc.xdc_transform)); diff --git a/build.c b/build.c @@ -6,6 +6,7 @@ * [ ]: cross compile/override baked compiler * [ ]: msvc build doesn't detect out of date files correctly * [ ]: seperate dwarf debug info + * [ ]: bug: need to account for member alignment in structs (eg. u64 in bake parameters) */ #define BEAMFORMER_IMPORT function diff --git a/generated/beamformer.c b/generated/beamformer.c @@ -135,6 +135,7 @@ typedef enum { typedef enum { BeamformerDecodeCompileFlags_CooperativeMatrix = 1 << 0, + BeamformerDecodeCompileFlags_UseSharedMemory = 1 << 1, } BeamformerDecodeCompileFlags; typedef enum { @@ -176,7 +177,7 @@ typedef enum { } BeamformerShaderKind; typedef struct { - b32 UseSharedMemory; + u64 HadamardBuffer; u32 DecodeMode; u32 OutputChannelStride; u32 OutputSampleStride; @@ -190,10 +191,11 @@ typedef struct { } BeamformerDecodeBakeParameters; typedef struct { + u64 FilterCoefficients; + u32 FilterLength; f32 SamplingFrequency; f32 DemodulationFrequency; u32 DecimationRate; - u32 FilterLength; u32 SampleCount; u32 BatchSampleCount; u32 InputChannelStride; @@ -205,6 +207,7 @@ typedef struct { } BeamformerFilterBakeParameters; typedef struct { + u64 ArrayParameters; u32 AcquisitionKind; b32 Sparse; i32 AcquisitionCount; @@ -238,14 +241,12 @@ typedef struct { } BeamformerReshapeBakeParameters; typedef struct { - u64 hadamard_buffer; u64 rf_buffer; u64 output_buffer; } BeamformerDecodePushConstants; typedef struct { u64 input_data; - u64 filter_coefficients; u32 output_element_offset; } BeamformerFilterPushConstants; @@ -253,7 +254,6 @@ typedef struct { m4 xdc_transform; m4 voxel_transform; v2 xdc_element_pitch; - u64 array_parameters; u64 output_frame; u64 incoherent_frame; u32 rf_element_offset; @@ -620,8 +620,7 @@ typedef enum { read_only global MetaStructMember *meta_struct_members_by_id[] = { (MetaStructMember []){ - {14, 0, 1, 0}, - {18, 4, 1, 0}, + {17, 0, 1, 0}, {18, 8, 1, 0}, {18, 12, 1, 0}, {18, 16, 1, 0}, @@ -631,13 +630,13 @@ read_only global MetaStructMember *meta_struct_members_by_id[] = { {18, 32, 1, 0}, {18, 36, 1, 0}, {18, 40, 1, 0}, + {18, 44, 1, 0}, }, (MetaStructMember []){ - {8, 0, 1, 0}, - {8, 4, 1, 0}, + {17, 0, 1, 0}, {18, 8, 1, 0}, - {18, 12, 1, 0}, - {18, 16, 1, 0}, + {8, 12, 1, 0}, + {8, 16, 1, 0}, {18, 20, 1, 0}, {18, 24, 1, 0}, {18, 28, 1, 0}, @@ -645,26 +644,29 @@ read_only global MetaStructMember *meta_struct_members_by_id[] = { {18, 36, 1, 0}, {18, 40, 1, 0}, {18, 44, 1, 0}, + {18, 48, 1, 0}, + {18, 52, 1, 0}, }, (MetaStructMember []){ - {18, 0, 1, 0}, - {14, 4, 1, 0}, - {10, 8, 1, 0}, - {10, 12, 1, 0}, + {17, 0, 1, 0}, + {18, 8, 1, 0}, + {14, 12, 1, 0}, {10, 16, 1, 0}, {10, 20, 1, 0}, - {8, 24, 1, 0}, - {8, 28, 1, 0}, + {10, 24, 1, 0}, + {10, 28, 1, 0}, {8, 32, 1, 0}, {8, 36, 1, 0}, - {18, 40, 1, 0}, + {8, 40, 1, 0}, {8, 44, 1, 0}, - {14, 48, 1, 0}, - {18, 52, 1, 0}, + {18, 48, 1, 0}, + {8, 52, 1, 0}, {14, 56, 1, 0}, - {8, 60, 1, 0}, - {8, 64, 1, 0}, - {18, 68, 1, 0}, + {18, 60, 1, 0}, + {14, 64, 1, 0}, + {8, 68, 1, 0}, + {8, 72, 1, 0}, + {18, 76, 1, 0}, }, (MetaStructMember []){ {18, 0, 1, 0}, @@ -681,7 +683,7 @@ read_only global MetaStructMember *meta_struct_members_by_id[] = { read_only global str8 *meta_struct_member_names_by_id[] = { (str8 []){ - str8_comp("UseSharedMemory"), + str8_comp("HadamardBuffer"), str8_comp("DecodeMode"), str8_comp("OutputChannelStride"), str8_comp("OutputSampleStride"), @@ -694,10 +696,11 @@ read_only global str8 *meta_struct_member_names_by_id[] = { str8_comp("CooperativeMatrixK"), }, (str8 []){ + str8_comp("FilterCoefficients"), + str8_comp("FilterLength"), str8_comp("SamplingFrequency"), str8_comp("DemodulationFrequency"), str8_comp("DecimationRate"), - str8_comp("FilterLength"), str8_comp("SampleCount"), str8_comp("BatchSampleCount"), str8_comp("InputChannelStride"), @@ -708,6 +711,7 @@ read_only global str8 *meta_struct_member_names_by_id[] = { str8_comp("OutputTransmitStride"), }, (str8 []){ + str8_comp("ArrayParameters"), str8_comp("AcquisitionKind"), str8_comp("Sparse"), str8_comp("AcquisitionCount"), @@ -741,9 +745,9 @@ read_only global str8 *meta_struct_member_names_by_id[] = { }; read_only global MetaStructInfo meta_struct_info_by_id[] = { - {str8_comp("DecodeBakeParameters"), 11, 44, 0}, - {str8_comp("FilterBakeParameters"), 12, 48, 0}, - {str8_comp("DASBakeParameters"), 18, 72, 0}, + {str8_comp("DecodeBakeParameters"), 11, 48, 0}, + {str8_comp("FilterBakeParameters"), 13, 56, 0}, + {str8_comp("DASBakeParameters"), 19, 80, 0}, {str8_comp("ReshapeBakeParameters"), 9, 36, 0}, }; @@ -819,10 +823,10 @@ read_only global str8 beamformer_shader_global_header_strings[] = { "\n"), str8_comp("" "#define CooperativeMatrix ((CompileFlags & (1 << 0)) != 0)\n" + "#define UseSharedMemory ((CompileFlags & (1 << 1)) != 0)\n" "\n"), str8_comp("" "layout(push_constant, std430) uniform PushConstants {\n" - " uint64_t hadamard_buffer;\n" " uint64_t rf_buffer;\n" " uint64_t output_buffer;\n" "};\n" @@ -841,7 +845,6 @@ read_only global str8 beamformer_shader_global_header_strings[] = { str8_comp("" "layout(push_constant, std430) uniform PushConstants {\n" " uint64_t input_data;\n" - " uint64_t filter_coefficients;\n" " uint32_t output_element_offset;\n" "};\n" "\n"), @@ -896,7 +899,6 @@ read_only global str8 beamformer_shader_global_header_strings[] = { " f32mat4 xdc_transform;\n" " f32mat4 voxel_transform;\n" " f32vec2 xdc_element_pitch;\n" - " uint64_t array_parameters;\n" " uint64_t output_frame;\n" " uint64_t incoherent_frame;\n" " uint32_t rf_element_offset;\n" @@ -1002,6 +1004,7 @@ read_only global i32 beamformer_shader_header_vector_lengths[] = { read_only global str8 *beamformer_shader_compile_flag_names[] = { (str8 []){ str8_comp("CooperativeMatrix"), + str8_comp("UseSharedMemory"), }, (str8 []){ str8_comp("ComplexFilter"), @@ -1021,7 +1024,7 @@ read_only global str8 *beamformer_shader_compile_flag_names[] = { }; read_only global u8 beamformer_shader_compile_flag_counts[] = { - 1, + 2, 2, 1, 0, diff --git a/shaders/das.glsl b/shaders/das.glsl @@ -170,14 +170,14 @@ float cylindrical_wave_transmit_distance(const vec3 point, const float focal_dep u16 tx_rx_orientation_for_acquisition(const s16 acquisition) { u16 result = u16(TransmitReceiveOrientation); - ComputeArrayParametersReference dp = ComputeArrayParametersReference(array_parameters); + ComputeArrayParametersReference dp = ComputeArrayParametersReference(ArrayParameters); if (!SingleOrientation) result = dp.transmit_receive_orientations[acquisition]; return result; } f32vec2 focal_vector_for_acquisition(const s16 acquisition) { - ComputeArrayParametersReference dp = ComputeArrayParametersReference(array_parameters); + ComputeArrayParametersReference dp = ComputeArrayParametersReference(ArrayParameters); f32vec2 result = SingleFocus ? f32vec2(TransmitAngle, FocusDepth) : dp.focal_vectors[acquisition]; return result; } @@ -230,7 +230,7 @@ RESULT_TYPE RCA(const vec3 world_point) RESULT_TYPE HERCULES(const vec3 world_point) { - ComputeArrayParametersReference dp = ComputeArrayParametersReference(array_parameters); + ComputeArrayParametersReference dp = ComputeArrayParametersReference(ArrayParameters); const uint16_t tx_rx_orientation = tx_rx_orientation_for_acquisition(int16_t(0)); const bool rx_cols = RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Columns; @@ -287,7 +287,7 @@ RESULT_TYPE FORCES(const vec3 xdc_world_point) { RESULT_TYPE result = RESULT_TYPE(0); - ComputeArrayParametersReference dp = ComputeArrayParametersReference(array_parameters); + ComputeArrayParametersReference dp = ComputeArrayParametersReference(ArrayParameters); float z_delta_squared = xdc_world_point.z * xdc_world_point.z; float transmit_y_delta = xdc_world_point.y - xdc_element_pitch.y * ChannelCount / 2; @@ -322,7 +322,7 @@ RESULT_TYPE READI_FORCES(const vec3 xdc_world_point) { RESULT_TYPE result = RESULT_TYPE(0); - ComputeArrayParametersReference dp = ComputeArrayParametersReference(array_parameters); + ComputeArrayParametersReference dp = ComputeArrayParametersReference(ArrayParameters); float z_delta_squared = xdc_world_point.z * xdc_world_point.z; float transmit_y_delta = xdc_world_point.y - xdc_element_pitch.y * ChannelCount / 2; diff --git a/shaders/decode.glsl b/shaders/decode.glsl @@ -50,10 +50,11 @@ void run_decode_large(void) for (s32 i = 0; i < ToProcess; i++) result[i] = OutputDataType(0); + Hadamard h = Hadamard(HadamardBuffer); for (s32 j = 0; j < TransmitCount; j++) { OutputDataType s = OutputDataType(rf[gl_LocalInvocationID.y][j]); for (s32 i = 0; i < ToProcess; i++) - result[i] += s * Hadamard(hadamard_buffer).x[TransmitCount * j + (i + transmit)]; + result[i] += s * h.x[TransmitCount * j + (i + transmit)]; } for (uint i = 0; i < ToProcess; i++) @@ -93,6 +94,7 @@ void run_decode_coop(void) u32 offset = ChunkChannelCount * TransmitCount * time_sample; + Hadamard h = Hadamard(HadamardBuffer); for (u32 k = 0; k < TransmitCount; k += CooperativeMatrixK) { u32 rf_tile_row = CooperativeMatrixM * tile_index.y; u32 rf_tile_col = k; @@ -101,9 +103,8 @@ void run_decode_coop(void) u32 hadamard_tile_row = k; u32 hadamard_tile_col = CooperativeMatrixN * tile_index.x; - coopMatLoad(hadamard_matrix, Hadamard(hadamard_buffer).x, - TransmitCount * hadamard_tile_row + hadamard_tile_col, TransmitCount, - gl_CooperativeMatrixLayoutRowMajor); + coopMatLoad(hadamard_matrix, h.x, TransmitCount * hadamard_tile_row + hadamard_tile_col, + TransmitCount, gl_CooperativeMatrixLayoutRowMajor); result = coopMatMulAdd(rf_matrix, hadamard_matrix, result); } @@ -132,10 +133,11 @@ void run_decode_small(void) for (s32 j = 0; j < TransmitCount; j++) result[j] = OutputDataType(0); + Hadamard h = Hadamard(HadamardBuffer); for (s32 i = 0; i < TransmitCount; i++) { OutputDataType s = OutputDataType(rf[i]); for (s32 j = 0; j < TransmitCount; j++) { - result[j] += s * Hadamard(hadamard_buffer).x[TransmitCount * i + j]; + result[j] += s * h.x[TransmitCount * i + j]; } } diff --git a/shaders/filter.glsl b/shaders/filter.glsl @@ -111,11 +111,12 @@ void main() } barrier(); + Filter f = Filter(FilterCoefficients); if (out_sample < SampleCount / DecimationRate) { RESULT_TYPE result = RESULT_TYPE(0); - uint offset = DecimationRate * thread_index; - for (uint j = 0; j < FilterLength; j++) - result += apply_filter(rf[offset + j], Filter(filter_coefficients).values[j]); + u32 offset = DecimationRate * thread_index; + for (u32 j = 0; j < FilterLength; j++) + result += apply_filter(rf[offset + j], f.values[j]); u32 out_offset = OutputChannelStride * channel + OutputTransmitStride * transmit + diff --git a/util.c b/util.c @@ -674,6 +674,12 @@ stream_append_struct_member(Stream *s, MetaStructMember *m, void *struct_base) memory_copy(&value, ((u8 *)struct_base + m->offset), sizeof(value)); stream_append_u64(s, value); }break; + case MetaKind_U64:{ + u64 value; + memory_copy(&value, ((u8 *)struct_base + m->offset), sizeof(value)); + stream_append_str8(s, str8("0x")); + stream_append_hex_u64(s, value); + }break; case MetaKind_S32:{ i32 value; memory_copy(&value, ((u8 *)struct_base + m->offset), sizeof(value));