ogl_beamforming

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

Commit: 2c765d746b92c2f5bd2443ed546abd8e2aaeb86e
Parent: 2d06c4b2b5174951a64d9b72f828b71d42958f69
Author: Randy Palamar
Date:   Sat, 31 May 2025 22:24:47 -0600

core: use common path for shader reloading

Diffstat:
Mbeamformer.c | 191++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mbeamformer.h | 46+++++++++++++++++++++++++++++++---------------
Mbeamformer_parameters.h | 24++++++++++++------------
Mbeamformer_work_queue.h | 8++++----
Mhelpers/ogl_beamformer_lib.c | 7++++---
Dshaders/render.glsl | 37-------------------------------------
Ashaders/render_2d.frag.glsl | 37+++++++++++++++++++++++++++++++++++++
Mstatic.c | 125++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mui.c | 16++++++++--------
Mutil.c | 2+-
Mutil.h | 8+++++---
11 files changed, 267 insertions(+), 234 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -9,6 +9,7 @@ * - In particular we will potentially need multiple GPUComputeContexts so that we * can overwrite one while the other is in use. * - make use of glFenceSync to guard buffer uploads + * [ ]: BeamformWorkQueue -> BeamformerWorkQueue */ #include "beamformer.h" @@ -267,7 +268,7 @@ compute_cursor_finished(struct compute_cursor *cursor) } function void -do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformComputeFrame *frame, ComputeShaderID shader) +do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformComputeFrame *frame, ShaderKind shader) { ComputeShaderCtx *csctx = &ctx->csctx; @@ -277,9 +278,9 @@ do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformComputeFrame *frame, u32 input_ssbo_idx = csctx->last_output_ssbo_index; switch (shader) { - case CS_DECODE: - case CS_DECODE_FLOAT: - case CS_DECODE_FLOAT_COMPLEX: + case ShaderKind_Decode: + case ShaderKind_DecodeFloat: + case ShaderKind_DecodeFloatComplex:{ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, csctx->raw_data_ssbo); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, csctx->rf_data_ssbos[output_ssbo_idx]); glBindImageTexture(0, csctx->hadamard_texture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8I); @@ -288,24 +289,24 @@ do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformComputeFrame *frame, ORONE(csctx->dec_data_dim.y / 32), ORONE(csctx->dec_data_dim.z)); csctx->last_output_ssbo_index = !csctx->last_output_ssbo_index; - break; - case CS_CUDA_DECODE: + }break; + case ShaderKind_CudaDecode:{ ctx->cuda_lib.decode(0, output_ssbo_idx, 0); csctx->last_output_ssbo_index = !csctx->last_output_ssbo_index; - break; - case CS_CUDA_HILBERT: + }break; + case ShaderKind_CudaHilbert: ctx->cuda_lib.hilbert(input_ssbo_idx, output_ssbo_idx); csctx->last_output_ssbo_index = !csctx->last_output_ssbo_index; break; - case CS_DEMOD: + case ShaderKind_Demodulate:{ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, csctx->rf_data_ssbos[input_ssbo_idx]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, csctx->rf_data_ssbos[output_ssbo_idx]); glDispatchCompute(ORONE(csctx->dec_data_dim.x / 32), ORONE(csctx->dec_data_dim.y / 32), ORONE(csctx->dec_data_dim.z)); csctx->last_output_ssbo_index = !csctx->last_output_ssbo_index; - break; - case CS_MIN_MAX: { + }break; + case ShaderKind_MinMax:{ u32 texture = frame->frame.texture; for (u32 i = 1; i < frame->frame.mips; i++) { glBindImageTexture(0, texture, i - 1, GL_TRUE, 0, GL_READ_ONLY, GL_RG32F); @@ -318,8 +319,8 @@ do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformComputeFrame *frame, glDispatchCompute(ORONE(width / 32), ORONE(height), ORONE(depth / 32)); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); } - } break; - case CS_DAS: { + }break; + case ShaderKind_DASCompute:{ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, csctx->rf_data_ssbos[input_ssbo_idx]); glBindImageTexture(0, frame->frame.texture, 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RG32F); glBindImageTexture(1, csctx->sparse_elements_texture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R16I); @@ -354,8 +355,8 @@ do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformComputeFrame *frame, ORONE(frame->frame.dim.z / 32)); #endif glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT|GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); - } break; - case CS_SUM: { + }break; + case ShaderKind_Sum:{ u32 aframe_index = ctx->averaged_frame_index % ARRAY_COUNT(ctx->averaged_frames); BeamformComputeFrame *aframe = ctx->averaged_frames + aframe_index; aframe->ready_to_present = 0; @@ -377,34 +378,23 @@ do_compute_shader(BeamformerCtx *ctx, Arena arena, BeamformComputeFrame *frame, do_sum_shader(csctx, in_textures, frame_count, 1 / (f32)frame_count, aframe->frame.texture, aframe->frame.dim); - aframe->frame.min_coordinate = frame->frame.min_coordinate; - aframe->frame.max_coordinate = frame->frame.max_coordinate; - aframe->frame.compound_count = frame->frame.compound_count; - aframe->frame.das_shader_id = frame->frame.das_shader_id; - } break; - default: ASSERT(0); + aframe->frame.min_coordinate = frame->frame.min_coordinate; + aframe->frame.max_coordinate = frame->frame.max_coordinate; + aframe->frame.compound_count = frame->frame.compound_count; + aframe->frame.das_shader_kind = frame->frame.das_shader_kind; + }break; + InvalidDefaultCase; } } function s8 -push_compute_shader_header(Arena *a, b32 parameters, ComputeShaderID shader) +shader_text_with_header(ShaderReloadContext *ctx, OS *os, Arena *arena, char *path) { - Stream sb = arena_stream(*a); - - stream_append_s8(&sb, s8("#version 460 core\n\n")); - - #define X(name, type, size, gltype, glsize, comment) "\t" #gltype " " #name #glsize "; " comment "\n" - if (parameters) { - stream_append_s8(&sb, s8("layout(std140, binding = 0) uniform parameters {\n" - BEAMFORMER_PARAMS_HEAD - BEAMFORMER_UI_PARAMS - BEAMFORMER_PARAMS_TAIL - "};\n\n")); - } - #undef X + Stream sb = arena_stream(*arena); + stream_append_s8s(&sb, s8("#version 460 core\n\n"), ctx->header); - switch (shader) { - case CS_DAS: { + switch (ctx->kind) { + case ShaderKind_DASCompute:{ #define X(type, id, pretty, fixed_tx) "#define DAS_ID_" #type " " #id "\n" stream_append_s8(&sb, s8("" "layout(local_size_x = " str(DAS_LOCAL_SIZE_X) ", " @@ -415,58 +405,76 @@ push_compute_shader_header(Arena *a, b32 parameters, ComputeShaderID shader) DAS_TYPES )); #undef X - } break; - case CS_DECODE_FLOAT: - case CS_DECODE_FLOAT_COMPLEX: { - if (shader == CS_DECODE_FLOAT) stream_append_s8(&sb, s8("#define INPUT_DATA_TYPE_FLOAT\n\n")); - else stream_append_s8(&sb, s8("#define INPUT_DATA_TYPE_FLOAT_COMPLEX\n\n")); + }break; + case ShaderKind_DecodeFloat: + case ShaderKind_DecodeFloatComplex:{ + if (ctx->kind == ShaderKind_DecodeFloat) + stream_append_s8(&sb, s8("#define INPUT_DATA_TYPE_FLOAT\n\n")); + else + stream_append_s8(&sb, s8("#define INPUT_DATA_TYPE_FLOAT_COMPLEX\n\n")); } /* FALLTHROUGH */ - case CS_DECODE: { + case ShaderKind_Decode:{ #define X(type, id, pretty) stream_append_s8(&sb, s8("#define DECODE_MODE_" #type " " #id "\n")); DECODE_TYPES #undef X - } break; - case CS_MIN_MAX: { + }break; + case ShaderKind_MinMax:{ stream_append_s8(&sb, s8("layout(location = " str(CS_MIN_MAX_MIPS_LEVEL_UNIFORM_LOC) ") uniform int u_mip_map;\n\n")); - } break; - case CS_SUM: { + }break; + case ShaderKind_Sum:{ stream_append_s8(&sb, s8("layout(location = " str(CS_SUM_PRESCALE_UNIFORM_LOC) ") uniform float u_sum_prescale = 1.0;\n\n")); - } break; - default: break; + }break; + default:{}break; } stream_append_s8(&sb, s8("\n#line 1\n")); - return arena_stream_commit(a, &sb); + + s8 result = arena_stream_commit(arena, &sb); + if (path) { + s8 file = os->read_whole_file(arena, path); + assert(file.data == result.data + result.len); + result.len += file.len; + } + + return result; } -function b32 -reload_compute_shader(BeamformerCtx *ctx, s8 path, s8 extra, ComputeShaderReloadContext *csr, Arena tmp) +DEBUG_EXPORT BEAMFORMER_RELOAD_SHADER_FN(beamformer_reload_shader) { - ComputeShaderCtx *cs = &ctx->csctx; - b32 result = 0; + i32 shader_count = 1 + (src->link != 0); + s8 *shader_texts = push_array(&arena, s8, shader_count); + u32 *shader_types = push_array(&arena, u32, shader_count); - /* NOTE: arena works as stack (since everything here is 1 byte aligned) */ - s8 header = push_compute_shader_header(&tmp, csr->needs_header, csr->shader); - s8 shader_text = ctx->os.read_whole_file(&tmp, (c8 *)path.data); - shader_text.data -= header.len; - shader_text.len += header.len; - - if (shader_text.data == header.data) { - Stream sb = arena_stream(tmp); - stream_append_s8s(&sb, path, extra); - s8 info = arena_stream_commit(&tmp, &sb); - u32 new_program = load_shader(&ctx->os, tmp, (s8 []){shader_text}, - (u32 []){GL_COMPUTE_SHADER}, 1, info); - if (new_program) { - glDeleteProgram(cs->programs[csr->shader]); - cs->programs[csr->shader] = new_program; - glUseProgram(cs->programs[csr->shader]); - glBindBufferBase(GL_UNIFORM_BUFFER, 0, cs->shared_ubo); - } + if (src->link) { + shader_texts[0] = shader_text_with_header(src->link, &ctx->os, &arena, (c8 *)src->link->path.data); + shader_types[0] = src->link->gl_type; + } + shader_texts[shader_count - 1] = shader_text_with_header(src, &ctx->os, &arena, (c8 *)src->path.data); + shader_types[shader_count - 1] = src->gl_type; + + u32 new_program = load_shader(&ctx->os, arena, shader_texts, shader_types, shader_count, shader_name); + if (new_program) { + glDeleteProgram(*src->shader); + *src->shader = new_program; + if (src->kind == ShaderKind_Render2D) ctx->frame_view_render_context.updated = 1; + } + return new_program != 0; +} + +function b32 +reload_compute_shader(BeamformerCtx *ctx, ShaderReloadContext *src, s8 name_extra, Arena arena) +{ + Stream sb = arena_stream(arena); + stream_append_s8s(&sb, src->name, name_extra); + s8 name = arena_stream_commit(&arena, &sb); + b32 result = beamformer_reload_shader(ctx, src, arena, name); + if (result) { + glUseProgram(*src->shader); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, ctx->csctx.shared_ubo); } else { - Stream sb = arena_stream(tmp); - stream_append_s8s(&sb, s8("failed to load: "), path, extra, s8("\n")); + sb.widx = 0; + stream_append_s8s(&sb, s8("failed to load: "), src->path, name_extra, s8("\n")); ctx->os.write_file(ctx->os.error_handle, stream_to_s8(&sb)); } @@ -485,15 +493,18 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena arena, iptr gl_co b32 can_commit = 1; switch (work->type) { case BW_RELOAD_SHADER: { - ComputeShaderReloadContext *csr = work->reload_shader_ctx; - b32 success = reload_compute_shader(ctx, csr->path, s8(""), csr, arena); - if (csr->shader == CS_DECODE) { + ShaderReloadContext *src = work->shader_reload_context; + b32 success = reload_compute_shader(ctx, src, s8(""), arena); + if (src->kind == ShaderKind_Decode) { /* TODO(rnp): think of a better way of doing this */ - csr->shader = CS_DECODE_FLOAT_COMPLEX; - success &= reload_compute_shader(ctx, csr->path, s8(" (F32C)"), csr, arena); - csr->shader = CS_DECODE_FLOAT; - success &= reload_compute_shader(ctx, csr->path, s8(" (F32)"), csr, arena); - csr->shader = CS_DECODE; + src->kind = ShaderKind_DecodeFloatComplex; + src->shader = cs->programs + ShaderKind_DecodeFloatComplex; + success &= reload_compute_shader(ctx, src, s8(" (F32C)"), arena); + src->kind = ShaderKind_DecodeFloat; + src->shader = cs->programs + ShaderKind_DecodeFloat; + success &= reload_compute_shader(ctx, src, s8(" (F32)"), arena); + src->kind = ShaderKind_Decode; + src->shader = cs->programs + ShaderKind_Decode; } if (success) { @@ -576,19 +587,19 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena arena, iptr gl_co } frame->in_flight = 1; - frame->frame.min_coordinate = v4_from_f32_array(bp->output_min_coordinate); - frame->frame.max_coordinate = v4_from_f32_array(bp->output_max_coordinate); - frame->frame.das_shader_id = bp->das_shader_id; - frame->frame.compound_count = bp->dec_data_dim[2]; + frame->frame.min_coordinate = v4_from_f32_array(bp->output_min_coordinate); + frame->frame.max_coordinate = v4_from_f32_array(bp->output_max_coordinate); + frame->frame.das_shader_kind = bp->das_shader_id; + frame->frame.compound_count = bp->dec_data_dim[2]; b32 did_sum_shader = 0; - u32 stage_count = sm->compute_stages_count; - ComputeShaderID *stages = sm->compute_stages; + u32 stage_count = sm->compute_stages_count; + ComputeShaderKind *stages = sm->compute_stages; for (u32 i = 0; i < stage_count; i++) { - did_sum_shader |= stages[i] == CS_SUM; + did_sum_shader |= stages[i] == ComputeShaderKind_Sum; frame->stats.timer_active[stages[i]] = 1; glBeginQuery(GL_TIME_ELAPSED, frame->stats.timer_ids[stages[i]]); - do_compute_shader(ctx, arena, frame, stages[i]); + do_compute_shader(ctx, arena, frame, (ShaderKind)stages[i]); glEndQuery(GL_TIME_ELAPSED); } /* NOTE(rnp): block until work completes so that we can record timings */ diff --git a/beamformer.h b/beamformer.h @@ -78,7 +78,7 @@ typedef struct { #define CS_SUM_PRESCALE_UNIFORM_LOC 1 typedef struct { - u32 programs[CS_LAST]; + u32 programs[ComputeShaderKind_Count]; /* NOTE: Decoded data is only relevant in the context of a single frame. We use two * buffers so that they can be swapped when chaining multiple compute stages */ @@ -101,19 +101,27 @@ typedef struct { } ComputeShaderCtx; typedef enum { -#define X(type, id, pretty, fixed_tx) DAS_ ##type = id, -DAS_TYPES -#undef X -DAS_LAST -} DASShaderID; + #define X(type, id, pretty, fixed_tx) DASShaderKind_##type = id, + DAS_TYPES + #undef X + DASShaderKind_Count +} DASShaderKind; + +typedef enum { + #define X(e, n, s, h, pn) ShaderKind_##e = n, + COMPUTE_SHADERS + #undef X + ShaderKind_Render2D, + ShaderKind_Count +} ShaderKind; typedef struct { /* TODO(rnp): there is assumption here that each shader will occur only once * per compute. add an insertion index and change these to hold the max number * of executed compute stages */ - u32 timer_ids[CS_LAST]; - f32 times[CS_LAST]; - b32 timer_active[CS_LAST]; + u32 timer_ids[ComputeShaderKind_Count]; + f32 times[ComputeShaderKind_Count]; + b32 timer_active[ComputeShaderKind_Count]; } ComputeShaderStats; typedef struct BeamformFrame { @@ -126,7 +134,7 @@ typedef struct BeamformFrame { v4 max_coordinate; u32 mips; - DASShaderID das_shader_id; + DASShaderKind das_shader_kind; u32 compound_count; u32 id; @@ -196,11 +204,15 @@ typedef struct { BeamformerSharedMemory *shared_memory; } BeamformerCtx; -struct ComputeShaderReloadContext { - BeamformerCtx *beamformer_ctx; - s8 path; - b32 needs_header; - ComputeShaderID shader; +struct ShaderReloadContext { + BeamformerCtx *beamformer_context; + s8 path; + s8 name; + s8 header; + u32 *shader; + ShaderReloadContext *link; + GLenum gl_type; + ShaderKind kind; }; #define BEAMFORMER_FRAME_STEP_FN(name) void name(BeamformerCtx *ctx, Arena *arena, \ @@ -213,4 +225,8 @@ typedef BEAMFORMER_COMPUTE_SETUP_FN(beamformer_compute_setup_fn); #define BEAMFORMER_COMPLETE_COMPUTE_FN(name) void name(iptr user_context, Arena arena, iptr gl_context) typedef BEAMFORMER_COMPLETE_COMPUTE_FN(beamformer_complete_compute_fn); +#define BEAMFORMER_RELOAD_SHADER_FN(name) b32 name(BeamformerCtx *ctx, ShaderReloadContext *src, \ + Arena arena, s8 shader_name) +typedef BEAMFORMER_RELOAD_SHADER_FN(beamformer_reload_shader_fn); + #endif /*_BEAMFORMER_H_ */ diff --git a/beamformer_parameters.h b/beamformer_parameters.h @@ -11,22 +11,22 @@ /* X(enumarant, number, shader file name, needs header, pretty name) */ #define COMPUTE_SHADERS \ - X(CUDA_DECODE, 0, "", 0, "CUDA Decoding") \ - X(CUDA_HILBERT, 1, "", 0, "CUDA Hilbert") \ - X(DAS, 2, "das", 1, "DAS") \ - X(DECODE, 3, "decode", 1, "Decoding") \ - X(DECODE_FLOAT, 4, "", 1, "Decoding (F32)") \ - X(DECODE_FLOAT_COMPLEX, 5, "", 1, "Decoding (F32C)") \ - X(DEMOD, 6, "demod", 1, "Demodulation") \ - X(MIN_MAX, 7, "min_max", 0, "Min/Max") \ - X(SUM, 8, "sum", 0, "Sum") + X(CudaDecode, 0, "", 0, "CUDA Decode") \ + X(CudaHilbert, 1, "", 0, "CUDA Hilbert") \ + X(DASCompute, 2, "das", 1, "DAS") \ + X(Decode, 3, "decode", 1, "Decode") \ + X(DecodeFloat, 4, "", 1, "Decode (F32)") \ + X(DecodeFloatComplex, 5, "", 1, "Decode (F32C)") \ + X(Demodulate, 6, "demod", 1, "Demodulate") \ + X(MinMax, 7, "min_max", 0, "Min/Max") \ + X(Sum, 8, "sum", 0, "Sum") typedef enum { - #define X(e, n, s, h, pn) CS_ ##e = n, + #define X(e, n, s, h, pn) ComputeShaderKind_##e = n, COMPUTE_SHADERS #undef X - CS_LAST -} ComputeShaderID; + ComputeShaderKind_Count +} ComputeShaderKind; /* X(type, id, pretty name) */ #define DECODE_TYPES \ diff --git a/beamformer_work_queue.h b/beamformer_work_queue.h @@ -3,7 +3,7 @@ #define _BEAMFORMER_WORK_QUEUE_H_ typedef struct BeamformComputeFrame BeamformComputeFrame; -typedef struct ComputeShaderReloadContext ComputeShaderReloadContext; +typedef struct ShaderReloadContext ShaderReloadContext; typedef enum { BW_COMPUTE, @@ -39,7 +39,7 @@ typedef struct { BeamformComputeFrame *frame; BeamformerUploadContext upload_context; BeamformOutputFrameContext output_frame_ctx; - ComputeShaderReloadContext *reload_shader_ctx; + ShaderReloadContext *shader_reload_context; void *generic; }; /* NOTE(rnp): mostly for __external__ processes to sync on. when passed from external @@ -84,8 +84,8 @@ typedef struct { }; }; - ComputeShaderID compute_stages[MAX_COMPUTE_SHADER_STAGES]; - u32 compute_stages_count; + ComputeShaderKind compute_stages[MAX_COMPUTE_SHADER_STAGES]; + u32 compute_stages_count; i32 parameters_sync; i32 parameters_head_sync; diff --git a/helpers/ogl_beamformer_lib.c b/helpers/ogl_beamformer_lib.c @@ -227,10 +227,11 @@ set_beamformer_pipeline(i32 *stages, i32 stages_count) if (stages_count <= countof(g_bp->compute_stages)) { if (check_shared_memory()) { g_bp->compute_stages_count = 0; - for (i32 i = 0; i < stages_count; i++) - if (BETWEEN(stages[i], 0, CS_LAST)) + for (i32 i = 0; i < stages_count; i++) { + if (BETWEEN(stages[i], 0, ComputeShaderKind_Count)) { g_bp->compute_stages[g_bp->compute_stages_count++] = stages[i]; - + } + } result = g_bp->compute_stages_count == stages_count; if (!result) { g_lib_last_error = BF_LIB_ERR_KIND_INVALID_COMPUTE_STAGE; diff --git a/shaders/render.glsl b/shaders/render.glsl @@ -1,37 +0,0 @@ -/* See LICENSE for license details. */ -layout(binding = 0) uniform sampler3D u_out_data_tex; - -/* 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() -{ - ivec3 out_data_dim = textureSize(u_out_data_tex, 0); - - //vec2 min_max = texelFetch(u_out_data_tex, ivec3(0), textureQueryLevels(u_out_data_tex) - 1).xy; - - /* TODO(rnp): select between x and y and specify slice */ - ivec2 coord = ivec2(fragment_texture_coordinate * vec2(out_data_dim.xz)); - ivec3 smp_coord = ivec3(coord.x, out_data_dim.y / 2, coord.y); - float smp = length(texelFetch(u_out_data_tex, smp_coord, 0).xy); - - float threshold_val = pow(10.0f, u_threshold / 20.0f); - smp = clamp(smp, 0.0f, threshold_val); - smp = smp / threshold_val; - smp = pow(smp, u_gamma); - - if (u_log_scale) { - smp = 20 * log(smp) / log(10); - smp = clamp(smp, -u_db_cutoff, 0) / -u_db_cutoff; - smp = 1 - smp; - } - - //v_out_colour = vec4(hsv2rgb(vec3(360 * smp, 0.8, 0.95)), 1); - v_out_colour = vec4(smp, smp, smp, 1); -} diff --git a/shaders/render_2d.frag.glsl b/shaders/render_2d.frag.glsl @@ -0,0 +1,37 @@ +/* See LICENSE for license details. */ +layout(binding = 0) uniform sampler3D u_out_data_tex; + +/* 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() +{ + ivec3 out_data_dim = textureSize(u_out_data_tex, 0); + + //vec2 min_max = texelFetch(u_out_data_tex, ivec3(0), textureQueryLevels(u_out_data_tex) - 1).xy; + + /* TODO(rnp): select between x and y and specify slice */ + ivec2 coord = ivec2(texture_coordinate * vec2(out_data_dim.xz)); + ivec3 smp_coord = ivec3(coord.x, out_data_dim.y / 2, coord.y); + float smp = length(texelFetch(u_out_data_tex, smp_coord, 0).xy); + + float threshold_val = pow(10.0f, u_threshold / 20.0f); + smp = clamp(smp, 0.0f, threshold_val); + smp = smp / threshold_val; + smp = pow(smp, u_gamma); + + if (u_log_scale) { + smp = 20 * log(smp) / log(10); + smp = clamp(smp, -u_db_cutoff, 0) / -u_db_cutoff; + smp = 1 - smp; + } + + //v_out_colour = vec4(hsv2rgb(vec3(360 * smp, 0.8, 0.95)), 1); + v_out_colour = vec4(smp, smp, smp, 1); +} diff --git a/static.c b/static.c @@ -11,6 +11,7 @@ global void *debug_lib; X(beamformer_frame_step) \ X(beamformer_complete_compute) \ X(beamformer_compute_setup) \ + X(beamformer_reload_shader) \ X(beamform_work_queue_push) \ X(beamform_work_queue_push_commit) @@ -21,7 +22,7 @@ DEBUG_ENTRY_POINTS function FILE_WATCH_CALLBACK_FN(debug_reload) { BeamformerInput *input = (BeamformerInput *)user_data; - Stream err = arena_stream(tmp); + Stream err = arena_stream(arena); /* NOTE(rnp): spin until compute thread finishes its work (we will probably * never reload while compute is in progress but just incase). */ @@ -180,60 +181,22 @@ dump_gl_params(GLParams *gl, Arena a, OS *os) #endif } -function FILE_WATCH_CALLBACK_FN(reload_render_shader) +function FILE_WATCH_CALLBACK_FN(reload_shader) { - FrameViewRenderContext *ctx = (typeof(ctx))user_data; - - local_persist s8 vertex = s8("" - "#version 460 core\n" - "\n" - "layout(location = 0) in vec2 vertex_position;\n" - "layout(location = 1) in vec2 vertex_texture_coordinate;\n" - "\n" - "layout(location = 0) out vec2 fragment_texture_coordinate;\n" - "\n" - "void main()\n" - "{\n" - "\tfragment_texture_coordinate = vertex_texture_coordinate;\n" - "\tgl_Position = vec4(vertex_position, 0, 1);\n" - "}\n"); - - s8 header = push_s8(&tmp, s8("" - "#version 460 core\n\n" - "layout(location = 0) in vec2 fragment_texture_coordinate;\n" - "layout(location = 0) out vec4 v_out_colour;\n\n" - "layout(location = " str(FRAME_VIEW_RENDER_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n" - "layout(location = " str(FRAME_VIEW_RENDER_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n" - "layout(location = " str(FRAME_VIEW_RENDER_GAMMA_LOC) ") uniform float u_gamma = 1;\n" - "layout(location = " str(FRAME_VIEW_RENDER_LOG_SCALE_LOC) ") uniform bool u_log_scale;\n" - "\n#line 1\n")); - - s8 fragment = os->read_whole_file(&tmp, (c8 *)path.data); - fragment.data -= header.len; - fragment.len += header.len; - ASSERT(fragment.data == header.data); - u32 new_program = load_shader(os, tmp, (s8 []){vertex, fragment}, - (u32 []){GL_VERTEX_SHADER, GL_FRAGMENT_SHADER}, 2, path); - if (new_program) { - glDeleteProgram(ctx->shader); - ctx->shader = new_program; - ctx->updated = 1; - } - - return 1; + ShaderReloadContext *ctx = (typeof(ctx))user_data; + return beamformer_reload_shader(ctx->beamformer_context, ctx, arena, ctx->name); } - -function FILE_WATCH_CALLBACK_FN(queue_compute_shader_reload) +function FILE_WATCH_CALLBACK_FN(reload_shader_indirect) { - ComputeShaderReloadContext *csr = (typeof(csr))user_data; - BeamformerCtx *ctx = csr->beamformer_ctx; + ShaderReloadContext *src = (typeof(src))user_data; + BeamformerCtx *ctx = src->beamformer_context; BeamformWork *work = beamform_work_queue_push(ctx->beamform_work_queue); if (work) { work->type = BW_RELOAD_SHADER; - work->reload_shader_ctx = csr; + work->shader_reload_context = src; beamform_work_queue_push_commit(ctx->beamform_work_queue); - ctx->os.wake_waiters(&ctx->os.compute_worker.sync_variable); + os->wake_waiters(&os->compute_worker.sync_variable); } return 1; } @@ -243,7 +206,7 @@ function FILE_WATCH_CALLBACK_FN(load_cuda_lib) CudaLib *cl = (CudaLib *)user_data; b32 result = os_file_exists((c8 *)path.data); if (result) { - Stream err = arena_stream(tmp); + Stream err = arena_stream(arena); stream_append_s8(&err, s8("loading CUDA lib: " OS_CUDA_LIB_NAME "\n")); os_unload_library(cl->lib); @@ -340,8 +303,8 @@ setup_beamformer(BeamformerCtx *ctx, Arena *memory) ctx->shared_memory->focal_vectors_sync = 1; /* NOTE: default compute shader pipeline */ - ctx->shared_memory->compute_stages[0] = CS_DECODE; - ctx->shared_memory->compute_stages[1] = CS_DAS; + ctx->shared_memory->compute_stages[0] = ComputeShaderKind_Decode; + ctx->shared_memory->compute_stages[1] = ComputeShaderKind_DASCompute; ctx->shared_memory->compute_stages_count = 2; if (ctx->gl.vendor_id == GL_VENDOR_NVIDIA @@ -364,14 +327,27 @@ setup_beamformer(BeamformerCtx *ctx, Arena *memory) glEnable(GL_DEBUG_OUTPUT); #endif - #define X(e, sn, f, nh, pretty_name) do if (s8(f).len > 0) { \ - ComputeShaderReloadContext *csr = push_struct(memory, typeof(*csr)); \ - csr->beamformer_ctx = ctx; \ - csr->shader = sn; \ - csr->needs_header = nh; \ - csr->path = s8(static_path_join("shaders", f ".glsl")); \ - os_add_file_watch(&ctx->os, memory, csr->path, queue_compute_shader_reload, (iptr)csr); \ - queue_compute_shader_reload(&ctx->os, csr->path, (iptr)csr, *memory); \ + #define X(name, type, size, gltype, glsize, comment) "\t" #gltype " " #name #glsize "; " comment "\n" + read_only local_persist s8 compute_parameters_header = s8("" + "layout(std140, binding = 0) uniform parameters {\n" + BEAMFORMER_PARAMS_HEAD + BEAMFORMER_UI_PARAMS + BEAMFORMER_PARAMS_TAIL + "};\n\n" + ); + #undef X + + #define X(e, sn, f, nh, pretty_name) do if (s8(f).len > 0) { \ + ShaderReloadContext *src = push_struct(memory, typeof(*src)); \ + src->beamformer_context = ctx; \ + if (nh) src->header = compute_parameters_header; \ + src->path = s8(static_path_join("shaders", f ".glsl")); \ + src->name = src->path; \ + src->shader = ctx->csctx.programs + ShaderKind_##e; \ + src->gl_type = GL_COMPUTE_SHADER; \ + src->kind = ShaderKind_##e; \ + os_add_file_watch(&ctx->os, memory, src->path, reload_shader_indirect, (iptr)src); \ + reload_shader_indirect(&ctx->os, src->path, (iptr)src, *memory); \ } while (0); COMPUTE_SHADERS #undef X @@ -399,7 +375,34 @@ setup_beamformer(BeamformerCtx *ctx, Arena *memory) glEnableVertexAttribArray(1); glBindVertexArray(0); - s8 render = s8(static_path_join("shaders", "render.glsl")); - reload_render_shader(&ctx->os, render, (iptr)fvr, *memory); - os_add_file_watch(&ctx->os, memory, render, reload_render_shader, (iptr)fvr); + ShaderReloadContext *render_2d = push_struct(memory, typeof(*render_2d)); + render_2d->beamformer_context = ctx; + render_2d->path = s8(static_path_join("shaders", "render_2d.frag.glsl")); + render_2d->name = s8("shaders/render_2d.glsl"); + render_2d->gl_type = GL_FRAGMENT_SHADER; + render_2d->kind = ShaderKind_Render2D; + render_2d->shader = &fvr->shader; + render_2d->header = s8("" + "layout(location = 0) in vec2 texture_coordinate;\n" + "layout(location = 0) out vec4 v_out_colour;\n\n" + "layout(location = " str(FRAME_VIEW_RENDER_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n" + "layout(location = " str(FRAME_VIEW_RENDER_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n" + "layout(location = " str(FRAME_VIEW_RENDER_GAMMA_LOC) ") uniform float u_gamma = 1;\n" + "layout(location = " str(FRAME_VIEW_RENDER_LOG_SCALE_LOC) ") uniform bool u_log_scale;\n" + "\n#line 1\n"); + render_2d->link = push_struct(memory, typeof(*render_2d)); + render_2d->link->gl_type = GL_VERTEX_SHADER; + render_2d->link->header = s8("" + "layout(location = 0) in vec2 v_position;\n" + "layout(location = 1) in vec2 v_texture_coordinate;\n" + "\n" + "layout(location = 0) out vec2 f_texture_coordinate;\n" + "\n" + "void main()\n" + "{\n" + "\tf_texture_coordinate = v_texture_coordinate;\n" + "\tgl_Position = vec4(v_position, 0, 1);\n" + "}\n"); + reload_shader(&ctx->os, render_2d->path, (iptr)render_2d, *memory); + os_add_file_watch(&ctx->os, memory, render_2d->path, reload_shader, (iptr)render_2d); } diff --git a/ui.c b/ui.c @@ -1253,17 +1253,17 @@ lerp_v4(v4 a, v4 b, f32 t) } function s8 -push_das_shader_id(Stream *s, DASShaderID shader, u32 transmit_count) +push_das_shader_kind(Stream *s, DASShaderKind shader, u32 transmit_count) { #define X(type, id, pretty, fixed_tx) s8(pretty), - local_persist s8 pretty_names[DAS_LAST + 1] = {DAS_TYPES s8("Invalid")}; + read_only local_persist s8 pretty_names[DASShaderKind_Count + 1] = {DAS_TYPES s8("Invalid")}; #undef X #define X(type, id, pretty, fixed_tx) fixed_tx, - local_persist u8 fixed_transmits[DAS_LAST + 1] = {DAS_TYPES 1}; + read_only local_persist u8 fixed_transmits[DASShaderKind_Count + 1] = {DAS_TYPES 0}; #undef X - stream_append_s8(s, pretty_names[MIN(shader, DAS_LAST)]); - if (!fixed_transmits[MIN(shader, DAS_LAST)]) { + stream_append_s8(s, pretty_names[MIN(shader, DASShaderKind_Count)]); + if (!fixed_transmits[MIN(shader, DASShaderKind_Count)]) { stream_append_byte(s, '-'); stream_append_u64(s, transmit_count); } @@ -1869,7 +1869,7 @@ draw_beamformer_frame_view(BeamformerUI *ui, Arena a, Variable *var, Rect displa { Stream buf = arena_stream(a); - s8 shader = push_das_shader_id(&buf, frame->das_shader_id, frame->compound_count); + s8 shader = push_das_shader_kind(&buf, frame->das_shader_kind, frame->compound_count); text_spec.font = &ui->font; text_spec.limits.size.w -= 16; v2 txt_s = measure_text(*text_spec.font, shader); @@ -1961,8 +1961,8 @@ draw_compute_progress_bar(BeamformerUI *ui, Arena arena, ComputeProgressBar *sta function v2 draw_compute_stats_view(BeamformerCtx *ctx, Arena arena, ComputeShaderStats *stats, Rect r) { - #define X(e, n, s, h, pn) [CS_##e] = s8(pn ":"), - local_persist s8 labels[CS_LAST] = { COMPUTE_SHADERS }; + #define X(e, n, s, h, pn) [ComputeShaderKind_##e] = s8(pn ":"), + read_only local_persist s8 labels[ComputeShaderKind_Count] = { COMPUTE_SHADERS }; #undef X BeamformerUI *ui = ctx->ui; diff --git a/util.c b/util.c @@ -123,7 +123,7 @@ end_temp_arena(TempArena ta) { Arena *a = ta.arena; if (a) { - ASSERT(a->beg >= ta.old_beg) + assert(a->beg >= ta.old_beg); a->beg = ta.old_beg; } } diff --git a/util.h b/util.h @@ -28,15 +28,17 @@ #define DEBUG_EXPORT #endif #define DEBUG_DECL(a) a - #define ASSERT(c) do { if (!(c)) debugbreak(); } while (0); + #define ASSERT(c) do { if (!(c)) debugbreak(); } while (0) #else #define DEBUG_EXPORT function #define DEBUG_DECL(a) #define ASSERT(c) #endif +#define assert ASSERT #define INVALID_CODE_PATH ASSERT(0) -#define INVALID_DEFAULT_CASE default: ASSERT(0) break +#define INVALID_DEFAULT_CASE default: ASSERT(0); break +#define InvalidDefaultCase default: assert(0); break #define function static #define global static @@ -217,7 +219,7 @@ typedef struct { b32 asleep; } GLWorkerThreadContext; -#define FILE_WATCH_CALLBACK_FN(name) b32 name(OS *os, s8 path, iptr user_data, Arena tmp) +#define FILE_WATCH_CALLBACK_FN(name) b32 name(OS *os, s8 path, iptr user_data, Arena arena) typedef FILE_WATCH_CALLBACK_FN(file_watch_callback); typedef struct {