ogl_beamforming

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

Commit: 8a48d013d4dfa2c00f7566b5312cceed14f76984
Parent: 5755f89922f954ba8853baac509c2b989908c8a8
Author: Randy Palamar
Date:   Fri, 21 Jun 2024 19:24:04 -0600

GPU UFORCES

Diffstat:
Mbeamformer.c | 27++++-----------------------
Mmain.c | 6++----
Mshaders/hadamard.glsl | 2+-
Mshaders/render.glsl | 2+-
Mshaders/uforces.glsl | 70+++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mutil.h | 3---
6 files changed, 57 insertions(+), 53 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -23,7 +23,6 @@ do_compute_shader(BeamformerCtx *ctx, u32 rf_ssbo_idx, enum compute_shaders shad glUniform3uiv(csctx->rf_data_dim_id, 1, csctx->rf_data_dim.E); glUniform3uiv(csctx->out_data_dim_id, 1, ctx->out_data_dim.E); - glUniform1ui(csctx->acquisition_id, ctx->acquisition); /* NOTE: Temporary flag for testing */ u32 data_idx = ctx->flags & DO_DECODE? 2 : rf_ssbo_idx; @@ -33,12 +32,12 @@ do_compute_shader(BeamformerCtx *ctx, u32 rf_ssbo_idx, enum compute_shaders shad glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, csctx->rf_data_ssbos[rf_ssbo_idx]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, csctx->rf_data_ssbos[2]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, csctx->hadamard_ssbo); - glDispatchCompute(csctx->rf_data_dim.x, csctx->rf_data_dim.y, csctx->rf_data_dim.z); + glDispatchCompute(csctx->rf_data_dim.x / 32, csctx->rf_data_dim.y / 32, csctx->rf_data_dim.z); break; case CS_UFORCES: glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, csctx->rf_data_ssbos[data_idx]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, ctx->out_data_ssbo); - glDispatchCompute(ctx->out_data_dim.x, ctx->out_data_dim.y, ctx->out_data_dim.z); + glDispatchCompute(ctx->out_data_dim.x / 32, ctx->out_data_dim.y / 32, ctx->out_data_dim.z); break; default: ASSERT(0); } @@ -51,26 +50,18 @@ draw_debug_overlay(BeamformerCtx *ctx, Arena arena) u32 fontsize = 32; u32 fontspace = 1; - s8 acq_txt = s8alloc(&arena, 64); s8 decode_txt = s8alloc(&arena, 64); s8 compute_txt = s8alloc(&arena, 64); - snprintf((char *)acq_txt.data, acq_txt.len, "Acquisition: %d", ctx->acquisition); snprintf((char *)decode_txt.data, decode_txt.len, "Decoding: %d", !!(ctx->flags & DO_DECODE)); snprintf((char *)compute_txt.data, compute_txt.len, "Compute: %d", !!(ctx->flags & DO_COMPUTE)); - v2 acq_fs = {.rl = MeasureTextEx(ctx->font, (char *)acq_txt.data, fontsize, fontspace)}; v2 decode_fs = {.rl = MeasureTextEx(ctx->font, (char *)decode_txt.data, fontsize, fontspace)}; v2 compute_fs = {.rl = MeasureTextEx(ctx->font, (char *)compute_txt.data, fontsize, fontspace)}; v2 scale = {.x = 90, .y = 20 }; - v2 dpos = {.x = 20, .y = ctx->window_size.y - acq_fs.y - decode_fs.y - compute_fs.y - 20}; - v2 dposa = {.x = dpos.x + acq_fs.x / scale.x, .y = dpos.y + acq_fs.y / scale.y }; - DrawTextEx(ctx->font, (char *)acq_txt.data, dposa.rl, fontsize, fontspace, Fade(BLACK, 0.8)); - DrawTextEx(ctx->font, (char *)acq_txt.data, dpos.rl, fontsize, fontspace, RED); - - dpos.y += 2 + acq_fs.y; - dposa = (v2){ .x = dpos.x + decode_fs.x / scale.x, .y = dpos.y + decode_fs.y / scale.y }; + v2 dpos = {.x = 20, .y = ctx->window_size.y - decode_fs.y - compute_fs.y - 20}; + v2 dposa = {.x = dpos.x + decode_fs.x / scale.x, .y = dpos.y + decode_fs.y / scale.y }; DrawTextEx(ctx->font, (char *)decode_txt.data, dposa.rl, fontsize, fontspace, Fade(BLACK, 0.8)); DrawTextEx(ctx->font, (char *)decode_txt.data, dpos.rl, fontsize, fontspace, RED); @@ -153,14 +144,4 @@ do_beamformer(BeamformerCtx *ctx, Arena arena, s8 rf_data) ctx->flags ^= DO_COMPUTE; if (IsKeyPressed(KEY_D)) ctx->flags ^= DO_DECODE; - if (IsKeyPressed(KEY_LEFT)) { - ctx->acquisition--; - if (ctx->acquisition < 0) - ctx->acquisition = ctx->csctx.rf_data_dim.d - 1; - } - if (IsKeyPressed(KEY_RIGHT)) { - ctx->acquisition++; - if (ctx->acquisition > ctx->csctx.rf_data_dim.d - 1) - ctx->acquisition = 0; - } } diff --git a/main.c b/main.c @@ -129,7 +129,6 @@ init_compute_shader_ctx(ComputeShaderCtx *ctx, Arena a, uv3 rf_data_dim) ctx->rf_data_dim_id = glGetUniformLocation(ctx->programs[CS_UFORCES], "u_rf_data_dim"); ctx->out_data_dim_id = glGetUniformLocation(ctx->programs[CS_UFORCES], "u_out_data_dim"); - ctx->acquisition_id = glGetUniformLocation(ctx->programs[CS_UFORCES], "u_acquisition"); ctx->rf_data_dim = rf_data_dim; size rf_data_size = rf_data_dim.w * rf_data_dim.h * rf_data_dim.d * sizeof(i32); @@ -222,7 +221,6 @@ reload_shaders(BeamformerCtx *ctx, Arena a) csctx->rf_data_dim_id = glGetUniformLocation(csctx->programs[CS_UFORCES], "u_rf_data_dim"); csctx->out_data_dim_id = glGetUniformLocation(csctx->programs[CS_UFORCES], "u_out_data_dim"); - csctx->acquisition_id = glGetUniformLocation(csctx->programs[CS_UFORCES], "u_acquisition"); Shader updated_fs = LoadShader(NULL, "shaders/render.glsl"); if (updated_fs.id != rlGetShaderIdDefault()) { @@ -244,8 +242,8 @@ main(void) os_file_stats decoded_stats = os_get_file_stats(decoded_name); s8 raw_rf_data = os_read_file(&temp_memory, decoded_name, decoded_stats.filesize); - ctx.window_size = (uv2){.w = 720, .h = 720}; - ctx.out_data_dim = (uv3){.w = 720, .h = 720, .d = 1}; + ctx.window_size = (uv2){.w = 2048, .h = 2048}; + ctx.out_data_dim = (uv3){.w = 2048, .h = 2048, .d = 1}; ctx.bg = PINK; ctx.fg = (Color){ .r = 0xea, .g = 0xe1, .b = 0xb4, .a = 0xff }; diff --git a/shaders/hadamard.glsl b/shaders/hadamard.glsl @@ -1,5 +1,5 @@ #version 460 core -layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; +layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in; layout(std430, binding = 1) readonly restrict buffer buffer_1 { int rf_data[]; diff --git a/shaders/render.glsl b/shaders/render.glsl @@ -23,7 +23,7 @@ void main() { ivec2 coord = ivec2(fragTexCoord * u_out_data_dim.xy); float smp = out_data[coord.y * u_out_data_dim.x + coord.x]; - smp = 20 * log(abs(smp) + 1e-12) + 60; + smp = 20 * log(abs(smp)) + 50; v_out_colour = vec4(hsv2rgb(vec3(smp, 0.8, 0.95)), 1); } diff --git a/shaders/uforces.glsl b/shaders/uforces.glsl @@ -1,5 +1,5 @@ #version 460 core -layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; +layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in; layout(std430, binding = 1) readonly restrict buffer buffer_1 { float rf_data[]; @@ -9,33 +9,61 @@ layout(std430, binding = 2) writeonly restrict buffer buffer_2 { float out_data[]; }; -layout(location = 3) uniform uvec3 u_rf_data_dim; -layout(location = 4) uniform uvec3 u_out_data_dim; -layout(location = 5) uniform uint u_acquisition; - -uint rf_idx(uint x, uint y, uint z) -{ - return u_rf_data_dim.y * u_rf_data_dim.x * z + u_rf_data_dim.x * y + x; -} - -uint out_idx(uint x, uint y, uint z) -{ - return u_out_data_dim.y * u_out_data_dim.x * z + u_out_data_dim.x * y + x; -} +layout(location = 3) uniform uvec3 u_rf_data_dim; +layout(location = 4) uniform uvec3 u_out_data_dim; +layout(location = 5) uniform float u_sound_speed = 1452; +layout(location = 6) uniform float u_sampling_frequency = 2.0833e7; +layout(location = 7) uniform float u_focal_depth = 0.07; +//layout(location = 10) uniform sampler2D u_element_positions; void main() { vec3 scale = vec3(u_rf_data_dim) / vec3(u_out_data_dim); + vec2 pixel = vec2(gl_GlobalInvocationID.xy); ivec3 rf_coord = ivec3(gl_GlobalInvocationID.xyz * scale); ivec3 out_coord = ivec3(gl_GlobalInvocationID.xyz); - uint x = rf_coord.x; - uint y = rf_coord.y; - uint z = u_acquisition; + /* NOTE: Convert pixel to physical coordinates */ + /* TODO: Send these in like the 3D program */ + + vec2 xdc_upper_left = vec2(-0.0096, -0.0096); + vec2 xdc_bottom_right = vec2( 0.0096, 0.0096); + //vec2 xdc_upper_left = texture(u_element_positions, ivec2(0, 0)).xy; + //vec2 xdc_bottom_right = texture(u_element_positions, ivec2(1, 1)).xy; + vec2 xdc_size = abs(xdc_upper_left - xdc_bottom_right); + + /* TODO: for now assume y-dimension is along transducer center */ + vec3 image_point = vec3( + xdc_upper_left.x + pixel.x * xdc_size.x / u_out_data_dim.x, + 0, + pixel.y * 80e-3 / u_out_data_dim.y + ); + + float dx = xdc_size.x / float(u_rf_data_dim.y); + float dzsign = sign(image_point.z - u_focal_depth); + + /* TODO: Send this into the GPU */ + float sparse_elems[] = {17, 33, 49, 65, 80, 96, 112}; + + float sum = 0; + float x = image_point.x - xdc_upper_left.x; + /* NOTE: skip first acquisition since its garbage */ + uint ridx = u_rf_data_dim.y * u_rf_data_dim.x; + for (uint i = 1; i < u_rf_data_dim.z; i++) { + vec3 focal_point = vec3(sparse_elems[i - 1] * dx, 0, u_focal_depth); + float transmit_dist = u_focal_depth + dzsign * distance(image_point, focal_point); - /* TODO: Probably should rotate in the fragment shader */ - uint oidx = out_idx(out_coord.y, out_coord.x, out_coord.z); + vec2 rdist = vec2(x, image_point.z); + for (uint j = 0; j < u_rf_data_dim.y; j++) { + float dist = transmit_dist + length(rdist); + uint rx = uint(dist * u_sampling_frequency / u_sound_speed); - uint ridx = rf_idx(x, y, z); - out_data[oidx] = rf_data[ridx]; + sum += rf_data[ridx + rx]; + rdist.x -= dx; + ridx += u_rf_data_dim.x; + } + ridx += u_rf_data_dim.y * u_rf_data_dim.x; + } + uint oidx = u_out_data_dim.x * out_coord.y + out_coord.x; + out_data[oidx] = sum; } diff --git a/util.h b/util.h @@ -82,7 +82,6 @@ typedef struct { uv3 rf_data_dim; i32 rf_data_dim_id; - i32 acquisition_id; i32 out_data_dim_id; } ComputeShaderCtx; @@ -103,8 +102,6 @@ typedef struct { u32 out_data_ssbo; uv3 out_data_dim; - i32 acquisition; - ComputeShaderCtx csctx; FragmentShaderCtx fsctx; } BeamformerCtx;