ogl_beamforming

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

static.c (13806B)


      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(beamformer_compute_setup)        \
     14 	X(beamform_work_queue_push)        \
     15 	X(beamform_work_queue_push_commit)
     16 
     17 #define X(name) static name ##_fn *name;
     18 DEBUG_ENTRY_POINTS
     19 #undef X
     20 
     21 static FILE_WATCH_CALLBACK_FN(debug_reload)
     22 {
     23 	BeamformerInput *input = (BeamformerInput *)user_data;
     24 	Stream err             = arena_stream(&tmp);
     25 
     26 	/* NOTE(rnp): spin until compute thread finishes its work (we will probably
     27 	 * never reload while compute is in progress but just incase). */
     28 	while (!atomic_load(&os->compute_worker.asleep));
     29 
     30 	os_unload_library(debug_lib);
     31 	debug_lib = os_load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME, &err);
     32 
     33 	#define X(name) name = os_lookup_dynamic_symbol(debug_lib, #name, &err);
     34 	DEBUG_ENTRY_POINTS
     35 	#undef X
     36 
     37 	stream_append_s8(&err, s8("Reloaded Main Executable\n"));
     38 	os->write_file(os->stderr, stream_to_s8(&err));
     39 
     40 	input->executable_reloaded = 1;
     41 
     42 	return 1;
     43 }
     44 
     45 static void
     46 debug_init(OS *os, iptr input, Arena *arena)
     47 {
     48 	os->add_file_watch(os, arena, s8(OS_DEBUG_LIB_NAME), debug_reload, input);
     49 	debug_reload(os, (s8){0}, input, *arena);
     50 
     51 	Arena  tmp = *arena;
     52 	Stream err = arena_stream(&tmp);
     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->stderr, stream_to_s8(&err));
     67 }
     68 
     69 #endif /* _DEBUG */
     70 
     71 #define static_path_join(a, b) (a OS_PATH_SEPERATOR b)
     72 
     73 struct gl_debug_ctx {
     74 	Stream  stream;
     75 	OS     *os;
     76 };
     77 
     78 static 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->stderr, stream_to_s8(e));
     96 	stream_reset(e, 0);
     97 }
     98 
     99 static 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_s8(err, s8("Unknown GL Vendor: "));
    118 		stream_append_s8(err, c_str_to_s8(vendor));
    119 		stream_append_byte(err, '\n');
    120 		os_fatal(stream_to_s8(err));
    121 	}
    122 
    123 	#define X(glname, name, suffix) glGetIntegerv(GL_##glname, &gl->name);
    124 	GL_PARAMETERS
    125 	#undef X
    126 
    127 	/* NOTE(rnp): sometimes GL_MINOR_VERSION doesn't actually report the drivers
    128 	 * supported version. Since at this point GL has been fully loaded we can
    129 	 * check that at least one of the GL 4.5 function pointers are available */
    130 	if (gl->version_minor < 5 && glCreateBuffers)
    131 		gl->version_minor = 5;
    132 }
    133 
    134 static void
    135 validate_gl_requirements(GLParams *gl, Arena a)
    136 {
    137 	Stream s = arena_stream(&a);
    138 
    139 	if (gl->max_ubo_size < sizeof(BeamformerParameters)) {
    140 		stream_append_s8(&s, s8("GPU must support UBOs of at least "));
    141 		stream_append_i64(&s, sizeof(BeamformerParameters));
    142 		stream_append_s8(&s, s8(" bytes!\n"));
    143 	}
    144 
    145 	if (gl->version_major < 4 || (gl->version_major == 4 && gl->version_minor < 5))
    146 		stream_append_s8(&s, s8("Only OpenGL Versions 4.5 or newer are supported!\n"));
    147 
    148 	if (s.widx) os_fatal(stream_to_s8(&s));
    149 }
    150 
    151 static void
    152 dump_gl_params(GLParams *gl, Arena a, OS *os)
    153 {
    154 	(void)gl; (void)a;
    155 #ifdef _DEBUG
    156 	s8 vendor = s8("vendor:");
    157 	iz max_width = vendor.len;
    158 	#define X(glname, name, suffix) if (s8(#name).len > max_width) max_width = s8(#name ":").len;
    159 	GL_PARAMETERS
    160 	#undef X
    161 	max_width++;
    162 
    163 	Stream s = arena_stream(&a);
    164 	stream_append_s8(&s, s8("---- GL Parameters ----\n"));
    165 	stream_append_s8(&s, vendor);
    166 	stream_pad(&s, ' ', max_width - vendor.len);
    167 	switch (gl->vendor_id) {
    168 	case GL_VENDOR_AMD:    stream_append_s8(&s, s8("AMD\n"));    break;
    169 	case GL_VENDOR_ARM:    stream_append_s8(&s, s8("ARM\n"));    break;
    170 	case GL_VENDOR_INTEL:  stream_append_s8(&s, s8("Intel\n"));  break;
    171 	case GL_VENDOR_NVIDIA: stream_append_s8(&s, s8("nVidia\n")); break;
    172 	}
    173 
    174 	#define X(glname, name, suffix) \
    175 		stream_append_s8(&s, s8(#name ":"));                \
    176 		stream_pad(&s, ' ', max_width - s8(#name ":").len); \
    177 		stream_append_i64(&s, gl->name);                    \
    178 		stream_append_s8(&s, s8(suffix));                   \
    179 		stream_append_byte(&s, '\n');
    180 	GL_PARAMETERS
    181 	#undef X
    182 	stream_append_s8(&s, s8("-----------------------\n"));
    183 	os->write_file(os->stderr, stream_to_s8(&s));
    184 #endif
    185 }
    186 
    187 function FILE_WATCH_CALLBACK_FN(reload_render_shader)
    188 {
    189 	FrameViewRenderContext *ctx = (typeof(ctx))user_data;
    190 
    191 	local_persist s8 vertex = s8(""
    192 	"#version 460 core\n"
    193 	"\n"
    194 	"layout(location = 0) in vec2 vertex_position;\n"
    195 	"layout(location = 1) in vec2 vertex_texture_coordinate;\n"
    196 	"\n"
    197 	"layout(location = 0) out vec2 fragment_texture_coordinate;\n"
    198 	"\n"
    199 	"void main()\n"
    200 	"{\n"
    201 	"\tfragment_texture_coordinate = vertex_texture_coordinate;\n"
    202 	"\tgl_Position = vec4(vertex_position, 0, 1);\n"
    203 	"}\n");
    204 
    205 	Arena *a = &tmp;
    206 	s8 header = {.data = a->beg};
    207 	push_s8(a, s8("#version 460 core\n\n"));
    208 	push_s8(a, s8("layout(location = 0) in  vec2 fragment_texture_coordinate;\n"));
    209 	push_s8(a, s8("layout(location = 0) out vec4 v_out_colour;\n\n"));
    210 	push_s8(a, s8("layout(location = " str(FRAME_VIEW_RENDER_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n"));
    211 	push_s8(a, s8("layout(location = " str(FRAME_VIEW_RENDER_THRESHOLD_LOC)     ") uniform float u_threshold = 40;\n"));
    212 	push_s8(a, s8("layout(location = " str(FRAME_VIEW_RENDER_GAMMA_LOC)         ") uniform float u_gamma     = 1;\n"));
    213 	push_s8(a, s8("layout(location = " str(FRAME_VIEW_RENDER_LOG_SCALE_LOC)     ") uniform bool  u_log_scale;\n"));
    214 	push_s8(a, s8("\n#line 1\n"));
    215 	header.len = a->beg - header.data;
    216 
    217 	s8 fragment    = os->read_whole_file(a, (c8 *)path.data);
    218 	fragment.data -= header.len;
    219 	fragment.len  += header.len;
    220 	ASSERT(fragment.data == header.data);
    221 	u32 new_program = load_shader(os, tmp, 0, vertex, fragment, (s8){0}, path, s8("Render Shader"));
    222 	if (new_program) {
    223 		glDeleteProgram(ctx->shader);
    224 		ctx->shader  = new_program;
    225 		ctx->updated = 1;
    226 	}
    227 
    228 	return 1;
    229 }
    230 
    231 
    232 static FILE_WATCH_CALLBACK_FN(queue_compute_shader_reload)
    233 {
    234 	ComputeShaderReloadContext *csr = (typeof(csr))user_data;
    235 	BeamformerCtx *ctx = csr->beamformer_ctx;
    236 	BeamformWork *work = beamform_work_queue_push(ctx->beamform_work_queue);
    237 	if (work) {
    238 		work->type = BW_RELOAD_SHADER;
    239 		work->reload_shader_ctx = csr;
    240 		beamform_work_queue_push_commit(ctx->beamform_work_queue);
    241 		ctx->os.wake_waiters(&ctx->os.compute_worker.sync_variable);
    242 	}
    243 	return 1;
    244 }
    245 
    246 static FILE_WATCH_CALLBACK_FN(load_cuda_lib)
    247 {
    248 	CudaLib *cl = (CudaLib *)user_data;
    249 	b32 result  = os_file_exists((c8 *)path.data);
    250 	if (result) {
    251 		Stream err = arena_stream(&tmp);
    252 
    253 		stream_append_s8(&err, s8("loading CUDA lib: " OS_CUDA_LIB_NAME "\n"));
    254 		os_unload_library(cl->lib);
    255 		cl->lib = os_load_library((c8 *)path.data, OS_CUDA_LIB_TEMP_NAME, &err);
    256 		#define X(name) cl->name = os_lookup_dynamic_symbol(cl->lib, #name, &err);
    257 		CUDA_LIB_FNS
    258 		#undef X
    259 
    260 		os->write_file(os->stderr, stream_to_s8(&err));
    261 	}
    262 
    263 	#define X(name) if (!cl->name) cl->name = name ## _stub;
    264 	CUDA_LIB_FNS
    265 	#undef X
    266 
    267 	return result;
    268 }
    269 
    270 
    271 #define GLFW_VISIBLE 0x00020004
    272 void glfwWindowHint(i32, i32);
    273 iptr glfwCreateWindow(i32, i32, char *, iptr, iptr);
    274 void glfwMakeContextCurrent(iptr);
    275 
    276 static OS_THREAD_ENTRY_POINT_FN(compute_worker_thread_entry_point)
    277 {
    278 	GLWorkerThreadContext *ctx = (GLWorkerThreadContext *)_ctx;
    279 
    280 	glfwMakeContextCurrent(ctx->window_handle);
    281 	ctx->gl_context = os_get_native_gl_context(ctx->window_handle);
    282 
    283 	beamformer_compute_setup(ctx->user_context, ctx->arena, ctx->gl_context);
    284 
    285 	for (;;) {
    286 		for (;;) {
    287 			i32 current = atomic_load(&ctx->sync_variable);
    288 			if (current && atomic_swap(&ctx->sync_variable, 0) == current)
    289 				break;
    290 
    291 			ctx->asleep = 1;
    292 			os_wait_on_value(&ctx->sync_variable, current, -1);
    293 			ctx->asleep = 0;
    294 		}
    295 		beamformer_complete_compute(ctx->user_context, ctx->arena, ctx->gl_context);
    296 	}
    297 
    298 	unreachable();
    299 
    300 	return 0;
    301 }
    302 
    303 static void
    304 setup_beamformer(BeamformerCtx *ctx, Arena *memory)
    305 {
    306 	ctx->window_size  = (uv2){.w = 1280, .h = 840};
    307 
    308 	SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN);
    309 	InitWindow(ctx->window_size.w, ctx->window_size.h, "OGL Beamformer");
    310 	/* NOTE: do this after initing so that the window starts out floating in tiling wm */
    311 	SetWindowState(FLAG_WINDOW_RESIZABLE);
    312 	SetWindowMinSize(840, ctx->window_size.h);
    313 
    314 	/* NOTE: Gather information about the GPU */
    315 	get_gl_params(&ctx->gl, &ctx->error_stream);
    316 	dump_gl_params(&ctx->gl, *memory, &ctx->os);
    317 	validate_gl_requirements(&ctx->gl, *memory);
    318 
    319 	glfwWindowHint(GLFW_VISIBLE, 0);
    320 	iptr raylib_window_handle = (iptr)GetPlatformWindowHandle();
    321 	GLWorkerThreadContext *worker = &ctx->os.compute_worker;
    322 	worker->window_handle = glfwCreateWindow(320, 240, "", 0, raylib_window_handle);
    323 	worker->handle        = os_create_thread(*memory, (iptr)worker, s8("[compute]"),
    324 	                                         compute_worker_thread_entry_point);
    325 	/* TODO(rnp): we should lock this down after we have something working */
    326 	worker->user_context  = (iptr)ctx;
    327 
    328 	glfwMakeContextCurrent(raylib_window_handle);
    329 
    330 	ctx->beamform_work_queue = push_struct(memory, BeamformWorkQueue);
    331 
    332 	ctx->shared_memory = os_open_shared_memory_area(OS_SMEM_NAME, BEAMFORMER_SHARED_MEMORY_SIZE);
    333 	if (!ctx->shared_memory)
    334 		os_fatal(s8("Get more ram lol\n"));
    335 	/* TODO(rnp): refactor - this is annoying */
    336 	ctx->shared_memory->parameters_sync      = 1;
    337 	ctx->shared_memory->parameters_head_sync = 1;
    338 	ctx->shared_memory->parameters_ui_sync   = 1;
    339 	ctx->shared_memory->raw_data_sync        = 1;
    340 	ctx->shared_memory->channel_mapping_sync = 1;
    341 	ctx->shared_memory->sparse_elements_sync = 1;
    342 	ctx->shared_memory->focal_vectors_sync   = 1;
    343 
    344 	/* NOTE: default compute shader pipeline */
    345 	ctx->shared_memory->compute_stages[0]    = CS_DECODE;
    346 	ctx->shared_memory->compute_stages[1]    = CS_DAS;
    347 	ctx->shared_memory->compute_stages_count = 2;
    348 
    349 	if (ctx->gl.vendor_id == GL_VENDOR_NVIDIA
    350 	    && load_cuda_lib(&ctx->os, s8(OS_CUDA_LIB_NAME), (iptr)&ctx->cuda_lib, *memory))
    351 	{
    352 		os_add_file_watch(&ctx->os, memory, s8(OS_CUDA_LIB_NAME), load_cuda_lib,
    353 		                  (iptr)&ctx->cuda_lib);
    354 	} else {
    355 		#define X(name) if (!ctx->cuda_lib.name) ctx->cuda_lib.name = name ## _stub;
    356 		CUDA_LIB_FNS
    357 		#undef X
    358 	}
    359 
    360 	/* NOTE: set up OpenGL debug logging */
    361 	struct gl_debug_ctx *gl_debug_ctx = push_struct(memory, typeof(*gl_debug_ctx));
    362 	gl_debug_ctx->stream = stream_alloc(memory, 1024);
    363 	gl_debug_ctx->os     = &ctx->os;
    364 	glDebugMessageCallback(gl_debug_logger, gl_debug_ctx);
    365 #ifdef _DEBUG
    366 	glEnable(GL_DEBUG_OUTPUT);
    367 #endif
    368 
    369 	#define X(e, sn, f, nh, pretty_name) do if (s8(f).len > 0) {                 \
    370 		ComputeShaderReloadContext *csr = push_struct(memory, typeof(*csr)); \
    371 		csr->beamformer_ctx = ctx;                                           \
    372 		csr->label          = s8("CS_" #e);                                  \
    373 		csr->shader         = sn;                                            \
    374 		csr->needs_header   = nh;                                            \
    375 		csr->path           = s8(static_path_join("shaders", f ".glsl"));    \
    376 		os_add_file_watch(&ctx->os, memory, csr->path, queue_compute_shader_reload, (iptr)csr); \
    377 		queue_compute_shader_reload(&ctx->os, csr->path, (iptr)csr, *memory); \
    378 	} while (0);
    379 	COMPUTE_SHADERS
    380 	#undef X
    381 	os_wake_waiters(&worker->sync_variable);
    382 
    383 	FrameViewRenderContext *fvr = &ctx->frame_view_render_context;
    384 	glGenFramebuffers(1, &fvr->framebuffer);
    385 	LABEL_GL_OBJECT(GL_FRAMEBUFFER, fvr->framebuffer, s8("Frame View Render Framebuffer"));
    386 	f32 vertices[] = {
    387 		-1,  1, 0, 0,
    388 		-1, -1, 0, 1,
    389 		 1, -1, 1, 1,
    390 		-1,  1, 0, 0,
    391 		 1, -1, 1, 1,
    392 		 1,  1, 1, 0,
    393 	};
    394 	glGenVertexArrays(1, &fvr->vao);
    395 	glBindVertexArray(fvr->vao);
    396 	glGenBuffers(1, &fvr->vbo);
    397 	glBindBuffer(GL_ARRAY_BUFFER, fvr->vbo);
    398 	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    399 	glVertexAttribPointer(0, 2, GL_FLOAT, 0, 4 * sizeof(f32), 0);
    400 	glEnableVertexAttribArray(0);
    401 	glVertexAttribPointer(1, 2, GL_FLOAT, 0, 4 * sizeof(f32), (void *)(2 * sizeof(f32)));
    402 	glEnableVertexAttribArray(1);
    403 	glBindVertexArray(0);
    404 
    405 	s8 render = s8(static_path_join("shaders", "render.glsl"));
    406 	reload_render_shader(&ctx->os, render, (iptr)fvr, *memory);
    407 	os_add_file_watch(&ctx->os, memory, render, reload_render_shader, (iptr)fvr);
    408 }