ogl_beamforming

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

Commit: 47aebd9ae07408e46643aca308f2a074f03d57f3
Parent: a092e5ef5bfa9cddb5ad89b07bc9051d32bc1564
Author: Randy Palamar
Date:   Tue, 25 Aug 2026 06:33:23 -0700

core/das: massage das to get 16 byte loads out of pointer; remove descriptor zoo

With a little bit of massaging we can get the compiler to emit 16
byte load instructions with a pointer instead of twice as many 8
byte loads. This provides an alternative fix to the old
performance issue which worked around by using and SSBO. Loading
from a pointer is much simpler and allows us to delete all the
descriptor set garbage. Also nvidia cards seem to perform slightly
better with pointers instead of SSBOs but I haven't tested this
extensively.

This performs quite a bit worse on my Qualcomm GPU but is still
workable. The extra complexity needed to use SSBOs is not really
worth it just to support Qualcomm's poorly designed GPU. If I need
more performance there it would be better to try using vendor
specific extensions which should actually accelerate things.

Diffstat:
Mbeamformer.c | 9---------
Mbeamformer.meta | 26+++-----------------------
Mbeamformer_core.c | 59+++++++++++++++++++++++------------------------------------
Mbeamformer_internal.h | 8--------
Mgenerated/beamformer.c | 79+++++++++++++++++++++++++++++--------------------------------------------------
Mshaders/das.glsl | 87+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mshaders/filter.glsl | 20+++++++-------------
Mvulkan.c | 133++-----------------------------------------------------------------------------
8 files changed, 111 insertions(+), 310 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -226,15 +226,6 @@ beamformer_init(BeamformerInput *input) // NOTE(rnp): if this becomes an issue we may be able to get by in some other way fatal(str8("Failed to allocate space for beamformed data\n")); } - - BeamformerShaderResourceInfo shader_resource_infos[] = { - { - .kind = BeamformerShaderResourceKind_Buffer, - .handle = cs->backlog.buffer->handle, - .slot = BeamformerShaderBufferSlot_BeamformedData, - }, - }; - vk_bind_shader_resources(shader_resource_infos, countof(shader_resource_infos)); } Arena *scratch = arena_create(); diff --git a/beamformer.meta b/beamformer.meta @@ -7,17 +7,6 @@ @Constant(16) MaxParameterBlocks @Constant(3) MaxRawDataFramesInFlight -@Enumeration ShaderResourceKind -{ - Buffer -} - -@Enumeration ShaderBufferSlot -{ - BeamformedData - PingPong -} - @Enumeration DecodeMode { None @@ -319,10 +308,6 @@ `read_only global str8 beamformer_interpolation_mode_strings[] = {` @Expand(InterpolationMode) ` str8_comp("$(name)"),` `};` - `` - `read_only global str8 beamformer_shader_resource_kind_strings[] = {` - @Expand(ShaderResourceKind) ` str8_comp("$(name)"),` - `};` } @ShaderGroup Compute @@ -361,9 +346,6 @@ @Shader(filter.glsl) Filter { - @Enumeration ShaderBufferSlot - @Enumeration ShaderResourceKind - @ShaderAlias Demodulate @Flags @@ -391,8 +373,8 @@ @PushConstants { - [input_data U64] - [output_element_offset U32] + [input_buffer U64] + [output_buffer U64] } } @@ -403,8 +385,6 @@ @Enumeration AcquisitionKind @Enumeration InterpolationMode @Enumeration RCAOrientation - @Enumeration ShaderBufferSlot - @Enumeration ShaderResourceKind @Flags { @@ -413,6 +393,7 @@ @Bake { + [RFData U64] [FocalVectors U64] [Hadamard U64] [IncoherentFrame U64] @@ -452,7 +433,6 @@ [voxel_transform M4] [xdc_element_pitch V2] [output_frame U64] - [rf_element_offset U32] [channel_offset S32] [readi_group U32] } diff --git a/beamformer_core.c b/beamformer_core.c @@ -583,6 +583,23 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A cp->rf_size = input_sample_count * pb->parameters.acquisition_count * chunk_channel_count * beamformer_data_kind_byte_size[das_data_kind]; + i64 buffer_size = PING_PONG_BUFFER_SLOTS * round_up_to(cp->rf_size, 64); + if (beamformer_context->compute_context.ping_pong_buffer.size < buffer_size) { + b32 cuda = cuda_supported(); + GPUBufferAllocateInfo allocate_info = { + .size = buffer_size, + .export = cuda ? &beamformer_context->compute_context.ping_pong_export_handle : 0, + .label = str8("PingPongBuffer"), + }; + gpu_buffer_allocate(&beamformer_context->compute_context.ping_pong_buffer, allocate_info); + + // TODO(rnp): figure out how to share with CUDA + // IMPORTANT: on linux the handle is returned to os and should be cleared after import + // see usage of glImportMemoryFdEXT and surrounding code in ui.c for examples + if (cuda) { + } + } + read_only local_persist BeamformerDataKind data_kind_to_element_kind[] = { [BeamformerDataKind_Int16] = BeamformerDataKind_Float16, [BeamformerDataKind_Float16] = BeamformerDataKind_Float16, @@ -887,6 +904,9 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A db->OutputSizeZ = cp->output_points.z; db->TransmitReceiveOrientation = pb->parameters.transmit_receive_orientation; + u64 pp_size = beamformer_context->compute_context.ping_pong_buffer.size / PING_PONG_BUFFER_SLOTS; + db->RFData = beamformer_context->compute_context.ping_pong_buffer.gpu_pointer + (PING_PONG_BUFFER_SLOTS - 1) * pp_size; + // NOTE(rnp): old gcc will miscompile an assignment memory_copy(cp->xdc_transform.E, pb->parameters.xdc_transform.E, sizeof(cp->xdc_transform)); @@ -1224,32 +1244,6 @@ beamformer_commit_parameter_block(BeamformerCtx *ctx, BeamformerComputePlan *cp, cp->acquisition_count = pb->parameters.acquisition_count; cp->acquisition_kind = pb->parameters.acquisition_kind; cp->contrast_mode = pb->parameters.contrast_mode; - - i64 buffer_size = PING_PONG_BUFFER_SLOTS * round_up_to(cp->rf_size, 64); - if (ctx->compute_context.ping_pong_buffer.size < buffer_size) { - b32 cuda = cuda_supported(); - GPUBufferAllocateInfo allocate_info = { - .size = buffer_size, - .export = cuda ? &ctx->compute_context.ping_pong_export_handle : 0, - .label = str8("PingPongBuffer"), - }; - gpu_buffer_allocate(&ctx->compute_context.ping_pong_buffer, allocate_info); - - BeamformerShaderResourceInfo shader_resource_infos[] = { - { - .kind = BeamformerShaderResourceKind_Buffer, - .handle = ctx->compute_context.ping_pong_buffer.handle, - .slot = BeamformerShaderBufferSlot_PingPong, - }, - }; - vk_bind_shader_resources(shader_resource_infos, countof(shader_resource_infos)); - - // TODO(rnp): figure out how to share with CUDA - // IMPORTANT: on linux the handle is returned to os and should be cleared after import - // see usage of glImportMemoryFdEXT and surrounding code in ui.c for examples - if (cuda) { - } - } }break; } } @@ -1299,16 +1293,12 @@ do_compute_shader(BeamformerCtx *ctx, GPUCommandList cmd, BeamformerComputePlan case BeamformerShaderKind_Filter: case BeamformerShaderKind_Demodulate: { - BeamformerDataKind output_data_kind = cp->shader_descriptors[shader_slot].output_data_kind; - - u64 element_size = beamformer_data_kind_byte_size[output_data_kind]; BeamformerFilterPushConstants pc = { - .input_data = shader_slot == 0 ? rf_pointer : pp_input_pointer, - .output_element_offset = output_index * pp_size / element_size, + .input_buffer = shader_slot == 0 ? rf_pointer : pp_input_pointer, }; - if ((shader_slot + 1) == das_index) - pc.output_element_offset = das_output_index * pp_size / element_size; + if ((shader_slot + 1) == das_index) pc.output_buffer = pp_das_pointer; + else pc.output_buffer = pp_output_pointer; if (shader_slot != 0 || (shader_slot + 1) == das_index) gpu_command_pipeline_barrier(cmd); @@ -1320,11 +1310,8 @@ do_compute_shader(BeamformerCtx *ctx, GPUCommandList cmd, BeamformerComputePlan }break; case BeamformerShaderKind_DAS:{ - u64 element_size = beamformer_data_kind_byte_size[cp->shader_descriptors[shader_slot].input_data_kind]; - BeamformerDASPushConstants pc = { .xdc_element_pitch = cp->xdc_element_pitch, - .rf_element_offset = das_output_index * pp_size / element_size, .output_frame = frame->gpu_pointer, .channel_offset = channel_offset, .readi_group = cp->readi_group, diff --git a/beamformer_internal.h b/beamformer_internal.h @@ -130,12 +130,6 @@ typedef struct { u32 normals_offset; } RenderModel; -typedef struct { - BeamformerShaderResourceKind kind; - GPUHandle handle; - u32 slot; -} BeamformerShaderResourceInfo; - #include "threads.c" #include "util_os_ui.c" @@ -159,8 +153,6 @@ DEBUG_IMPORT void vk_render_model_allocate(GPUBuffer *, void *indices, u64 index DEBUG_IMPORT void vk_render_model_range_upload(GPUBuffer *, void *data, u64 offset, u64 size, b32 non_temporal); DEBUG_IMPORT void vk_render_model_release(GPUBuffer *); -DEBUG_IMPORT void vk_bind_shader_resources(BeamformerShaderResourceInfo *infos, u64 info_count); - /* NOTE: Pipelines do not have bindings. Data should be passed using push constants. * In particular the push constants should contain pointers to gpu memory using the * BufferDeviceAddress extension. */ diff --git a/generated/beamformer.c b/generated/beamformer.c @@ -13,17 +13,6 @@ #define BeamformerMaxRawDataFramesInFlight (3) typedef enum { - BeamformerShaderResourceKind_Buffer = 0, - BeamformerShaderResourceKind_Count, -} BeamformerShaderResourceKind; - -typedef enum { - BeamformerShaderBufferSlot_BeamformedData = 0, - BeamformerShaderBufferSlot_PingPong = 1, - BeamformerShaderBufferSlot_Count, -} BeamformerShaderBufferSlot; - -typedef enum { BeamformerDecodeMode_None = 0, BeamformerDecodeMode_Hadamard = 1, BeamformerDecodeMode_Count, @@ -197,6 +186,7 @@ typedef struct { } BeamformerFilterBakeParameters; typedef struct { + u64 RFData; u64 FocalVectors; u64 Hadamard; u64 IncoherentFrame; @@ -249,8 +239,8 @@ typedef struct { } BeamformerDecodePushConstants; typedef struct { - u64 input_data; - u32 output_element_offset; + u64 input_buffer; + u64 output_buffer; } BeamformerFilterPushConstants; typedef struct { @@ -258,7 +248,6 @@ typedef struct { m4 voxel_transform; v2 xdc_element_pitch; u64 output_frame; - u32 rf_element_offset; i32 channel_offset; u32 readi_group; } BeamformerDASPushConstants; @@ -577,10 +566,6 @@ read_only global str8 beamformer_interpolation_mode_strings[] = { str8_comp("Cubic"), }; -read_only global str8 beamformer_shader_resource_kind_strings[] = { - str8_comp("Buffer"), -}; - typedef enum { BeamformerStructKind_DecodeBakeParameters = 0, BeamformerStructKind_FilterBakeParameters = 1, @@ -625,27 +610,28 @@ read_only global MetaStructMember *meta_struct_members_by_id[] = { {17, 16, 1, 0}, {17, 24, 1, 0}, {17, 32, 1, 0}, - {18, 40, 1, 0}, - {14, 44, 1, 0}, - {10, 48, 1, 0}, - {10, 52, 1, 0}, + {17, 40, 1, 0}, + {18, 48, 1, 0}, + {14, 52, 1, 0}, {10, 56, 1, 0}, {10, 60, 1, 0}, - {8, 64, 1, 0}, - {8, 68, 1, 0}, + {10, 64, 1, 0}, + {10, 68, 1, 0}, {8, 72, 1, 0}, {8, 76, 1, 0}, - {18, 80, 1, 0}, + {8, 80, 1, 0}, {8, 84, 1, 0}, - {14, 88, 1, 0}, - {18, 92, 1, 0}, + {18, 88, 1, 0}, + {8, 92, 1, 0}, {14, 96, 1, 0}, - {8, 100, 1, 0}, - {8, 104, 1, 0}, - {18, 108, 1, 0}, - {18, 112, 1, 0}, + {18, 100, 1, 0}, + {14, 104, 1, 0}, + {8, 108, 1, 0}, + {8, 112, 1, 0}, {18, 116, 1, 0}, {18, 120, 1, 0}, + {18, 124, 1, 0}, + {18, 128, 1, 0}, }, (MetaStructMember []){ {17, 0, 1, 0}, @@ -695,6 +681,7 @@ read_only global str8 *meta_struct_member_names_by_id[] = { str8_comp("OutputTransmitStride"), }, (str8 []){ + str8_comp("RFData"), str8_comp("FocalVectors"), str8_comp("Hadamard"), str8_comp("IncoherentFrame"), @@ -743,7 +730,7 @@ read_only global str8 *meta_struct_member_names_by_id[] = { read_only global MetaStructInfo meta_struct_info_by_id[] = { {str8_comp("DecodeBakeParameters"), 11, 48, 0}, {str8_comp("FilterBakeParameters"), 13, 56, 0}, - {str8_comp("DASBakeParameters"), 26, 124, 0}, + {str8_comp("DASBakeParameters"), 27, 132, 0}, {str8_comp("CoherencyWeightingBakeParameters"), 3, 16, 0}, {str8_comp("ReshapeBakeParameters"), 9, 36, 0}, }; @@ -829,20 +816,13 @@ read_only global str8 beamformer_shader_global_header_strings[] = { "};\n" "\n"), str8_comp("" - "#define ShaderBufferSlot_BeamformedData 0\n" - "#define ShaderBufferSlot_PingPong 1\n" - "\n"), - str8_comp("" - "#define ShaderResourceKind_Buffer 0\n" - "\n"), - str8_comp("" "#define ComplexFilter ((CompileFlags & (1 << 0)) != 0)\n" "#define Demodulate ((CompileFlags & (1 << 1)) != 0)\n" "\n"), str8_comp("" "layout(push_constant, std430) uniform PushConstants {\n" - " uint64_t input_data;\n" - " uint32_t output_element_offset;\n" + " uint64_t input_buffer;\n" + " uint64_t output_buffer;\n" "};\n" "\n"), str8_comp("#define MaxChannelCount (256)\n\n"), @@ -880,7 +860,6 @@ read_only global str8 beamformer_shader_global_header_strings[] = { " f32mat4 voxel_transform;\n" " f32vec2 xdc_element_pitch;\n" " uint64_t output_frame;\n" - " uint32_t rf_element_offset;\n" " int32_t channel_offset;\n" " uint32_t readi_group;\n" "};\n" @@ -952,19 +931,19 @@ read_only global b8 beamformer_shader_primitive_is_vertex[] = { read_only global i32 *beamformer_shader_header_vectors[] = { (i32 []){0, 1, 2}, - (i32 []){3, 4, 5, 6}, - (i32 []){7, 8, 9, 10, 3, 4, 11, 12}, - (i32 []){13}, - (i32 []){14, 15}, + (i32 []){3, 4}, + (i32 []){5, 6, 7, 8, 9, 10}, + (i32 []){11}, + (i32 []){12, 13}, 0, - (i32 []){16}, - (i32 []){17}, + (i32 []){14}, + (i32 []){15}, }; read_only global i32 beamformer_shader_header_vector_lengths[] = { 3, - 4, - 8, + 2, + 6, 1, 2, 0, diff --git a/shaders/das.glsl b/shaders/das.glsl @@ -31,13 +31,8 @@ #define RESULT_STORE(a) (a) #endif -layout(set = ShaderResourceKind_Buffer, binding = ShaderBufferSlot_PingPong) readonly buffer RF { - InputDataType rf[]; -}; - -layout(std430, buffer_reference) buffer Output { - OutputDataType x[]; -}; +layout(std430, buffer_reference) buffer Input { InputDataType x[]; }; +layout(std430, buffer_reference) buffer Output { OutputDataType x[]; }; layout(std430, buffer_reference) buffer IncoherentOutput { f32 x[]; @@ -48,6 +43,7 @@ layout(std430, buffer_reference) buffer F32 { f32 x[]; }; layout(std430, buffer_reference) buffer S16 { s16 x[]; }; layout(std430, buffer_reference) buffer U8 { u8 x[]; }; layout(std430, buffer_reference) buffer V2 { vec2 x[]; }; +layout(std430, buffer_reference) buffer V4 { vec4 x[]; }; #define RX_ORIENTATION(tx_rx) bitfieldExtract((tx_rx), 0, 4) #define TX_ORIENTATION(tx_rx) bitfieldExtract((tx_rx), 4, 4) @@ -68,7 +64,7 @@ vec2 rotate_iq(const vec2 iq, const float time) #endif /* NOTE: See: https://cubic.org/docs/hermite.htm */ -SAMPLE_TYPE cubic(const int offset, const float t) +SAMPLE_TYPE cubic(const u64 rf_pointer, const f32 t) { const mat4 h = mat4( 2, -3, 0, 1, @@ -77,12 +73,17 @@ SAMPLE_TYPE cubic(const int offset, const float t) 1, -1, 0, 0 ); - SAMPLE_TYPE samples[4] = { - rf[offset + 0], - rf[offset + 1], - rf[offset + 2], - rf[offset + 3], - }; + #if InputDataKind == DataKind_Float32 + vec4 samples = V4(rf_pointer).x[0]; + #else + SAMPLE_TYPE samples[4]; + vec4 load1 = V4(rf_pointer).x[0]; + vec4 load2 = V4(rf_pointer).x[1]; + samples[0] = load1.xy; + samples[1] = load1.zw; + samples[2] = load2.xy; + samples[3] = load2.zw; + #endif vec4 S = vec4(t * t * t, t * t, t, 1); SAMPLE_TYPE P1 = samples[1]; @@ -100,26 +101,31 @@ SAMPLE_TYPE cubic(const int offset, const float t) return result; } -SAMPLE_TYPE sample_rf(const int rf_offset, const float index) +SAMPLE_TYPE sample_rf(const u64 rf_pointer, const f32 index) { SAMPLE_TYPE result = SAMPLE_TYPE(0); switch (InterpolationMode) { case InterpolationMode_Nearest:{ if (index >= 0.f && index < (f32(SampleCount) - 0.5f)) - result = rotate_iq(rf[rf_offset + int(round(index))], index / SamplingFrequency); + result = rotate_iq(Input(rf_pointer + InputDataKindByteSize * u32(round(index))).x[0], index / SamplingFrequency); }break; case InterpolationMode_Linear:{ if (index >= 0.f && index < f32(SampleCount - 1)) { - s32 n = rf_offset + int(index); - f32 t = fract(index); - result = (1 - t) * rf[n] + t * rf[n + 1]; + #if InputDataKind == DataKind_Float32 + vec2 rf = V2(rf_pointer + InputDataKindByteSize * u32(index)).x[0]; + #else + vec4 load = V4(rf_pointer + InputDataKindByteSize * u32(index)).x[0]; + vec2 rf[2] = {load.xy, load.zw}; + #endif + f32 t = fract(index); + result = (1 - t) * rf[0] + t * rf[1]; result = rotate_iq(result, index / SamplingFrequency); } }break; case InterpolationMode_Cubic:{ if (index >= 1.f && index < f32(SampleCount - 2)) - result = rotate_iq(cubic(rf_offset + int(index), fract(index)), index / SamplingFrequency); + result = rotate_iq(cubic(rf_pointer + InputDataKindByteSize * u32(index), fract(index)), index / SamplingFrequency); }break; } return result; @@ -211,8 +217,9 @@ RESULT_TYPE RCA(const vec3 world_point) vec2 xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows); float transmit_distance = rca_transmit_distance(world_point, focal_vector, tx_rx_orientation); - int rf_offset = int(rf_element_offset) + acquisition * SampleCount; - rf_offset -= int(InterpolationMode == InterpolationMode_Cubic); + u64 rf_pointer = RFData + InputDataKindByteSize * acquisition * SampleCount; + rf_pointer -= InputDataKindByteSize * u32(InterpolationMode == InterpolationMode_Cubic); + for (f32 chunk_channel = 0.f; chunk_channel < f32(ChunkChannelCount); chunk_channel += 1.f) { f32 rx_channel = f32(channel_offset) + chunk_channel; vec3 rx_center = vec3(rx_channel * xdc_element_pitch, 0); @@ -220,11 +227,11 @@ RESULT_TYPE RCA(const vec3 world_point) f32 a_arg = abs(FNumber * receive_vector.x / abs(xdc_world_point.y)); if (a_arg < 0.5f) { - float sidx = sample_index(transmit_distance + length(receive_vector)); - SAMPLE_TYPE value = apodize(a_arg) * sample_rf(rf_offset, sidx); + f32 index = sample_index(transmit_distance + length(receive_vector)); + SAMPLE_TYPE value = apodize(a_arg) * sample_rf(rf_pointer, index); result += RESULT_STORE(value); } - rf_offset += SampleCount * AcquisitionCount; + rf_pointer += InputDataKindByteSize * SampleCount * AcquisitionCount; } } return result; @@ -246,8 +253,8 @@ RESULT_TYPE HERCULES(const vec3 world_point) RESULT_TYPE result = RESULT_TYPE(0); for (f32 chunk_channel = 0.f; chunk_channel < f32(ChunkChannelCount); chunk_channel += 1.f) { f32 rx_channel = f32(channel_offset) + chunk_channel; - s32 rf_offset = s32(rf_element_offset) + s32(chunk_channel) * SampleCount * AcquisitionCount + s32(Sparse) * SampleCount; - rf_offset -= s32(InterpolationMode == InterpolationMode_Cubic); + u64 rf_pointer = RFData + InputDataKindByteSize * (u32(chunk_channel) * SampleCount * AcquisitionCount + u32(Sparse) * SampleCount); + rf_pointer -= InputDataKindByteSize * u32(InterpolationMode == InterpolationMode_Cubic); // NOTE(rnp): this wouldn't be so messy if we just forced an orientation like with FORCES vec2 element_receive_delta_squared = xy_world_point; @@ -273,11 +280,11 @@ RESULT_TYPE HERCULES(const vec3 world_point) apodization *= apodize(f_number_over_z * sqrt(element_delta_squared)); float index = transmit_index + sqrt(z_delta_squared + element_delta_squared) * SamplingFrequency / SpeedOfSound; - SAMPLE_TYPE value = apodization * sample_rf(rf_offset, index); + SAMPLE_TYPE value = apodization * sample_rf(rf_pointer, index); result += RESULT_STORE(value); } - rf_offset += SampleCount; + rf_pointer += InputDataKindByteSize * SampleCount; } } return result; @@ -297,8 +304,8 @@ RESULT_TYPE FORCES(const vec3 xdc_world_point) float a_arg = abs(FNumber * receive_x_delta / xdc_world_point.z); if (a_arg < 0.5f) { - s32 rf_offset = s32(rf_element_offset) + s32(chunk_channel) * SampleCount * AcquisitionCount + s32(Sparse) * SampleCount; - rf_offset -= s32(InterpolationMode == InterpolationMode_Cubic); + u64 rf_pointer = RFData + InputDataKindByteSize * (u32(chunk_channel) * SampleCount * AcquisitionCount + u32(Sparse) * SampleCount); + rf_pointer -= InputDataKindByteSize * u32(InterpolationMode == InterpolationMode_Cubic); f32 receive_index = sample_index(sqrt(receive_x_delta * receive_x_delta + z_delta_squared)); f32 apodization = apodize(a_arg); @@ -307,9 +314,9 @@ RESULT_TYPE FORCES(const vec3 xdc_world_point) f32 transmit_x_delta = xdc_world_point.x - xdc_element_pitch.x * tx_channel; f32 transmit_index = sqrt(transmit_yz_squared + transmit_x_delta * transmit_x_delta) * SamplingFrequency / SpeedOfSound; - SAMPLE_TYPE value = apodization * sample_rf(rf_offset, receive_index + transmit_index); - result += RESULT_STORE(value); - rf_offset += SampleCount; + SAMPLE_TYPE value = apodization * sample_rf(rf_pointer, receive_index + transmit_index); + result += RESULT_STORE(value); + rf_pointer += InputDataKindByteSize * SampleCount; } } } @@ -333,8 +340,8 @@ RESULT_TYPE READI_FORCES(const vec3 xdc_world_point) f32 a_arg = abs(FNumber * receive_x_delta / xdc_world_point.z); if (a_arg < 0.5f) { - s32 channel_rf_offset = s32(rf_element_offset) + s32(chunk_channel) * SampleCount * AcquisitionCount; - channel_rf_offset -= s32(InterpolationMode == InterpolationMode_Cubic); + u64 channel_rf_pointer = RFData + InputDataKindByteSize * u32(chunk_channel) * SampleCount * AcquisitionCount; + channel_rf_pointer -= InputDataKindByteSize * u32(InterpolationMode == InterpolationMode_Cubic); f32 receive_index = sample_index(sqrt(receive_x_delta * receive_x_delta + z_delta_squared)); f32 apodization = apodize(a_arg); @@ -344,16 +351,16 @@ RESULT_TYPE READI_FORCES(const vec3 xdc_world_point) // acquisition, the second element in each group is beamformed using the second acquisition, etc. for (s32 tx_group = 0; tx_group < s32(ReadiGroupCount); tx_group++) { f32 group_apodization = apodization * F16(Hadamard).x[hadamard_offset + tx_group]; - s32 rf_offset = channel_rf_offset; + u64 rf_pointer = channel_rf_pointer; for (f32 tx_event = 0; tx_event < f32(AcquisitionCount); tx_event += 1.f) { f32 tx_element = f32(tx_group) * f32(AcquisitionCount) + tx_event; f32 transmit_x_delta = xdc_world_point.x - xdc_element_pitch.x * tx_element; f32 transmit_index = sqrt(transmit_yz_squared + transmit_x_delta * transmit_x_delta) * SamplingFrequency / SpeedOfSound; - SAMPLE_TYPE value = group_apodization * sample_rf(rf_offset, receive_index + transmit_index); - result += RESULT_STORE(value); - rf_offset += SampleCount; + SAMPLE_TYPE value = group_apodization * sample_rf(rf_pointer, receive_index + transmit_index); + result += RESULT_STORE(value); + rf_pointer += InputDataKindByteSize * SampleCount; } } } diff --git a/shaders/filter.glsl b/shaders/filter.glsl @@ -35,13 +35,8 @@ #define apply_filter(iq, h) ((iq) * (h)) #endif -layout(std430, buffer_reference, buffer_reference_align = 64) restrict readonly buffer Input { - InputDataType x[]; -}; - -layout(set = ShaderResourceKind_Buffer, binding = ShaderBufferSlot_PingPong) buffer Output { - OutputDataType output_data[]; -}; +layout(std430, buffer_reference) restrict readonly buffer Input { InputDataType x[]; }; +layout(std430, buffer_reference) restrict writeonly buffer Output { OutputDataType x[]; }; layout(std430, buffer_reference, buffer_reference_align = 64) restrict readonly buffer Filter { FILTER_TYPE values[FilterLength]; @@ -87,7 +82,7 @@ void main() in_offset /= 2; // NOTE(rnp): broken out to avoid overflow from the subtraction - u64 input_address = input_data + in_offset; + u64 input_address = input_buffer + in_offset; input_address += InputDataKindByteSize * (DecimationRate * gl_WorkGroupID.x * gl_WorkGroupSize.x); input_address -= InputDataKindByteSize * (FilterLength - 1); @@ -120,16 +115,15 @@ void main() u32 out_offset = OutputChannelStride * channel + OutputTransmitStride * transmit + - OutputSampleStride * out_sample + - output_element_offset; + OutputSampleStride * out_sample; if (BatchSampleCount != 0) { // NOTE(rnp): deinterleave - output_data[out_offset] = OutputDataType(result.x); + Output(output_buffer).x[out_offset] = OutputDataType(result.x); out_offset += BatchSampleCount; - output_data[out_offset] = OutputDataType(result.y); + Output(output_buffer).x[out_offset] = OutputDataType(result.y); } else { - output_data[out_offset] = OutputDataType(result); + Output(output_buffer).x[out_offset] = OutputDataType(result); } } } diff --git a/vulkan.c b/vulkan.c @@ -2,7 +2,6 @@ // TODO(rnp) // [ ]: what is needed for HDR? I think it makes sense to just default to it nowadays // [ ]: once opengl is removed switch images to SRGB and/or 16 bit Float -// [ ]: VK_KHR_robustness2 probably shouldn't be required but it also might not matter #include "beamformer_internal.h" #include "vulkan.h" @@ -127,12 +126,6 @@ typedef struct { VkDevice device; VkPhysicalDevice physical_device; - VkDescriptorPool descriptor_pool; - VkDescriptorSetLayout descriptor_set_layouts[BeamformerShaderResourceKind_Count]; - VkDescriptorSet descriptor_sets[BeamformerShaderResourceKind_Count]; - // NOTE(rnp): must store these if we want to allow partial updates easily - VkDescriptorBufferInfo descriptor_buffer_infos[BeamformerShaderBufferSlot_Count]; - // NOTE(rnp): fallback for when a shader fails to compile VulkanPipeline default_compute_pipeline; VulkanPipeline default_graphics_pipeline; @@ -186,7 +179,6 @@ read_only global const char *vk_required_instance_extensions[] = { X("VK_KHR_8bit_storage") \ X("VK_KHR_external_memory") \ X("VK_KHR_external_semaphore") \ - X("VK_KHR_robustness2") \ X("VK_KHR_storage_buffer_storage_class") \ X("VK_KHR_timeline_semaphore") \ VK_OS_REQUIRED_DEVICE_EXTENSIONS_LIST @@ -212,6 +204,8 @@ read_only global str8 vk_optional_device_extensions[] = {VK_OPTIONAL_DEVICE_EXTE #define VK_REQUIRED_PHYSICAL_12_FEATURES \ X(bufferDeviceAddress) \ X(shaderFloat16) \ + X(shaderInt8) \ + X(storageBuffer8BitAccess) \ X(timelineSemaphore) \ X(vulkanMemoryModel) \ @@ -621,8 +615,6 @@ vk_compute_pipeline_from_info(Arena *arena, VulkanPipelineCreateInfo *info, u32 VkPipelineLayoutCreateInfo pipeline_layout_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .setLayoutCount = countof(vulkan_context->descriptor_set_layouts), - .pSetLayouts = vulkan_context->descriptor_set_layouts, .pushConstantRangeCount = push_constants_size ? 1 : 0, .pPushConstantRanges = push_constants_size ? &push_constant_range : 0, }; @@ -687,8 +679,6 @@ vk_graphics_pipeline_from_infos(Arena *arena, VulkanPipelineCreateInfo *infos, u VkPipelineLayoutCreateInfo pipeline_layout_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .setLayoutCount = countof(vulkan_context->descriptor_set_layouts), - .pSetLayouts = vulkan_context->descriptor_set_layouts, .pushConstantRangeCount = push_constants_size ? 1 : 0, .pPushConstantRanges = push_constants_size ? &pcr : 0, }; @@ -1682,13 +1672,6 @@ vk_load_queues(Arena *arena, Stream *err) device_create_info.pNext = &coop_mat_features; } - VkPhysicalDeviceRobustness2FeaturesKHR robust2 = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_KHR, - .pNext = (void *)device_create_info.pNext, - .nullDescriptor = 1, - }; - device_create_info.pNext = &robust2; - VkPhysicalDeviceVulkan13Features v13f = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, .pNext = (void *)device_create_info.pNext, @@ -1804,85 +1787,6 @@ vk_load_graphics(void) } } -function void -vk_load_descriptor_block(void) -{ - // NOTE(rnp): - // * One Descriptor Pool - // * One Descriptor Set Per Resource Kind - // * Shaders know the ResourceKind enumeration - // * Shaders know the per set binding points - - VulkanContext *vk = vulkan_context; - - // NOTE(rnp): Pool - VkDescriptorPoolSize pool_sizes[] = { - { - .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .descriptorCount = BeamformerShaderBufferSlot_Count, - }, - }; - static_assert(countof(pool_sizes) == BeamformerShaderResourceKind_Count, ""); - - VkDescriptorPoolCreateInfo pool_create_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .maxSets = BeamformerShaderResourceKind_Count, - .poolSizeCount = countof(pool_sizes), - .pPoolSizes = pool_sizes, - }; - - vkCreateDescriptorPool(vk->device, &pool_create_info, 0, &vk->descriptor_pool); - - // NOTE(rnp): Set Layouts - VkDescriptorSetLayoutCreateInfo layout_create_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - }; - - { - VkDescriptorSetLayoutBinding layout_bindings[BeamformerShaderBufferSlot_Count]; - for EachEnumValue(BeamformerShaderBufferSlot, it) { - layout_bindings[it] = (VkDescriptorSetLayoutBinding){ - .binding = it, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_ALL, - }; - } - layout_create_info.bindingCount = countof(layout_bindings), - layout_create_info.pBindings = layout_bindings, - vkCreateDescriptorSetLayout(vk->device, &layout_create_info, 0, - vk->descriptor_set_layouts + BeamformerShaderResourceKind_Buffer); - } - - // NOTE(rnp): Sets - VkDescriptorSetAllocateInfo set_allocate_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .descriptorPool = vk->descriptor_pool, - .descriptorSetCount = countof(vk->descriptor_sets), - .pSetLayouts = vk->descriptor_set_layouts, - }; - static_assert(countof(vk->descriptor_set_layouts) == countof(vk->descriptor_sets), ""); - vkAllocateDescriptorSets(vk->device, &set_allocate_info, vk->descriptor_sets); - - vk_label_object(DESCRIPTOR_POOL, vk->descriptor_pool, str8("Beamformer Resources"), str8("Pool")); - - Temp scratch; - DeferLoop(take_lock(&vk->arena_lock, -1), release_lock(&vk->arena_lock)) - DeferLoop(scratch = temp_begin(vk->arena), temp_end(scratch)) - { - for EachElement(vk->descriptor_sets, it) { - Stream sb = arena_stream(vk->arena); - stream_append_str8s(&sb, str8("Beamformer "), beamformer_shader_resource_kind_strings[it], str8("s")); - vk_label_object(DESCRIPTOR_SET, vk->descriptor_sets[it], stream_to_str8(&sb), str8("Set")); - vk_label_object(DESCRIPTOR_SET_LAYOUT, vk->descriptor_set_layouts[it], stream_to_str8(&sb), str8("Set Layout")); - } - } - - // NOTE(rnp): junk API requirement that doesn't allow 0 initialization - for EachElement(vk->descriptor_buffer_infos, it) - vk->descriptor_buffer_infos[it].range = VK_WHOLE_SIZE; -} - /////////////////////// // NOTE(rnp): User API @@ -1906,7 +1810,6 @@ vk_load(OSLibrary vulkan_library_handle, Stream *err) vk_load_physical_device(vk->arena, err); vk_load_queues(vk->arena, err); vk_load_graphics(); - vk_load_descriptor_block(); read_only local_persist str8 default_compute_shader = str8("" "#version 430 core\n" @@ -2435,36 +2338,6 @@ vk_pipeline_release(VulkanHandle h) } } -DEBUG_IMPORT void -vk_bind_shader_resources(BeamformerShaderResourceInfo *infos, u64 info_count) -{ - VulkanContext *vk = vulkan_context; - - VkWriteDescriptorSet write_sets[BeamformerShaderResourceKind_Count] = {0}; - - for EachIndex(info_count, it) { - switch (infos[it].kind) { - case BeamformerShaderResourceKind_Buffer:{ - VulkanBuffer *vb = vk_entity_data(infos[it].handle.value, VulkanEntityKind_Buffer); - vk->descriptor_buffer_infos[infos[it].slot].buffer = vb->buffer; - vk->descriptor_buffer_infos[infos[it].slot].offset = 0; - vk->descriptor_buffer_infos[infos[it].slot].range = vb->memory_size; - }break; - - InvalidDefaultCase; - } - } - - write_sets[BeamformerShaderResourceKind_Buffer].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write_sets[BeamformerShaderResourceKind_Buffer].dstSet = vk->descriptor_sets[BeamformerShaderResourceKind_Buffer]; - write_sets[BeamformerShaderResourceKind_Buffer].dstBinding = 0; - write_sets[BeamformerShaderResourceKind_Buffer].descriptorCount = countof(vk->descriptor_buffer_infos); - write_sets[BeamformerShaderResourceKind_Buffer].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - write_sets[BeamformerShaderResourceKind_Buffer].pBufferInfo = vk->descriptor_buffer_infos; - - vkUpdateDescriptorSets(vk->device, countof(write_sets), write_sets, 0, 0); -} - DEBUG_IMPORT GPUCommandList gpu_command_list_begin(GPUTimeline timeline) { @@ -2530,8 +2403,6 @@ gpu_command_bind_pipeline(GPUCommandList command, VulkanHandle pipeline) VkCommandBuffer cmd = vk_command_buffer(command); vkCmdBindPipeline(cmd, bind_point, vp->pipeline); - vkCmdBindDescriptorSets(cmd, bind_point, vp->layout, 0, countof(vk->descriptor_sets), - vk->descriptor_sets, 0, 0); vcp->bound_pipeline = vp; } }