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


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