ogl_beamforming

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

static.c (9993B)


      1 /* See LICENSE for license details. */
      2 static struct {
      3 	s8  label;
      4 	s8  path;
      5 	b32 needs_header;
      6 } compute_shaders[CS_LAST] = {
      7 	#define X(e, n, s, h, pn) [CS_##e] = {s8(#e), s8("shaders/" s ".glsl"), h},
      8 	COMPUTE_SHADERS
      9 	#undef X
     10 };
     11 
     12 #ifndef _DEBUG
     13 
     14 #include "beamformer.c"
     15 #define do_debug(...)
     16 
     17 #else
     18 static void *debug_lib;
     19 
     20 static beamformer_frame_step_fn *beamformer_frame_step;
     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 		beamformer_frame_step = os_lookup_dynamic_symbol(debug_lib, "beamformer_frame_step", 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, Stream *err)
     59 {
     60 	char *vendor = (char *)glGetString(GL_VENDOR);
     61 	if (!vendor) {
     62 		stream_append_s8(err, s8("Failed to determine GL Vendor\n"));
     63 		os_fatal(stream_to_s8(err));
     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 	/* NOTE(rnp): freedreno - might need different handling on win32 but this is fine for now */
     70 	case 'f': gl->vendor_id = GL_VENDOR_ARM;    break;
     71 	default:
     72 		stream_append_s8(err, s8("Unknown GL Vendor: "));
     73 		stream_append_s8(err, cstr_to_s8(vendor));
     74 		stream_append_byte(err, '\n');
     75 		os_fatal(stream_to_s8(err));
     76 		break;
     77 	}
     78 
     79 	glGetIntegerv(GL_MAJOR_VERSION,                 &gl->version_major);
     80 	glGetIntegerv(GL_MINOR_VERSION,                 &gl->version_minor);
     81 	glGetIntegerv(GL_MAX_TEXTURE_SIZE,              &gl->max_2d_texture_dim);
     82 	glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE,           &gl->max_3d_texture_dim);
     83 	glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &gl->max_ssbo_size);
     84 	glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE,        &gl->max_ubo_size);
     85 }
     86 
     87 static void
     88 validate_gl_requirements(GLParams *gl)
     89 {
     90 	ASSERT(gl->max_ubo_size >= sizeof(BeamformerParameters));
     91 	/* NOTE: nVidia's driver seems to misreport the version */
     92 	b32 invalid = 0;
     93 	if (gl->version_major < 4)
     94 		invalid = 1;
     95 
     96 	switch (gl->vendor_id) {
     97 	case GL_VENDOR_AMD:
     98 	case GL_VENDOR_ARM:
     99 	case GL_VENDOR_INTEL:
    100 		if (gl->version_major == 4 && gl->version_minor < 5)
    101 			invalid = 1;
    102 		break;
    103 	case GL_VENDOR_NVIDIA:
    104 		if (gl->version_major == 4 && gl->version_minor < 3)
    105 			invalid = 1;
    106 		break;
    107 	}
    108 
    109 	if (invalid)
    110 		os_fatal(s8("Only OpenGL Versions 4.5 or newer are supported!\n"));
    111 }
    112 
    113 static void
    114 dump_gl_params(GLParams *gl, Arena a)
    115 {
    116 	(void)gl; (void)a;
    117 #ifdef _DEBUG
    118 	Stream s = arena_stream(&a);
    119 	stream_append_s8(&s, s8("---- GL Parameters ----\n"));
    120 	switch (gl->vendor_id) {
    121 	case GL_VENDOR_AMD:    stream_append_s8(&s, s8("Vendor: AMD\n"));    break;
    122 	case GL_VENDOR_ARM:    stream_append_s8(&s, s8("Vendor: ARM\n"));    break;
    123 	case GL_VENDOR_INTEL:  stream_append_s8(&s, s8("Vendor: Intel\n"));  break;
    124 	case GL_VENDOR_NVIDIA: stream_append_s8(&s, s8("Vendor: nVidia\n")); break;
    125 	}
    126 	stream_append_s8(&s, s8("Version: "));
    127 	stream_append_i64(&s, gl->version_major);
    128 	stream_append_byte(&s, '.');
    129 	stream_append_i64(&s, gl->version_minor);
    130 	stream_append_s8(&s, s8("\nMax 1D/2D Texture Dimension: "));
    131 	stream_append_i64(&s, gl->max_2d_texture_dim);
    132 	stream_append_s8(&s, s8("\nMax 3D Texture Dimension: "));
    133 	stream_append_i64(&s, gl->max_3d_texture_dim);
    134 	stream_append_s8(&s, s8("\nMax SSBO Size: "));
    135 	stream_append_i64(&s, gl->max_ssbo_size);
    136 	stream_append_s8(&s, s8("\nMax UBO Size: "));
    137 	stream_append_i64(&s, gl->max_ubo_size);
    138 	stream_append_s8(&s, s8("\n-----------------------\n"));
    139 	if (!s.errors)
    140 		os_write_err_msg(stream_to_s8(&s));
    141 #endif
    142 }
    143 
    144 static u32
    145 compile_shader(Arena a, u32 type, s8 shader)
    146 {
    147 	u32 sid = glCreateShader(type);
    148 	glShaderSource(sid, 1, (const char **)&shader.data, (int *)&shader.len);
    149 	glCompileShader(sid);
    150 
    151 	i32 res = 0;
    152 	glGetShaderiv(sid, GL_COMPILE_STATUS, &res);
    153 
    154 	char *stype;
    155 	switch (type) {
    156 	case GL_COMPUTE_SHADER:  stype = "Compute";  break;
    157 	case GL_FRAGMENT_SHADER: stype = "Fragment"; break;
    158 	}
    159 
    160 	if (res == GL_FALSE) {
    161 		TraceLog(LOG_WARNING, "SHADER: [ID %u] %s shader failed to compile", sid, stype);
    162 		i32 len = 0;
    163 		glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &len);
    164 		s8 err = s8alloc(&a, len);
    165 		glGetShaderInfoLog(sid, len, (int *)&err.len, (char *)err.data);
    166 		TraceLog(LOG_WARNING, "SHADER: [ID %u] Compile error: %s", sid, (char *)err.data);
    167 		glDeleteShader(sid);
    168 	} else {
    169 		TraceLog(LOG_INFO, "SHADER: [ID %u] %s shader compiled successfully", sid, stype);
    170 	}
    171 
    172 	return sid;
    173 }
    174 
    175 static void
    176 reload_shaders(BeamformerCtx *ctx, Arena a)
    177 {
    178 	ComputeShaderCtx *csctx = &ctx->csctx;
    179 	s8 header_in_arena = push_s8(&a, s8(COMPUTE_SHADER_HEADER));
    180 	for (u32 i = 0; i < ARRAY_COUNT(csctx->programs); i++) {
    181 		FileStats fs = os_get_file_stats((char *)compute_shaders[i].path.data);
    182 		if (fs.filesize <= 0)
    183 			continue;
    184 
    185 		Arena tmp = a;
    186 		s8 shader_text = os_read_file(&tmp, (char *)compute_shaders[i].path.data, fs.filesize);
    187 		if (shader_text.len == -1) {
    188 			stream_append_s8(&ctx->error_stream, s8("failed to read shader: "));
    189 			stream_append_s8(&ctx->error_stream, compute_shaders[i].path);
    190 			stream_append_byte(&ctx->error_stream, '\n');
    191 			/* TODO: maybe we don't need to fail here */
    192 			os_fatal(stream_to_s8(&ctx->error_stream));
    193 		}
    194 		/* NOTE: arena works as stack (since everything here is 1 byte aligned) */
    195 		if (compute_shaders[i].needs_header) {
    196 			shader_text.data -= header_in_arena.len;
    197 			shader_text.len  += header_in_arena.len;
    198 			ASSERT(shader_text.data == header_in_arena.data);
    199 		}
    200 
    201 		u32 shader_id  = compile_shader(tmp, GL_COMPUTE_SHADER, shader_text);
    202 
    203 		if (shader_id) {
    204 			glDeleteProgram(csctx->programs[i]);
    205 			csctx->programs[i] = rlLoadComputeShaderProgram(shader_id);
    206 			glUseProgram(csctx->programs[i]);
    207 			glBindBufferBase(GL_UNIFORM_BUFFER, 0, csctx->shared_ubo);
    208 			LABEL_GL_OBJECT(GL_PROGRAM, csctx->programs[i], compute_shaders[i].label);
    209 		}
    210 
    211 		glDeleteShader(shader_id);
    212 	}
    213 
    214 	#define X(idx, name) csctx->name##_id = glGetUniformLocation(csctx->programs[idx], "u_" #name);
    215 	CS_UNIFORMS
    216 	#undef X
    217 
    218 	Shader updated_fs = LoadShader(NULL, "shaders/render.glsl");
    219 	if (updated_fs.id != rlGetShaderIdDefault()) {
    220 		UnloadShader(ctx->fsctx.shader);
    221 		LABEL_GL_OBJECT(GL_PROGRAM, updated_fs.id, s8("Render"));
    222 		ctx->fsctx.shader       = updated_fs;
    223 		ctx->fsctx.db_cutoff_id = GetShaderLocation(updated_fs, "u_db_cutoff");
    224 		ctx->fsctx.threshold_id = GetShaderLocation(updated_fs, "u_threshold");
    225 	}
    226 }
    227 
    228 static void
    229 validate_cuda_lib(CudaLib *cl)
    230 {
    231 	#define X(name) if (!cl->name) cl->name = name ## _stub;
    232 	CUDA_LIB_FNS
    233 	#undef X
    234 }
    235 
    236 static void
    237 check_and_load_cuda_lib(CudaLib *cl, Stream *error_stream)
    238 {
    239 	FileStats current = os_get_file_stats(OS_CUDA_LIB_NAME);
    240 	if (cl->timestamp == current.timestamp || current.filesize < 32)
    241 		return;
    242 
    243 	TraceLog(LOG_INFO, "Loading CUDA lib: %s", OS_CUDA_LIB_NAME);
    244 
    245 	cl->timestamp = current.timestamp;
    246 	os_unload_library(cl->lib);
    247 	cl->lib = os_load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME, error_stream);
    248 	#define X(name) cl->name = os_lookup_dynamic_symbol(cl->lib, #name, error_stream);
    249 	CUDA_LIB_FNS
    250 	#undef X
    251 	validate_cuda_lib(cl);
    252 }
    253 
    254 static void
    255 setup_beamformer(BeamformerCtx *ctx, Arena temp_memory)
    256 {
    257 	ctx->window_size  = (uv2){.w = 1280, .h = 840};
    258 
    259 	SetConfigFlags(FLAG_VSYNC_HINT);
    260 	InitWindow(ctx->window_size.w, ctx->window_size.h, "OGL Beamformer");
    261 	/* NOTE: do this after initing so that the window starts out floating in tiling wm */
    262 	SetWindowState(FLAG_WINDOW_RESIZABLE);
    263 	SetWindowMinSize(INFO_COLUMN_WIDTH * 2, ctx->window_size.h);
    264 
    265 	/* NOTE: Gather information about the GPU */
    266 	get_gl_params(&ctx->gl, &ctx->error_stream);
    267 	dump_gl_params(&ctx->gl, temp_memory);
    268 	validate_gl_requirements(&ctx->gl);
    269 
    270 	ctx->fsctx.db        = -50.0f;
    271 	ctx->fsctx.threshold =  40.0f;
    272 
    273 	ctx->params = os_open_shared_memory_area(OS_SMEM_NAME, sizeof(*ctx->params));
    274 	/* TODO: properly handle this? */
    275 	ASSERT(ctx->params);
    276 
    277 	/* NOTE: default compute shader pipeline */
    278 	ctx->params->compute_stages[0]    = CS_HADAMARD;
    279 	ctx->params->compute_stages[1]    = CS_DAS;
    280 	ctx->params->compute_stages[2]    = CS_MIN_MAX;
    281 	ctx->params->compute_stages_count = 3;
    282 
    283 	/* NOTE: make sure function pointers are valid even if we are not using the cuda lib */
    284 	validate_cuda_lib(&ctx->cuda_lib);
    285 
    286 	/* NOTE: set up OpenGL debug logging */
    287 	glDebugMessageCallback(gl_debug_logger, &ctx->error_stream);
    288 #ifdef _DEBUG
    289 	glEnable(GL_DEBUG_OUTPUT);
    290 #endif
    291 
    292 	/* NOTE: allocate space for Uniform Buffer but don't send anything yet */
    293 	glCreateBuffers(1, &ctx->csctx.shared_ubo);
    294 	glNamedBufferStorage(ctx->csctx.shared_ubo, sizeof(BeamformerParameters), 0, GL_DYNAMIC_STORAGE_BIT);
    295 	LABEL_GL_OBJECT(GL_BUFFER, ctx->csctx.shared_ubo, s8("Beamformer_Parameters"));
    296 
    297 	glGenQueries(ARRAY_COUNT(ctx->csctx.timer_fences) * CS_LAST, (u32 *)ctx->csctx.timer_ids);
    298 	glGenQueries(ARRAY_COUNT(ctx->partial_compute_ctx.timer_ids), ctx->partial_compute_ctx.timer_ids);
    299 
    300 	reload_shaders(ctx, temp_memory);
    301 }