ogl_beamforming

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

Commit: 0d7e5e3f8abcd482e616e918c38277e251503b01
Parent: 0163bc9079c9e3c72220ae82a3c40923f160e76e
Author: Randy Palamar
Date:   Tue, 18 Jun 2024 15:10:09 -0600

reorganize structs and actually add shaders

Diffstat:
Mbeamformer.c | 40++++++++++++++++++++++------------------
Mmain.c | 59++++++++++++++++++++++++++++-------------------------------
Ashaders/render.glsl | 29+++++++++++++++++++++++++++++
Ashaders/uforces.glsl | 39+++++++++++++++++++++++++++++++++++++++
Mutil.c | 12+++++-------
5 files changed, 123 insertions(+), 56 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -14,6 +14,21 @@ #include "util.c" +static void +do_compute_shader(BeamformerCtx *ctx, enum compute_shaders shader) +{ + ComputeShaderCtx *csctx = &ctx->csctx; + glUseProgram(csctx->programs[shader]); + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, csctx->rf_data_ssbo); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, ctx->out_data_ssbo); + + glUniform3uiv(csctx->rf_data_dim_id, 1, csctx->rf_data_dim.E); + glUniform3uiv(csctx->out_data_dim_id, 1, ctx->out_data_dim.E); + + glDispatchCompute(ctx->out_data_dim.x, ctx->out_data_dim.y, ctx->out_data_dim.z); +} + DEBUG_EXPORT void do_beamformer(BeamformerCtx *ctx, Arena arena, s8 rf_data) { @@ -45,31 +60,20 @@ do_beamformer(BeamformerCtx *ctx, Arena arena, s8 rf_data) scale.y *= -1.0; } - { - ComputeShaderCtx *csctx = &ctx->csctx; - glUseProgram(csctx->programs[CS_UFORCES]); - - glBindBuffer(GL_SHADER_STORAGE_BUFFER, csctx->rf_data_ssbo); - glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, rf_data.len, rf_data.data); - - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, csctx->rf_data_ssbo); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, csctx->out_img_ssbo); + /* Load RF Data into GPU */ + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ctx->csctx.rf_data_ssbo); + glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, rf_data.len, rf_data.data); - glUniform3uiv(csctx->u_rf_dim_id, 1, csctx->rf_data_dim.E); - glUniform3ui(csctx->u_out_dim_id, ctx->out_img_dim.x, ctx->out_img_dim.y, 0); - - glDispatchCompute(ctx->out_img_dim.x, ctx->out_img_dim.y, 1); - } + do_compute_shader(ctx, CS_UFORCES); BeginDrawing(); - ClearBackground(PINK); + ClearBackground(ctx->bg); BeginShaderMode(ctx->fsctx.shader); glUseProgram(ctx->fsctx.shader.id); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ctx->csctx.out_img_ssbo); - glUniform2uiv(ctx->fsctx.u_out_dim_id, 1, ctx->out_img_dim.E); - ASSERT(ctx->fsctx.u_out_dim_id != -1); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ctx->out_data_ssbo); + glUniform3uiv(ctx->fsctx.out_data_dim_id, 1, ctx->out_data_dim.E); DrawTexture(ctx->fsctx.output, 0, 0, WHITE); EndShaderMode(); diff --git a/main.c b/main.c @@ -95,7 +95,7 @@ update_output_image_dimensions(BeamformerCtx *ctx, uv2 new_size) #endif static void -init_compute_shader_ctx(ComputeShaderCtx *ctx, uv3 rf_data_dim, uv2 out_img_dim) +init_compute_shader_ctx(ComputeShaderCtx *ctx, uv3 rf_data_dim) { for (u32 i = 0; i < ARRAY_COUNT(ctx->programs); i++) { char *shader_text = LoadFileText(compute_shader_paths[i]); @@ -105,30 +105,27 @@ init_compute_shader_ctx(ComputeShaderCtx *ctx, uv3 rf_data_dim, uv2 out_img_dim) UnloadFileText(shader_text); } - ctx->u_rf_dim_id = glGetUniformLocation(ctx->programs[CS_UFORCES], "u_rf_dim"); - ctx->u_out_dim_id = glGetUniformLocation(ctx->programs[CS_UFORCES], "u_out_dim"); - - ctx->rf_data_dim = 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->rf_data_dim = rf_data_dim; size rf_data_size = rf_data_dim.w * rf_data_dim.h * rf_data_dim.d * sizeof(f32); - size out_img_size = out_img_dim.w * out_img_dim.h * sizeof(f32); - - ctx->rf_data_ssbo = rlLoadShaderBuffer(rf_data_size, NULL, GL_DYNAMIC_COPY); - ctx->out_img_ssbo = rlLoadShaderBuffer(out_img_size, NULL, GL_DYNAMIC_COPY); + ctx->rf_data_ssbo = rlLoadShaderBuffer(rf_data_size, NULL, GL_DYNAMIC_COPY); } static void -init_fragment_shader_ctx(FragmentShaderCtx *ctx, uv2 window_size) +init_fragment_shader_ctx(FragmentShaderCtx *ctx, uv3 out_data_dim) { - ctx->shader = LoadShader(NULL, "shaders/render.glsl"); - ctx->u_out_dim_id = glGetUniformLocation(ctx->shader.id, "u_out_img_dim"); - glUniform2uiv(ctx->u_out_dim_id, 1, window_size.E); + ctx->shader = LoadShader(NULL, "shaders/render.glsl"); + + ctx->out_data_dim_id = glGetUniformLocation(ctx->shader.id, "u_out_data_dim"); + glUniform3uiv(ctx->out_data_dim_id, 1, out_data_dim.E); /* TODO: add min max uniform */ /* output texture for image blitting */ Texture2D new; - new.width = window_size.w; - new.height = window_size.h; + new.width = out_data_dim.w; + new.height = out_data_dim.h; new.mipmaps = 1; new.format = RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32; new.id = rlLoadTexture(0, new.width, new.height, new.format, new.mipmaps); @@ -167,18 +164,16 @@ compile_shader(Arena a, u32 type, s8 shader) } static void -reload_shaders(Arena a, BeamformerCtx *ctx) +reload_shaders(BeamformerCtx *ctx, Arena a) { - u32 cs_programs[ARRAY_COUNT(((ComputeShaderCtx *)0)->programs)]; - ComputeShaderCtx *csctx = &ctx->csctx; - for (u32 i = 0; i < ARRAY_COUNT(cs_programs); i++) { + for (u32 i = 0; i < ARRAY_COUNT(csctx->programs); i++) { Arena tmp = a; os_file_stats fs = os_get_file_stats(compute_shader_paths[i]); s8 shader_text = os_read_file(&tmp, compute_shader_paths[i], fs.filesize); u32 shader_id = compile_shader(tmp, GL_COMPUTE_SHADER, shader_text); - if (shader_id != rlGetShaderIdDefault()) { + if (shader_id) { glDeleteProgram(csctx->programs[i]); csctx->programs[i] = rlLoadComputeShaderProgram(shader_id); } @@ -186,15 +181,15 @@ reload_shaders(Arena a, BeamformerCtx *ctx) glDeleteShader(shader_id); } - csctx->u_rf_dim_id = glGetUniformLocation(csctx->programs[CS_UFORCES], "u_rf_dim"); - csctx->u_out_dim_id = glGetUniformLocation(csctx->programs[CS_UFORCES], "u_out_dim"); + 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"); Shader updated_fs = LoadShader(NULL, "shaders/render.glsl"); if (updated_fs.id != rlGetShaderIdDefault()) { UnloadShader(ctx->fsctx.shader); - ctx->fsctx.shader = updated_fs; - ctx->fsctx.u_out_dim_id = GetShaderLocation(updated_fs, "u_out_img_dim"); - glUniform2ui(ctx->fsctx.u_out_dim_id, ctx->out_img_dim.x, ctx->out_img_dim.y); + ctx->fsctx.shader = updated_fs; + ctx->fsctx.out_data_dim_id = GetShaderLocation(updated_fs, "u_out_data_dim"); + glUniform3uiv(ctx->fsctx.out_data_dim_id, 1, ctx->out_data_dim.E); } } @@ -209,17 +204,19 @@ main(void) os_file_stats decoded_stats = os_get_file_stats(decoded_name); s8 raw_rf_data = os_read_file(&temp_arena, decoded_name, decoded_stats.filesize); - ctx.window_size = (uv2){.w = 720, .h = 720}; - ctx.out_img_dim = (uv2){.w = 720, .h = 720}; + ctx.window_size = (uv2){.w = 720, .h = 720}; + ctx.out_data_dim = (uv3){.w = 720, .h = 720, .d = 1}; - ctx.bg = DARKGRAY; + ctx.bg = PINK; ctx.fg = (Color){ .r = 0xea, .g = 0xe1, .b = 0xb4, .a = 0xff }; SetConfigFlags(FLAG_VSYNC_HINT); InitWindow(ctx.window_size.w, ctx.window_size.h, "OGL Beamformer"); - init_compute_shader_ctx(&ctx.csctx, (uv3){.w = 4093, .h = 128, .d = 1}, ctx.window_size); - init_fragment_shader_ctx(&ctx.fsctx, ctx.window_size); + 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, (uv3){.w = 4093, .h = 128, .d = 1}); + init_fragment_shader_ctx(&ctx.fsctx, ctx.out_data_dim); ctx.flags |= RELOAD_SHADERS; @@ -228,7 +225,7 @@ main(void) if (ctx.flags & RELOAD_SHADERS) { ctx.flags &= ~RELOAD_SHADERS; - reload_shaders(temp_arena, &ctx); + reload_shaders(&ctx, temp_arena); } do_beamformer(&ctx, temp_arena, raw_rf_data); diff --git a/shaders/render.glsl b/shaders/render.glsl @@ -0,0 +1,29 @@ +#version 430 + +in vec2 fragTexCoord; +out vec4 v_out_colour; + +layout(std430, binding = 1) readonly buffer beamformed_data +{ + float out_data[]; +}; + +layout(location = 1) uniform uvec3 u_out_data_dim; + +/* input: h [0,360] | s,v [0, 1] * + * output: rgb [0,1] */ +vec3 hsv2rgb(vec3 hsv) +{ + vec3 k = mod(vec3(5, 3, 1) + hsv.x / 60, 6); + k = max(min(min(k, 4 - k), 1), 0); + return hsv.z - hsv.z * hsv.y * k; +} + +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; + + v_out_colour = vec4(hsv2rgb(vec3(smp, 0.8, 0.95)), 1); +} diff --git a/shaders/uforces.glsl b/shaders/uforces.glsl @@ -0,0 +1,39 @@ +#version 460 core +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[]; +}; + +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; + +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; +} + +void main() +{ + vec3 scale = vec3(u_out_data_dim) / vec3(u_rf_data_dim); + ivec3 rf_coord = ivec3(gl_GlobalInvocationID.xyz * scale); + ivec3 out_coord = ivec3(gl_GlobalInvocationID.xyz); + + /* 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.x, out_coord.y, out_coord.z); + uint ridx = rf_idx(x, y, z); + out_data[oidx] = rf_data[ridx]; +} diff --git a/util.c b/util.c @@ -67,19 +67,16 @@ enum program_flags { typedef struct { u32 programs[CS_LAST]; - u32 out_img_ssbo; - u32 rf_data_ssbo; uv3 rf_data_dim; - - i32 u_rf_dim_id; - i32 u_out_dim_id; + i32 rf_data_dim_id; + i32 out_data_dim_id; } ComputeShaderCtx; typedef struct { Shader shader; Texture2D output; - i32 u_out_dim_id; + i32 out_data_dim_id; } FragmentShaderCtx; typedef struct { @@ -88,7 +85,8 @@ typedef struct { Color bg, fg; - uv2 out_img_dim; /* shared output image dimension */ + u32 out_data_ssbo; + uv3 out_data_dim; ComputeShaderCtx csctx; FragmentShaderCtx fsctx;