ogl_beamforming

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

static.c (14344B)


      1 /* See LICENSE for license details. */
      2 #ifndef _DEBUG
      3 
      4 #include "beamformer.c"
      5 #define debug_init(...)
      6 
      7 #else
      8 global void *debug_lib;
      9 
     10 #define DEBUG_ENTRY_POINTS \
     11 	X(beamformer_frame_step)           \
     12 	X(beamformer_complete_compute)     \
     13 	X(beamformer_compute_setup)        \
     14 	X(beamformer_reload_shader)        \
     15 	X(beamform_work_queue_push)        \
     16 	X(beamform_work_queue_push_commit)
     17 
     18 #define X(name) global name ##_fn *name;
     19 DEBUG_ENTRY_POINTS
     20 #undef X
     21 
     22 function FILE_WATCH_CALLBACK_FN(debug_reload)
     23 {
     24 	BeamformerInput *input = (BeamformerInput *)user_data;
     25 	Stream err             = arena_stream(arena);
     26 
     27 	/* NOTE(rnp): spin until compute thread finishes its work (we will probably
     28 	 * never reload while compute is in progress but just incase). */
     29 	while (!atomic_load(&os->compute_worker.asleep));
     30 
     31 	os_unload_library(debug_lib);
     32 	debug_lib = os_load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME, &err);
     33 
     34 	#define X(name) name = os_lookup_dynamic_symbol(debug_lib, #name, &err);
     35 	DEBUG_ENTRY_POINTS
     36 	#undef X
     37 
     38 	stream_append_s8(&err, s8("Reloaded Main Executable\n"));
     39 	os->write_file(os->error_handle, stream_to_s8(&err));
     40 
     41 	input->executable_reloaded = 1;
     42 
     43 	return 1;
     44 }
     45 
     46 function void
     47 debug_init(OS *os, iptr input, Arena *arena)
     48 {
     49 	os->add_file_watch(os, arena, s8(OS_DEBUG_LIB_NAME), debug_reload, input);
     50 	debug_reload(os, s8(""), input, *arena);
     51 
     52 	Stream err = arena_stream(*arena);
     53 	void *rdoc = os_get_module(OS_RENDERDOC_SONAME, 0);
     54 	if (rdoc) {
     55 		renderdoc_get_api_fn *get_api = os_lookup_dynamic_symbol(rdoc, "RENDERDOC_GetAPI", &err);
     56 		if (get_api) {
     57 			RenderDocAPI *api = 0;
     58 			if (get_api(10600, (void **)&api)) {
     59 				os->start_frame_capture = RENDERDOC_START_FRAME_CAPTURE(api);
     60 				os->end_frame_capture   = RENDERDOC_END_FRAME_CAPTURE(api);
     61 				stream_append_s8(&err, s8("loaded: " OS_RENDERDOC_SONAME "\n"));
     62 			}
     63 		}
     64 	}
     65 
     66 	os->write_file(os->error_handle, stream_to_s8(&err));
     67 }
     68 
     69 #endif /* _DEBUG */
     70 
     71 #define static_path_join(a, b) (a OS_PATH_SEPARATOR b)
     72 
     73 struct gl_debug_ctx {
     74 	Stream  stream;
     75 	OS     *os;
     76 };
     77 
     78 function void
     79 gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, i32 len, const char *msg, const void *userctx)
     80 {
     81 	(void)src; (void)type; (void)id;
     82 
     83 	struct gl_debug_ctx *ctx = (struct gl_debug_ctx *)userctx;
     84 	Stream *e = &ctx->stream;
     85 	stream_append_s8(e, s8("[GL DEBUG "));
     86 	switch (lvl) {
     87 	case GL_DEBUG_SEVERITY_HIGH:         stream_append_s8(e, s8("HIGH]: "));         break;
     88 	case GL_DEBUG_SEVERITY_MEDIUM:       stream_append_s8(e, s8("MEDIUM]: "));       break;
     89 	case GL_DEBUG_SEVERITY_LOW:          stream_append_s8(e, s8("LOW]: "));          break;
     90 	case GL_DEBUG_SEVERITY_NOTIFICATION: stream_append_s8(e, s8("NOTIFICATION]: ")); break;
     91 	default:                             stream_append_s8(e, s8("INVALID]: "));      break;
     92 	}
     93 	stream_append(e, (char *)msg, len);
     94 	stream_append_byte(e, '\n');
     95 	ctx->os->write_file(ctx->os->error_handle, stream_to_s8(e));
     96 	stream_reset(e, 0);
     97 }
     98 
     99 function void
    100 get_gl_params(GLParams *gl, Stream *err)
    101 {
    102 	char *vendor = (char *)glGetString(GL_VENDOR);
    103 	if (!vendor) {
    104 		stream_append_s8(err, s8("Failed to determine GL Vendor\n"));
    105 		os_fatal(stream_to_s8(err));
    106 	}
    107 	/* TODO(rnp): str prefix of */
    108 	switch (vendor[0]) {
    109 	case 'A': gl->vendor_id = GL_VENDOR_AMD;    break;
    110 	case 'I': gl->vendor_id = GL_VENDOR_INTEL;  break;
    111 	case 'N': gl->vendor_id = GL_VENDOR_NVIDIA; break;
    112 	/* NOTE(rnp): freedreno */
    113 	case 'f': gl->vendor_id = GL_VENDOR_ARM;    break;
    114 	/* NOTE(rnp): Microsoft Corporation - weird win32 thing (microsoft is just using mesa for the driver) */
    115 	case 'M': gl->vendor_id = GL_VENDOR_ARM;    break;
    116 	default:
    117 		stream_append_s8s(err, s8("Unknown GL Vendor: "), c_str_to_s8(vendor), s8("\n"));
    118 		os_fatal(stream_to_s8(err));
    119 	}
    120 
    121 	#define X(glname, name, suffix) glGetIntegerv(GL_##glname, &gl->name);
    122 	GL_PARAMETERS
    123 	#undef X
    124 
    125 	/* NOTE(rnp): sometimes GL_MINOR_VERSION doesn't actually report the drivers
    126 	 * supported version. Since at this point GL has been fully loaded we can
    127 	 * check that at least one of the GL 4.5 function pointers are available */
    128 	if (gl->version_minor < 5 && glCreateBuffers)
    129 		gl->version_minor = 5;
    130 }
    131 
    132 function void
    133 validate_gl_requirements(GLParams *gl, Arena a)
    134 {
    135 	Stream s = arena_stream(a);
    136 
    137 	if (gl->max_ubo_size < sizeof(BeamformerParameters)) {
    138 		stream_append_s8(&s, s8("GPU must support UBOs of at least "));
    139 		stream_append_i64(&s, sizeof(BeamformerParameters));
    140 		stream_append_s8(&s, s8(" bytes!\n"));
    141 	}
    142 
    143 	if (gl->version_major < 4 || (gl->version_major == 4 && gl->version_minor < 5))
    144 		stream_append_s8(&s, s8("Only OpenGL Versions 4.5 or newer are supported!\n"));
    145 
    146 	if (s.widx) os_fatal(stream_to_s8(&s));
    147 }
    148 
    149 function void
    150 dump_gl_params(GLParams *gl, Arena a, OS *os)
    151 {
    152 	(void)gl; (void)a;
    153 #ifdef _DEBUG
    154 	s8 vendor = s8("vendor:");
    155 	iz max_width = vendor.len;
    156 	#define X(glname, name, suffix) if (s8(#name).len > max_width) max_width = s8(#name ":").len;
    157 	GL_PARAMETERS
    158 	#undef X
    159 	max_width++;
    160 
    161 	Stream s = arena_stream(a);
    162 	stream_append_s8s(&s, s8("---- GL Parameters ----\n"), vendor);
    163 	stream_pad(&s, ' ', max_width - vendor.len);
    164 	switch (gl->vendor_id) {
    165 	case GL_VENDOR_AMD:    stream_append_s8(&s, s8("AMD\n"));    break;
    166 	case GL_VENDOR_ARM:    stream_append_s8(&s, s8("ARM\n"));    break;
    167 	case GL_VENDOR_INTEL:  stream_append_s8(&s, s8("Intel\n"));  break;
    168 	case GL_VENDOR_NVIDIA: stream_append_s8(&s, s8("nVidia\n")); break;
    169 	}
    170 
    171 	#define X(glname, name, suffix) \
    172 		stream_append_s8(&s, s8(#name ":"));                \
    173 		stream_pad(&s, ' ', max_width - s8(#name ":").len); \
    174 		stream_append_i64(&s, gl->name);                    \
    175 		stream_append_s8(&s, s8(suffix));                   \
    176 		stream_append_byte(&s, '\n');
    177 	GL_PARAMETERS
    178 	#undef X
    179 	stream_append_s8(&s, s8("-----------------------\n"));
    180 	os->write_file(os->error_handle, stream_to_s8(&s));
    181 #endif
    182 }
    183 
    184 function FILE_WATCH_CALLBACK_FN(reload_shader)
    185 {
    186 	ShaderReloadContext *ctx = (typeof(ctx))user_data;
    187 	return beamformer_reload_shader(ctx->beamformer_context, ctx, arena, ctx->name);
    188 }
    189 
    190 function FILE_WATCH_CALLBACK_FN(reload_shader_indirect)
    191 {
    192 	ShaderReloadContext *src = (typeof(src))user_data;
    193 	BeamformerCtx *ctx = src->beamformer_context;
    194 	BeamformWork *work = beamform_work_queue_push(ctx->beamform_work_queue);
    195 	if (work) {
    196 		work->type = BW_RELOAD_SHADER;
    197 		work->shader_reload_context = src;
    198 		beamform_work_queue_push_commit(ctx->beamform_work_queue);
    199 		os->wake_waiters(&os->compute_worker.sync_variable);
    200 	}
    201 	return 1;
    202 }
    203 
    204 function FILE_WATCH_CALLBACK_FN(load_cuda_lib)
    205 {
    206 	CudaLib *cl = (CudaLib *)user_data;
    207 	b32 result  = os_file_exists((c8 *)path.data);
    208 	if (result) {
    209 		Stream err = arena_stream(arena);
    210 
    211 		stream_append_s8(&err, s8("loading CUDA lib: " OS_CUDA_LIB_NAME "\n"));
    212 		os_unload_library(cl->lib);
    213 		cl->lib = os_load_library((c8 *)path.data, OS_CUDA_LIB_TEMP_NAME, &err);
    214 		#define X(name, symname) cl->name = os_lookup_dynamic_symbol(cl->lib, symname, &err);
    215 		CUDA_LIB_FNS
    216 		#undef X
    217 
    218 		os->write_file(os->error_handle, stream_to_s8(&err));
    219 	}
    220 
    221 	#define X(name, symname) if (!cl->name) cl->name = cuda_ ## name ## _stub;
    222 	CUDA_LIB_FNS
    223 	#undef X
    224 
    225 	return result;
    226 }
    227 
    228 
    229 #define GLFW_VISIBLE 0x00020004
    230 void glfwWindowHint(i32, i32);
    231 iptr glfwCreateWindow(i32, i32, char *, iptr, iptr);
    232 void glfwMakeContextCurrent(iptr);
    233 
    234 function OS_THREAD_ENTRY_POINT_FN(compute_worker_thread_entry_point)
    235 {
    236 	GLWorkerThreadContext *ctx = (GLWorkerThreadContext *)_ctx;
    237 
    238 	glfwMakeContextCurrent(ctx->window_handle);
    239 	ctx->gl_context = os_get_native_gl_context(ctx->window_handle);
    240 
    241 	beamformer_compute_setup(ctx->user_context, ctx->arena, ctx->gl_context);
    242 
    243 	for (;;) {
    244 		for (;;) {
    245 			i32 current = atomic_load(&ctx->sync_variable);
    246 			if (current && atomic_swap(&ctx->sync_variable, 0) == current)
    247 				break;
    248 
    249 			ctx->asleep = 1;
    250 			os_wait_on_value(&ctx->sync_variable, current, -1);
    251 			ctx->asleep = 0;
    252 		}
    253 		beamformer_complete_compute(ctx->user_context, ctx->arena, ctx->gl_context);
    254 	}
    255 
    256 	unreachable();
    257 
    258 	return 0;
    259 }
    260 
    261 function void
    262 setup_beamformer(BeamformerCtx *ctx, Arena *memory)
    263 {
    264 	ctx->window_size  = (uv2){.w = 1280, .h = 840};
    265 
    266 	SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN);
    267 	InitWindow(ctx->window_size.w, ctx->window_size.h, "OGL Beamformer");
    268 	/* NOTE: do this after initing so that the window starts out floating in tiling wm */
    269 	SetWindowState(FLAG_WINDOW_RESIZABLE);
    270 	SetWindowMinSize(840, ctx->window_size.h);
    271 
    272 	/* NOTE: Gather information about the GPU */
    273 	get_gl_params(&ctx->gl, &ctx->error_stream);
    274 	dump_gl_params(&ctx->gl, *memory, &ctx->os);
    275 	validate_gl_requirements(&ctx->gl, *memory);
    276 
    277 	glfwWindowHint(GLFW_VISIBLE, 0);
    278 	iptr raylib_window_handle = (iptr)GetPlatformWindowHandle();
    279 	GLWorkerThreadContext *worker = &ctx->os.compute_worker;
    280 	worker->window_handle = glfwCreateWindow(320, 240, "", 0, raylib_window_handle);
    281 	worker->handle        = os_create_thread(*memory, (iptr)worker, s8("[compute]"),
    282 	                                         compute_worker_thread_entry_point);
    283 	/* TODO(rnp): we should lock this down after we have something working */
    284 	worker->user_context  = (iptr)ctx;
    285 
    286 	glfwMakeContextCurrent(raylib_window_handle);
    287 
    288 	ctx->beamform_work_queue = push_struct(memory, BeamformWorkQueue);
    289 
    290 	ctx->shared_memory = os_create_shared_memory_area(OS_SHARED_MEMORY_NAME, BEAMFORMER_SHARED_MEMORY_SIZE);
    291 	if (!ctx->shared_memory)
    292 		os_fatal(s8("Get more ram lol\n"));
    293 	mem_clear(ctx->shared_memory, 0, sizeof(*ctx->shared_memory));
    294 
    295 	ctx->shared_memory->version = BEAMFORMER_PARAMETERS_VERSION;
    296 	/* TODO(rnp): refactor - this is annoying */
    297 	ctx->shared_memory->parameters_sync      = 1;
    298 	ctx->shared_memory->parameters_head_sync = 1;
    299 	ctx->shared_memory->parameters_ui_sync   = 1;
    300 	ctx->shared_memory->raw_data_sync        = 1;
    301 	ctx->shared_memory->channel_mapping_sync = 1;
    302 	ctx->shared_memory->sparse_elements_sync = 1;
    303 	ctx->shared_memory->focal_vectors_sync   = 1;
    304 
    305 	/* NOTE: default compute shader pipeline */
    306 	ctx->shared_memory->compute_stages[0]    = ComputeShaderKind_Decode;
    307 	ctx->shared_memory->compute_stages[1]    = ComputeShaderKind_DASCompute;
    308 	ctx->shared_memory->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, symname) if (!ctx->cuda_lib.name) ctx->cuda_lib.name = cuda_ ## 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 	#define X(name, type, size, gltype, glsize, comment) "\t" #gltype " " #name #glsize "; " comment "\n"
    331 	read_only local_persist s8 compute_parameters_header = s8(""
    332 		"layout(std140, binding = 0) uniform parameters {\n"
    333 		BEAMFORMER_PARAMS_HEAD
    334 		BEAMFORMER_UI_PARAMS
    335 		BEAMFORMER_PARAMS_TAIL
    336 		"};\n\n"
    337 	);
    338 	#undef X
    339 
    340 	#define X(e, sn, f, nh, pretty_name) do if (s8(f).len > 0) {          \
    341 		ShaderReloadContext *src = push_struct(memory, typeof(*src)); \
    342 		src->beamformer_context  = ctx;                               \
    343 		if (nh) src->header = compute_parameters_header;              \
    344 		src->path    = s8(static_path_join("shaders", f ".glsl"));    \
    345 		src->name    = src->path;                                     \
    346 		src->shader  = ctx->csctx.programs + ShaderKind_##e;          \
    347 		src->gl_type = GL_COMPUTE_SHADER;                             \
    348 		src->kind    = ShaderKind_##e;                                \
    349 		src->link    = src;                                           \
    350 		os_add_file_watch(&ctx->os, memory, src->path, reload_shader_indirect, (iptr)src); \
    351 		reload_shader_indirect(&ctx->os, src->path, (iptr)src, *memory); \
    352 	} while (0);
    353 	COMPUTE_SHADERS
    354 	#undef X
    355 	os_wake_waiters(&worker->sync_variable);
    356 
    357 	FrameViewRenderContext *fvr = &ctx->frame_view_render_context;
    358 	glGenFramebuffers(1, &fvr->framebuffer);
    359 	LABEL_GL_OBJECT(GL_FRAMEBUFFER, fvr->framebuffer, s8("Frame View Render Framebuffer"));
    360 	f32 vertices[] = {
    361 		-1,  1, 0, 0,
    362 		-1, -1, 0, 1,
    363 		 1, -1, 1, 1,
    364 		-1,  1, 0, 0,
    365 		 1, -1, 1, 1,
    366 		 1,  1, 1, 0,
    367 	};
    368 	glGenVertexArrays(1, &fvr->vao);
    369 	glBindVertexArray(fvr->vao);
    370 	glGenBuffers(1, &fvr->vbo);
    371 	glBindBuffer(GL_ARRAY_BUFFER, fvr->vbo);
    372 	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    373 	glVertexAttribPointer(0, 2, GL_FLOAT, 0, 4 * sizeof(f32), 0);
    374 	glEnableVertexAttribArray(0);
    375 	glVertexAttribPointer(1, 2, GL_FLOAT, 0, 4 * sizeof(f32), (void *)(2 * sizeof(f32)));
    376 	glEnableVertexAttribArray(1);
    377 	glBindVertexArray(0);
    378 
    379 	ShaderReloadContext *render_2d = push_struct(memory, typeof(*render_2d));
    380 	render_2d->beamformer_context = ctx;
    381 	render_2d->path    = s8(static_path_join("shaders", "render_2d.frag.glsl"));
    382 	render_2d->name    = s8("shaders/render_2d.glsl");
    383 	render_2d->gl_type = GL_FRAGMENT_SHADER;
    384 	render_2d->kind    = ShaderKind_Render2D;
    385 	render_2d->shader  = &fvr->shader;
    386 	render_2d->header  = s8(""
    387 	"layout(location = 0) in  vec2 texture_coordinate;\n"
    388 	"layout(location = 0) out vec4 v_out_colour;\n\n"
    389 	"layout(location = " str(FRAME_VIEW_RENDER_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n"
    390 	"layout(location = " str(FRAME_VIEW_RENDER_THRESHOLD_LOC)     ") uniform float u_threshold = 40;\n"
    391 	"layout(location = " str(FRAME_VIEW_RENDER_GAMMA_LOC)         ") uniform float u_gamma     = 1;\n"
    392 	"layout(location = " str(FRAME_VIEW_RENDER_LOG_SCALE_LOC)     ") uniform bool  u_log_scale;\n"
    393 	"\n#line 1\n");
    394 	render_2d->link = push_struct(memory, typeof(*render_2d));
    395 	render_2d->link->gl_type = GL_VERTEX_SHADER;
    396 	render_2d->link->link    = render_2d;
    397 	render_2d->link->header  = s8(""
    398 	"layout(location = 0) in vec2 v_position;\n"
    399 	"layout(location = 1) in vec2 v_texture_coordinate;\n"
    400 	"\n"
    401 	"layout(location = 0) out vec2 f_texture_coordinate;\n"
    402 	"\n"
    403 	"void main()\n"
    404 	"{\n"
    405 	"\tf_texture_coordinate = v_texture_coordinate;\n"
    406 	"\tgl_Position = vec4(v_position, 0, 1);\n"
    407 	"}\n");
    408 	reload_shader(&ctx->os, render_2d->path, (iptr)render_2d, *memory);
    409 	os_add_file_watch(&ctx->os, memory, render_2d->path, reload_shader, (iptr)render_2d);
    410 }