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 }