ogl_beamforming

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

main_linux.c (11166B)


      1 /* See LICENSE for license details. */
      2 #ifndef BEAMFORMER_DEBUG
      3   #define BEAMFORMER_IMPORT static
      4   #define BEAMFORMER_EXPORT static
      5   #define BASE_EXPORT       static
      6 #endif
      7 
      8 #define BASE_IMPORT static
      9 
     10 #include "base_platform.h"
     11 
     12 #if !OS_LINUX
     13 #error This file is only meant to be compiled for Linux
     14 #endif
     15 
     16 #include "beamformer.c"
     17 #include "base_linux.c"
     18 
     19 #define OS_SHARED_MEMORY_SIZE  GB(2)
     20 
     21 #define OS_DEBUG_LIB_NAME      "./beamformer.so"
     22 #define OS_DEBUG_LIB_TEMP_NAME "./beamformer_temp.so"
     23 
     24 #define OS_CUDA_LIB_NAME       "./external/cuda_toolkit.so"
     25 #define OS_CUDA_LIB_TEMP_NAME  "./external/cuda_toolkit_temp.so"
     26 
     27 #define OS_RENDERDOC_SONAME    "librenderdoc.so"
     28 
     29 #define OS_VULKAN_SONAME_LIST \
     30 	X("libvulkan.so") \
     31 	X("libvulkan.so.1") \
     32 
     33 #include <dlfcn.h>
     34 
     35 global BeamformerInput beamformer_input_main;
     36 
     37 BEAMFORMER_IMPORT void
     38 os_console_log(u8 *data, i64 length)
     39 {
     40 	os_write_file(STDERR_FILENO, data, length);
     41 }
     42 
     43 BEAMFORMER_IMPORT void
     44 os_fatal(u8 *data, i64 length)
     45 {
     46 	os_write_file(STDERR_FILENO, data, length);
     47 	os_exit(1);
     48 	unreachable();
     49 }
     50 
     51 BEAMFORMER_IMPORT void
     52 os_release_handle(OSHandle h)
     53 {
     54 	if ValidHandle(h)
     55 		close(h.value[0]);
     56 }
     57 
     58 BEAMFORMER_IMPORT void *
     59 os_lookup_symbol(OSLibrary library, const char *symbol)
     60 {
     61 	void *result = 0;
     62 	if ValidHandle(library) result = dlsym((void *)library.value[0], symbol);
     63 	return result;
     64 }
     65 
     66 function void *
     67 allocate_shared_memory(char *name, i64 requested_capacity, u64 *capacity)
     68 {
     69 	u64 rounded_capacity = round_up_to(requested_capacity, ARCH_X64? KB(4) : os_linux_context.system_info.page_size);
     70 	void *result = 0;
     71 	i32 fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
     72 	if (fd > 0 && ftruncate(fd, rounded_capacity) != -1) {
     73 		void *new = mmap(0, rounded_capacity, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
     74 		if (new != MAP_FAILED) {
     75 			*capacity = rounded_capacity;
     76 			result    = new;
     77 		}
     78 	}
     79 	if (fd > 0) close(fd);
     80 	return result;
     81 }
     82 
     83 function OSLinuxFileWatchDirectory *
     84 os_lookup_file_watch_directory(u64 hash)
     85 {
     86 	OSLinuxFileWatchDirectory *result = 0;
     87 	for (OSLinuxFileWatchDirectory *fwd = os_linux_context.file_watch_directories.first; fwd; fwd = fwd->next) {
     88 		if (fwd->hash == hash) {
     89 			result = fwd;
     90 			break;
     91 		}
     92 	}
     93 	return result;
     94 }
     95 
     96 function void
     97 os_linux_add_file_watch(str8 path, void *user_context, OSLinuxFileWatchKind kind)
     98 {
     99 	str8 directory   = path;
    100 	directory.length = str8_scan_backwards(path, '/');
    101 	assert(directory.length > 0);
    102 
    103 	u64 hash = u64_hash_from_str8(directory);
    104 	OSLinuxFileWatchDirectory *dir = os_lookup_file_watch_directory(hash);
    105 	if (!dir) {
    106 		assert(path.data[directory.length] == '/');
    107 		OSLinuxEntity *fwd = os_entity_allocate(OSLinuxEntityKind_FileWatchDirectory);
    108 		dir = &fwd->as.file_watch_directory;
    109 		DLLInsert(0, os_linux_context.file_watch_directories.first,
    110 		          os_linux_context.file_watch_directories.last, dir, next, prev);
    111 
    112 		dir->hash   = hash;
    113 		dir->name   = push_str8(os_linux_context.arena, directory);
    114 		u32 mask    = IN_MOVED_TO|IN_CLOSE_WRITE;
    115 		dir->handle = inotify_add_watch(os_linux_context.inotify_handle, (c8 *)dir->name.data, mask);
    116 	}
    117 
    118 	OSLinuxEntity    *fwe = os_entity_allocate(OSLinuxEntityKind_FileWatch);
    119 	OSLinuxFileWatch *fw  = &fwe->as.file_watch;
    120 	DLLInsert(0, dir->first_child, dir->last_child, fw, next, prev);
    121 	fw->user_context = user_context;
    122 	fw->hash         = u64_hash_from_str8(str8_cut_head(path, dir->name.length + 1));
    123 	fw->kind         = kind;
    124 	fw->parent       = dir;
    125 }
    126 
    127 BEAMFORMER_IMPORT void
    128 os_add_file_watch(const char *path, int64_t path_length, void *user_context)
    129 {
    130 	str8 path_str = {.data = (u8 *)path, .length = path_length};
    131 	os_linux_add_file_watch(path_str, user_context, OSLinuxFileWatchKind_User);
    132 }
    133 
    134 function void
    135 os_window_resize_callback(void *window, i32 width, i32 height)
    136 {
    137 	OSWindow event_window = {0};
    138 	for (OSLinuxEntity *we = os_linux_context.windows.first; we; we = we->as.window.next) {
    139 		if (we->as.window.handle == window) {
    140 			event_window.value[0] = (u64)we;
    141 			break;
    142 		}
    143 	}
    144 
    145 	os_push_input_event(beamformer_input, (BeamformerInputEvent){
    146 		.kind      = BeamformerInputEventKind_WindowResize,
    147 		.window_resize = {
    148 			.width = (u32)width, .height = (u32)height,
    149 			.window = event_window,
    150 		},
    151 	});
    152 
    153 	raylib_window_resize(window, width, height);
    154 }
    155 
    156 BEAMFORMER_IMPORT OSWindow
    157 os_window_create(u8 *title, i64 title_length, i32 width, i32 height)
    158 {
    159 	OSLinuxEntity *we = os_entity_allocate(OSLinuxEntityKind_Window);
    160 	OSWindow result = {(u64)we};
    161 	DLLInsert(0, os_linux_context.windows.first, os_linux_context.windows.last, we, as.window.next, as.window.prev);
    162 
    163 	SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN);
    164 
    165 	str8 name = {.data = title, .length = title_length};
    166 	Temp scratch;
    167 	DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock))
    168 	DeferLoop(scratch = temp_begin(os_linux_context.arena), temp_end(scratch))
    169 	{
    170 		str8 title_string = push_str8(scratch.arena, name);
    171 		InitWindow(width, height, (char *)title_string.data);
    172 	}
    173 
    174 	we->as.window.handle = GetPlatformWindowHandle();
    175 	os_window_equip_common(&beamformer_input_main, we->as.window.handle);
    176 	raylib_window_resize = glfwSetWindowSizeCallback(we->as.window.handle, os_window_resize_callback);
    177 
    178 	/* NOTE: do this after initing so that the window starts out floating in tiling wm */
    179 	SetWindowState(FLAG_WINDOW_RESIZABLE);
    180 	SetWindowMinSize(320, 240);
    181 
    182 	return result;
    183 }
    184 
    185 BEAMFORMER_IMPORT u8 *
    186 os_get_clipboard_text(i64 *length)
    187 {
    188 	str8 result = str8_from_c_str((char *)GetClipboardText());
    189 	if (length) *length = result.length;
    190 	return result.data;
    191 }
    192 
    193 BEAMFORMER_IMPORT void
    194 os_set_clipboard_text(u8 *data, i64 length)
    195 {
    196 	Temp scratch;
    197 	DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock))
    198 	DeferLoop(scratch = temp_begin(os_linux_context.arena), temp_end(scratch))
    199 	{
    200 		str8 string = push_str8(scratch.arena, (str8){.data = data, .length = length});
    201 		SetClipboardText((char *)string.data);
    202 	}
    203 }
    204 
    205 function OSLibrary
    206 load_library(char *name, char *temp_name, u32 flags)
    207 {
    208 	if (temp_name && os_copy_file(name, temp_name))
    209 		name = temp_name;
    210 
    211 	OSLibrary result = {(u64)dlopen(name, flags)};
    212 	if (result.value[0] == 0) result.value[0] = OSInvalidHandleValue;
    213 
    214 	if (temp_name) unlink(temp_name);
    215 
    216 	return result;
    217 }
    218 
    219 #if BEAMFORMER_DEBUG
    220 function void
    221 debug_library_reload(void *beamformer)
    222 {
    223 	local_persist OSLibrary beamformer_library_handle = {OSInvalidHandleValue};
    224 
    225 	if ValidHandle(beamformer_library_handle) {
    226 		beamformer_debug_hot_release(beamformer, &beamformer_input_main);
    227 		dlclose((void *)beamformer_library_handle.value[0]);
    228 		beamformer_library_handle = (OSLibrary){OSInvalidHandleValue};
    229 	}
    230 
    231 	OSLibrary new_handle = load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME, RTLD_NOW|RTLD_LOCAL);
    232 	if (InvalidHandle(beamformer_library_handle) && InvalidHandle(new_handle))
    233 		fatal(str8("[os] failed to load: " OS_DEBUG_LIB_NAME "\n"));
    234 
    235 	if ValidHandle(new_handle) {
    236 		beamformer_debug_hot_reload(new_handle);
    237 		beamformer_library_handle = new_handle;
    238 	}
    239 }
    240 #else
    241 #define debug_library_reload(a) (void)(a)
    242 #endif /* BEAMFORMER_DEBUG */
    243 
    244 function void
    245 load_platform_libraries(void)
    246 {
    247 	#if BEAMFORMER_DEBUG
    248 		debug_library_reload(0);
    249 		os_linux_add_file_watch(str8(OS_DEBUG_LIB_NAME), (void *)BeamformerInputEventKind_ExecutableReload,
    250 		                        OSLinuxFileWatchKind_Platform);
    251 	#endif
    252 
    253 	BeamformerInput *input = &beamformer_input_main;
    254 	input->vulkan_library_handle = (OSLibrary){OSInvalidHandleValue};
    255 	#define X(name) \
    256 		if InvalidHandle(input->vulkan_library_handle) \
    257 			input->vulkan_library_handle = load_library(name, 0, RTLD_NOW|RTLD_LOCAL);
    258 	OS_VULKAN_SONAME_LIST
    259 	#undef X
    260 
    261 	if InvalidHandle(input->vulkan_library_handle)
    262 		fatal(str8("[os] fatal error: failed to find valid vulkan library\n"));
    263 
    264 	input->cuda_library_handle = load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME, RTLD_NOW|RTLD_LOCAL);
    265 
    266 	#if BEAMFORMER_RENDERDOC_HOOKS
    267 	local_persist OSLibrary renderdoc_handle = {OSInvalidHandleValue};
    268 	renderdoc_handle = load_library(OS_RENDERDOC_SONAME, 0, RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD);
    269 	load_renderdoc_functions(input, renderdoc_handle);
    270 	#endif
    271 }
    272 
    273 function void
    274 dispatch_file_watch_events(void *beamformer)
    275 {
    276 	local_persist alignas(16) u8 mem[KB(4)];
    277 	struct inotify_event *event;
    278 
    279 	u64 current_time = os_timer_count();
    280 
    281 	i64 rlen;
    282 	while ((rlen = read(os_linux_context.inotify_handle, mem, countof(mem))) > 0) {
    283 		for (u8 *data = mem; data < mem + rlen; data += sizeof(*event) + event->len) {
    284 			event = (struct inotify_event *)data;
    285 			for (OSLinuxFileWatchDirectory *dir = os_linux_context.file_watch_directories.first; dir; dir = dir->next) {
    286 				if (event->wd != dir->handle)
    287 					continue;
    288 
    289 				str8 file = str8_from_c_str(event->name);
    290 				u64  hash = u64_hash_from_str8(file);
    291 				for (OSLinuxFileWatch *fw = dir->first_child; fw; fw = fw->next) if (fw->hash == hash) {
    292 					// NOTE(rnp): avoid multiple updates in a single frame
    293 					if (fw->update_time < current_time) {
    294 						BeamformerInputEvent input_event = {0};
    295 						if (fw->kind == OSLinuxFileWatchKind_Platform) {
    296 							assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload);
    297 							if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload)
    298 								debug_library_reload(beamformer);
    299 							input_event.kind = (u64)fw->user_context;
    300 						} else {
    301 							input_event.kind = BeamformerInputEventKind_FileEvent;
    302 							input_event.file_watch_user_context = fw->user_context;
    303 						}
    304 						os_push_input_event(&beamformer_input_main, input_event);
    305 					}
    306 					fw->update_time = current_time;
    307 					break;
    308 				}
    309 			}
    310 		}
    311 	}
    312 }
    313 
    314 BASE_IMPORT void
    315 entry_point(i32 argc, char *argv[])
    316 {
    317 	os_linux_context.inotify_handle = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
    318 
    319 	BeamformerInput *input = &beamformer_input_main;
    320 	input->shared_memory   = allocate_shared_memory(OS_SHARED_MEMORY_NAME, OS_SHARED_MEMORY_SIZE,
    321 	                                                &input->shared_memory_size);
    322 	if (input->shared_memory) {
    323 		input->shared_memory_name        = str8(OS_SHARED_MEMORY_NAME).data;
    324 		input->shared_memory_name_length = str8(OS_SHARED_MEMORY_NAME).length;
    325 	}
    326 
    327 	os_push_input_event(input, (BeamformerInputEvent){
    328 		.kind = BeamformerInputEventKind_ExecutableReload,
    329 	});
    330 
    331 	load_platform_libraries();
    332 
    333 	void *beamformer = beamformer_init(input);
    334 
    335 	struct pollfd fds[1] = {{0}};
    336 	fds[0].fd     = os_linux_context.inotify_handle;
    337 	fds[0].events = POLLIN;
    338 
    339 	while (!WindowShouldClose() && !beamformer_should_close(beamformer, input)) {
    340 		os_build_frame_input(input);
    341 
    342 		poll(fds, countof(fds), 0);
    343 		if (fds[0].revents & POLLIN)
    344 			dispatch_file_watch_events(beamformer);
    345 
    346 		beamformer_frame_step(beamformer, input);
    347 
    348 		// NOTE(rnp): this must happen at the end of frame to allow the pre loop events through
    349 		// TODO(rnp): hack: until raylib is removed this happens in ui since raylib will cause
    350 		// glfw to call the input callbacks in during EndDrawing()
    351 		//input->event_count = 0;
    352 	}
    353 
    354 	beamformer_terminate(beamformer, input);
    355 
    356 	/* NOTE: make sure this will get cleaned up after external
    357 	 * programs release their references */
    358 	shm_unlink(OS_SHARED_MEMORY_NAME);
    359 }