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


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