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 (13978B)


      1 /* See LICENSE for license details. */
      2 #include "compiler.h"
      3 
      4 #if !OS_LINUX
      5 #error This file is only meant to be compiled for Linux
      6 #endif
      7 
      8 #ifndef BEAMFORMER_DEBUG
      9   #define BEAMFORMER_IMPORT static
     10   #define BEAMFORMER_EXPORT static
     11 #endif
     12 
     13 #include "beamformer.c"
     14 #include "os_linux.c"
     15 
     16 #define OS_SHARED_MEMORY_SIZE  GB(2)
     17 
     18 #define OS_DEBUG_LIB_NAME      "./beamformer.so"
     19 #define OS_DEBUG_LIB_TEMP_NAME "./beamformer_temp.so"
     20 
     21 #define OS_CUDA_LIB_NAME       "./external/cuda_toolkit.so"
     22 #define OS_CUDA_LIB_TEMP_NAME  "./external/cuda_toolkit_temp.so"
     23 
     24 #define OS_RENDERDOC_SONAME    "librenderdoc.so"
     25 
     26 #define OS_VULKAN_SONAME_LIST \
     27 	X("libvulkan.so") \
     28 	X("libvulkan.so.1") \
     29 
     30 #include <dlfcn.h>
     31 
     32 typedef struct OSLinuxEntity OSLinuxEntity;
     33 typedef struct {
     34 	void          *handle;
     35 	OSLinuxEntity *prev, *next;
     36 } OSLinuxWindow;
     37 
     38 typedef enum {
     39 	OSLinuxFileWatchKind_Platform,
     40 	OSLinuxFileWatchKind_User,
     41 } OSLinuxFileWatchKind;
     42 
     43 typedef struct OSLinuxFileWatchDirectory OSLinuxFileWatchDirectory;
     44 typedef struct OSLinuxFileWatch OSLinuxFileWatch;
     45 struct OSLinuxFileWatch {
     46 	OSLinuxFileWatchKind  kind;
     47 	u64                   hash;
     48 	u64                   update_time;
     49 	void                 *user_context;
     50 
     51 	OSLinuxFileWatchDirectory *parent;
     52 	OSLinuxFileWatch *prev, *next;
     53 };
     54 
     55 struct OSLinuxFileWatchDirectory {
     56 	u64  hash;
     57 	i64  handle;
     58 	str8 name;
     59 
     60 	OSLinuxFileWatch *first_child;
     61 	OSLinuxFileWatch *last_child;
     62 	OSLinuxFileWatchDirectory *prev, *next;
     63 };
     64 
     65 typedef enum {
     66 	OSLinuxEntityKind_Window,
     67 	OSLinuxEntityKind_FileWatch,
     68 	OSLinuxEntityKind_FileWatchDirectory,
     69 } OSLinuxEntityKind;
     70 
     71 struct OSLinuxEntity {
     72 	OSLinuxEntityKind kind;
     73 	union {
     74 		OSLinuxFileWatch          file_watch;
     75 		OSLinuxFileWatchDirectory file_watch_directory;
     76 		OSLinuxWindow             window;
     77 	} as;
     78 	OSLinuxEntity *next;
     79 };
     80 
     81 typedef struct {
     82 	Arena         arena;
     83 	i32           arena_lock;
     84 
     85 	i32           inotify_handle;
     86 
     87 	BeamformerInput *input;
     88 
     89 	OSSystemInfo system_info;
     90 
     91 	struct {
     92 		OSLinuxFileWatchDirectory *first;
     93 		OSLinuxFileWatchDirectory *last;
     94 	} file_watch_directories;
     95 
     96 	struct {
     97 		OSLinuxEntity *first;
     98 		OSLinuxEntity *last;
     99 	} windows;
    100 
    101 	OSLinuxEntity *entity_freelist;
    102 } OSLinux_Context;
    103 global OSLinux_Context os_linux_context;
    104 
    105 function OSLinuxEntity *
    106 os_entity_allocate(OSLinuxEntityKind kind)
    107 {
    108 	OSLinuxEntity *result = 0;
    109 	DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock))
    110 	{
    111 		result = SLLPopFreelist(os_linux_context.entity_freelist);
    112 		if (!result) result = push_struct_no_zero(&os_linux_context.arena, OSLinuxEntity);
    113 	}
    114 
    115 	zero_struct(result);
    116 	result->kind = kind;
    117 	return result;
    118 }
    119 
    120 BEAMFORMER_IMPORT OSSystemInfo *
    121 os_system_info(void)
    122 {
    123 	return &os_linux_context.system_info;
    124 }
    125 
    126 BEAMFORMER_IMPORT OSThread
    127 os_create_thread(const char *name, void *user_context, os_thread_entry_point_fn *fn)
    128 {
    129 	pthread_t thread;
    130 	pthread_create(&thread, 0, (void *)fn, (void *)user_context);
    131 
    132 	if (name) {
    133 		char buffer[16];
    134 		s8 name_str = c_str_to_s8((char *)name);
    135 		u64  length = (u64)CLAMP(name_str.len, 0, countof(buffer) - 1);
    136 		mem_copy(buffer, (char *)name, length);
    137 		buffer[length] = 0;
    138 		pthread_setname_np(thread, buffer);
    139 	}
    140 
    141 	OSThread result = {(u64)thread};
    142 	return result;
    143 }
    144 
    145 BEAMFORMER_IMPORT OSBarrier
    146 os_barrier_alloc(u32 count)
    147 {
    148 	OSBarrier result = {0};
    149 	DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock))
    150 	{
    151 		pthread_barrier_t *barrier = push_struct(&os_linux_context.arena, pthread_barrier_t);
    152 		pthread_barrier_init(barrier, 0, count);
    153 		result.value[0] = (u64)barrier;
    154 	}
    155 	return result;
    156 }
    157 
    158 BEAMFORMER_IMPORT void
    159 os_barrier_enter(OSBarrier barrier)
    160 {
    161 	pthread_barrier_t *b = (pthread_barrier_t *)barrier.value[0];
    162 	if (b) pthread_barrier_wait(b);
    163 }
    164 
    165 BEAMFORMER_IMPORT void
    166 os_console_log(u8 *data, i64 length)
    167 {
    168 	os_write_file(STDERR_FILENO, data, length);
    169 }
    170 
    171 BEAMFORMER_IMPORT void
    172 os_fatal(u8 *data, i64 length)
    173 {
    174 	os_write_file(STDERR_FILENO, data, length);
    175 	os_exit(1);
    176 	unreachable();
    177 }
    178 
    179 BEAMFORMER_IMPORT void *
    180 os_lookup_symbol(OSLibrary library, const char *symbol)
    181 {
    182 	void *result = 0;
    183 	if ValidHandle(library) result = dlsym((void *)library.value[0], symbol);
    184 	return result;
    185 }
    186 
    187 function void *
    188 allocate_shared_memory(char *name, iz requested_capacity, u64 *capacity)
    189 {
    190 	u64 rounded_capacity = round_up_to(requested_capacity, ARCH_X64? KB(4) : os_linux_context.system_info.page_size);
    191 	void *result = 0;
    192 	i32 fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
    193 	if (fd > 0 && ftruncate(fd, rounded_capacity) != -1) {
    194 		void *new = mmap(0, rounded_capacity, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    195 		if (new != MAP_FAILED) {
    196 			*capacity = rounded_capacity;
    197 			result    = new;
    198 		}
    199 	}
    200 	if (fd > 0) close(fd);
    201 	return result;
    202 }
    203 
    204 function OSLinuxFileWatchDirectory *
    205 os_lookup_file_watch_directory(u64 hash)
    206 {
    207 	OSLinuxFileWatchDirectory *result = 0;
    208 	for (OSLinuxFileWatchDirectory *fwd = os_linux_context.file_watch_directories.first; fwd; fwd = fwd->next) {
    209 		if (fwd->hash == hash) {
    210 			result = fwd;
    211 			break;
    212 		}
    213 	}
    214 	return result;
    215 }
    216 
    217 function void
    218 os_linux_add_file_watch(str8 path, void *user_context, OSLinuxFileWatchKind kind)
    219 {
    220 	str8 directory   = path;
    221 	directory.length = str8_scan_backwards(path, '/');
    222 	assert(directory.length > 0);
    223 
    224 	u64 hash = u64_hash_from_str8(directory);
    225 	OSLinuxFileWatchDirectory *dir = os_lookup_file_watch_directory(hash);
    226 	if (!dir) {
    227 		assert(path.data[directory.length] == '/');
    228 		OSLinuxEntity *fwd = os_entity_allocate(OSLinuxEntityKind_FileWatchDirectory);
    229 		dir = &fwd->as.file_watch_directory;
    230 		DLLInsert(0, os_linux_context.file_watch_directories.first,
    231 		          os_linux_context.file_watch_directories.last, dir, next, prev);
    232 
    233 		dir->hash   = hash;
    234 		dir->name   = push_str8(&os_linux_context.arena, directory);
    235 		u32 mask    = IN_MOVED_TO|IN_CLOSE_WRITE;
    236 		dir->handle = inotify_add_watch(os_linux_context.inotify_handle, (c8 *)dir->name.data, mask);
    237 	}
    238 
    239 	OSLinuxEntity    *fwe = os_entity_allocate(OSLinuxEntityKind_FileWatch);
    240 	OSLinuxFileWatch *fw  = &fwe->as.file_watch;
    241 	DLLInsert(0, dir->first_child, dir->last_child, fw, next, prev);
    242 	fw->user_context = user_context;
    243 	fw->hash         = u64_hash_from_str8(str8_cut_head(path, dir->name.length + 1));
    244 	fw->kind         = kind;
    245 	fw->parent       = dir;
    246 }
    247 
    248 BEAMFORMER_IMPORT void
    249 os_add_file_watch(const char *path, int64_t path_length, void *user_context)
    250 {
    251 	str8 path_str = {.data = (u8 *)path, .length = path_length};
    252 	os_linux_add_file_watch(path_str, user_context, OSLinuxFileWatchKind_User);
    253 }
    254 
    255 function void
    256 os_window_resize_callback(void *window, i32 width, i32 height)
    257 {
    258 	OSWindow event_window = {0};
    259 	for (OSLinuxEntity *we = os_linux_context.windows.first; we; we = we->as.window.next) {
    260 		if (we->as.window.handle == window) {
    261 			event_window.value[0] = (u64)we;
    262 			break;
    263 		}
    264 	}
    265 
    266 	os_push_input_event(beamformer_input, (BeamformerInputEvent){
    267 		.kind      = BeamformerInputEventKind_WindowResize,
    268 		.window_resize = {
    269 			.width = (u32)width, .height = (u32)height,
    270 			.window = event_window,
    271 		},
    272 	});
    273 
    274 	raylib_window_resize(window, width, height);
    275 }
    276 
    277 BEAMFORMER_IMPORT OSWindow
    278 os_window_create(u8 *title, i64 title_length, i32 width, i32 height)
    279 {
    280 	OSLinuxEntity *we = os_entity_allocate(OSLinuxEntityKind_Window);
    281 	OSWindow result = {(u64)we};
    282 	DLLInsert(0, os_linux_context.windows.first, os_linux_context.windows.last, we, as.window.next, as.window.prev);
    283 
    284 	SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN);
    285 
    286 	str8 name = {.data = title, .length = title_length};
    287 	DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock))
    288 	{
    289 		Arena scratch = os_linux_context.arena;
    290 		name.length = Min(name.length, arena_capacity(&scratch, u8) - 1);
    291 		str8 title_string = push_str8(&scratch, name);
    292 		InitWindow(width, height, (char *)title_string.data);
    293 	}
    294 
    295 	we->as.window.handle = GetPlatformWindowHandle();
    296 	os_window_equip_common(os_linux_context.input, we->as.window.handle);
    297 	raylib_window_resize = glfwSetWindowSizeCallback(we->as.window.handle, os_window_resize_callback);
    298 
    299 	/* NOTE: do this after initing so that the window starts out floating in tiling wm */
    300 	SetWindowState(FLAG_WINDOW_RESIZABLE);
    301 	SetWindowMinSize(320, 240);
    302 
    303 	return result;
    304 }
    305 
    306 function OSLibrary
    307 load_library(char *name, char *temp_name, u32 flags)
    308 {
    309 	if (temp_name && os_copy_file(name, temp_name))
    310 		name = temp_name;
    311 
    312 	OSLibrary result = {(u64)dlopen(name, flags)};
    313 	if (result.value[0] == 0) result.value[0] = OSInvalidHandleValue;
    314 
    315 	if (temp_name) unlink(temp_name);
    316 
    317 	return result;
    318 }
    319 
    320 #if BEAMFORMER_DEBUG
    321 function void
    322 debug_library_reload(BeamformerInput *input)
    323 {
    324 	local_persist OSLibrary beamformer_library_handle = {OSInvalidHandleValue};
    325 
    326 	if ValidHandle(beamformer_library_handle) {
    327 		beamformer_debug_hot_release(input);
    328 		dlclose((void *)beamformer_library_handle.value[0]);
    329 		beamformer_library_handle = (OSLibrary){OSInvalidHandleValue};
    330 	}
    331 
    332 	OSLibrary new_handle = load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME, RTLD_NOW|RTLD_LOCAL);
    333 	if (InvalidHandle(beamformer_library_handle) && InvalidHandle(new_handle))
    334 		fatal(s8("[os] failed to load: " OS_DEBUG_LIB_NAME "\n"));
    335 
    336 	if ValidHandle(new_handle) {
    337 		beamformer_debug_hot_reload(new_handle, input);
    338 		beamformer_library_handle = new_handle;
    339 	}
    340 }
    341 #else
    342 #define debug_library_reload(a) (void)(a)
    343 #endif /* BEAMFORMER_DEBUG */
    344 
    345 function void
    346 load_platform_libraries(BeamformerInput *input)
    347 {
    348 	#if BEAMFORMER_DEBUG
    349 		debug_library_reload(input);
    350 		os_linux_add_file_watch(str8(OS_DEBUG_LIB_NAME), (void *)BeamformerInputEventKind_ExecutableReload,
    351 		                        OSLinuxFileWatchKind_Platform);
    352 	#endif
    353 
    354 	input->vulkan_library_handle = (OSLibrary){OSInvalidHandleValue};
    355 	#define X(name) \
    356 		if InvalidHandle(input->vulkan_library_handle) \
    357 			input->vulkan_library_handle = load_library(name, 0, RTLD_NOW|RTLD_LOCAL);
    358 	OS_VULKAN_SONAME_LIST
    359 	#undef X
    360 
    361 	if InvalidHandle(input->vulkan_library_handle)
    362 		fatal(s8("[os] fatal error: failed to find valid vulkan library\n"));
    363 
    364 	input->cuda_library_handle = load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME, RTLD_NOW|RTLD_LOCAL);
    365 
    366 	#if BEAMFORMER_RENDERDOC_HOOKS
    367 	local_persist OSLibrary renderdoc_handle = {OSInvalidHandleValue};
    368 	renderdoc_handle = load_library(OS_RENDERDOC_SONAME, 0, RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD);
    369 	load_renderdoc_functions(input, renderdoc_handle);
    370 	#endif
    371 }
    372 
    373 function void
    374 dispatch_file_watch_events(BeamformerInput *input)
    375 {
    376 	Arena arena = os_linux_context.arena;
    377 	u8 *mem     = arena_alloc(&arena, .size = 4096, .align = 16);
    378 	struct inotify_event *event;
    379 
    380 	u64 current_time = os_timer_count();
    381 
    382 	i64 rlen;
    383 	while ((rlen = read(os_linux_context.inotify_handle, mem, 4096)) > 0) {
    384 		for (u8 *data = mem; data < mem + rlen; data += sizeof(*event) + event->len) {
    385 			event = (struct inotify_event *)data;
    386 			for (OSLinuxFileWatchDirectory *dir = os_linux_context.file_watch_directories.first; dir; dir = dir->next) {
    387 				if (event->wd != dir->handle)
    388 					continue;
    389 
    390 				str8 file = str8_from_c_str(event->name);
    391 				u64  hash = u64_hash_from_str8(file);
    392 				for (OSLinuxFileWatch *fw = dir->first_child; fw; fw = fw->next) if (fw->hash == hash) {
    393 					// NOTE(rnp): avoid multiple updates in a single frame
    394 					if (fw->update_time < current_time) {
    395 						BeamformerInputEvent input_event = {0};
    396 						if (fw->kind == OSLinuxFileWatchKind_Platform) {
    397 							assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload);
    398 							if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload)
    399 								debug_library_reload(input);
    400 							input_event.kind = (u64)fw->user_context;
    401 						} else {
    402 							input_event.kind = BeamformerInputEventKind_FileEvent;
    403 							input_event.file_watch_user_context = fw->user_context;
    404 						}
    405 						os_push_input_event(input, input_event);
    406 					}
    407 					fw->update_time = current_time;
    408 					break;
    409 				}
    410 			}
    411 		}
    412 	}
    413 }
    414 
    415 extern i32
    416 main(void)
    417 {
    418 	os_linux_context.system_info.timer_frequency         = os_timer_frequency();
    419 	os_linux_context.system_info.logical_processor_count = os_number_of_processors();
    420 	os_linux_context.system_info.page_size               = ARCH_X64? KB(4) : getauxval(AT_PAGESZ);
    421 	os_linux_context.system_info.path_separator_byte     = '/';
    422 
    423 	Arena program_memory = os_alloc_arena(MB(16) + MB(1));
    424 
    425 	os_linux_context.arena = sub_arena(&program_memory, MB(1), KB(4));
    426 	os_linux_context.inotify_handle = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
    427 
    428 	BeamformerInput *input = push_struct(&program_memory, BeamformerInput);
    429 	os_linux_context.input = input;
    430 	input->memory          = program_memory.beg;
    431 	input->memory_size     = program_memory.end - program_memory.beg;
    432 	input->shared_memory   = allocate_shared_memory(OS_SHARED_MEMORY_NAME, OS_SHARED_MEMORY_SIZE,
    433 	                                                &input->shared_memory_size);
    434 	if (input->shared_memory) {
    435 		input->shared_memory_name        = s8(OS_SHARED_MEMORY_NAME).data;
    436 		input->shared_memory_name_length = s8(OS_SHARED_MEMORY_NAME).len;
    437 	}
    438 
    439 	os_push_input_event(input, (BeamformerInputEvent){
    440 		.kind = BeamformerInputEventKind_ExecutableReload,
    441 	});
    442 
    443 	load_platform_libraries(input);
    444 
    445 	beamformer_init(input);
    446 
    447 	struct pollfd fds[1] = {{0}};
    448 	fds[0].fd     = os_linux_context.inotify_handle;
    449 	fds[0].events = POLLIN;
    450 
    451 	while (!WindowShouldClose() && !beamformer_should_close(input)) {
    452 		os_build_frame_input(input);
    453 
    454 		poll(fds, countof(fds), 0);
    455 		if (fds[0].revents & POLLIN)
    456 			dispatch_file_watch_events(input);
    457 
    458 		beamformer_frame_step(input);
    459 
    460 		// NOTE(rnp): this must happen at the end of frame to allow the pre loop events through
    461 		// TODO(rnp): hack: until raylib is removed this happens in ui since raylib will cause
    462 		// glfw to call the input callbacks in during EndDrawing()
    463 		//input->event_count = 0;
    464 	}
    465 
    466 	beamformer_terminate(input);
    467 
    468 	/* NOTE: make sure this will get cleaned up after external
    469 	 * programs release their references */
    470 	shm_unlink(OS_SHARED_MEMORY_NAME);
    471 }