Commit: 34fd201e170e415c0b021995bba191e2783b3938
Parent: 8b2e08c3def0f7c6fcc1166a27c802b1e2dd2308
Author: Randy Palamar
Date: Thu, 15 Oct 2026 20:00:54 -0700
das: checkpoint on multi-tile beamforming
Diffstat:
5 files changed, 99 insertions(+), 57 deletions(-)
diff --git a/beamformer.meta b/beamformer.meta
@@ -430,42 +430,46 @@
[Hadamard U32]
[IncoherentFrame U32]
[SparseElements U32]
+ [TransducerTransforms U32]
[TransmitReceiveOrientations U32]
[AcquisitionKind U32]
[AcquisitionCount S32]
- [ReceiveChannelCount S32]
- [ChunkChannelCount S32]
[SampleCount S32]
[AcquisitionByteStride U32]
[ChannelByteStride U32]
+ [ReceiveChannelCount U16]
+ [TransmitChannelCount U16]
+ [ReceiveTileCount U16]
+ [TransmitTileCount U16]
+ [ChunkChannelCount U16]
+ [TransmitReceiveOrientation U16]
+
+ [InterpolationMode U16]
+
+ [ReadiGroupCount U16]
[SamplingFrequency F32]
[DemodulationFrequency F32]
[SpeedOfSound F32]
[TimeOffset F32]
- [InterpolationMode U32]
[FNumber F32]
- [TransmitReceiveOrientation U32]
[FocusDepth F32]
[TransmitAngle F32]
[OutputSizeX U32]
[OutputSizeY U32]
[OutputSizeZ U32]
-
- [ReadiGroupCount U32]
}
@PushConstants
{
- [xdc_transform M4]
[voxel_transform M4]
[xdc_element_pitch V2]
[rf_data U64]
[output_frame U64]
- [channel_offset S32]
+ [channel_offset U32]
[readi_group U32]
}
}
diff --git a/beamformer_core.c b/beamformer_core.c
@@ -924,13 +924,19 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A
db->FNumber = pb->parameters.f_number;
db->AcquisitionKind = pb->parameters.acquisition_kind;
db->SampleCount = node->rf_dimensions.E[BeamformerRFDimension_Samples];
- db->ReceiveChannelCount = pb->parameters.channel_count;
db->AcquisitionCount = node->rf_dimensions.E[BeamformerRFDimension_Events];
db->ChunkChannelCount = node->rf_dimensions.E[BeamformerRFDimension_Channels];
db->ChannelByteStride = input_stride.E[BeamformerRFDimension_Channels]
* beamformer_data_kind_byte_size[node->input_data_kind];
db->AcquisitionByteStride = input_stride.E[BeamformerRFDimension_Events]
* beamformer_data_kind_byte_size[node->input_data_kind];
+
+ // TODO(rnp): pass these in
+ db->ReceiveChannelCount = pb->parameters.channel_count;
+ db->TransmitChannelCount = pb->parameters.channel_count;
+ db->ReceiveTileCount = 1;
+ db->TransmitTileCount = 1;
+
db->InterpolationMode = pb->parameters.interpolation_mode;
db->TransmitAngle = pb->parameters.focal_vector.E[0];
db->FocusDepth = pb->parameters.focal_vector.E[1];
@@ -940,9 +946,6 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A
db->OutputSizeZ = cp->output_points.z;
db->TransmitReceiveOrientation = pb->parameters.transmit_receive_orientation;
- // NOTE(rnp): old gcc will miscompile an assignment
- memory_copy(cp->xdc_transform.E, pb->parameters.xdc_transform.E, sizeof(cp->xdc_transform));
-
cp->voxel_transform = m4_mul(cp->ui_voxel_transform, pb->parameters.das_voxel_transform);
cp->xdc_element_pitch = pb->parameters.xdc_element_pitch;
@@ -963,32 +966,33 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A
sd->layout = layout_for_output(cp->output_points);
sd->dispatch = dispatch_for_output(sd->layout, cp->output_points);
+ sd->uses_heap = 1;
+ db->TransducerTransforms = gpu_resource_push(resource_builder, m4, db->TransmitTileCount * db->ReceiveTileCount,
+ .data = &pb->parameters.xdc_transform,
+ .name = str8("transducer_transforms"));
+
if (!single_focus) {
db->FocalVectors = gpu_resource_push(resource_builder, v2, db->AcquisitionCount,
.data = pb->focal_vectors,
.name = str8("focal_vectors"));
- sd->uses_heap = 1;
}
if (!single_orientation) {
db->TransmitReceiveOrientations = gpu_resource_push(resource_builder, u8, db->AcquisitionCount,
.data = pb->transmit_receive_orientations,
.name = str8("transmit_receive_orientations"));
- sd->uses_heap = 1;
}
if (sparse) {
db->SparseElements = gpu_resource_push(resource_builder, i16, db->AcquisitionCount,
.data = pb->sparse_elements,
.name = str8("sparse_elements"));
- sd->uses_heap = 1;
}
if (pb->parameters.coherency_weighting) {
db->IncoherentFrame = gpu_resource_push(resource_builder, u32, 0,
.size = beamformer_incoherent_frame_byte_size(cp->output_points, das_data_kind),
.name = str8("incoherent_buffer"));
- sd->uses_heap = 1;
}
cp->readi_group = pb->parameters.readi_group;
@@ -997,7 +1001,6 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A
db->Hadamard = gpu_resource_push(resource_builder, f16, order * order,
.data = make_hadamard_transpose(scratch, order, 0),
.name = str8("readi_hadamard"));
- sd->uses_heap = 1;
}
}break;
@@ -1333,7 +1336,6 @@ do_compute_shader(GPUCommandList cmd, BeamformerComputePlan *cp, BeamformerFrame
.readi_group = cp->readi_group,
};
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));
gpu_command_push_constants(cmd, 0, sizeof(pc), &pc);
gpu_command_dispatch_compute(cmd, dispatch);
diff --git a/generated/beamformer.c b/generated/beamformer.c
@@ -215,27 +215,31 @@ typedef struct {
u32 Hadamard;
u32 IncoherentFrame;
u32 SparseElements;
+ u32 TransducerTransforms;
u32 TransmitReceiveOrientations;
u32 AcquisitionKind;
i32 AcquisitionCount;
- i32 ReceiveChannelCount;
- i32 ChunkChannelCount;
i32 SampleCount;
u32 AcquisitionByteStride;
u32 ChannelByteStride;
+ u16 ReceiveChannelCount;
+ u16 TransmitChannelCount;
+ u16 ReceiveTileCount;
+ u16 TransmitTileCount;
+ u16 ChunkChannelCount;
+ u16 TransmitReceiveOrientation;
+ u16 InterpolationMode;
+ u16 ReadiGroupCount;
f32 SamplingFrequency;
f32 DemodulationFrequency;
f32 SpeedOfSound;
f32 TimeOffset;
- u32 InterpolationMode;
f32 FNumber;
- u32 TransmitReceiveOrientation;
f32 FocusDepth;
f32 TransmitAngle;
u32 OutputSizeX;
u32 OutputSizeY;
u32 OutputSizeZ;
- u32 ReadiGroupCount;
} BeamformerDASBakeParameters;
typedef struct {
@@ -267,12 +271,11 @@ typedef struct {
} BeamformerFilterPushConstants;
typedef struct {
- m4 xdc_transform;
m4 voxel_transform;
v2 xdc_element_pitch;
u64 rf_data;
u64 output_frame;
- i32 channel_offset;
+ u32 channel_offset;
u32 readi_group;
} BeamformerDASPushConstants;
@@ -521,22 +524,26 @@ read_only MetaStructMember *meta_struct_members_by_id[] = {
{18, 12, 1, 0},
{18, 16, 1, 0},
{18, 20, 1, 0},
- {10, 24, 1, 0},
+ {18, 24, 1, 0},
{10, 28, 1, 0},
{10, 32, 1, 0},
- {10, 36, 1, 0},
+ {18, 36, 1, 0},
{18, 40, 1, 0},
- {18, 44, 1, 0},
- {8, 48, 1, 0},
- {8, 52, 1, 0},
- {8, 56, 1, 0},
+ {19, 44, 1, 0},
+ {19, 46, 1, 0},
+ {19, 48, 1, 0},
+ {19, 50, 1, 0},
+ {19, 52, 1, 0},
+ {19, 54, 1, 0},
+ {19, 56, 1, 0},
+ {19, 58, 1, 0},
{8, 60, 1, 0},
- {18, 64, 1, 0},
+ {8, 64, 1, 0},
{8, 68, 1, 0},
- {18, 72, 1, 0},
+ {8, 72, 1, 0},
{8, 76, 1, 0},
{8, 80, 1, 0},
- {18, 84, 1, 0},
+ {8, 84, 1, 0},
{18, 88, 1, 0},
{18, 92, 1, 0},
{18, 96, 1, 0},
@@ -593,27 +600,31 @@ read_only str8 *meta_struct_member_names_by_id[] = {
str8_comp("Hadamard"),
str8_comp("IncoherentFrame"),
str8_comp("SparseElements"),
+ str8_comp("TransducerTransforms"),
str8_comp("TransmitReceiveOrientations"),
str8_comp("AcquisitionKind"),
str8_comp("AcquisitionCount"),
- str8_comp("ReceiveChannelCount"),
- str8_comp("ChunkChannelCount"),
str8_comp("SampleCount"),
str8_comp("AcquisitionByteStride"),
str8_comp("ChannelByteStride"),
+ str8_comp("ReceiveChannelCount"),
+ str8_comp("TransmitChannelCount"),
+ str8_comp("ReceiveTileCount"),
+ str8_comp("TransmitTileCount"),
+ str8_comp("ChunkChannelCount"),
+ str8_comp("TransmitReceiveOrientation"),
+ str8_comp("InterpolationMode"),
+ str8_comp("ReadiGroupCount"),
str8_comp("SamplingFrequency"),
str8_comp("DemodulationFrequency"),
str8_comp("SpeedOfSound"),
str8_comp("TimeOffset"),
- str8_comp("InterpolationMode"),
str8_comp("FNumber"),
- str8_comp("TransmitReceiveOrientation"),
str8_comp("FocusDepth"),
str8_comp("TransmitAngle"),
str8_comp("OutputSizeX"),
str8_comp("OutputSizeY"),
str8_comp("OutputSizeZ"),
- str8_comp("ReadiGroupCount"),
},
(str8 []){
str8_comp("IncoherentSum"),
@@ -636,7 +647,7 @@ read_only str8 *meta_struct_member_names_by_id[] = {
read_only MetaStructInfo meta_struct_info_by_id[] = {
{str8_comp("DecodeBakeParameters"), 11, 44, 0},
{str8_comp("FilterBakeParameters"), 13, 52, 0},
- {str8_comp("DASBakeParameters"), 25, 100, 0},
+ {str8_comp("DASBakeParameters"), 29, 100, 0},
{str8_comp("CoherencyWeightingBakeParameters"), 3, 12, 0},
{str8_comp("ReshapeBakeParameters"), 9, 36, 0},
};
@@ -764,12 +775,11 @@ read_only str8 beamformer_shader_global_header_strings[] = {
"\n"),
str8_comp(""
"layout(push_constant, std430) uniform PushConstants {\n"
- " f32mat4 xdc_transform;\n"
" f32mat4 voxel_transform;\n"
" f32vec2 xdc_element_pitch;\n"
" uint64_t rf_data;\n"
" uint64_t output_frame;\n"
- " int32_t channel_offset;\n"
+ " uint32_t channel_offset;\n"
" uint32_t readi_group;\n"
"};\n"
"\n"),
diff --git a/shaders/das.glsl b/shaders/das.glsl
@@ -31,12 +31,6 @@
#define RESULT_STORE(a) (a)
#endif
-// NOTE(rnp): we don't want das to get recompiled when it isn't actually using the Heap
-// but we also don't want to check everywhere in here for the existence of the Heap
-#ifndef HeapBase
- #define HeapBase u64(0)
-#endif
-
layout(std430, buffer_reference) readonly buffer Input { InputDataType x[]; };
layout(std430, buffer_reference) buffer Output { OutputDataType x[]; };
@@ -52,6 +46,9 @@ layout(std430, buffer_reference) readonly buffer F32V4 { f32vec4 x[]; };
layout(std430, buffer_reference) readonly buffer F16V2 { f16vec2 x[]; };
layout(std430, buffer_reference) readonly buffer F16V4 { f16vec4 x[]; };
+// NOTE(rnp): don't mark this readonly, scalar loads perform worse where we are using this
+layout(std430, buffer_reference) buffer M4 { mat4 x[]; };
+
#define RX_ORIENTATION(tx_rx) bitfieldExtract((tx_rx), 0, 4)
#define TX_ORIENTATION(tx_rx) bitfieldExtract((tx_rx), 4, 4)
@@ -153,7 +150,7 @@ SAMPLE_TYPE sample_rf(const u64 rf_pointer, const f32 index)
{
SAMPLE_TYPE result = SAMPLE_TYPE(0);
- switch (InterpolationMode) {
+ switch (u32(InterpolationMode)) {
case InterpolationMode_Nearest:{
if (index >= 0.f && index < (f32(SampleCount) - 0.5f))
result = rotate_iq(Input(rf_pointer + InputDataKindByteSize * u32(round(index))).x[0], index / SamplingFrequency);
@@ -261,9 +258,23 @@ f32 rca_transmit_distance(const vec3 world_point, const vec2 focal_vector, const
return result;
}
+mat4 transducer_transform(const u32 acquisition)
+{
+ const u32 receive_tile_channels = ReceiveChannelCount / ReceiveTileCount;
+ const u32 receive_index = channel_offset / receive_tile_channels;
+
+ const u32 transmit_tile_channels = TransmitChannelCount / TransmitTileCount;
+ const u32 transmit_index = acquisition / transmit_tile_channels;
+
+ mat4 result = M4(HeapBase + TransducerTransforms).x[transmit_index * ReceiveTileCount + receive_index];
+ return result;
+}
+
RESULT_TYPE RCA(const vec3 world_point)
{
RESULT_TYPE result = RESULT_TYPE(0);
+
+ const mat4 xdc_transform = transducer_transform(0);
for (s32 acquisition = 0; acquisition < s32(AcquisitionCount); acquisition++) {
const u8 tx_rx_orientation = tx_rx_orientation_for_acquisition(acquisition);
const bool rx_rows = RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Rows;
@@ -292,6 +303,9 @@ RESULT_TYPE RCA(const vec3 world_point)
RESULT_TYPE HERCULES(const vec3 world_point)
{
+ // TODO(rnp): this whole thing needs have an outer loop over transmit tiles
+ const mat4 xdc_transform = transducer_transform(0);
+
const u8 tx_rx_orientation = tx_rx_orientation_for_acquisition(0);
const bool rx_cols = RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Columns;
const vec2 focal_vector = focal_vector_for_acquisition(0);
@@ -343,11 +357,12 @@ RESULT_TYPE FORCES(const vec3 world_point)
{
RESULT_TYPE result = RESULT_TYPE(0);
- const u8 tx_rx_orientation = tx_rx_orientation_for_acquisition(0);
- const s32 rx_axis = s32(RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Rows);
- const f32 element_pitch = xdc_element_pitch[rx_axis];
+ const mat4 xdc_transform = transducer_transform(0);
+ const u8 tx_rx_orientation = tx_rx_orientation_for_acquisition(0);
+ const u32 rx_axis = u32(RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Rows);
+ const f32 element_pitch = xdc_element_pitch[rx_axis];
// TODO(rnp): the sign of the origin offset might be flipped
- const f32 origin_offset = FocusDepth * tan(radians(TransmitAngle));
+ const f32 origin_offset = FocusDepth * tan(radians(TransmitAngle));
vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
f32 lateral_coord = xdc_world_point[rx_axis];
@@ -396,11 +411,12 @@ RESULT_TYPE READI_FORCES(const vec3 world_point)
{
RESULT_TYPE result = RESULT_TYPE(0);
- const u8 tx_rx_orientation = tx_rx_orientation_for_acquisition(0);
- const s32 rx_axis = s32(RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Rows);
- const f32 element_pitch = xdc_element_pitch[rx_axis];
+ const mat4 xdc_transform = transducer_transform(0);
+ const u8 tx_rx_orientation = tx_rx_orientation_for_acquisition(0);
+ const u32 rx_axis = u32(RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Rows);
+ const f32 element_pitch = xdc_element_pitch[rx_axis];
// TODO(rnp): the sign of the origin offset might be flipped
- const f32 origin_offset = FocusDepth * tan(radians(TransmitAngle));
+ const f32 origin_offset = FocusDepth * tan(radians(TransmitAngle));
vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
f32 lateral_coord = xdc_world_point[rx_axis];
diff --git a/util.c b/util.c
@@ -647,6 +647,11 @@ stream_append_struct_member(Stream *s, const MetaStructMember *m, const void *st
memory_copy(&value, ((u8 *)struct_base + m->offset), sizeof(value));
stream_append_str8(s, value ? str8("True") : str8("False"));
}break;
+ case MetaKind_U16:{
+ u16 value;
+ memory_copy(&value, ((u8 *)struct_base + m->offset), sizeof(value));
+ stream_append_u64(s, value);
+ }break;
case MetaKind_U32:{
u32 value;
memory_copy(&value, ((u8 *)struct_base + m->offset), sizeof(value));
@@ -658,6 +663,11 @@ stream_append_struct_member(Stream *s, const MetaStructMember *m, const void *st
stream_append_str8(s, str8("0x"));
stream_append_hex_u64(s, value);
}break;
+ case MetaKind_S16:{
+ i16 value;
+ memory_copy(&value, ((u8 *)struct_base + m->offset), sizeof(value));
+ stream_append_i64(s, value);
+ }break;
case MetaKind_S32:{
i32 value;
memory_copy(&value, ((u8 *)struct_base + m->offset), sizeof(value));