ogl_beamforming

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

main.c (8998B)


      1 /* See LICENSE for license details. */
      2 #include "beamformer.h"
      3 
      4 static char *compute_shader_paths[CS_LAST] = {
      5 	[CS_HADAMARD] = "shaders/hadamard.glsl",
      6 	[CS_HERCULES] = "shaders/hercules.glsl",
      7 	[CS_DEMOD]    = "shaders/demod.glsl",
      8 	[CS_MIN_MAX]  = "shaders/min_max.glsl",
      9 	[CS_UFORCES]  = "shaders/uforces.glsl",
     10 };
     11 
     12 #ifndef _DEBUG
     13 
     14 #include "beamformer.c"
     15 static void do_debug(void) { }
     16 
     17 #else
     18 static os_library_handle libhandle;
     19 
     20 typedef void do_beamformer_fn(BeamformerCtx *, Arena);
     21 static do_beamformer_fn *do_beamformer;
     22 
     23 static void
     24 do_debug(void)
     25 {
     26 	static os_filetime updated_time;
     27 	os_file_stats test_stats = os_get_file_stats(OS_DEBUG_LIB_NAME);
     28 	if (test_stats.filesize > 32 && os_filetime_is_newer(test_stats.timestamp, updated_time)) {
     29 		os_unload_library(libhandle);
     30 		libhandle     = os_load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME);
     31 		do_beamformer = os_lookup_dynamic_symbol(libhandle, "do_beamformer");
     32 		updated_time  = test_stats.timestamp;
     33 	}
     34 }
     35 
     36 #endif /* _DEBUG */
     37 
     38 /* NOTE: cuda lib stubs */
     39 INIT_CUDA_CONFIGURATION_FN(init_cuda_configuration_stub) {}
     40 REGISTER_CUDA_BUFFERS_FN(register_cuda_buffers_stub) {}
     41 CUDA_DECODE_FN(cuda_decode_stub) {}
     42 CUDA_HILBERT_FN(cuda_hilbert_stub) {}
     43 
     44 static void
     45 gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, i32 len, const char *msg, const void *userctx)
     46 {
     47 	(void)src; (void)type; (void)id; (void)userctx;
     48 	fputs("[GL DEBUG ", stderr);
     49 	switch (lvl) {
     50 	case GL_DEBUG_SEVERITY_HIGH:         fputs("HIGH]: ",         stderr); break;
     51 	case GL_DEBUG_SEVERITY_MEDIUM:       fputs("MEDIUM]: ",       stderr); break;
     52 	case GL_DEBUG_SEVERITY_LOW:          fputs("LOW]: ",          stderr); break;
     53 	case GL_DEBUG_SEVERITY_NOTIFICATION: fputs("NOTIFICATION]: ", stderr); break;
     54 	default:                             fputs("INVALID]: ",      stderr); break;
     55 	}
     56 	fwrite(msg, 1, len, stderr);
     57 	fputc('\n', stderr);
     58 }
     59 
     60 static u32
     61 compile_shader(Arena a, u32 type, s8 shader)
     62 {
     63 	u32 sid = glCreateShader(type);
     64 	glShaderSource(sid, 1, (const char **)&shader.data, (int *)&shader.len);
     65 	glCompileShader(sid);
     66 
     67 	i32 res = 0;
     68 	glGetShaderiv(sid, GL_COMPILE_STATUS, &res);
     69 
     70 	char *stype;
     71 	switch (type) {
     72 	case GL_COMPUTE_SHADER:  stype = "Compute";  break;
     73 	case GL_FRAGMENT_SHADER: stype = "Fragment"; break;
     74 	}
     75 
     76 	if (res == GL_FALSE) {
     77 		TraceLog(LOG_WARNING, "SHADER: [ID %u] %s shader failed to compile", sid, stype);
     78 		i32 len = 0;
     79 		glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &len);
     80 		s8 err = s8alloc(&a, len);
     81 		glGetShaderInfoLog(sid, len, (int *)&err.len, (char *)err.data);
     82 		TraceLog(LOG_WARNING, "SHADER: [ID %u] Compile error: %s", sid, (char *)err.data);
     83 		glDeleteShader(sid);
     84 	} else {
     85 		TraceLog(LOG_INFO, "SHADER: [ID %u] %s shader compiled successfully", sid, stype);
     86 	}
     87 
     88 	return sid;
     89 }
     90 
     91 static void
     92 init_fragment_shader_ctx(FragmentShaderCtx *ctx, uv4 out_data_dim)
     93 {
     94 	ctx->output = LoadRenderTexture(out_data_dim.x, out_data_dim.y);
     95 	ctx->db     = -50.0f;
     96 }
     97 
     98 static void
     99 reload_shaders(BeamformerCtx *ctx, Arena a)
    100 {
    101 	ComputeShaderCtx *csctx = &ctx->csctx;
    102 	for (u32 i = 0; i < ARRAY_COUNT(csctx->programs); i++) {
    103 		if (!compute_shader_paths[i])
    104 			continue;
    105 
    106 		Arena tmp = a;
    107 		os_file_stats fs = os_get_file_stats(compute_shader_paths[i]);
    108 		s8 shader_text   = os_read_file(&tmp, compute_shader_paths[i], fs.filesize);
    109 		u32 shader_id    = compile_shader(tmp, GL_COMPUTE_SHADER, shader_text);
    110 
    111 		if (shader_id) {
    112 			glDeleteProgram(csctx->programs[i]);
    113 			csctx->programs[i] = rlLoadComputeShaderProgram(shader_id);
    114 			ctx->flags |= DO_COMPUTE;
    115 		}
    116 
    117 		glDeleteShader(shader_id);
    118 	}
    119 
    120 	ctx->export_ctx.volume_texture_id  = glGetUniformLocation(csctx->programs[CS_HERCULES],
    121 	                                                          "u_out_volume_tex");
    122 	csctx->volume_export_pass_id       = glGetUniformLocation(csctx->programs[CS_HERCULES],
    123 	                                                          "u_volume_export_pass");
    124 	csctx->volume_export_dim_offset_id = glGetUniformLocation(csctx->programs[CS_HERCULES],
    125 	                                                         "u_volume_export_dim_offset");
    126 
    127 	csctx->out_data_tex_id = glGetUniformLocation(csctx->programs[CS_UFORCES], "u_out_data_tex");
    128 	csctx->mip_view_tex_id = glGetUniformLocation(csctx->programs[CS_MIN_MAX], "u_mip_view_tex");
    129 	csctx->mips_level_id   = glGetUniformLocation(csctx->programs[CS_MIN_MAX], "u_mip_map");
    130 
    131 	Shader updated_fs = LoadShader(NULL, "shaders/render.glsl");
    132 	if (updated_fs.id != rlGetShaderIdDefault()) {
    133 		UnloadShader(ctx->fsctx.shader);
    134 		ctx->fsctx.shader          = updated_fs;
    135 		ctx->fsctx.out_data_tex_id = GetShaderLocation(updated_fs, "u_out_data_tex");
    136 		ctx->fsctx.db_cutoff_id    = GetShaderLocation(updated_fs, "u_db_cutoff");
    137 	}
    138 }
    139 
    140 static void
    141 validate_cuda_lib(CudaLib *cl)
    142 {
    143 	if (!cl->init_cuda_configuration) cl->init_cuda_configuration = init_cuda_configuration_stub;
    144 	if (!cl->register_cuda_buffers)   cl->register_cuda_buffers   = register_cuda_buffers_stub;
    145 	if (!cl->cuda_decode)             cl->cuda_decode             = cuda_decode_stub;
    146 	if (!cl->cuda_hilbert)            cl->cuda_hilbert            = cuda_hilbert_stub;
    147 }
    148 
    149 static void
    150 check_and_load_cuda_lib(CudaLib *cl)
    151 {
    152 	os_file_stats current = os_get_file_stats(OS_CUDA_LIB_NAME);
    153 	if (!os_filetime_is_newer(current.timestamp, cl->timestamp) || current.filesize < 32)
    154 		return;
    155 
    156 	TraceLog(LOG_INFO, "Loading CUDA lib: %s", OS_CUDA_LIB_NAME);
    157 
    158 	cl->timestamp = current.timestamp;
    159 	os_unload_library(cl->lib);
    160 	cl->lib = os_load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME);
    161 
    162 	cl->init_cuda_configuration = os_lookup_dynamic_symbol(cl->lib, "init_cuda_configuration");
    163 	cl->register_cuda_buffers   = os_lookup_dynamic_symbol(cl->lib, "register_cuda_buffers");
    164 	cl->cuda_decode             = os_lookup_dynamic_symbol(cl->lib, "cuda_decode");
    165 	cl->cuda_hilbert            = os_lookup_dynamic_symbol(cl->lib, "cuda_hilbert");
    166 
    167 	validate_cuda_lib(cl);
    168 }
    169 
    170 int
    171 main(void)
    172 {
    173 	BeamformerCtx ctx = {0};
    174 
    175 	Arena temp_memory = os_alloc_arena((Arena){0}, 8 * MEGABYTE);
    176 
    177 	ctx.window_size  = (uv2){.w = 1280, .h = 840};
    178 
    179 	ctx.out_data_dim          = (uv4){.x = 1, .y = 1, .z = 1};
    180 	ctx.export_ctx.volume_dim = (uv4){.x = 1, .y = 1, .z = 1};
    181 
    182 	SetConfigFlags(FLAG_VSYNC_HINT);
    183 	InitWindow(ctx.window_size.w, ctx.window_size.h, "OGL Beamformer");
    184 	/* NOTE: do this after initing so that the window starts out floating in tiling wm */
    185 	SetWindowState(FLAG_WINDOW_RESIZABLE);
    186 	SetWindowMinSize(INFO_COLUMN_WIDTH * 2, ctx.window_size.h);
    187 
    188 	/* TODO: build these into the binary */
    189 	ctx.font       = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 28, 0, 0);
    190 	ctx.small_font = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 22, 0, 0);
    191 
    192 	ctx.is.cursor_blink_t = 1;
    193 
    194 	init_fragment_shader_ctx(&ctx.fsctx, ctx.out_data_dim);
    195 
    196 	ctx.data_pipe = os_open_named_pipe(OS_PIPE_NAME);
    197 	ctx.params    = os_open_shared_memory_area(OS_SMEM_NAME);
    198 	/* TODO: properly handle this? */
    199 	ASSERT(ctx.data_pipe.file != OS_INVALID_FILE);
    200 	ASSERT(ctx.params);
    201 
    202 	ctx.params->raw.output_points = ctx.out_data_dim;
    203 	/* NOTE: default compute shader pipeline */
    204 	ctx.params->compute_stages[0]    = CS_HADAMARD;
    205 	ctx.params->compute_stages[1]    = CS_DEMOD;
    206 	ctx.params->compute_stages[2]    = CS_UFORCES;
    207 	ctx.params->compute_stages[3]    = CS_MIN_MAX;
    208 	ctx.params->compute_stages_count = 4;
    209 
    210 	/* NOTE: Determine which graphics vendor we are running on */
    211 	{
    212 		const u8 *vendor = glGetString(GL_VENDOR);
    213 		if (!vendor)
    214 			die("Failed to determine GL Vendor\n");
    215 		switch (vendor[0]) {
    216 		case 'A': ctx.gl_vendor_id = GL_VENDOR_AMD;       break;
    217 		case 'I': ctx.gl_vendor_id = GL_VENDOR_INTEL;     break;
    218 		case 'N': ctx.gl_vendor_id = GL_VENDOR_NVIDIA;    break;
    219 		default:  die("Unknown GL Vendor: %s\n", vendor); break;
    220 		}
    221 	}
    222 
    223 	/* NOTE: make sure function pointers are valid even if we are not using the cuda lib */
    224 	validate_cuda_lib(&ctx.cuda_lib);
    225 
    226 	/* NOTE: set up OpenGL debug logging */
    227 	glDebugMessageCallback(gl_debug_logger, NULL);
    228 #ifdef _DEBUG
    229 	glEnable(GL_DEBUG_OUTPUT);
    230 #endif
    231 
    232 	/* NOTE: allocate space for Uniform Buffer but don't send anything yet */
    233 	glCreateBuffers(1, &ctx.csctx.shared_ubo);
    234 	glNamedBufferStorage(ctx.csctx.shared_ubo, sizeof(BeamformerParameters), 0, GL_DYNAMIC_STORAGE_BIT);
    235 
    236 	glGenQueries(ARRAY_COUNT(ctx.csctx.timer_fences) * CS_LAST, (u32 *)ctx.csctx.timer_ids);
    237 	glGenQueries(ARRAY_COUNT(ctx.export_ctx.timer_ids), ctx.export_ctx.timer_ids);
    238 
    239 	/* NOTE: do not DO_COMPUTE on first frame */
    240 	reload_shaders(&ctx, temp_memory);
    241 	ctx.flags &= ~DO_COMPUTE;
    242 
    243 	while(!WindowShouldClose()) {
    244 		do_debug();
    245 		if (ctx.gl_vendor_id == GL_VENDOR_NVIDIA)
    246 			check_and_load_cuda_lib(&ctx.cuda_lib);
    247 
    248 		if (ctx.flags & RELOAD_SHADERS) {
    249 			ctx.flags &= ~RELOAD_SHADERS;
    250 			reload_shaders(&ctx, temp_memory);
    251 		}
    252 
    253 		do_beamformer(&ctx, temp_memory);
    254 	}
    255 
    256 	/* NOTE: make sure this will get cleaned up after external
    257 	 * programs release their references */
    258 	os_remove_shared_memory(OS_SMEM_NAME);
    259 
    260 	/* NOTE: garbage code needed for Linux */
    261 	os_close_named_pipe(ctx.data_pipe);
    262 }