ogl_beamforming

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

Commit: 26ba3b663e942b3c37e953f401a80ffcccb60bed
Parent: 4940910923dc59a36f6eaed57e6672e3d9d4c128
Author: Randy Palamar
Date:   Thu, 19 Sep 2024 15:49:12 -0600

first pass at multi array beamforming

This is untested but should work if the parameters are specified
correctly and the start times from the two arrays are the same (we
assume that the sent rf data contains all necessary channels).

Some additional work is required to reuse this for code for
multiframe averaging.

Diffstat:
Mbeamformer.c | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mbeamformer.h | 11++++++++---
Mbeamformer_parameters.h | 10++++++----
Mmain.c | 8++++++--
Mshaders/demod.glsl | 7++++---
Mshaders/hadamard.glsl | 7++++---
Mshaders/hercules.glsl | 32+++++++++++++++-----------------
Mshaders/render.glsl | 2+-
Ashaders/sum.glsl | 13+++++++++++++
Mshaders/uforces.glsl | 25++++++++++++++-----------
Mui.c | 1+
11 files changed, 128 insertions(+), 71 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -2,7 +2,6 @@ #include "beamformer.h" #include "ui.c" - static size decoded_data_size(ComputeShaderCtx *cs) { @@ -27,11 +26,13 @@ alloc_output_image(BeamformerCtx *ctx) /* TODO: does this actually matter or is 0 fine? */ ctx->out_texture_unit = 0; ctx->out_texture_mips = _tzcnt_u32(max_dim) + 1; - glActiveTexture(GL_TEXTURE0 + ctx->out_texture_unit); - glDeleteTextures(1, &ctx->out_texture); - glGenTextures(1, &ctx->out_texture); - glBindTexture(GL_TEXTURE_3D, ctx->out_texture); - glTexStorage3D(GL_TEXTURE_3D, ctx->out_texture_mips, GL_RG32F, odim.x, odim.y, odim.z); + glActiveTexture(GL_TEXTURE0); + glDeleteTextures(ARRAY_COUNT(ctx->out_textures), ctx->out_textures); + glGenTextures(ARRAY_COUNT(ctx->out_textures), ctx->out_textures); + for (u32 i = 0; i < ARRAY_COUNT(ctx->out_textures); i++) { + glBindTexture(GL_TEXTURE_3D, ctx->out_textures[i]); + glTexStorage3D(GL_TEXTURE_3D, ctx->out_texture_mips, GL_RG32F, odim.x, odim.y, odim.z); + } UnloadRenderTexture(ctx->fsctx.output); /* TODO: select odim.x vs odim.y */ @@ -104,13 +105,10 @@ alloc_shader_storage(BeamformerCtx *ctx, Arena a) } static m3 -observation_direction_to_xdc_space(v3 direction, BeamformerParameters *bp, u32 idx) +observation_direction_to_xdc_space(v3 direction, v3 origin, v3 corner1, v3 corner2) { - /* TODO: multiple xdc support */ - (void)idx; - - v3 edge1 = sub_v3(bp->xdc_corner1.xyz, bp->xdc_origin.xyz); - v3 edge2 = sub_v3(bp->xdc_corner2.xyz, bp->xdc_origin.xyz); + v3 edge1 = sub_v3(corner1, origin); + v3 edge2 = sub_v3(corner2, origin); v3 xdc_normal = cross(edge1, edge2); xdc_normal.z = ABS(xdc_normal.z); @@ -148,7 +146,7 @@ do_volume_computation_step(BeamformerCtx *ctx, enum compute_shaders shader) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_3D, e->volume_texture); glBindImageTexture(0, e->volume_texture, 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_R32F); - glUniform1i(e->volume_texture_id, 0); + glUniform1i(cs->out_data_tex_id, 0); glUniform1i(cs->volume_export_pass_id, 1); /* NOTE: We must tile this otherwise GL will kill us for taking too long */ @@ -174,6 +172,7 @@ do_compute_shader(BeamformerCtx *ctx, enum compute_shaders shader) ComputeShaderCtx *csctx = &ctx->csctx; uv2 rf_raw_dim = ctx->params->raw.rf_raw_dim; size rf_raw_size = rf_raw_dim.x * rf_raw_dim.y * sizeof(i16); + u32 sum_count = ARRAY_COUNT(ctx->out_textures); /* TODO: ctx->params->raw.frame_average_count */ glBeginQuery(GL_TIME_ELAPSED, csctx->timer_ids[csctx->timer_index][shader]); @@ -212,16 +211,15 @@ do_compute_shader(BeamformerCtx *ctx, enum compute_shaders shader) ORONE(csctx->dec_data_dim.z)); csctx->last_output_ssbo_index = !csctx->last_output_ssbo_index; break; - case CS_MIN_MAX: - glBindImageTexture(ctx->out_texture_unit, ctx->out_texture, 0, GL_FALSE, 0, + case CS_MIN_MAX: { + u32 texture = ctx->out_textures[ctx->out_texture_index]; + glBindImageTexture(ctx->out_texture_unit, texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RG32F); glUniform1i(csctx->out_data_tex_id, ctx->out_texture_unit); for (u32 i = 1; i < ctx->out_texture_mips; i++) { u32 otu = ctx->out_texture_unit; - glBindImageTexture(otu + 1, ctx->out_texture, i - 1, - GL_FALSE, 0, GL_READ_ONLY, GL_RG32F); - glBindImageTexture(otu + 2, ctx->out_texture, i, - GL_FALSE, 0, GL_WRITE_ONLY, GL_RG32F); + glBindImageTexture(otu + 1, texture, i - 1, GL_FALSE, 0, GL_READ_ONLY, GL_RG32F); + glBindImageTexture(otu + 2, texture, i - 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RG32F); glUniform1i(csctx->out_data_tex_id, otu + 1); glUniform1i(csctx->mip_view_tex_id, otu + 2); glUniform1i(csctx->mips_level_id, i); @@ -232,7 +230,7 @@ do_compute_shader(BeamformerCtx *ctx, enum compute_shaders shader) glDispatchCompute(ORONE(width / 32), ORONE(height), ORONE(depth / 32)); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); } - break; + } break; case CS_HERCULES: case CS_UFORCES: if (ctx->export_ctx.state & ES_START) { @@ -248,20 +246,50 @@ do_compute_shader(BeamformerCtx *ctx, enum compute_shaders shader) glUniform3iv(csctx->volume_export_dim_offset_id, 1, (i32 []){0, 0, 0}); glUniform1i(csctx->volume_export_pass_id, 0); - { + glActiveTexture(GL_TEXTURE0 + ctx->out_texture_unit); + for (u32 i = 0; i < ctx->params->raw.array_count; i++) { BeamformerParameters *bp = &ctx->params->raw; - m3 xdc_transform = observation_direction_to_xdc_space((v3){.z = 1}, bp, 0); - glActiveTexture(GL_TEXTURE0 + ctx->out_texture_unit); - glBindTexture(GL_TEXTURE_3D, ctx->out_texture); - glBindImageTexture(ctx->out_texture_unit, ctx->out_texture, 0, GL_TRUE, 0, + u32 tex_idx = (ctx->out_texture_index + i) % ARRAY_COUNT(ctx->out_textures); + u32 texture = ctx->out_textures[tex_idx]; + m3 xdc_transform = observation_direction_to_xdc_space((v3){.z = 1}, + bp->xdc_origin[i].xyz, + bp->xdc_corner1[i].xyz, + bp->xdc_corner2[i].xyz); + glBindTexture(GL_TEXTURE_3D, texture); + glBindImageTexture(ctx->out_texture_unit, texture, 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RG32F); glUniform1i(csctx->out_data_tex_id, ctx->out_texture_unit); + glUniform1i(csctx->xdc_index_id, i); glUniformMatrix3fv(csctx->xdc_transform_id, 1, GL_FALSE, xdc_transform.E); glDispatchCompute(ORONE(ctx->out_data_dim.x / 32), ctx->out_data_dim.y, ORONE(ctx->out_data_dim.z / 32)); } - break; + if (ctx->params->raw.array_count == 1) + break; + /* TODO: we can't use this as a ring buffer yet since we need to keep a fixed + * index for output display */ + //ctx->out_texture_index = (ctx->out_texture_index + ctx->params->raw.array_count) + // % ARRAY_COUNT(ctx->out_textures); + sum_count = ctx->params->raw.array_count; + /* NOTE: FALLTHROUGH; sum implicit when beamforming mulitple arrays */ + case CS_SUM: { + u32 otu = 0; + /* TODO: extra "display" texture to always sum into */ + u32 out_texture = ctx->out_textures[ctx->out_texture_index]; + glBindImageTexture(otu, out_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RG32F); + glUniform1i(csctx->sum_out_img_id, otu); + for (u32 i = ctx->out_texture_index + 1; i < sum_count; i++) { + u32 tex_idx = i % ARRAY_COUNT(ctx->out_textures); + u32 in_texture = ctx->out_textures[tex_idx]; + glBindImageTexture(otu + 1, in_texture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RG32F); + glUniform1i(csctx->sum_in_img_id, otu + 1); + glDispatchCompute(ORONE(ctx->out_data_dim.x / 32), + ctx->out_data_dim.y, + ORONE(ctx->out_data_dim.z / 32)); + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + } + } break; default: ASSERT(0); } @@ -416,7 +444,8 @@ do_beamformer(BeamformerCtx *ctx, Arena arena) FragmentShaderCtx *fs = &ctx->fsctx; glUseProgram(fs->shader.id); glActiveTexture(GL_TEXTURE0 + ctx->out_texture_unit); - glBindTexture(GL_TEXTURE_3D, ctx->out_texture); + /* TODO: seperate display texture */ + glBindTexture(GL_TEXTURE_3D, ctx->out_textures[ctx->out_texture_index]); glUniform1i(fs->out_data_tex_id, ctx->out_texture_unit); glUniform1f(fs->db_cutoff_id, fs->db); DrawTexture(fs->output.texture, 0, 0, WHITE); diff --git a/beamformer.h b/beamformer.h @@ -193,6 +193,10 @@ typedef struct { i32 volume_export_pass_id; i32 volume_export_dim_offset_id; i32 xdc_transform_id; + i32 xdc_index_id; + + i32 sum_out_img_id; + i32 sum_in_img_id; } ComputeShaderCtx; typedef struct { @@ -215,9 +219,7 @@ typedef struct { u32 timer_ids[2]; f32 runtime; u32 volume_texture; - i32 volume_texture_id; u32 rf_data_ssbo; - u32 output_ssbo; u32 state; u32 dispatch_index; } ExportCtx; @@ -236,7 +238,10 @@ typedef struct { InputState is; uv4 out_data_dim; - u32 out_texture; + /* NOTE: circular buffer of output textures; useful for multi array or averaging */ + /* TODO: support averaging for multi array imaging */ + u32 out_textures[4]; + u32 out_texture_index; u32 out_texture_unit; u32 out_texture_mips; diff --git a/beamformer_parameters.h b/beamformer_parameters.h @@ -6,7 +6,8 @@ enum compute_shaders { CS_HADAMARD = 3, CS_HERCULES = 4, CS_MIN_MAX = 5, - CS_UFORCES = 6, + CS_SUM = 6, + CS_UFORCES = 7, CS_LAST }; @@ -16,14 +17,15 @@ typedef struct { u16 channel_mapping[512]; /* Transducer Channel to Verasonics Channel */ u32 uforces_channels[128]; /* Channels used for virtual UFORCES elements */ f32 lpf_coefficients[64]; /* Low Pass Filter Cofficients */ - v4 xdc_origin; /* [m] Corner of transducer being treated as origin */ - v4 xdc_corner1; /* [m] Corner of transducer along first axis (arbitrary) */ - v4 xdc_corner2; /* [m] Corner of transducer along second axis (arbitrary) */ + v4 xdc_origin[4]; /* [m] Corner of transducer being treated as origin */ + v4 xdc_corner1[4]; /* [m] Corner of transducer along first axis (arbitrary) */ + v4 xdc_corner2[4]; /* [m] Corner of transducer along second axis (arbitrary) */ uv4 dec_data_dim; /* Samples * Channels * Acquisitions; last element ignored */ uv4 output_points; /* Width * Height * Depth; last element ignored */ v4 output_min_coordinate; /* [m] Back-Top-Left corner of output region (w ignored) */ v4 output_max_coordinate; /* [m] Front-Bottom-Right corner of output region (w ignored)*/ uv2 rf_raw_dim; /* Raw Data Dimensions */ + u32 array_count; /* Number of Arrays (4 max) */ u32 channel_offset; /* Offset into channel_mapping: 0 or 128 (rows or columns) */ u32 lpf_order; /* Order of Low Pass Filter */ f32 speed_of_sound; /* [m/s] */ diff --git a/main.c b/main.c @@ -6,6 +6,7 @@ static char *compute_shader_paths[CS_LAST] = { [CS_HERCULES] = "shaders/hercules.glsl", [CS_DEMOD] = "shaders/demod.glsl", [CS_MIN_MAX] = "shaders/min_max.glsl", + [CS_SUM] = "shaders/sum.glsl", [CS_UFORCES] = "shaders/uforces.glsl", }; @@ -117,19 +118,22 @@ reload_shaders(BeamformerCtx *ctx, Arena a) glDeleteShader(shader_id); } - ctx->export_ctx.volume_texture_id = glGetUniformLocation(csctx->programs[CS_HERCULES], - "u_out_volume_tex"); csctx->volume_export_pass_id = glGetUniformLocation(csctx->programs[CS_HERCULES], "u_volume_export_pass"); csctx->volume_export_dim_offset_id = glGetUniformLocation(csctx->programs[CS_HERCULES], "u_volume_export_dim_offset"); csctx->xdc_transform_id = glGetUniformLocation(csctx->programs[CS_UFORCES], "u_xdc_transform"); + csctx->xdc_index_id = glGetUniformLocation(csctx->programs[CS_UFORCES], + "u_xdc_index"); csctx->out_data_tex_id = glGetUniformLocation(csctx->programs[CS_UFORCES], "u_out_data_tex"); csctx->mip_view_tex_id = glGetUniformLocation(csctx->programs[CS_MIN_MAX], "u_mip_view_tex"); csctx->mips_level_id = glGetUniformLocation(csctx->programs[CS_MIN_MAX], "u_mip_map"); + csctx->sum_out_img_id = glGetUniformLocation(csctx->programs[CS_SUM], "u_sum_out_img"); + csctx->sum_in_img_id = glGetUniformLocation(csctx->programs[CS_SUM], "u_sum_in_img"); + Shader updated_fs = LoadShader(NULL, "shaders/render.glsl"); if (updated_fs.id != rlGetShaderIdDefault()) { UnloadShader(ctx->fsctx.shader); diff --git a/shaders/demod.glsl b/shaders/demod.glsl @@ -14,14 +14,15 @@ layout(std140, binding = 0) uniform parameters { uvec4 channel_mapping[64]; /* Transducer Channel to Verasonics Channel */ uvec4 uforces_channels[32]; /* Channels used for virtual UFORCES elements */ vec4 lpf_coefficients[16]; /* Low Pass Filter Cofficients */ - vec4 xdc_origin; /* [m] Corner of transducer being treated as origin */ - vec4 xdc_corner1; /* [m] Corner of transducer along first axis (arbitrary) */ - vec4 xdc_corner2; /* [m] Corner of transducer along second axis (arbitrary) */ + vec4 xdc_origin[4]; /* [m] Corner of transducer being treated as origin */ + vec4 xdc_corner1[4]; /* [m] Corner of transducer along first axis (arbitrary) */ + vec4 xdc_corner2[4]; /* [m] Corner of transducer along second axis (arbitrary) */ uvec4 dec_data_dim; /* Samples * Channels * Acquisitions; last element ignored */ uvec4 output_points; /* Width * Height * Depth; last element ignored */ vec4 output_min_coord; /* [m] Top left corner of output region */ vec4 output_max_coord; /* [m] Bottom right corner of output region */ uvec2 rf_raw_dim; /* Raw Data Dimensions */ + uint array_count; /* Number of Arrays (4 max) */ uint channel_offset; /* Offset into channel_mapping: 0 or 128 (rows or columns) */ uint lpf_order; /* Order of Low Pass Filter */ float speed_of_sound; /* [m/s] */ diff --git a/shaders/hadamard.glsl b/shaders/hadamard.glsl @@ -18,14 +18,15 @@ layout(std140, binding = 0) uniform parameters { uvec4 channel_mapping[64]; /* Transducer Channel to Verasonics Channel */ uvec4 uforces_channels[32]; /* Channels used for virtual UFORCES elements */ vec4 lpf_coefficients[16]; /* Low Pass Filter Cofficients */ - vec4 xdc_origin; /* [m] Corner of transducer being treated as origin */ - vec4 xdc_corner1; /* [m] Corner of transducer along first axis (arbitrary) */ - vec4 xdc_corner2; /* [m] Corner of transducer along second axis (arbitrary) */ + vec4 xdc_origin[4]; /* [m] Corner of transducer being treated as origin */ + vec4 xdc_corner1[4]; /* [m] Corner of transducer along first axis (arbitrary) */ + vec4 xdc_corner2[4]; /* [m] Corner of transducer along second axis (arbitrary) */ uvec4 dec_data_dim; /* Samples * Channels * Acquisitions; last element ignored */ uvec4 output_points; /* Width * Height * Depth; last element ignored */ vec4 output_min_coord; /* [m] Top left corner of output region */ vec4 output_max_coord; /* [m] Bottom right corner of output region */ uvec2 rf_raw_dim; /* Raw Data Dimensions */ + uint array_count; /* Number of Arrays (4 max) */ uint channel_offset; /* Offset into channel_mapping: 0 or 128 (rows or columns) */ uint lpf_order; /* Order of Low Pass Filter */ float speed_of_sound; /* [m/s] */ diff --git a/shaders/hercules.glsl b/shaders/hercules.glsl @@ -10,14 +10,15 @@ layout(std140, binding = 0) uniform parameters { uvec4 channel_mapping[64]; /* Transducer Channel to Verasonics Channel */ uvec4 uforces_channels[32]; /* Channels used for virtual UFORCES elements */ vec4 lpf_coefficients[16]; /* Low Pass Filter Cofficients */ - vec4 xdc_origin; /* [m] Corner of transducer being treated as origin */ - vec4 xdc_corner1; /* [m] Corner of transducer along first axis (arbitrary) */ - vec4 xdc_corner2; /* [m] Corner of transducer along second axis (arbitrary) */ + vec4 xdc_origin[4]; /* [m] Corner of transducer being treated as origin */ + vec4 xdc_corner1[4]; /* [m] Corner of transducer along first axis (arbitrary) */ + vec4 xdc_corner2[4]; /* [m] Corner of transducer along second axis (arbitrary) */ uvec4 dec_data_dim; /* Samples * Channels * Acquisitions; last element ignored */ uvec4 output_points; /* Width * Height * Depth; last element ignored */ vec4 output_min_coord; /* [m] Top left corner of output region */ vec4 output_max_coord; /* [m] Bottom right corner of output region */ uvec2 rf_raw_dim; /* Raw Data Dimensions */ + uint array_count; /* Number of Arrays (4 max) */ uint channel_offset; /* Offset into channel_mapping: 0 or 128 (rows or columns) */ uint lpf_order; /* Order of Low Pass Filter */ float speed_of_sound; /* [m/s] */ @@ -31,11 +32,11 @@ layout(std140, binding = 0) uniform parameters { }; layout(rg32f, location = 1) uniform writeonly image3D u_out_data_tex; -layout(r32f, location = 2) uniform writeonly image3D u_out_volume_tex; -layout(location = 3) uniform int u_volume_export_pass; -layout(location = 4) uniform ivec3 u_volume_export_dim_offset; -layout(location = 5) uniform mat3 u_xdc_transform; +layout(location = 2) uniform int u_volume_export_pass; +layout(location = 3) uniform ivec3 u_volume_export_dim_offset; +layout(location = 4) uniform mat3 u_xdc_transform; +layout(location = 5) uniform int u_xdc_index; #define C_SPLINE 0.5 @@ -76,10 +77,7 @@ vec2 cubic(uint ridx, float x) vec3 calc_image_point(vec3 voxel) { - ivec3 out_data_dim; - if (u_volume_export_pass == 0) out_data_dim = imageSize(u_out_data_tex); - else out_data_dim = imageSize(u_out_volume_tex); - + ivec3 out_data_dim = imageSize(u_out_data_tex); vec4 output_size = abs(output_max_coord - output_min_coord); vec3 image_point = output_min_coord.xyz + voxel * output_size.xyz / out_data_dim.xyz; @@ -97,8 +95,8 @@ void main() ivec3 out_coord = ivec3(gl_GlobalInvocationID.xyz) + u_volume_export_dim_offset; /* NOTE: Convert pixel to physical coordinates */ - vec3 edge1 = xdc_corner1.xyz - xdc_origin.xyz; - vec3 edge2 = xdc_corner2.xyz - xdc_origin.xyz; + vec3 edge1 = xdc_corner1[u_xdc_index].xyz - xdc_origin[u_xdc_index].xyz; + vec3 edge2 = xdc_corner2[u_xdc_index].xyz - xdc_origin[u_xdc_index].xyz; vec3 image_point = calc_image_point(voxel); /* NOTE: used for constant F# dynamic receive apodization. This is implemented as: @@ -116,7 +114,7 @@ void main() /* NOTE: lerp along a line from one edge of the xdc to the other in the imaging plane */ vec3 delta = edge1 / float(dec_data_dim.y); - vec3 xdc_start = xdc_origin.xyz; + vec3 xdc_start = xdc_origin[u_xdc_index].xyz; xdc_start += edge2 / 2; vec3 starting_point = image_point - xdc_start; @@ -142,8 +140,9 @@ void main() transmit_dist = length(vec2(image_point.x, image_point.z - focal_depth)); } + /* NOTE: skip over channels corresponding to other arrays */ + uint ridx = u_xdc_index * (dec_data_dim.y / array_count) * dec_data_dim.x * dec_data_dim.z; int direction = beamform_plane * (u_volume_export_pass ^ 1); - uint ridx = 0; /* NOTE: For Each Acquistion in Raw Data */ for (uint i = 0; i < dec_data_dim.z; i++) { /* NOTE: For Each Virtual Source */ @@ -169,6 +168,5 @@ void main() rdist[direction ^ 1] -= delta[direction ^ 1]; } float val = length(sum); - if (u_volume_export_pass == 0) imageStore(u_out_data_tex, out_coord, vec4(val)); - else imageStore(u_out_volume_tex, out_coord, vec4(val)); + imageStore(u_out_data_tex, out_coord, vec4(val)); } diff --git a/shaders/render.glsl b/shaders/render.glsl @@ -25,7 +25,7 @@ void main() vec2 min_max = texelFetch(u_out_data_tex, ivec3(0), textureQueryLevels(u_out_data_tex) - 1).xy; ivec3 smp_coord = ivec3(coord.x, 0, coord.y); - float smp = texelFetch(u_out_data_tex, smp_coord, 0).x; + float smp = length(texelFetch(u_out_data_tex, smp_coord, 0).xy); float absmax = max(abs(min_max.y), abs(min_max.x)); smp = 20 * log(abs(smp) / absmax) / log(10); diff --git a/shaders/sum.glsl b/shaders/sum.glsl @@ -0,0 +1,13 @@ +#version 430 core + +layout(local_size_x = 32, local_size_y = 1, local_size_z = 32) in; + +layout(rg32f, location = 1) uniform image3D u_out_img; +layout(rg32f, location = 2) readonly uniform image3D u_in_img; + +void main() +{ + ivec3 voxel = ivec3(gl_GlobalInvocationID); + vec4 sum = imageLoad(u_out_img, voxel) + imageLoad(u_in_img, voxel); + imageStore(u_out_img, voxel, sum); +} diff --git a/shaders/uforces.glsl b/shaders/uforces.glsl @@ -10,14 +10,15 @@ layout(std140, binding = 0) uniform parameters { uvec4 channel_mapping[64]; /* Transducer Channel to Verasonics Channel */ uvec4 uforces_channels[32]; /* Channels used for virtual UFORCES elements */ vec4 lpf_coefficients[16]; /* Low Pass Filter Cofficients */ - vec4 xdc_origin; /* [m] Corner of transducer being treated as origin */ - vec4 xdc_corner1; /* [m] Corner of transducer along first axis (arbitrary) */ - vec4 xdc_corner2; /* [m] Corner of transducer along second axis (arbitrary) */ + vec4 xdc_origin[4]; /* [m] Corner of transducer being treated as origin */ + vec4 xdc_corner1[4]; /* [m] Corner of transducer along first axis (arbitrary) */ + vec4 xdc_corner2[4]; /* [m] Corner of transducer along second axis (arbitrary) */ uvec4 dec_data_dim; /* Samples * Channels * Acquisitions; last element ignored */ uvec4 output_points; /* Width * Height * Depth; last element ignored */ vec4 output_min_coord; /* [m] Top left corner of output region */ vec4 output_max_coord; /* [m] Bottom right corner of output region */ uvec2 rf_raw_dim; /* Raw Data Dimensions */ + uint array_count; /* Number of Arrays (4 max) */ uint channel_offset; /* Offset into channel_mapping: 0 or 128 (rows or columns) */ uint lpf_order; /* Order of Low Pass Filter */ float speed_of_sound; /* [m/s] */ @@ -31,11 +32,11 @@ layout(std140, binding = 0) uniform parameters { }; layout(rg32f, location = 1) writeonly uniform image3D u_out_data_tex; -layout(r32f, location = 2) uniform writeonly image3D u_out_volume_tex; -layout(location = 3) uniform int u_volume_export_pass; -layout(location = 4) uniform ivec3 u_volume_export_dim_offset; -layout(location = 5) uniform mat3 u_xdc_transform; +layout(location = 2) uniform int u_volume_export_pass; +layout(location = 3) uniform ivec3 u_volume_export_dim_offset; +layout(location = 4) uniform mat3 u_xdc_transform; +layout(location = 5) uniform int u_xdc_index; #define C_SPLINE 0.5 @@ -91,8 +92,8 @@ void main() ivec3 out_coord = ivec3(gl_GlobalInvocationID); /* NOTE: Convert voxel to physical coordinates */ - vec3 edge1 = xdc_corner1.xyz - xdc_origin.xyz; - vec3 edge2 = xdc_corner2.xyz - xdc_origin.xyz; + vec3 edge1 = xdc_corner1[u_xdc_index].xyz - xdc_origin[u_xdc_index].xyz; + vec3 edge2 = xdc_corner2[u_xdc_index].xyz - xdc_origin[u_xdc_index].xyz; vec3 image_point = calc_image_point(voxel); /* NOTE: used for constant F# dynamic receive apodization. This is implemented as: @@ -110,7 +111,7 @@ void main() /* NOTE: lerp along a line from one edge of the xdc to the other in the imaging plane */ vec3 delta = edge1 / float(dec_data_dim.y); - vec3 xdc_start = xdc_origin.xyz; + vec3 xdc_start = xdc_origin[u_xdc_index].xyz; xdc_start += edge2 / 2; vec3 starting_point = image_point - xdc_start; @@ -119,8 +120,10 @@ void main() float time_correction = time_offset + lpf_order / sampling_frequency; vec2 sum = vec2(0); + /* NOTE: skip over channels corresponding to other arrays */ + uint ridx = u_xdc_index * (dec_data_dim.y / array_count) * dec_data_dim.x * dec_data_dim.z; /* NOTE: skip first acquisition in uforces since its garbage */ - uint ridx = dec_data_dim.y * dec_data_dim.x * uforces; + ridx += dec_data_dim.y * dec_data_dim.x * uforces; for (uint i = uforces; i < dec_data_dim.z; i++) { uint base_idx = (i - uforces) / 4; uint sub_idx = (i - uforces) % 4; diff --git a/ui.c b/ui.c @@ -585,6 +585,7 @@ draw_debug_overlay(BeamformerCtx *ctx, Arena arena, Rect r) [CS_HADAMARD] = s8("Decoding:"), [CS_HERCULES] = s8("HERCULES:"), [CS_MIN_MAX] = s8("Min/Max:"), + [CS_SUM] = s8("Sum:"), [CS_UFORCES] = s8("UFORCES:"), };