Commit: c7cd4986b63922418730083ba1ed8c05bb497ebe
Parent: 8bd1e76f64dbcb6774967455319a3fb107dcc418
Author: Randy Palamar
Date: Fri, 10 Oct 2025 15:16:48 -0600
shaders/das: add linear interpolation, use named interpolation modes
note: the if conditions in the switch cases are measurably faster
than the branchless thing before. Linear is most noticeable with a
4.3% improvement vs using the branchless computation
Diffstat:
7 files changed, 74 insertions(+), 28 deletions(-)
diff --git a/beamformer.c b/beamformer.c
@@ -453,6 +453,7 @@ das_ubo_from_beamformer_parameters(BeamformerComputePlan *cp, BeamformerDASUBO *
cp->das_bake.sample_count = bp->sample_count;
cp->das_bake.channel_count = bp->channel_count;
cp->das_bake.acquisition_count = bp->acquisition_count;
+ cp->das_bake.interpolation_mode = bp->interpolation_mode;
cp->das_bake.transmit_angle = bp->focal_vector[0];
cp->das_bake.focus_depth = bp->focal_vector[1];
cp->das_bake.transmit_receive_orientation = bp->transmit_receive_orientation;
@@ -470,9 +471,6 @@ das_ubo_from_beamformer_parameters(BeamformerComputePlan *cp, BeamformerDASUBO *
if (bp->das_shader_id == BeamformerAcquisitionKind_HERO_PA)
result |= BeamformerShaderDASFlags_ReceiveOnly;
- if (bp->interpolate)
- result |= BeamformerShaderDASFlags_Interpolate;
-
return result;
}
diff --git a/beamformer.meta b/beamformer.meta
@@ -19,11 +19,16 @@
[HERO_PA HERO-PA 0]
}
-@Expand(AcquisitionKind)
+@Table([name]) InterpolationMode
{
- @Enumeration(AcquisitionKind `$(name)`)
+ [Nearest]
+ [Linear]
+ [Cubic]
}
+@Expand(AcquisitionKind) @Enumeration(AcquisitionKind `$(name)`)
+@Expand(InterpolationMode) @Enumeration(InterpolationMode `$(name)`)
+
@Emit
{
`read_only global u8 beamformer_acquisition_kind_has_fixed_transmits[] = {`
@@ -38,6 +43,13 @@
`};`
}
+@Emit
+{
+ `read_only global s8 beamformer_interpolation_mode_strings[] = {`
+ @Expand(InterpolationMode) ` s8_comp("$(name)"),`
+ `};`
+}
+
@ShaderGroup Compute
{
@Shader CudaDecode
@@ -92,8 +104,9 @@
{
@Enumeration(AcquisitionKind)
@Enumeration(DataKind)
+ @Enumeration(InterpolationMode)
@Enumeration(RCAOrientation)
- @Flags([Fast Sparse Interpolate CoherencyWeighting ReceiveOnly SingleFocus SingleOrientation])
+ @Flags([Fast Sparse CoherencyWeighting ReceiveOnly SingleFocus SingleOrientation])
@Bake
{
@@ -101,6 +114,7 @@
@BakeInt(AcquisitionKind acquisition_kind )
@BakeInt(ChannelCount channel_count )
@BakeInt(DataKind data_kind )
+ @BakeInt(InterpolationMode interpolation_mode )
@BakeInt(SampleCount sample_count )
@BakeInt(TransmitReceiveOrientation transmit_receive_orientation)
diff --git a/beamformer_parameters.h b/beamformer_parameters.h
@@ -79,7 +79,7 @@ typedef enum {BEAMFORMER_CONSTANTS_LIST} BeamformerConstants;
X(speed_of_sound, float, , single, 1, "[m/s]") \
X(f_number, float, , single, 1, "F# (set to 0 to disable)") \
X(off_axis_pos, float, , single, 1, "[m] Position on screen normal to beamform in TPW/VLS/HERCULES") \
- X(interpolate, uint32_t, , uint32, 1, "Perform Cubic Interpolation of RF Samples") \
+ X(interpolation_mode, uint32_t, , uint32, 1, "Nearest, Linear, or Cubic Interpolation of RF Samples") \
X(coherency_weighting, uint32_t, , uint32, 1, "Apply coherency weighting to output data") \
X(beamform_plane, uint32_t, , uint32, 1, "Plane to Beamform in TPW/VLS/HERCULES") \
X(decimation_rate, uint32_t, , uint32, 1, "Number of times to decimate")
diff --git a/generated/beamformer.meta.c b/generated/beamformer.meta.c
@@ -45,6 +45,13 @@ typedef enum {
} BeamformerAcquisitionKind;
typedef enum {
+ BeamformerInterpolationMode_Nearest = 0,
+ BeamformerInterpolationMode_Linear = 1,
+ BeamformerInterpolationMode_Cubic = 2,
+ BeamformerInterpolationMode_Count,
+} BeamformerInterpolationMode;
+
+typedef enum {
BeamformerShaderDecodeFlags_DilateOutput = (1 << 0),
} BeamformerShaderDecodeFlags;
@@ -57,11 +64,10 @@ typedef enum {
typedef enum {
BeamformerShaderDASFlags_Fast = (1 << 0),
BeamformerShaderDASFlags_Sparse = (1 << 1),
- BeamformerShaderDASFlags_Interpolate = (1 << 2),
- BeamformerShaderDASFlags_CoherencyWeighting = (1 << 3),
- BeamformerShaderDASFlags_ReceiveOnly = (1 << 4),
- BeamformerShaderDASFlags_SingleFocus = (1 << 5),
- BeamformerShaderDASFlags_SingleOrientation = (1 << 6),
+ BeamformerShaderDASFlags_CoherencyWeighting = (1 << 2),
+ BeamformerShaderDASFlags_ReceiveOnly = (1 << 3),
+ BeamformerShaderDASFlags_SingleFocus = (1 << 4),
+ BeamformerShaderDASFlags_SingleOrientation = (1 << 5),
} BeamformerShaderDASFlags;
typedef enum {
@@ -123,6 +129,7 @@ typedef union {
u32 acquisition_kind;
u32 channel_count;
u32 data_kind;
+ u32 interpolation_mode;
u32 sample_count;
u32 transmit_receive_orientation;
f32 demodulation_frequency;
@@ -133,7 +140,7 @@ typedef union {
f32 time_offset;
f32 transmit_angle;
};
- u32 E[13];
+ u32 E[14];
} BeamformerShaderDASBakeParameters;
read_only global s8 beamformer_shader_names[] = {
@@ -223,6 +230,11 @@ read_only global s8 beamformer_shader_global_header_strings[] = {
"#define AcquisitionKind_Flash 10\n"
"#define AcquisitionKind_HERO_PA 11\n"
"\n"),
+ s8_comp(""
+ "#define InterpolationMode_Nearest 0\n"
+ "#define InterpolationMode_Linear 1\n"
+ "#define InterpolationMode_Cubic 2\n"
+ "\n"),
};
read_only global s8 *beamformer_shader_flag_strings[] = {
@@ -237,7 +249,6 @@ read_only global s8 *beamformer_shader_flag_strings[] = {
(s8 []){
s8_comp("Fast"),
s8_comp("Sparse"),
- s8_comp("Interpolate"),
s8_comp("CoherencyWeighting"),
s8_comp("ReceiveOnly"),
s8_comp("SingleFocus"),
@@ -251,7 +262,7 @@ read_only global s8 *beamformer_shader_flag_strings[] = {
read_only global u8 beamformer_shader_flag_strings_count[] = {
1,
3,
- 7,
+ 6,
0,
0,
0,
@@ -260,7 +271,7 @@ read_only global u8 beamformer_shader_flag_strings_count[] = {
read_only global i32 *beamformer_shader_header_vectors[] = {
(i32 []){0, 1},
(i32 []){0, 3},
- (i32 []){4, 0, 2},
+ (i32 []){4, 0, 5, 2},
0,
0,
0,
@@ -269,7 +280,7 @@ read_only global i32 *beamformer_shader_header_vectors[] = {
read_only global i32 beamformer_shader_header_vector_lengths[] = {
2,
2,
- 3,
+ 4,
0,
0,
0,
@@ -306,6 +317,7 @@ read_only global s8 *beamformer_shader_bake_parameter_names[] = {
s8_comp("AcquisitionKind"),
s8_comp("ChannelCount"),
s8_comp("DataKind"),
+ s8_comp("InterpolationMode"),
s8_comp("SampleCount"),
s8_comp("TransmitReceiveOrientation"),
s8_comp("DemodulationFrequency"),
@@ -324,7 +336,7 @@ read_only global s8 *beamformer_shader_bake_parameter_names[] = {
read_only global u8 *beamformer_shader_bake_parameter_is_float[] = {
(u8 []){0, 0, 0, 0, 0, 0, 0, 0, 0},
(u8 []){0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
- (u8 []){0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1},
+ (u8 []){0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1},
0,
0,
0,
@@ -333,7 +345,7 @@ read_only global u8 *beamformer_shader_bake_parameter_is_float[] = {
read_only global i32 beamformer_shader_bake_parameter_counts[] = {
9,
12,
- 13,
+ 14,
0,
0,
0,
@@ -369,3 +381,9 @@ read_only global s8 beamformer_acquisition_kind_strings[] = {
s8_comp("HERO-PA"),
};
+read_only global s8 beamformer_interpolation_mode_strings[] = {
+ s8_comp("Nearest"),
+ s8_comp("Linear"),
+ s8_comp("Cubic"),
+};
+
diff --git a/shaders/das.glsl b/shaders/das.glsl
@@ -95,10 +95,25 @@ SAMPLE_TYPE cubic(const int base_index, const float index)
SAMPLE_TYPE sample_rf(const int channel, const int transmit, const float index)
{
- SAMPLE_TYPE result = SAMPLE_TYPE(index >= 0.0f) * SAMPLE_TYPE((int(index) + 1 + Interpolate) < SampleCount);
+ SAMPLE_TYPE result = SAMPLE_TYPE(0);
int base_index = int(channel * SampleCount * AcquisitionCount + transmit * SampleCount);
- if (bool(Interpolate)) result *= cubic(base_index, index);
- else result *= rf_data[base_index + int(round(index))];
+ switch (InterpolationMode) {
+ case InterpolationMode_Nearest:{
+ if (index >= 0 && int(round(index)) < SampleCount)
+ result = rf_data[base_index + int(round(index))];
+ }break;
+ case InterpolationMode_Linear:{
+ if (index >= 0 && round(index) < SampleCount) {
+ float tk, t = modf(index, tk);
+ int n = base_index + int(tk);
+ result = (1 - t) * rf_data[n] + t * rf_data[n + 1];
+ }
+ }break;
+ case InterpolationMode_Cubic:{
+ if (index >= 0 && (int(index) + 2) < SampleCount)
+ result = cubic(base_index, index);
+ }break;
+ }
result = rotate_iq(result, index / SamplingFrequency);
return result;
}
diff --git a/tests/throughput.c b/tests/throughput.c
@@ -328,9 +328,9 @@ execute_study(s8 study, Arena arena, Stream path, Options *options)
bp.output_max_coordinate[1] = 0;
bp.output_max_coordinate[2] = g_axial_extent.y;
- bp.f_number = g_f_number;
- bp.beamform_plane = 0;
- bp.interpolate = 1;
+ bp.f_number = g_f_number;
+ bp.beamform_plane = 0;
+ bp.interpolation_mode = BeamformerInterpolationMode_Cubic;
bp.decimation_rate = 1;
bp.demodulation_frequency = bp.sampling_frequency / 4;
diff --git a/ui.c b/ui.c
@@ -1215,10 +1215,11 @@ add_beamformer_parameters_view(Variable *parent, BeamformerCtx *ctx)
add_beamformer_variable(ui, group, &ui->arena, s8("F#:"), s8(""), &bp->f_number, (v2){.y = 1e3f},
1, 0.1f, V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
- read_only local_persist s8 true_false_labels[] = {s8_comp("False"), s8_comp("True")};
- add_variable_cycler(ui, group, &ui->arena, V_CAUSES_COMPUTE, ui->font, s8("Interpolate:"),
- &bp->interpolate, true_false_labels, countof(true_false_labels));
+ add_variable_cycler(ui, group, &ui->arena, V_CAUSES_COMPUTE, ui->font, s8("Interpolation:"),
+ &bp->interpolation_mode, beamformer_interpolation_mode_strings,
+ countof(beamformer_interpolation_mode_strings));
+ read_only local_persist s8 true_false_labels[] = {s8_comp("False"), s8_comp("True")};
add_variable_cycler(ui, group, &ui->arena, V_CAUSES_COMPUTE, ui->font, s8("Coherency Weighting:"),
&bp->coherency_weighting, true_false_labels, countof(true_false_labels));