ogl_beamforming

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

static.c (12211B)


      1 /* See LICENSE for license details. */
      2 #ifndef _DEBUG
      3 
      4 #include "beamformer.c"
      5 #define debug_init(...)
      6 
      7 #else
      8 static void *debug_lib;
      9 
     10 #define DEBUG_ENTRY_POINTS \
     11 	X(beamformer_frame_step)           \
     12 	X(beamformer_complete_compute)     \
     13 	X(beamform_work_queue_push)        \
     14 	X(beamform_work_queue_push_commit)
     15 
     16 #define X(name) static name ##_fn *name;
     17 DEBUG_ENTRY_POINTS
     18 #undef X
     19 
     20 static FILE_WATCH_CALLBACK_FN(debug_reload)
     21 {
     22 	BeamformerInput *input = (BeamformerInput *)user_data;
     23 	Stream err             = arena_stream(&tmp);
     24 
     25 	/* NOTE(rnp): spin until compute thread finishes its work (we will probably
     26 	 * never reload while compute is in progress but just incase). */
     27 	while (!atomic_load(&os->compute_worker.asleep));
     28 
     29 	os_unload_library(debug_lib);
     30 	debug_lib = os_load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME, &err);
     31 
     32 	#define X(name) name = os_lookup_dynamic_symbol(debug_lib, #name, &err);
     33 	DEBUG_ENTRY_POINTS
     34 	#undef X
     35 
     36 	stream_append_s8(&err, s8("Reloaded Main Executable\n"));
     37 	os->write_file(os->stderr, stream_to_s8(&err));
     38 
     39 	input->executable_reloaded = 1;
     40 
     41 	return 1;
     42 }
     43 
     44 static void
     45 debug_init(OS *os, iptr input, Arena *arena)
     46 {
     47 	os->add_file_watch(os, arena, s8(OS_DEBUG_LIB_NAME), debug_reload, input);
     48 	debug_reload(os, (s8){0}, input, *arena);
     49 
     50 	Arena  tmp = *arena;
     51 	Stream err = arena_stream(&tmp);
     52 	void *rdoc = os_get_module(OS_RENDERDOC_SONAME, 0);
     53 	if (rdoc) {
     54 		renderdoc_get_api_fn *get_api = os_lookup_dynamic_symbol(rdoc, "RENDERDOC_GetAPI", &err);
     55 		if (get_api) {
     56 			RenderDocAPI *api = 0;
     57 			if (get_api(10600, (void **)&api)) {
     58 				os->start_frame_capture = RENDERDOC_START_FRAME_CAPTURE(api);
     59 				os->end_frame_capture   = RENDERDOC_END_FRAME_CAPTURE(api);
     60 				stream_append_s8(&err, s8("loaded: " OS_RENDERDOC_SONAME "\n"));
     61 			}
     62 		}
     63 	}
     64 
     65 	os->write_file(os->stderr, stream_to_s8(&err));
     66 }
     67 
     68 #endif /* _DEBUG */
     69 
     70 #define static_path_join(a, b) (a OS_PATH_SEPERATOR b)
     71 
     72 struct gl_debug_ctx {
     73 	Stream  stream;
     74 	OS     *os;
     75 };
     76 
     77 static void
     78 gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, i32 len, const char *msg, const void *userctx)
     79 {
     80 	(void)src; (void)type; (void)id;
     81 
     82 	struct gl_debug_ctx *ctx = (struct gl_debug_ctx *)userctx;
     83 	Stream *e = &ctx->stream;
     84 	stream_append_s8(e, s8("[GL DEBUG "));
     85 	switch (lvl) {
     86 	case GL_DEBUG_SEVERITY_HIGH:         stream_append_s8(e, s8("HIGH]: "));         break;
     87 	case GL_DEBUG_SEVERITY_MEDIUM:       stream_append_s8(e, s8("MEDIUM]: "));       break;
     88 	case GL_DEBUG_SEVERITY_LOW:          stream_append_s8(e, s8("LOW]: "));          break;
     89 	case GL_DEBUG_SEVERITY_NOTIFICATION: stream_append_s8(e, s8("NOTIFICATION]: ")); break;
     90 	default:                             stream_append_s8(e, s8("INVALID]: "));      break;
     91 	}
     92 	stream_append(e, (char *)msg, len);
     93 	stream_append_byte(e, '\n');
     94 	ctx->os->write_file(ctx->os->stderr, stream_to_s8(e));
     95 	stream_reset(e, 0);
     96 }
     97 
     98 static void
     99 get_gl_params(GLParams *gl, Stream *err)
    100 {
    101 	char *vendor = (char *)glGetString(GL_VENDOR);
    102 	if (!vendor) {
    103 		stream_append_s8(err, s8("Failed to determine GL Vendor\n"));
    104 		os_fatal(stream_to_s8(err));
    105 	}
    106 	/* TODO(rnp): str prefix of */
    107 	switch (vendor[0]) {
    108 	case 'A': gl->vendor_id = GL_VENDOR_AMD;    break;
    109 	case 'I': gl->vendor_id = GL_VENDOR_INTEL;  break;
    110 	case 'N': gl->vendor_id = GL_VENDOR_NVIDIA; break;
    111 	/* NOTE(rnp): freedreno */
    112 	case 'f': gl->vendor_id = GL_VENDOR_ARM;    break;
    113 	/* NOTE(rnp): Microsoft Corporation - weird win32 thing (microsoft is just using mesa for the driver) */
    114 	case 'M': gl->vendor_id = GL_VENDOR_ARM;    break;
    115 	default:
    116 		stream_append_s8(err, s8("Unknown GL Vendor: "));
    117 		stream_append_s8(err, c_str_to_s8(vendor));
    118 		stream_append_byte(err, '\n');
    119 		os_fatal(stream_to_s8(err));
    120 		break;
    121 	}
    122 
    123 	glGetIntegerv(GL_MAJOR_VERSION,                 &gl->version_major);
    124 	glGetIntegerv(GL_MINOR_VERSION,                 &gl->version_minor);
    125 	glGetIntegerv(GL_MAX_TEXTURE_SIZE,              &gl->max_2d_texture_dim);
    126 	glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE,           &gl->max_3d_texture_dim);
    127 	glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &gl->max_ssbo_size);
    128 	glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE,        &gl->max_ubo_size);
    129 	glGetIntegerv(GL_MAX_SERVER_WAIT_TIMEOUT,       &gl->max_server_wait_time);
    130 
    131 	/* NOTE(rnp): sometimes GL_MINOR_VERSION doesn't actually report the drivers
    132 	 * supported version. Since at this point GL has been fully loaded we can
    133 	 * check that at least one of the GL 4.5 function pointers are available */
    134 	if (gl->version_minor < 5 && glCreateBuffers)
    135 		gl->version_minor = 5;
    136 }
    137 
    138 static void
    139 validate_gl_requirements(GLParams *gl, Arena a)
    140 {
    141 	Stream s = arena_stream(&a);
    142 
    143 	if (gl->max_ubo_size < sizeof(BeamformerParameters)) {
    144 		stream_append_s8(&s, s8("GPU must support UBOs of at least "));
    145 		stream_append_i64(&s, sizeof(BeamformerParameters));
    146 		stream_append_s8(&s, s8(" bytes!\n"));
    147 	}
    148 
    149 	if (gl->version_major < 4 || (gl->version_major == 4 && gl->version_minor < 5))
    150 		stream_append_s8(&s, s8("Only OpenGL Versions 4.5 or newer are supported!\n"));
    151 
    152 	if (s.widx) os_fatal(stream_to_s8(&s));
    153 }
    154 
    155 static void
    156 dump_gl_params(GLParams *gl, Arena a, OS *os)
    157 {
    158 	(void)gl; (void)a;
    159 #ifdef _DEBUG
    160 	Stream s = arena_stream(&a);
    161 	stream_append_s8(&s, s8("---- GL Parameters ----\n"));
    162 	switch (gl->vendor_id) {
    163 	case GL_VENDOR_AMD:    stream_append_s8(&s, s8("Vendor: AMD\n"));    break;
    164 	case GL_VENDOR_ARM:    stream_append_s8(&s, s8("Vendor: ARM\n"));    break;
    165 	case GL_VENDOR_INTEL:  stream_append_s8(&s, s8("Vendor: Intel\n"));  break;
    166 	case GL_VENDOR_NVIDIA: stream_append_s8(&s, s8("Vendor: nVidia\n")); break;
    167 	}
    168 	stream_append_s8(&s, s8("Version: "));
    169 	stream_append_i64(&s, gl->version_major);
    170 	stream_append_byte(&s, '.');
    171 	stream_append_i64(&s, gl->version_minor);
    172 	stream_append_s8(&s, s8("\nMax 1D/2D Texture Dimension: "));
    173 	stream_append_i64(&s, gl->max_2d_texture_dim);
    174 	stream_append_s8(&s, s8("\nMax 3D Texture Dimension:    "));
    175 	stream_append_i64(&s, gl->max_3d_texture_dim);
    176 	stream_append_s8(&s, s8("\nMax SSBO Size:               "));
    177 	stream_append_i64(&s, gl->max_ssbo_size);
    178 	stream_append_s8(&s, s8("\nMax UBO Size:                "));
    179 	stream_append_i64(&s, gl->max_ubo_size);
    180 	stream_append_s8(&s, s8("\nMax Server Wait Time [ns]:   "));
    181 	stream_append_i64(&s, gl->max_server_wait_time);
    182 	stream_append_s8(&s, s8("\n-----------------------\n"));
    183 	os->write_file(os->stderr, stream_to_s8(&s));
    184 #endif
    185 }
    186 
    187 static FILE_WATCH_CALLBACK_FN(reload_render_shader)
    188 {
    189 	FragmentShaderCtx *ctx = (FragmentShaderCtx *)user_data;
    190 	Shader updated_fs      = LoadShader(0, (c8 *)path.data);
    191 
    192 	if (updated_fs.id) {
    193 		UnloadShader(ctx->shader);
    194 		LABEL_GL_OBJECT(GL_PROGRAM, updated_fs.id, s8("Render Shader"));
    195 		ctx->shader       = updated_fs;
    196 		ctx->db_cutoff_id = GetShaderLocation(updated_fs, "u_db_cutoff");
    197 		ctx->threshold_id = GetShaderLocation(updated_fs, "u_threshold");
    198 		ctx->updated      = 1;
    199 	}
    200 
    201 	return 1;
    202 }
    203 
    204 
    205 static FILE_WATCH_CALLBACK_FN(queue_compute_shader_reload)
    206 {
    207 	ComputeShaderReloadContext *csr = (typeof(csr))user_data;
    208 	BeamformerCtx *ctx = csr->beamformer_ctx;
    209 	BeamformWork *work = beamform_work_queue_push(ctx->beamform_work_queue);
    210 	if (work) {
    211 		work->type = BW_RELOAD_SHADER;
    212 		work->reload_shader_ctx = csr;
    213 		beamform_work_queue_push_commit(ctx->beamform_work_queue);
    214 		ctx->os.wake_thread(ctx->os.compute_worker.sync_handle);
    215 	}
    216 	return 1;
    217 }
    218 
    219 static FILE_WATCH_CALLBACK_FN(load_cuda_lib)
    220 {
    221 	CudaLib *cl = (CudaLib *)user_data;
    222 	b32 result  = os_file_exists((c8 *)path.data);
    223 	if (result) {
    224 		Stream err = arena_stream(&tmp);
    225 
    226 		stream_append_s8(&err, s8("loading CUDA lib: " OS_CUDA_LIB_NAME "\n"));
    227 		os_unload_library(cl->lib);
    228 		cl->lib = os_load_library((c8 *)path.data, OS_CUDA_LIB_TEMP_NAME, &err);
    229 		#define X(name) cl->name = os_lookup_dynamic_symbol(cl->lib, #name, &err);
    230 		CUDA_LIB_FNS
    231 		#undef X
    232 
    233 		os->write_file(os->stderr, stream_to_s8(&err));
    234 	}
    235 
    236 	#define X(name) if (!cl->name) cl->name = name ## _stub;
    237 	CUDA_LIB_FNS
    238 	#undef X
    239 
    240 	return result;
    241 }
    242 
    243 
    244 #define GLFW_VISIBLE 0x00020004
    245 void glfwWindowHint(i32, i32);
    246 iptr glfwCreateWindow(i32, i32, char *, iptr, iptr);
    247 void glfwMakeContextCurrent(iptr);
    248 
    249 static OS_THREAD_ENTRY_POINT_FN(compute_worker_thread_entry_point)
    250 {
    251 	GLWorkerThreadContext *ctx = (GLWorkerThreadContext *)_ctx;
    252 
    253 	glfwMakeContextCurrent(ctx->window_handle);
    254 	ctx->gl_context = os_get_native_gl_context(ctx->window_handle);
    255 
    256 	for (;;) {
    257 		ctx->asleep = 1;
    258 		os_sleep_thread(ctx->sync_handle);
    259 		ctx->asleep = 0;
    260 		beamformer_complete_compute(ctx->user_context, ctx->arena, ctx->gl_context);
    261 	}
    262 
    263 	unreachable();
    264 
    265 	return 0;
    266 }
    267 
    268 static void
    269 setup_beamformer(BeamformerCtx *ctx, Arena *memory)
    270 {
    271 	ctx->window_size  = (uv2){.w = 1280, .h = 840};
    272 
    273 	SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN);
    274 	InitWindow(ctx->window_size.w, ctx->window_size.h, "OGL Beamformer");
    275 	/* NOTE: do this after initing so that the window starts out floating in tiling wm */
    276 	SetWindowState(FLAG_WINDOW_RESIZABLE);
    277 	SetWindowMinSize(840, ctx->window_size.h);
    278 
    279 	/* NOTE: Gather information about the GPU */
    280 	get_gl_params(&ctx->gl, &ctx->error_stream);
    281 	dump_gl_params(&ctx->gl, *memory, &ctx->os);
    282 	validate_gl_requirements(&ctx->gl, *memory);
    283 
    284 	glfwWindowHint(GLFW_VISIBLE, 0);
    285 	iptr raylib_window_handle = (iptr)GetPlatformWindowHandle();
    286 	GLWorkerThreadContext *worker = &ctx->os.compute_worker;
    287 	worker->window_handle = glfwCreateWindow(320, 240, "", 0, raylib_window_handle);
    288 	worker->sync_handle   = os_create_sync_object(memory);
    289 	worker->handle        = os_create_thread(*memory, (iptr)worker, s8("[compute]"),
    290 	                                         compute_worker_thread_entry_point);
    291 	/* TODO(rnp): we should lock this down after we have something working */
    292 	worker->user_context  = (iptr)ctx;
    293 
    294 	glfwMakeContextCurrent(raylib_window_handle);
    295 
    296 	ctx->beamform_work_queue = push_struct(memory, BeamformWorkQueue);
    297 
    298 	ctx->averaged_frames[0].id = 0;
    299 	ctx->averaged_frames[1].id = 1;
    300 
    301 	ctx->params = os_open_shared_memory_area(OS_SMEM_NAME, sizeof(*ctx->params));
    302 	/* TODO: properly handle this? */
    303 	ASSERT(ctx->params);
    304 
    305 	/* NOTE: default compute shader pipeline */
    306 	ctx->params->compute_stages[0]    = CS_DECODE;
    307 	ctx->params->compute_stages[1]    = CS_DAS;
    308 	ctx->params->compute_stages_count = 2;
    309 
    310 	if (ctx->gl.vendor_id == GL_VENDOR_NVIDIA
    311 	    && load_cuda_lib(&ctx->os, s8(OS_CUDA_LIB_NAME), (iptr)&ctx->cuda_lib, *memory))
    312 	{
    313 		os_add_file_watch(&ctx->os, memory, s8(OS_CUDA_LIB_NAME), load_cuda_lib,
    314 		                  (iptr)&ctx->cuda_lib);
    315 	} else {
    316 		#define X(name) if (!ctx->cuda_lib.name) ctx->cuda_lib.name = name ## _stub;
    317 		CUDA_LIB_FNS
    318 		#undef X
    319 	}
    320 
    321 	/* NOTE: set up OpenGL debug logging */
    322 	struct gl_debug_ctx *gl_debug_ctx = push_struct(memory, typeof(*gl_debug_ctx));
    323 	gl_debug_ctx->stream = stream_alloc(memory, 1024);
    324 	gl_debug_ctx->os     = &ctx->os;
    325 	glDebugMessageCallback(gl_debug_logger, gl_debug_ctx);
    326 #ifdef _DEBUG
    327 	glEnable(GL_DEBUG_OUTPUT);
    328 #endif
    329 
    330 	/* NOTE: allocate space for Uniform Buffer but don't send anything yet */
    331 	glCreateBuffers(1, &ctx->csctx.shared_ubo);
    332 	glNamedBufferStorage(ctx->csctx.shared_ubo, sizeof(BeamformerParameters), 0, GL_DYNAMIC_STORAGE_BIT);
    333 	LABEL_GL_OBJECT(GL_BUFFER, ctx->csctx.shared_ubo, s8("Beamformer_Parameters"));
    334 
    335 	#define X(e, sn, f, nh, pretty_name) do if (s8(f).len > 0) {                 \
    336 		ComputeShaderReloadContext *csr = push_struct(memory, typeof(*csr)); \
    337 		csr->beamformer_ctx = ctx;                                           \
    338 		csr->label          = s8("CS_" #e);                                  \
    339 		csr->shader         = sn;                                            \
    340 		csr->needs_header   = nh;                                            \
    341 		csr->path           = s8(static_path_join("shaders", f ".glsl"));    \
    342 		os_add_file_watch(&ctx->os, memory, csr->path, queue_compute_shader_reload, (iptr)csr); \
    343 		queue_compute_shader_reload(&ctx->os, csr->path, (iptr)csr, *memory); \
    344 	} while (0);
    345 	COMPUTE_SHADERS
    346 	#undef X
    347 	os_wake_thread(worker->sync_handle);
    348 
    349 	s8 render = s8(static_path_join("shaders", "render.glsl"));
    350 	reload_render_shader(&ctx->os, render, (iptr)&ctx->fsctx, *memory);
    351 	os_add_file_watch(&ctx->os, memory, render, reload_render_shader, (iptr)&ctx->fsctx);
    352 
    353 	ctx->ready_for_rf = 1;
    354 }