ogl_beamforming

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

static.c (13671B)


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