ogl_beamforming

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

main.c (11501B)


      1 /* See LICENSE for license details. */
      2 #include "beamformer.h"
      3 
      4 static s8 compute_shader_paths[CS_LAST] = {
      5 	[CS_HADAMARD] = s8("shaders/hadamard.glsl"),
      6 	[CS_HERCULES] = s8("shaders/hercules.glsl"),
      7 	[CS_DEMOD]    = s8("shaders/demod.glsl"),
      8 	[CS_MIN_MAX]  = s8("shaders/min_max.glsl"),
      9 	[CS_SUM]      = s8("shaders/sum.glsl"),
     10 	[CS_UFORCES]  = s8("shaders/uforces.glsl"),
     11 };
     12 
     13 #ifndef _DEBUG
     14 
     15 #include "beamformer.c"
     16 static void do_debug(void) { }
     17 
     18 #else
     19 static os_library_handle libhandle;
     20 
     21 typedef void do_beamformer_fn(BeamformerCtx *, Arena);
     22 static do_beamformer_fn *do_beamformer;
     23 
     24 static void
     25 do_debug(void)
     26 {
     27 	static f32 updated_time;
     28 	FileStats test_stats = os_get_file_stats(OS_DEBUG_LIB_NAME);
     29 	if (test_stats.filesize > 32 && test_stats.timestamp > updated_time) {
     30 		os_unload_library(libhandle);
     31 		libhandle     = os_load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME);
     32 		do_beamformer = os_lookup_dynamic_symbol(libhandle, "do_beamformer");
     33 		updated_time  = test_stats.timestamp;
     34 	}
     35 }
     36 
     37 #endif /* _DEBUG */
     38 
     39 /* NOTE: cuda lib stubs */
     40 INIT_CUDA_CONFIGURATION_FN(init_cuda_configuration_stub) {}
     41 REGISTER_CUDA_BUFFERS_FN(register_cuda_buffers_stub) {}
     42 CUDA_DECODE_FN(cuda_decode_stub) {}
     43 CUDA_HILBERT_FN(cuda_hilbert_stub) {}
     44 
     45 static void
     46 gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, i32 len, const char *msg, const void *userctx)
     47 {
     48 	(void)src; (void)type; (void)id; (void)userctx;
     49 
     50 	u8 buf[128];
     51 	Stream s = {.data = buf, .cap = ARRAY_COUNT(buf)};
     52 	stream_append_s8(&s, s8("[GL DEBUG "));
     53 	switch (lvl) {
     54 	case GL_DEBUG_SEVERITY_HIGH:         stream_append_s8(&s, s8("HIGH]: "));         break;
     55 	case GL_DEBUG_SEVERITY_MEDIUM:       stream_append_s8(&s, s8("MEDIUM]: "));       break;
     56 	case GL_DEBUG_SEVERITY_LOW:          stream_append_s8(&s, s8("LOW]: "));          break;
     57 	case GL_DEBUG_SEVERITY_NOTIFICATION: stream_append_s8(&s, s8("NOTIFICATION]: ")); break;
     58 	default:                             stream_append_s8(&s, s8("INVALID]: "));      break;
     59 	}
     60 	os_write_err_msg(stream_to_s8(s));
     61 	os_write_err_msg((s8){.len = len, .data = (u8 *)msg});
     62 	os_write_err_msg(s8("\n"));
     63 }
     64 
     65 static void
     66 get_gl_params(GLParams *gl)
     67 {
     68 	char *vendor = (char *)glGetString(GL_VENDOR);
     69 	if (!vendor) {
     70 		os_write_err_msg(s8("Failed to determine GL Vendor\n"));
     71 		os_fail();
     72 	}
     73 	switch (vendor[0]) {
     74 	case 'A': gl->vendor_id = GL_VENDOR_AMD;                      break;
     75 	case 'I': gl->vendor_id = GL_VENDOR_INTEL;                    break;
     76 	case 'N': gl->vendor_id = GL_VENDOR_NVIDIA;                   break;
     77 	default: {
     78 		os_write_err_msg(s8("Unknown GL Vendor: "));
     79 		os_write_err_msg(cstr_to_s8(vendor));
     80 		os_write_err_msg(s8("\n"));
     81 		os_fail();
     82 	} break;
     83 	}
     84 
     85 	glGetIntegerv(GL_MAJOR_VERSION,                 &gl->version_major);
     86 	glGetIntegerv(GL_MINOR_VERSION,                 &gl->version_minor);
     87 	glGetIntegerv(GL_MAX_TEXTURE_SIZE,              &gl->max_2d_texture_dim);
     88 	glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE,           &gl->max_3d_texture_dim);
     89 	glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &gl->max_ssbo_size);
     90 	glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE,        &gl->max_ubo_size);
     91 }
     92 
     93 static void
     94 validate_gl_requirements(GLParams *gl)
     95 {
     96 	ASSERT(gl->max_ubo_size >= sizeof(BeamformerParameters));
     97 	if (gl->version_major < 4 || (gl->version_major == 4 && gl->version_minor < 5)) {
     98 		os_write_err_msg(s8("Only OpenGL Versions 4.5 or newer are supported!\n"));
     99 		os_fail();
    100 	}
    101 }
    102 
    103 static void
    104 dump_gl_params(GLParams *gl, Arena a)
    105 {
    106 	(void)gl; (void)a;
    107 #ifdef _DEBUG
    108 	Stream s = stream_alloc(&a, 1 * MEGABYTE);
    109 	stream_append_s8(&s, s8("---- GL Parameters ----\n"));
    110 	switch (gl->vendor_id) {
    111 	case GL_VENDOR_AMD:    stream_append_s8(&s, s8("Vendor: AMD\n"));    break;
    112 	case GL_VENDOR_INTEL:  stream_append_s8(&s, s8("Vendor: Intel\n"));  break;
    113 	case GL_VENDOR_NVIDIA: stream_append_s8(&s, s8("Vendor: nVidia\n")); break;
    114 	}
    115 	stream_append_s8(&s, s8("Version: "));
    116 	stream_append_i64(&s, gl->version_major);
    117 	stream_append_s8(&s, s8("."));
    118 	stream_append_i64(&s, gl->version_minor);
    119 	stream_append_s8(&s, s8("\nMax 1D/2D Texture Dimension: "));
    120 	stream_append_i64(&s, gl->max_2d_texture_dim);
    121 	stream_append_s8(&s, s8("\nMax 3D Texture Dimension: "));
    122 	stream_append_i64(&s, gl->max_3d_texture_dim);
    123 	stream_append_s8(&s, s8("\nMax SSBO Size: "));
    124 	stream_append_i64(&s, gl->max_ssbo_size);
    125 	stream_append_s8(&s, s8("\nMax UBO Size: "));
    126 	stream_append_i64(&s, gl->max_ubo_size);
    127 	stream_append_s8(&s, s8("\n-----------------------\n"));
    128 	if (!s.errors)
    129 		os_write_err_msg(stream_to_s8(s));
    130 #endif
    131 }
    132 
    133 static u32
    134 compile_shader(Arena a, u32 type, s8 shader)
    135 {
    136 	u32 sid = glCreateShader(type);
    137 	glShaderSource(sid, 1, (const char **)&shader.data, (int *)&shader.len);
    138 	glCompileShader(sid);
    139 
    140 	i32 res = 0;
    141 	glGetShaderiv(sid, GL_COMPILE_STATUS, &res);
    142 
    143 	char *stype;
    144 	switch (type) {
    145 	case GL_COMPUTE_SHADER:  stype = "Compute";  break;
    146 	case GL_FRAGMENT_SHADER: stype = "Fragment"; break;
    147 	}
    148 
    149 	if (res == GL_FALSE) {
    150 		TraceLog(LOG_WARNING, "SHADER: [ID %u] %s shader failed to compile", sid, stype);
    151 		i32 len = 0;
    152 		glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &len);
    153 		s8 err = s8alloc(&a, len);
    154 		glGetShaderInfoLog(sid, len, (int *)&err.len, (char *)err.data);
    155 		TraceLog(LOG_WARNING, "SHADER: [ID %u] Compile error: %s", sid, (char *)err.data);
    156 		glDeleteShader(sid);
    157 	} else {
    158 		TraceLog(LOG_INFO, "SHADER: [ID %u] %s shader compiled successfully", sid, stype);
    159 	}
    160 
    161 	return sid;
    162 }
    163 
    164 static void
    165 init_fragment_shader_ctx(FragmentShaderCtx *ctx, uv4 out_data_dim)
    166 {
    167 	ctx->output = LoadRenderTexture(out_data_dim.x, out_data_dim.y);
    168 	ctx->db     = -50.0f;
    169 }
    170 
    171 static void
    172 reload_shaders(BeamformerCtx *ctx, Arena a)
    173 {
    174 	ComputeShaderCtx *csctx = &ctx->csctx;
    175 	for (u32 i = 0; i < ARRAY_COUNT(csctx->programs); i++) {
    176 		if (!compute_shader_paths[i].len)
    177 			continue;
    178 
    179 		Arena tmp = a;
    180 		FileStats fs   = os_get_file_stats((char *)compute_shader_paths[i].data);
    181 		s8 shader_text = os_read_file(&tmp, (char *)compute_shader_paths[i].data, fs.filesize);
    182 		if (shader_text.len == -1) {
    183 			os_write_err_msg(s8("failed to read shader: "));
    184 			os_write_err_msg(compute_shader_paths[i]);
    185 			os_write_err_msg(s8("\n"));
    186 			os_fail();
    187 		}
    188 		u32 shader_id  = compile_shader(tmp, GL_COMPUTE_SHADER, shader_text);
    189 
    190 		if (shader_id) {
    191 			glDeleteProgram(csctx->programs[i]);
    192 			csctx->programs[i] = rlLoadComputeShaderProgram(shader_id);
    193 			ctx->flags |= DO_COMPUTE;
    194 		}
    195 
    196 		glDeleteShader(shader_id);
    197 	}
    198 
    199 	csctx->volume_export_pass_id       = glGetUniformLocation(csctx->programs[CS_HERCULES],
    200 	                                                          "u_volume_export_pass");
    201 	csctx->volume_export_dim_offset_id = glGetUniformLocation(csctx->programs[CS_HERCULES],
    202 	                                                         "u_volume_export_dim_offset");
    203 	csctx->xdc_transform_id            = glGetUniformLocation(csctx->programs[CS_UFORCES],
    204 	                                                          "u_xdc_transform");
    205 	csctx->xdc_index_id                = glGetUniformLocation(csctx->programs[CS_UFORCES],
    206 	                                                          "u_xdc_index");
    207 
    208 	csctx->mips_level_id   = glGetUniformLocation(csctx->programs[CS_MIN_MAX], "u_mip_map");
    209 
    210 	csctx->sum_prescale_id = glGetUniformLocation(csctx->programs[CS_SUM], "u_prescale");
    211 
    212 	Shader updated_fs = LoadShader(NULL, "shaders/render.glsl");
    213 	if (updated_fs.id != rlGetShaderIdDefault()) {
    214 		UnloadShader(ctx->fsctx.shader);
    215 		ctx->fsctx.shader       = updated_fs;
    216 		ctx->fsctx.db_cutoff_id = GetShaderLocation(updated_fs, "u_db_cutoff");
    217 	}
    218 }
    219 
    220 static void
    221 validate_cuda_lib(CudaLib *cl)
    222 {
    223 	if (!cl->init_cuda_configuration) cl->init_cuda_configuration = init_cuda_configuration_stub;
    224 	if (!cl->register_cuda_buffers)   cl->register_cuda_buffers   = register_cuda_buffers_stub;
    225 	if (!cl->cuda_decode)             cl->cuda_decode             = cuda_decode_stub;
    226 	if (!cl->cuda_hilbert)            cl->cuda_hilbert            = cuda_hilbert_stub;
    227 }
    228 
    229 static void
    230 check_and_load_cuda_lib(CudaLib *cl)
    231 {
    232 	FileStats current = os_get_file_stats(OS_CUDA_LIB_NAME);
    233 	if (cl->timestamp == current.timestamp || current.filesize < 32)
    234 		return;
    235 
    236 	TraceLog(LOG_INFO, "Loading CUDA lib: %s", OS_CUDA_LIB_NAME);
    237 
    238 	cl->timestamp = current.timestamp;
    239 	os_unload_library(cl->lib);
    240 	cl->lib = os_load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME);
    241 
    242 	cl->init_cuda_configuration = os_lookup_dynamic_symbol(cl->lib, "init_cuda_configuration");
    243 	cl->register_cuda_buffers   = os_lookup_dynamic_symbol(cl->lib, "register_cuda_buffers");
    244 	cl->cuda_decode             = os_lookup_dynamic_symbol(cl->lib, "cuda_decode");
    245 	cl->cuda_hilbert            = os_lookup_dynamic_symbol(cl->lib, "cuda_hilbert");
    246 
    247 	validate_cuda_lib(cl);
    248 }
    249 
    250 int
    251 main(void)
    252 {
    253 	BeamformerCtx ctx = {0};
    254 
    255 	Arena temp_memory = os_alloc_arena((Arena){0}, 8 * MEGABYTE);
    256 
    257 	ctx.window_size  = (uv2){.w = 1280, .h = 840};
    258 
    259 	ctx.out_data_dim          = (uv4){.x = 1, .y = 1, .z = 1};
    260 	ctx.export_ctx.volume_dim = (uv4){.x = 1, .y = 1, .z = 1};
    261 
    262 	SetConfigFlags(FLAG_VSYNC_HINT);
    263 	InitWindow(ctx.window_size.w, ctx.window_size.h, "OGL Beamformer");
    264 	/* NOTE: do this after initing so that the window starts out floating in tiling wm */
    265 	SetWindowState(FLAG_WINDOW_RESIZABLE);
    266 	SetWindowMinSize(INFO_COLUMN_WIDTH * 2, ctx.window_size.h);
    267 
    268 	/* NOTE: Gather information about the GPU */
    269 	get_gl_params(&ctx.gl);
    270 	dump_gl_params(&ctx.gl, temp_memory);
    271 	validate_gl_requirements(&ctx.gl);
    272 
    273 	/* TODO: build these into the binary */
    274 	ctx.font       = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 28, 0, 0);
    275 	ctx.small_font = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 22, 0, 0);
    276 
    277 	ctx.is.cursor_blink_t = 1;
    278 
    279 	init_fragment_shader_ctx(&ctx.fsctx, ctx.out_data_dim);
    280 
    281 	ctx.data_pipe = os_open_named_pipe(OS_PIPE_NAME);
    282 	ctx.params    = os_open_shared_memory_area(OS_SMEM_NAME);
    283 	/* TODO: properly handle this? */
    284 	ASSERT(ctx.data_pipe.file != OS_INVALID_FILE);
    285 	ASSERT(ctx.params);
    286 
    287 	ctx.params->raw.output_points = ctx.out_data_dim;
    288 	/* NOTE: default compute shader pipeline */
    289 	ctx.params->compute_stages[0]    = CS_HADAMARD;
    290 	ctx.params->compute_stages[1]    = CS_DEMOD;
    291 	ctx.params->compute_stages[2]    = CS_UFORCES;
    292 	ctx.params->compute_stages[3]    = CS_MIN_MAX;
    293 	ctx.params->compute_stages_count = 4;
    294 
    295 	/* NOTE: make sure function pointers are valid even if we are not using the cuda lib */
    296 	validate_cuda_lib(&ctx.cuda_lib);
    297 
    298 	/* NOTE: set up OpenGL debug logging */
    299 	glDebugMessageCallback(gl_debug_logger, NULL);
    300 #ifdef _DEBUG
    301 	glEnable(GL_DEBUG_OUTPUT);
    302 #endif
    303 
    304 	/* NOTE: allocate space for Uniform Buffer but don't send anything yet */
    305 	glCreateBuffers(1, &ctx.csctx.shared_ubo);
    306 	glNamedBufferStorage(ctx.csctx.shared_ubo, sizeof(BeamformerParameters), 0, GL_DYNAMIC_STORAGE_BIT);
    307 
    308 	glGenQueries(ARRAY_COUNT(ctx.csctx.timer_fences) * CS_LAST, (u32 *)ctx.csctx.timer_ids);
    309 	glGenQueries(ARRAY_COUNT(ctx.export_ctx.timer_ids), ctx.export_ctx.timer_ids);
    310 
    311 	/* NOTE: do not DO_COMPUTE on first frame */
    312 	reload_shaders(&ctx, temp_memory);
    313 	ctx.flags &= ~DO_COMPUTE;
    314 
    315 	while(!WindowShouldClose()) {
    316 		do_debug();
    317 		if (ctx.gl.vendor_id == GL_VENDOR_NVIDIA)
    318 			check_and_load_cuda_lib(&ctx.cuda_lib);
    319 
    320 		if (ctx.flags & RELOAD_SHADERS) {
    321 			ctx.flags &= ~RELOAD_SHADERS;
    322 			reload_shaders(&ctx, temp_memory);
    323 		}
    324 
    325 		do_beamformer(&ctx, temp_memory);
    326 	}
    327 
    328 	/* NOTE: make sure this will get cleaned up after external
    329 	 * programs release their references */
    330 	os_remove_shared_memory(OS_SMEM_NAME);
    331 
    332 	/* NOTE: garbage code needed for Linux */
    333 	os_close_named_pipe(ctx.data_pipe);
    334 }