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


      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_DEBUG_LIB_NAME      "./beamformer.so"
     17 #define OS_DEBUG_LIB_TEMP_NAME "./beamformer_temp.so"
     18 
     19 #define OS_CUDA_LIB_NAME       "./external/cuda_toolkit.so"
     20 #define OS_CUDA_LIB_TEMP_NAME  "./external/cuda_toolkit_temp.so"
     21 
     22 #define OS_RENDERDOC_SONAME    "librenderdoc.so"
     23 
     24 #include <dlfcn.h>
     25 
     26 typedef enum {
     27 	OSLinux_FileWatchKindPlatform,
     28 	OSLinux_FileWatchKindUser,
     29 } OSLinux_FileWatchKind;
     30 
     31 typedef struct {
     32 	OSLinux_FileWatchKind kind;
     33 	u64                   hash;
     34 	u64                   update_time;
     35 	void *                user_context;
     36 } OSLinux_FileWatch;
     37 
     38 typedef struct {
     39 	u64  hash;
     40 	iptr handle;
     41 	s8   name;
     42 
     43 	OSLinux_FileWatch * data;
     44 	iz                  count;
     45 	iz                  capacity;
     46 } OSLinux_FileWatchDirectory;
     47 DA_STRUCT(OSLinux_FileWatchDirectory, OSLinux_FileWatchDirectory);
     48 
     49 typedef struct {
     50 	Arena         arena;
     51 	i32           arena_lock;
     52 
     53 	i32           inotify_handle;
     54 
     55 	OSLinux_FileWatchDirectoryList file_watch_list;
     56 
     57 	OSSystemInfo system_info;
     58 } OSLinux_Context;
     59 global OSLinux_Context os_linux_context;
     60 
     61 BEAMFORMER_IMPORT OSSystemInfo *
     62 os_system_info(void)
     63 {
     64 	return &os_linux_context.system_info;
     65 }
     66 
     67 BEAMFORMER_IMPORT OSThread
     68 os_create_thread(const char *name, void *user_context, os_thread_entry_point_fn *fn)
     69 {
     70 	pthread_t thread;
     71 	pthread_create(&thread, 0, (void *)fn, (void *)user_context);
     72 
     73 	if (name) {
     74 		char buffer[16];
     75 		s8 name_str = c_str_to_s8((char *)name);
     76 		u64  length = (u64)CLAMP(name_str.len, 0, countof(buffer) - 1);
     77 		mem_copy(buffer, (char *)name, length);
     78 		buffer[length] = 0;
     79 		pthread_setname_np(thread, buffer);
     80 	}
     81 
     82 	OSThread result = {(u64)thread};
     83 	return result;
     84 }
     85 
     86 BEAMFORMER_IMPORT OSBarrier
     87 os_barrier_alloc(u32 count)
     88 {
     89 	OSBarrier result = {0};
     90 	DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock))
     91 	{
     92 		pthread_barrier_t *barrier = push_struct(&os_linux_context.arena, pthread_barrier_t);
     93 		pthread_barrier_init(barrier, 0, count);
     94 		result.value[0] = (u64)barrier;
     95 	}
     96 	return result;
     97 }
     98 
     99 BEAMFORMER_IMPORT void
    100 os_barrier_enter(OSBarrier barrier)
    101 {
    102 	pthread_barrier_t *b = (pthread_barrier_t *)barrier.value[0];
    103 	if (b) pthread_barrier_wait(b);
    104 }
    105 
    106 BEAMFORMER_IMPORT void
    107 os_console_log(u8 *data, i64 length)
    108 {
    109 	os_write_file(STDERR_FILENO, data, length);
    110 }
    111 
    112 BEAMFORMER_IMPORT void
    113 os_fatal(u8 *data, i64 length)
    114 {
    115 	os_write_file(STDERR_FILENO, data, length);
    116 	os_exit(1);
    117 	unreachable();
    118 }
    119 
    120 BEAMFORMER_IMPORT void *
    121 os_lookup_symbol(OSLibrary library, const char *symbol)
    122 {
    123 	void *result = 0;
    124 	if ValidHandle(library) result = dlsym((void *)library.value[0], symbol);
    125 	return result;
    126 }
    127 
    128 function void *
    129 allocate_shared_memory(char *name, iz requested_capacity, u64 *capacity)
    130 {
    131 	u64 rounded_capacity = round_up_to(requested_capacity, ARCH_X64? KB(4) : os_linux_context.system_info.page_size);
    132 	void *result = 0;
    133 	i32 fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
    134 	if (fd > 0 && ftruncate(fd, rounded_capacity) != -1) {
    135 		void *new = mmap(0, rounded_capacity, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    136 		if (new != MAP_FAILED) {
    137 			*capacity = rounded_capacity;
    138 			result    = new;
    139 		}
    140 	}
    141 	if (fd > 0) close(fd);
    142 	return result;
    143 }
    144 
    145 function OSLinux_FileWatchDirectory *
    146 os_lookup_file_watch_directory(OSLinux_FileWatchDirectoryList *ctx, u64 hash)
    147 {
    148 	OSLinux_FileWatchDirectory *result = 0;
    149 	for (iz i = 0; !result && i < ctx->count; i++)
    150 		if (ctx->data[i].hash == hash)
    151 			result = ctx->data + i;
    152 	return result;
    153 }
    154 
    155 function void
    156 os_linux_add_file_watch(s8 path, void *user_context, OSLinux_FileWatchKind kind)
    157 {
    158 	s8 directory  = path;
    159 	directory.len = s8_scan_backwards(path, '/');
    160 	assert(directory.len > 0);
    161 
    162 	OSLinux_FileWatchDirectoryList *fwctx = &os_linux_context.file_watch_list;
    163 
    164 	u64 hash = u64_hash_from_s8(directory);
    165 	OSLinux_FileWatchDirectory *dir = os_lookup_file_watch_directory(fwctx, hash);
    166 	if (!dir) {
    167 		assert(path.data[directory.len] == '/');
    168 		dir = da_push(&os_linux_context.arena, fwctx);
    169 		dir->hash   = hash;
    170 		dir->name   = push_s8(&os_linux_context.arena, directory);
    171 		u32 mask    = IN_MOVED_TO|IN_CLOSE_WRITE;
    172 		dir->handle = inotify_add_watch(os_linux_context.inotify_handle, (c8 *)dir->name.data, mask);
    173 	}
    174 
    175 	OSLinux_FileWatch *fw = da_push(&os_linux_context.arena, dir);
    176 	fw->user_context = user_context;
    177 	fw->hash         = u64_hash_from_s8(s8_cut_head(path, dir->name.len + 1));
    178 	fw->kind         = kind;
    179 }
    180 
    181 BEAMFORMER_IMPORT void
    182 os_add_file_watch(const char *path, int64_t path_length, void *user_context)
    183 {
    184 	s8 path_str = {.data = (u8 *)path, .len = path_length};
    185 	os_linux_add_file_watch(path_str, user_context, OSLinux_FileWatchKindUser);
    186 }
    187 
    188 function OSLibrary
    189 load_library(char *name, char *temp_name, u32 flags)
    190 {
    191 	if (temp_name && os_copy_file(name, temp_name))
    192 		name = temp_name;
    193 
    194 	OSLibrary result = {(u64)dlopen(name, flags)};
    195 	if (result.value[0] == 0) result.value[0] = OSInvalidHandleValue;
    196 
    197 	if (temp_name) unlink(temp_name);
    198 
    199 	return result;
    200 }
    201 
    202 #if BEAMFORMER_DEBUG
    203 function void
    204 debug_library_reload(BeamformerInput *input)
    205 {
    206 	local_persist OSLibrary beamformer_library_handle = {OSInvalidHandleValue};
    207 	OSLibrary new_handle = load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME, RTLD_NOW|RTLD_LOCAL);
    208 
    209 	if (InvalidHandle(beamformer_library_handle) && InvalidHandle(new_handle))
    210 		fatal(s8("[os] failed to load: " OS_DEBUG_LIB_NAME "\n"));
    211 
    212 	if ValidHandle(new_handle) {
    213 		beamformer_debug_hot_reload(new_handle, input);
    214 
    215 		if ValidHandle(beamformer_library_handle)
    216 			dlclose((void *)beamformer_library_handle.value[0]);
    217 		beamformer_library_handle = new_handle;
    218 	}
    219 }
    220 #endif /* BEAMFORMER_DEBUG */
    221 
    222 function void
    223 load_platform_libraries(BeamformerInput *input)
    224 {
    225 	#if BEAMFORMER_DEBUG
    226 		debug_library_reload(input);
    227 		os_linux_add_file_watch(s8(OS_DEBUG_LIB_NAME), (void *)BeamformerInputEventKind_ExecutableReload,
    228 		                        OSLinux_FileWatchKindPlatform);
    229 	#endif
    230 
    231 	input->cuda_library_handle = load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME, RTLD_NOW|RTLD_LOCAL);
    232 
    233 	#if BEAMFORMER_RENDERDOC_HOOKS
    234 	local_persist OSLibrary renderdoc_handle = {OSInvalidHandleValue};
    235 	renderdoc_handle = load_library(OS_RENDERDOC_SONAME, 0, RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD);
    236 	if ValidHandle(renderdoc_handle) {
    237 		renderdoc_get_api_fn *get_api = os_lookup_symbol(renderdoc_handle, "RENDERDOC_GetAPI");
    238 		if (get_api) {
    239 			RenderDocAPI *api = 0;
    240 			if (get_api(10600, (void **)&api)) {
    241 				input->renderdoc_start_frame_capture = RENDERDOC_START_FRAME_CAPTURE(api);
    242 				input->renderdoc_end_frame_capture   = RENDERDOC_END_FRAME_CAPTURE(api);
    243 			}
    244 		}
    245 	}
    246 	#endif
    247 }
    248 
    249 function void
    250 dispatch_file_watch_events(BeamformerInput *input)
    251 {
    252 	OSLinux_FileWatchDirectoryList *fwctx = &os_linux_context.file_watch_list;
    253 	Arena arena = os_linux_context.arena;
    254 	u8 *mem     = arena_alloc(&arena, .size = 4096, .align = 16);
    255 	struct inotify_event *event;
    256 
    257 	u64 current_time = os_timer_count();
    258 
    259 	iz rlen;
    260 	while ((rlen = read(os_linux_context.inotify_handle, mem, 4096)) > 0) {
    261 		for (u8 *data = mem; data < mem + rlen; data += sizeof(*event) + event->len) {
    262 			event = (struct inotify_event *)data;
    263 			for (u32 i = 0; i < fwctx->count; i++) {
    264 				OSLinux_FileWatchDirectory *dir = fwctx->data + i;
    265 				if (event->wd != dir->handle)
    266 					continue;
    267 
    268 				s8  file = c_str_to_s8(event->name);
    269 				u64 hash = u64_hash_from_s8(file);
    270 				for (u32 j = 0; j < dir->count; j++) {
    271 					OSLinux_FileWatch *fw = dir->data + j;
    272 					if (fw->hash == hash) {
    273 						// NOTE(rnp): avoid multiple updates in a single frame
    274 						if (fw->update_time < current_time) {
    275 							BeamformerInputEvent input_event = {0};
    276 							if (fw->kind == OSLinux_FileWatchKindPlatform) {
    277 								assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload);
    278 								#if BEAMFORMER_DEBUG
    279 									if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload)
    280 										debug_library_reload(input);
    281 								#endif
    282 								input_event.kind = (u64)fw->user_context;
    283 							} else {
    284 								input_event.kind = BeamformerInputEventKind_FileEvent;
    285 								input_event.file_watch_user_context = fw->user_context;
    286 							}
    287 							input->event_queue[input->event_count++] = input_event;
    288 						}
    289 						fw->update_time = current_time;
    290 						break;
    291 					}
    292 				}
    293 			}
    294 		}
    295 	}
    296 }
    297 
    298 extern i32
    299 main(void)
    300 {
    301 	os_linux_context.system_info.timer_frequency         = os_timer_frequency();
    302 	os_linux_context.system_info.logical_processor_count = os_number_of_processors();
    303 	os_linux_context.system_info.page_size               = ARCH_X64? KB(4) : getauxval(AT_PAGESZ);
    304 	os_linux_context.system_info.path_separator_byte     = '/';
    305 
    306 	Arena program_memory = os_alloc_arena(MB(16) + KB(16));
    307 
    308 	os_linux_context.arena = sub_arena(&program_memory, KB(16), KB(4));
    309 	os_linux_context.inotify_handle = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
    310 
    311 	BeamformerInput *input = push_struct(&program_memory, BeamformerInput);
    312 	input->memory          = program_memory.beg;
    313 	input->memory_size     = program_memory.end - program_memory.beg;
    314 	input->shared_memory   = allocate_shared_memory(OS_SHARED_MEMORY_NAME, BEAMFORMER_SHARED_MEMORY_SIZE,
    315 	                                                &input->shared_memory_size);
    316 	if (input->shared_memory) {
    317 		input->shared_memory_name        = s8(OS_SHARED_MEMORY_NAME).data;
    318 		input->shared_memory_name_length = s8(OS_SHARED_MEMORY_NAME).len;
    319 	}
    320 
    321 	input->event_queue[input->event_count++] = (BeamformerInputEvent){
    322 		.kind = BeamformerInputEventKind_ExecutableReload,
    323 	};
    324 
    325 	load_platform_libraries(input);
    326 
    327 	beamformer_init(input);
    328 
    329 	struct pollfd fds[1] = {{0}};
    330 	fds[0].fd     = os_linux_context.inotify_handle;
    331 	fds[0].events = POLLIN;
    332 
    333 	while (!WindowShouldClose() && !beamformer_should_close(input)) {
    334 		poll(fds, countof(fds), 0);
    335 		if (fds[0].revents & POLLIN)
    336 			dispatch_file_watch_events(input);
    337 
    338 		Vector2 new_mouse = GetMousePosition();
    339 		input->last_mouse_x = input->mouse_x;
    340 		input->last_mouse_y = input->mouse_y;
    341 		input->mouse_x      = new_mouse.x;
    342 		input->mouse_y      = new_mouse.y;
    343 
    344 		beamformer_frame_step(input);
    345 
    346 		input->event_count  = 0;
    347 	}
    348 
    349 	beamformer_terminate(input);
    350 
    351 	/* NOTE: make sure this will get cleaned up after external
    352 	 * programs release their references */
    353 	shm_unlink(OS_SHARED_MEMORY_NAME);
    354 }