ogl_beamforming

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

static.c (10176B)


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