ogl_beamforming

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

Commit: 5755f89922f954ba8853baac509c2b989908c8a8
Parent: b121b2c6f32ad34016c3748c67713a328a9fb852
Author: Randy Palamar
Date:   Fri, 21 Jun 2024 06:22:05 -0600

update for real uforces data

Diffstat:
Mbeamformer.c | 31++++++++++++++++++++++++++-----
Mmain.c | 23++++++++++++++++-------
Mshaders/hadamard.glsl | 17+++++++++--------
Mshaders/uforces.glsl | 12+++++++-----
Mutil.h | 4++++
5 files changed, 62 insertions(+), 25 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -23,6 +23,7 @@ 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; @@ -50,16 +51,26 @@ 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 *)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)); + 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 - 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 }; + + 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 }; 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); @@ -110,7 +121,7 @@ do_beamformer(BeamformerCtx *ctx, Arena arena, s8 rf_data) ASSERT(rf_ssbo_idx == 0 || rf_ssbo_idx == 1); /* NOTE: Load RF Data into GPU */ - /* TODO: This should be done in a seperate thread */ + /* TODO: This should be done in a separate thread */ glBindBuffer(GL_SHADER_STORAGE_BUFFER, ctx->csctx.rf_data_ssbos[!rf_ssbo_idx]); glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, rf_data.len, rf_data.data); @@ -142,4 +153,14 @@ 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,19 +129,27 @@ 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(f32); - for (u32 i = 0; i < ARRAY_COUNT(ctx->rf_data_ssbos); i++) - ctx->rf_data_ssbos[i] = rlLoadShaderBuffer(rf_data_size, NULL, GL_DYNAMIC_COPY); + size rf_data_size = rf_data_dim.w * rf_data_dim.h * rf_data_dim.d * sizeof(i32); + + glGenBuffers(ARRAY_COUNT(ctx->rf_data_ssbos), ctx->rf_data_ssbos); + for (u32 i = 0; i < ARRAY_COUNT(ctx->rf_data_ssbos); i++) { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ctx->rf_data_ssbos[i]); + glBufferData(GL_SHADER_STORAGE_BUFFER, rf_data_size, 0, GL_DYNAMIC_COPY); + /* TODO: This doesn't actually work; need to use + * a texture to store i16 data and load i32 data */ + glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R32I, GL_R16I, GL_SHORT, 0); + } ctx->rf_data_idx = 0; /* NOTE: store hadamard in GPU once; it won't change for a particular imaging session */ - ctx->hadamard_dim = (uv2){ .x = rf_data_dim.y, .y = rf_data_dim.y }; + ctx->hadamard_dim = (uv2){ .x = rf_data_dim.d, .y = rf_data_dim.d }; size hadamard_elements = ctx->hadamard_dim.x * ctx->hadamard_dim.y; i32 *hadamard = alloc(&a, i32, hadamard_elements); fill_hadamard(hadamard, ctx->hadamard_dim.x); - ctx->hadamard_ssbo = rlLoadShaderBuffer(hadamard_elements * sizeof(i32), hadamard, GL_DYNAMIC_COPY); + ctx->hadamard_ssbo = rlLoadShaderBuffer(hadamard_elements * sizeof(i32), hadamard, GL_STATIC_DRAW); } static void @@ -214,8 +222,9 @@ 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"); + Shader updated_fs = LoadShader(NULL, "shaders/render.glsl"); if (updated_fs.id != rlGetShaderIdDefault()) { UnloadShader(ctx->fsctx.shader); ctx->fsctx.shader = updated_fs; @@ -248,7 +257,7 @@ main(void) size out_data_size = ctx.out_data_dim.w * ctx.out_data_dim.h * ctx.out_data_dim.d * sizeof(f32); ctx.out_data_ssbo = rlLoadShaderBuffer(out_data_size, NULL, GL_DYNAMIC_COPY); - init_compute_shader_ctx(&ctx.csctx, temp_memory, (uv3){.w = 4093, .h = 128, .d = 1}); + init_compute_shader_ctx(&ctx.csctx, temp_memory, (uv3){.w = 3456, .h = 128, .d = 8}); init_fragment_shader_ctx(&ctx.fsctx, ctx.out_data_dim); ctx.flags |= RELOAD_SHADERS; diff --git a/shaders/hadamard.glsl b/shaders/hadamard.glsl @@ -2,7 +2,7 @@ layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; layout(std430, binding = 1) readonly restrict buffer buffer_1 { - float rf_data[]; + int rf_data[]; }; layout(std430, binding = 2) writeonly restrict buffer buffer_2 { @@ -22,10 +22,11 @@ void main() * the result is stored to the same row, column index of the output data. */ uint time_sample = gl_GlobalInvocationID.x; - uint column = gl_GlobalInvocationID.y; + uint channel = gl_GlobalInvocationID.y; + uint acq = gl_GlobalInvocationID.z; /* offset to get the correct column in hadamard matrix */ - uint hoff = column * u_rf_data_dim.y; + uint hoff = u_rf_data_dim.z * acq; /* TODO: make sure incoming data is organized so that stride is 1 * i.e. each column should be a single time sample for all channels @@ -33,13 +34,13 @@ void main() */ /* offset to get the time sample and row in rf data */ - uint rstride = u_rf_data_dim.x; - uint rfoff = column * rstride + time_sample; + uint rstride = u_rf_data_dim.x * u_rf_data_dim.y; + uint rfoff = u_rf_data_dim.x * channel + time_sample; /* N-D dot product */ - float sum = 0; - for (int i = 0; i < u_rf_data_dim.y; i++) + int sum = 0; + for (int i = 0; i < u_rf_data_dim.z; i++) sum += hadamard[hoff + i] * rf_data[rfoff + rstride * i]; - out_data[rfoff] = float(sum); + out_data[rfoff + rstride * acq] = float(sum); } diff --git a/shaders/uforces.glsl b/shaders/uforces.glsl @@ -11,6 +11,7 @@ layout(std430, binding = 2) writeonly restrict buffer buffer_2 { 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) { @@ -24,16 +25,17 @@ uint out_idx(uint x, uint y, uint z) void main() { - vec3 scale = vec3(u_out_data_dim) / vec3(u_rf_data_dim); + vec3 scale = vec3(u_rf_data_dim) / vec3(u_out_data_dim); 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; + /* TODO: Probably should rotate in the fragment shader */ - uint x = rf_coord.y; - uint y = rf_coord.x; - uint z = 0; + uint oidx = out_idx(out_coord.y, out_coord.x, out_coord.z); - uint oidx = out_idx(out_coord.x, out_coord.y, out_coord.z); uint ridx = rf_idx(x, y, z); out_data[oidx] = rf_data[ridx]; } diff --git a/util.h b/util.h @@ -14,6 +14,7 @@ #endif typedef uint8_t u8; +typedef int16_t i16; typedef int32_t i32; typedef uint32_t u32; typedef uint32_t b32; @@ -81,6 +82,7 @@ typedef struct { uv3 rf_data_dim; i32 rf_data_dim_id; + i32 acquisition_id; i32 out_data_dim_id; } ComputeShaderCtx; @@ -101,6 +103,8 @@ typedef struct { u32 out_data_ssbo; uv3 out_data_dim; + i32 acquisition; + ComputeShaderCtx csctx; FragmentShaderCtx fsctx; } BeamformerCtx;