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 }