ogl_beamforming

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

Commit: f7518ff88f916cfd61c95b307910fae2fb16a9ca
Parent: c6f25ad89a2e480feae755d19978ccee18133dc9
Author: Randy Palamar
Date:   Fri, 12 Sep 2025 12:43:19 -0600

core/das: upload array of transmit/receive orientations for RCA methods

each acquistion event could utilize a different orientation pair in these methods

Diffstat:
Mbeamformer.c | 42++++++++++++++++--------------------------
Mbeamformer.h | 13+++++++++----
Mbeamformer.meta | 2+-
Mbeamformer_parameters.h | 23+++++++++++------------
Mbeamformer_shared_memory.c | 14++++++++------
Mgenerated/beamformer.meta.c | 4----
Mhelpers/ogl_beamformer_lib.c | 20++++++++++++--------
Mhelpers/ogl_beamformer_lib_base.h | 3+++
Mshaders/das.glsl | 23+++++++++++++----------
Mtests/throughput.c | 13+++++++++----
10 files changed, 82 insertions(+), 75 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -96,11 +96,11 @@ beamformer_compute_plan_for_block(BeamformerComputeContext *cc, u32 block, Arena #undef X #define X(_k, t, ...) t, - GLenum gl_kind[] = {BEAMFORMER_COMPUTE_TEXTURE_LIST}; + GLenum gl_kind[] = {BEAMFORMER_COMPUTE_TEXTURE_LIST_FULL}; #undef X read_only local_persist s8 tex_prefix[] = { #define X(k, ...) s8_comp(#k "["), - BEAMFORMER_COMPUTE_TEXTURE_LIST + BEAMFORMER_COMPUTE_TEXTURE_LIST_FULL #undef X }; glCreateTextures(GL_TEXTURE_1D, BeamformerComputeTextureKind_Count - 1, result->textures); @@ -455,10 +455,6 @@ das_ubo_from_beamformer_parameters(BeamformerDASUBO *du, BeamformerParameters *b du->shader_flags = 0; if (bp->coherency_weighting) du->shader_flags |= BeamformerShaderDASFlags_CoherencyWeighting; - if (bp->transmit_mode == BeamformerRCAOrientation_Columns) - du->shader_flags |= BeamformerShaderDASFlags_TxColumns; - if (bp->receive_mode == BeamformerRCAOrientation_Columns) - du->shader_flags |= BeamformerShaderDASFlags_RxColumns; } function void @@ -713,35 +709,28 @@ beamformer_commit_parameter_block(BeamformerCtx *ctx, BeamformerComputePlan *cp, alloc_beamform_frame(&ctx->gl, ctx->averaged_frames + 1, cp->output_points, gl_kind, s8("Averaged Frame"), arena); } }break; - case BeamformerParameterBlockRegion_ChannelMapping: + case BeamformerParameterBlockRegion_ChannelMapping:{ + cuda_set_channel_mapping(pb->channel_mapping); + } /* FALLTHROUGH */ case BeamformerParameterBlockRegion_FocalVectors: case BeamformerParameterBlockRegion_SparseElements: + case BeamformerParameterBlockRegion_TransmitReceiveOrientations: { BeamformerComputeTextureKind texture_kind = 0; - u32 texture_type = 0, texture_format = 0; - /* TODO(rnp): this whole thing could be a table */ + u32 pixel_type = 0, texture_format = 0; switch (region) { - case BeamformerParameterBlockRegion_ChannelMapping:{ - texture_kind = BeamformerComputeTextureKind_ChannelMapping; - texture_type = GL_SHORT; - texture_format = GL_RED_INTEGER; - /* TODO(rnp): cuda lib */ - cuda_set_channel_mapping(pb->channel_mapping); - }break; - case BeamformerParameterBlockRegion_FocalVectors:{ - texture_kind = BeamformerComputeTextureKind_FocalVectors; - texture_type = GL_FLOAT; - texture_format = GL_RG; - }break; - case BeamformerParameterBlockRegion_SparseElements:{ - texture_kind = BeamformerComputeTextureKind_SparseElements; - texture_type = GL_SHORT; - texture_format = GL_RED_INTEGER; + #define X(kind, _gl, tf, pt, ...) \ + case BeamformerParameterBlockRegion_## kind:{ \ + texture_kind = BeamformerComputeTextureKind_## kind; \ + texture_format = tf; \ + pixel_type = pt; \ }break; + BEAMFORMER_COMPUTE_TEXTURE_LIST + #undef X InvalidDefaultCase; } glTextureSubImage1D(cp->textures[texture_kind], 0, 0, BeamformerMaxChannelCount, - texture_format, texture_type, + texture_format, pixel_type, (u8 *)pb + BeamformerParameterBlockRegionOffsets[region]); }break; } @@ -858,6 +847,7 @@ do_compute_shader(BeamformerCtx *ctx, BeamformerComputePlan *cp, BeamformerFrame glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, cc->ping_pong_ssbos[input_ssbo_idx], 0, cp->rf_size); glBindImageTexture(1, sparse_texture, 0, 0, 0, GL_READ_ONLY, GL_R16I); glBindImageTexture(2, cp->textures[BeamformerComputeTextureKind_FocalVectors], 0, 0, 0, GL_READ_ONLY, GL_RG32F); + glBindImageTexture(3, cp->textures[BeamformerComputeTextureKind_TransmitReceiveOrientations], 0, 0, 0, GL_READ_ONLY, GL_R8I); glProgramUniform1ui(program, DAS_CYCLE_T_UNIFORM_LOC, das_cycle_t++); diff --git a/beamformer.h b/beamformer.h @@ -180,15 +180,20 @@ static_assert((sizeof(BeamformerDASUBO) & 15) == 0, "UBO size must be a multiple typedef enum {BEAMFORMER_COMPUTE_UBO_LIST BeamformerComputeUBOKind_Count} BeamformerComputeUBOKind; #undef X +// X(kind, gl_kind, texture_format, pixel_type) #define BEAMFORMER_COMPUTE_TEXTURE_LIST \ - X(ChannelMapping, GL_R16I) \ - X(FocalVectors, GL_RG32F) \ - X(SparseElements, GL_R16I) \ + X(ChannelMapping, GL_R16I, GL_RED_INTEGER, GL_SHORT) \ + X(FocalVectors, GL_RG32F, GL_RG, GL_FLOAT) \ + X(SparseElements, GL_R16I, GL_RED_INTEGER, GL_SHORT) \ + X(TransmitReceiveOrientations, GL_R8I, GL_RED_INTEGER, GL_BYTE) + +#define BEAMFORMER_COMPUTE_TEXTURE_LIST_FULL \ + BEAMFORMER_COMPUTE_TEXTURE_LIST \ X(Hadamard, GL_R8I) typedef enum { #define X(k, ...) BeamformerComputeTextureKind_##k, - BEAMFORMER_COMPUTE_TEXTURE_LIST + BEAMFORMER_COMPUTE_TEXTURE_LIST_FULL #undef X BeamformerComputeTextureKind_Count } BeamformerComputeTextureKind; diff --git a/beamformer.meta b/beamformer.meta @@ -45,7 +45,7 @@ @Enumeration(RCAOrientation) - @Flags([CoherencyWeighting RxColumns TxColumns]) + @Flags([CoherencyWeighting]) } @Shader(min_max.glsl) MinMax diff --git a/beamformer_parameters.h b/beamformer_parameters.h @@ -84,10 +84,8 @@ typedef enum {BEAMFORMER_CONSTANTS_LIST} BeamformerConstants; X(acquisition_count, uint32_t, , uint32, 1, "") \ X(das_shader_id, uint32_t, , uint32, 1, "") \ X(time_offset, float, , single, 1, "pulse length correction time [s]") \ - X(decode, uint8_t, , uint8, 1, "Decode or just reshape data") \ - X(transmit_mode, uint8_t, , uint8, 1, "Method/Orientation of Transmit") \ - X(receive_mode, uint8_t, , uint8, 1, "Method/Orientation of Receive") \ - X(sampling_mode, uint8_t, , uint8, 1, "") + X(decode, uint16_t, , uint16, 1, "Decode or just reshape data") \ + X(sampling_mode, uint16_t, , uint16, 1, "") #define BEAMFORMER_UI_PARAMS \ X(output_min_coordinate, float, [3], single, 3, "[m] Back-Top-Left corner of output region") \ @@ -104,14 +102,15 @@ typedef enum {BEAMFORMER_CONSTANTS_LIST} BeamformerConstants; X(decimation_rate, uint32_t, , uint32, 1, "Number of times to decimate") #define BEAMFORMER_SIMPLE_PARAMS \ - X(channel_mapping, int16_t, [BeamformerMaxChannelCount], int16, BeamformerMaxChannelCount) \ - X(sparse_elements, int16_t, [BeamformerMaxChannelCount], int16, BeamformerMaxChannelCount) \ - X(steering_angles, float, [BeamformerMaxChannelCount], single, BeamformerMaxChannelCount) \ - X(focal_depths, float, [BeamformerMaxChannelCount], single, BeamformerMaxChannelCount) \ - X(compute_stages, int32_t, [BeamformerMaxComputeShaderStages], int32, BeamformerMaxComputeShaderStages) \ - X(compute_stage_parameters, int16_t, [BeamformerMaxComputeShaderStages], int16, BeamformerMaxComputeShaderStages) \ - X(compute_stages_count, uint32_t, , uint32, 1) \ - X(data_kind, int32_t, , int32, 1) + X(channel_mapping, int16_t, [BeamformerMaxChannelCount], int16, BeamformerMaxChannelCount) \ + X(sparse_elements, int16_t, [BeamformerMaxChannelCount], int16, BeamformerMaxChannelCount) \ + X(transmit_receive_orientations, uint8_t, [BeamformerMaxChannelCount], uint8, BeamformerMaxChannelCount) \ + X(steering_angles, float, [BeamformerMaxChannelCount], single, BeamformerMaxChannelCount) \ + X(focal_depths, float, [BeamformerMaxChannelCount], single, BeamformerMaxChannelCount) \ + X(compute_stages, int32_t, [BeamformerMaxComputeShaderStages], int32, BeamformerMaxComputeShaderStages) \ + X(compute_stage_parameters, int16_t, [BeamformerMaxComputeShaderStages], int16, BeamformerMaxComputeShaderStages) \ + X(compute_stages_count, uint32_t, , uint32, 1) \ + X(data_kind, int32_t, , int32, 1) #define X(name, type, size, ...) type name size; typedef struct {BEAMFORMER_PARAMS_HEAD} BeamformerParametersHead; diff --git a/beamformer_shared_memory.c b/beamformer_shared_memory.c @@ -1,5 +1,5 @@ /* See LICENSE for license details. */ -#define BEAMFORMER_SHARED_MEMORY_VERSION (14UL) +#define BEAMFORMER_SHARED_MEMORY_VERSION (15UL) typedef struct BeamformerFrame BeamformerFrame; typedef struct ShaderReloadContext ShaderReloadContext; @@ -96,11 +96,12 @@ typedef enum {BEAMFORMER_LIVE_IMAGING_DIRTY_FLAG_LIST} BeamformerLiveImagingDirt #undef X #define BEAMFORMER_PARAMETER_BLOCK_REGION_LIST \ - X(ComputePipeline, pipeline) \ - X(ChannelMapping, channel_mapping) \ - X(FocalVectors, focal_vectors) \ - X(Parameters, parameters) \ - X(SparseElements, sparse_elements) + X(ComputePipeline, pipeline) \ + X(ChannelMapping, channel_mapping) \ + X(FocalVectors, focal_vectors) \ + X(Parameters, parameters) \ + X(SparseElements, sparse_elements) \ + X(TransmitReceiveOrientations, transmit_receive_orientations) typedef enum { #define X(k, ...) BeamformerParameterBlockRegion_##k, @@ -138,6 +139,7 @@ typedef struct { alignas(16) i16 channel_mapping[BeamformerMaxChannelCount]; alignas(16) i16 sparse_elements[BeamformerMaxChannelCount]; + alignas(16) u8 transmit_receive_orientations[BeamformerMaxChannelCount]; /* NOTE(rnp): interleaved transmit angle, focal depth pairs */ alignas(16) v2 focal_vectors[BeamformerMaxChannelCount]; } BeamformerParameterBlock; diff --git a/generated/beamformer.meta.c b/generated/beamformer.meta.c @@ -43,8 +43,6 @@ typedef enum { BeamformerShaderDASFlags_Sparse = (1 << 1), BeamformerShaderDASFlags_Interpolate = (1 << 2), BeamformerShaderDASFlags_CoherencyWeighting = (1 << 3), - BeamformerShaderDASFlags_RxColumns = (1 << 4), - BeamformerShaderDASFlags_TxColumns = (1 << 5), } BeamformerShaderDASFlags; typedef enum { @@ -245,8 +243,6 @@ read_only global s8 beamformer_shader_local_header_strings[] = { "#define ShaderFlags_Sparse (1 << 1)\n" "#define ShaderFlags_Interpolate (1 << 2)\n" "#define ShaderFlags_CoherencyWeighting (1 << 3)\n" - "#define ShaderFlags_RxColumns (1 << 4)\n" - "#define ShaderFlags_TxColumns (1 << 5)\n" "\n"), {0}, {0}, diff --git a/helpers/ogl_beamformer_lib.c b/helpers/ogl_beamformer_lib.c @@ -410,9 +410,10 @@ beamformer_wait_for_compute_dispatch(i32 timeout_ms) } #define BEAMFORMER_UPLOAD_FNS \ - X(channel_mapping, i16, 1, ChannelMapping) \ - X(sparse_elements, i16, 1, SparseElements) \ - X(focal_vectors, f32, 2, FocalVectors) + X(channel_mapping, i16, 1, ChannelMapping) \ + X(focal_vectors, f32, 2, FocalVectors) \ + X(sparse_elements, i16, 1, SparseElements) \ + X(transmit_receive_orientations, u8, 1, TransmitReceiveOrientations) #define X(name, dtype, elements, region_name) \ b32 beamformer_push_##name ##_at(dtype *data, u32 count, u32 block) { \ @@ -493,17 +494,20 @@ beamformer_push_simple_parameters_at(BeamformerSimpleParameters *bp, u32 block) { b32 result = validate_simple_parameters(bp); if (result) { + alignas(64) v2 focal_vectors[countof(bp->steering_angles)]; + for (u32 i = 0; i < countof(bp->steering_angles); i++) + focal_vectors[i] = (v2){{bp->steering_angles[i], bp->focal_depths[i]}}; + result &= beamformer_push_parameters_at((BeamformerParameters *)bp, block); result &= beamformer_push_pipeline_at(bp->compute_stages, bp->compute_stages_count, (BeamformerDataKind)bp->data_kind, block); result &= beamformer_push_channel_mapping_at(bp->channel_mapping, bp->channel_count, block); + result &= beamformer_push_focal_vectors_at((f32 *)focal_vectors, countof(focal_vectors), block); + result &= beamformer_push_transmit_receive_orientations_at(bp->transmit_receive_orientations, + bp->acquisition_count, block); + if (bp->das_shader_id == BeamformerDASKind_UFORCES || bp->das_shader_id == BeamformerDASKind_UHERCULES) result &= beamformer_push_sparse_elements_at(bp->sparse_elements, bp->acquisition_count, block); - alignas(64) v2 focal_vectors[countof(bp->steering_angles)]; - for (u32 i = 0; i < countof(bp->steering_angles); i++) - focal_vectors[i] = (v2){{bp->steering_angles[i], bp->focal_depths[i]}}; - result &= beamformer_push_focal_vectors_at((f32 *)focal_vectors, countof(focal_vectors), block); - for (u32 stage = 0; stage < bp->compute_stages_count; stage++) result &= beamformer_set_pipeline_stage_parameters_at(stage, bp->compute_stage_parameters[stage], block); } diff --git a/helpers/ogl_beamformer_lib_base.h b/helpers/ogl_beamformer_lib_base.h @@ -112,6 +112,9 @@ LIB_FN uint32_t beamformer_push_sparse_elements_at(int16_t *elements, uint32_t c LIB_FN uint32_t beamformer_push_focal_vectors(float *vectors, uint32_t count); LIB_FN uint32_t beamformer_push_focal_vectors_at(float *vectors, uint32_t count, uint32_t parameter_slot); +LIB_FN uint32_t beamformer_push_transmit_receive_orientations(uint8_t *values, uint32_t count); +LIB_FN uint32_t beamformer_push_transmit_receive_orientations_at(uint8_t *values, uint32_t count, uint32_t parameter_slot); + //////////////////// // Filter Creation diff --git a/shaders/das.glsl b/shaders/das.glsl @@ -40,6 +40,10 @@ layout(TEXTURE_KIND, binding = 0) writeonly restrict uniform image3D u_out_data layout(r16i, binding = 1) readonly restrict uniform iimage1D sparse_elements; layout(rg32f, binding = 2) readonly restrict uniform image1D focal_vectors; +layout(r8i, binding = 3) readonly restrict uniform iimage1D transmit_receive_orientations; + +#define RX_ORIENTATION_MASK (1 << 0) +#define TX_ORIENTATION_MASK (1 << 1) #define C_SPLINE 0.5 @@ -139,8 +143,8 @@ float cylindrical_wave_transmit_distance(vec3 point, float focal_depth, float tr #if (ShaderFlags & ShaderFlags_Fast) RESULT_TYPE RCA(vec3 world_point) { - bool tx_rows = bool((shader_flags & ShaderFlags_TxColumns) == 0); - bool rx_rows = bool((shader_flags & ShaderFlags_RxColumns) == 0); + bool tx_rows = (imageLoad(transmit_receive_orientations, u_channel).x & TX_ORIENTATION_MASK) == 0; + bool rx_rows = (imageLoad(transmit_receive_orientations, u_channel).x & RX_ORIENTATION_MASK) == 0; vec2 xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows); vec2 focal_vector = imageLoad(focal_vectors, u_channel).xy; float transmit_angle = radians(focal_vector.x); @@ -170,15 +174,14 @@ RESULT_TYPE RCA(vec3 world_point) #else RESULT_TYPE RCA(vec3 world_point) { - bool tx_rows = bool((shader_flags & ShaderFlags_TxColumns) == 0); - bool rx_rows = bool((shader_flags & ShaderFlags_RxColumns) == 0); - vec2 xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows); - RESULT_TYPE result = RESULT_TYPE(0); for (int transmit = 0; transmit < acquisition_count; transmit++) { vec2 focal_vector = imageLoad(focal_vectors, transmit).xy; float transmit_angle = radians(focal_vector.x); float focal_depth = focal_vector.y; + bool tx_rows = (imageLoad(transmit_receive_orientations, u_channel).x & TX_ORIENTATION_MASK) == 0; + bool rx_rows = (imageLoad(transmit_receive_orientations, u_channel).x & RX_ORIENTATION_MASK) == 0; + vec2 xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows); float transmit_distance; if (isinf(focal_depth)) { @@ -208,8 +211,8 @@ RESULT_TYPE RCA(vec3 world_point) RESULT_TYPE HERCULES(vec3 world_point) { vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz; - bool tx_rows = bool((shader_flags & ShaderFlags_TxColumns) == 0); - bool rx_cols = bool((shader_flags & ShaderFlags_RxColumns)); + bool tx_rows = (imageLoad(transmit_receive_orientations, 0).x & TX_ORIENTATION_MASK) == 0; + bool rx_cols = (imageLoad(transmit_receive_orientations, 0).x & RX_ORIENTATION_MASK) != 0; vec2 focal_vector = imageLoad(focal_vectors, 0).xy; float transmit_angle = radians(focal_vector.x); float focal_depth = focal_vector.y; @@ -245,8 +248,8 @@ RESULT_TYPE HERCULES(vec3 world_point) RESULT_TYPE HERCULES(vec3 world_point) { vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz; - bool tx_rows = bool((shader_flags & ShaderFlags_TxColumns) == 0); - bool rx_cols = bool((shader_flags & ShaderFlags_RxColumns)); + bool tx_rows = (imageLoad(transmit_receive_orientations, 0).x & TX_ORIENTATION_MASK) == 0; + bool rx_cols = (imageLoad(transmit_receive_orientations, 0).x & RX_ORIENTATION_MASK) != 0; vec2 focal_vector = imageLoad(focal_vectors, 0).xy; float transmit_angle = radians(focal_vector.x); float focal_depth = focal_vector.y; diff --git a/tests/throughput.c b/tests/throughput.c @@ -205,8 +205,6 @@ beamformer_parameters_from_zemp_bp_v1(zemp_bp_v1 *zbp, BeamformerParameters *out out->sample_count = zbp->decoded_data_dim[0]; out->channel_count = zbp->decoded_data_dim[1]; out->acquisition_count = zbp->decoded_data_dim[2]; - out->transmit_mode = (u8)((zbp->transmit_mode & 2) >> 1); - out->receive_mode = (u8)((zbp->transmit_mode & 1) >> 0); out->decode = (u8)zbp->decode_mode; out->das_shader_id = zbp->beamform_mode; out->time_offset = zbp->time_offset; @@ -354,11 +352,18 @@ execute_study(s8 study, Arena arena, Stream path, Options *options) } { - alignas(64) v2 focal_vectors[countof(zbp->focal_depths)]; - for (u32 i = 0; i < countof(zbp->focal_depths); i++) + alignas(64) v2 focal_vectors[BeamformerMaxChannelCount]; + for (u32 i = 0; i < countof(focal_vectors); i++) focal_vectors[i] = (v2){{zbp->transmit_angles[i], zbp->focal_depths[i]}}; beamformer_push_focal_vectors((f32 *)focal_vectors, countof(focal_vectors)); } + { + alignas(64) u8 transmit_receive_orientations[BeamformerMaxChannelCount]; + for (u32 i = 0; i < countof(transmit_receive_orientations); i++) + transmit_receive_orientations[i] = (u8)zbp->transmit_mode; + beamformer_push_transmit_receive_orientations(transmit_receive_orientations, + countof(transmit_receive_orientations)); + } beamformer_push_channel_mapping(zbp->channel_mapping, countof(zbp->channel_mapping)); beamformer_push_sparse_elements(zbp->sparse_elements, countof(zbp->sparse_elements));