main_w32.c (13062B)
1 /* See LICENSE for license details. */ 2 #ifdef BEAMFORMER_DEBUG 3 #define BEAMFORMER_IMPORT __declspec(dllexport) 4 #define BASE_EXPORT __declspec(dllexport) 5 #else 6 #define BEAMFORMER_IMPORT static 7 #define BEAMFORMER_EXPORT static 8 #define BASE_EXPORT static 9 #endif 10 11 #define BASE_IMPORT static 12 13 #include "base_platform.h" 14 15 // NOTE(rnp): for test compilations on linux (we don't use headers from windows) */ 16 #if OS_LINUX 17 #undef OS_WINDOWS 18 #undef OS_LINUX 19 #undef __declspec 20 #undef __stdcall 21 #define OS_WINDOWS 1 22 #define OS_LINUX 0 23 #define __declspec(x) 24 #define __stdcall 25 #define _WIN32 26 #endif 27 28 #if !OS_WINDOWS 29 #error This file is only meant to be compiled for Win32 30 #endif 31 32 #include "beamformer.c" 33 #include "base_win32.c" 34 35 W32(b32) FreeLibrary(u64); 36 W32(void *) GetModuleHandleA(const c8 *); 37 W32(void *) GetProcAddress(u64, const c8 *); 38 W32(void *) LoadLibraryA(const c8 *); 39 40 #define OS_SHARED_MEMORY_SIZE GB(2) 41 42 #define OS_DEBUG_LIB_NAME ".\\beamformer.dll" 43 #define OS_DEBUG_LIB_TEMP_NAME ".\\beamformer_temp.dll" 44 45 #define OS_CUDA_LIB_NAME "external\\cuda_toolkit.dll" 46 #define OS_CUDA_LIB_TEMP_NAME "external\\cuda_toolkit_temp.dll" 47 48 #define OS_RENDERDOC_SONAME "renderdoc.dll" 49 50 #define OS_VULKAN_SONAME_LIST \ 51 X("vulkan-1.dll") \ 52 53 global BeamformerInput beamformer_input_main; 54 55 BEAMFORMER_IMPORT void 56 os_console_log(u8 *data, i64 length) 57 { 58 os_write_file(os_w32_context.error_handle, data, length); 59 } 60 61 BEAMFORMER_IMPORT void 62 os_fatal(u8 *data, i64 length) 63 { 64 os_write_file(os_w32_context.error_handle, data, length); 65 os_exit(1); 66 unreachable(); 67 } 68 69 BEAMFORMER_IMPORT void 70 os_release_handle(OSHandle h) 71 { 72 if ValidHandle(h) 73 CloseHandle(h.value[0]); 74 } 75 76 BEAMFORMER_IMPORT void * 77 os_lookup_symbol(OSLibrary library, const char *symbol) 78 { 79 void *result = 0; 80 if ValidHandle(library) result = GetProcAddress(library.value[0], symbol); 81 return result; 82 } 83 84 function void * 85 allocate_shared_memory(char *name, i64 requested_capacity, u64 *capacity) 86 { 87 u64 rounded_capacity = round_up_to(requested_capacity, os_w32_context.system_info.page_size); 88 void *result = 0; 89 iptr h = CreateFileMappingA(-1, 0, PAGE_READWRITE, (rounded_capacity >> 32u), 90 (rounded_capacity & 0xFFFFFFFFul), name); 91 if (h != INVALID_FILE) { 92 result = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, rounded_capacity); 93 if (result) *capacity = rounded_capacity; 94 } 95 return result; 96 } 97 98 function OSW32FileWatchDirectory * 99 os_lookup_file_watch_directory(u64 hash) 100 { 101 OSW32FileWatchDirectory *result = 0; 102 for (OSW32FileWatchDirectory *fwd = os_w32_context.file_watch_directories.first; fwd; fwd = fwd->next) { 103 if (fwd->hash == hash) { 104 result = fwd; 105 break; 106 } 107 } 108 return result; 109 } 110 111 function void 112 os_w32_add_file_watch(str8 path, void *user_context, OSW32FileWatchKind kind) 113 { 114 str8 directory = path; 115 directory.length = str8_scan_backwards(path, '\\'); 116 assert(directory.length > 0); 117 118 u64 hash = u64_hash_from_str8(directory); 119 OSW32FileWatchDirectory *dir = os_lookup_file_watch_directory(hash); 120 if (!dir) { 121 assert(path.data[directory.length] == '\\'); 122 123 OSW32Entity *fwd = os_entity_allocate(OSW32EntityKind_FileWatchDirectory); 124 dir = &fwd->as.file_watch_directory; 125 DLLInsert(0, os_w32_context.file_watch_directories.first, 126 os_w32_context.file_watch_directories.last, dir, next, prev); 127 128 dir->hash = hash; 129 dir->name = push_str8(os_w32_context.arena, directory); 130 dir->handle = CreateFileA((c8 *)dir->name.data, GENERIC_READ, FILE_SHARE_READ, 0, 131 OPEN_EXISTING, 132 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, 0); 133 134 dir->event.tag = W32IOEvent_FileWatch; 135 dir->event.context = (iptr)dir; 136 CreateIoCompletionPort(dir->handle, os_w32_context.io_completion_handle, (uptr)&dir->event, 0); 137 138 dir->buffer = arena_alloc(os_w32_context.arena, .size = OSW32_FileWatchDirectoryBufferSize); 139 ReadDirectoryChangesW(dir->handle, dir->buffer, OSW32_FileWatchDirectoryBufferSize, 0, 140 FILE_NOTIFY_CHANGE_LAST_WRITE, 0, &dir->overlapped, 0); 141 } 142 143 OSW32Entity *fwe = os_entity_allocate(OSW32EntityKind_FileWatch); 144 OSW32FileWatch *fw = &fwe->as.file_watch; 145 DLLInsert(0, dir->first_child, dir->last_child, fw, next, prev); 146 fw->user_context = user_context; 147 fw->hash = u64_hash_from_str8(str8_cut_head(path, dir->name.length + 1)); 148 fw->kind = kind; 149 fw->parent = dir; 150 } 151 152 BEAMFORMER_IMPORT void 153 os_add_file_watch(const char *path, int64_t path_length, void *user_context) 154 { 155 str8 path_str = {.data = (u8 *)path, .length = path_length}; 156 os_w32_add_file_watch(path_str, user_context, OSW32FileWatchKind_User); 157 } 158 159 function void 160 os_window_resize_callback(void *window, i32 width, i32 height) 161 { 162 OSWindow event_window = {0}; 163 for (OSW32Entity *we = os_w32_context.windows.first; we; we = we->as.window.next) { 164 if (we->as.window.handle == window) { 165 event_window.value[0] = (u64)we; 166 break; 167 } 168 } 169 170 os_push_input_event(beamformer_input, (BeamformerInputEvent){ 171 .kind = BeamformerInputEventKind_WindowResize, 172 .window_resize = { 173 .width = (u32)width, .height = (u32)height, 174 .window = event_window, 175 }, 176 }); 177 178 raylib_window_resize(window, width, height); 179 } 180 181 BEAMFORMER_IMPORT OSWindow 182 os_window_create(u8 *title, i64 title_length, i32 width, i32 height) 183 { 184 OSW32Entity *we = os_entity_allocate(OSW32EntityKind_Window); 185 OSWindow result = {(u64)we}; 186 DLLInsert(0, os_w32_context.windows.first, os_w32_context.windows.last, we, as.window.next, as.window.prev); 187 188 SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN); 189 190 str8 name = {.data = title, .length = title_length}; 191 Temp scratch; 192 DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) 193 DeferLoop(scratch = temp_begin(os_w32_context.arena), temp_end(scratch)) 194 { 195 str8 title_string = push_str8(scratch.arena, name); 196 InitWindow(width, height, (char *)title_string.data); 197 } 198 199 we->as.window.handle = GetPlatformWindowHandle(); 200 os_window_equip_common(&beamformer_input_main, we->as.window.handle); 201 raylib_window_resize = glfwSetWindowSizeCallback(we->as.window.handle, os_window_resize_callback); 202 203 /* NOTE: do this after initing so that the window starts out floating in tiling wm */ 204 SetWindowState(FLAG_WINDOW_RESIZABLE); 205 SetWindowMinSize(320, 240); 206 207 return result; 208 } 209 210 BEAMFORMER_IMPORT u8 * 211 os_get_clipboard_text(i64 *length) 212 { 213 str8 result = str8_from_c_str((char *)GetClipboardText()); 214 if (length) *length = result.length; 215 return result.data; 216 } 217 218 BEAMFORMER_IMPORT void 219 os_set_clipboard_text(u8 *data, i64 length) 220 { 221 Temp scratch; 222 DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) 223 DeferLoop(scratch = temp_begin(os_w32_context.arena), temp_end(scratch)) 224 { 225 str8 string = push_str8(scratch.arena, (str8){.data = data, .length = length}); 226 SetClipboardText((char *)string.data); 227 } 228 } 229 230 #if BEAMFORMER_RENDERDOC_HOOKS 231 function OSLibrary 232 get_module(char *name) 233 { 234 OSLibrary result = {(u64)GetModuleHandleA(name)}; 235 if (result.value[0] == 0) result.value[0] = OSInvalidHandleValue; 236 return result; 237 } 238 #endif 239 240 function OSLibrary 241 load_library(char *name, char *temp_name) 242 { 243 if (temp_name && os_copy_file(name, temp_name)) 244 name = temp_name; 245 246 OSLibrary result = {(u64)LoadLibraryA(name)}; 247 if (result.value[0] == 0) result.value[0] = OSInvalidHandleValue; 248 249 if (temp_name) DeleteFileA(temp_name); 250 251 return result; 252 } 253 254 #if BEAMFORMER_DEBUG 255 function void 256 debug_library_reload(void *beamformer) 257 { 258 local_persist OSLibrary beamformer_library_handle = {OSInvalidHandleValue}; 259 260 if ValidHandle(beamformer_library_handle) { 261 beamformer_debug_hot_release(beamformer, &beamformer_input_main); 262 FreeLibrary(beamformer_library_handle.value[0]); 263 beamformer_library_handle = (OSLibrary){OSInvalidHandleValue}; 264 } 265 266 OSLibrary new_handle = load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME); 267 if (InvalidHandle(beamformer_library_handle) && InvalidHandle(new_handle)) 268 fatal(str8("[os] failed to load: " OS_DEBUG_LIB_NAME "\n")); 269 270 if ValidHandle(new_handle) { 271 beamformer_debug_hot_reload(new_handle); 272 beamformer_library_handle = new_handle; 273 } 274 } 275 #else 276 #define debug_library_reload(a) (void)(a) 277 #endif /* BEAMFORMER_DEBUG */ 278 279 function void 280 load_platform_libraries(void) 281 { 282 #if BEAMFORMER_DEBUG 283 debug_library_reload(0); 284 os_w32_add_file_watch(str8(OS_DEBUG_LIB_NAME), (void *)BeamformerInputEventKind_ExecutableReload, 285 OSW32FileWatchKind_Platform); 286 #endif 287 288 BeamformerInput *input = &beamformer_input_main; 289 input->vulkan_library_handle = (OSLibrary){OSInvalidHandleValue}; 290 #define X(name) \ 291 if InvalidHandle(input->vulkan_library_handle) \ 292 input->vulkan_library_handle = load_library(name, 0); 293 OS_VULKAN_SONAME_LIST 294 #undef X 295 296 if InvalidHandle(input->vulkan_library_handle) 297 fatal(str8("[os] fatal error: failed to find valid vulkan library\n")); 298 299 input->cuda_library_handle = load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME); 300 301 #if BEAMFORMER_RENDERDOC_HOOKS 302 local_persist OSLibrary renderdoc_handle = {OSInvalidHandleValue}; 303 renderdoc_handle = get_module(OS_RENDERDOC_SONAME); 304 load_renderdoc_functions(input, renderdoc_handle); 305 #endif 306 } 307 308 function void 309 dispatch_file_watch(void *beamformer, u64 current_time, OSW32FileWatchDirectory *fw_dir, Arena *scratch) 310 { 311 i64 offset = 0; 312 w32_file_notify_info *fni = (w32_file_notify_info *)fw_dir->buffer; 313 Stream e = stream_alloc(scratch, KB(1)); 314 do { 315 if (fni->action != FILE_ACTION_MODIFIED) { 316 stream_append_str8(&e, str8("[os] unknown file watch event: ")); 317 stream_append_u64(&e, fni->action); 318 stream_append_byte(&e, '\n'); 319 os_write_file(os_w32_context.error_handle, e.data, e.widx); 320 stream_reset(&e, 0); 321 } 322 323 str8 file_name = str8_from_str16(scratch, (str16){.data = fni->filename, .length = fni->filename_size / 2}); 324 u64 hash = u64_hash_from_str8(file_name); 325 for (OSW32FileWatch *fw = fw_dir->first_child; fw; fw = fw->next) if (fw->hash == hash) { 326 // NOTE(rnp): avoid multiple updates in a single frame 327 if (fw->update_time < current_time) { 328 BeamformerInputEvent input_event = {0}; 329 if (fw->kind == OSW32FileWatchKind_Platform) { 330 assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload); 331 if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload) 332 debug_library_reload(beamformer); 333 input_event.kind = (u64)fw->user_context; 334 } else { 335 input_event.kind = BeamformerInputEventKind_FileEvent; 336 input_event.file_watch_user_context = fw->user_context; 337 } 338 os_push_input_event(&beamformer_input_main, input_event); 339 } 340 fw->update_time = current_time; 341 break; 342 } 343 344 offset = fni->next_entry_offset; 345 fni = (w32_file_notify_info *)((u8 *)fni + offset); 346 } while (offset); 347 } 348 349 function void 350 clear_io_queue(void *beamformer, Arena *scratch) 351 { 352 iptr handle = os_w32_context.io_completion_handle; 353 w32_overlapped *overlapped; 354 u32 bytes_read; 355 uptr user_data; 356 357 u64 current_time = os_timer_count(); 358 359 while (GetQueuedCompletionStatus(handle, &bytes_read, &user_data, &overlapped, 0)) { 360 w32_io_completion_event *event = (w32_io_completion_event *)user_data; 361 switch (event->tag) { 362 case W32IOEvent_FileWatch:{ 363 OSW32FileWatchDirectory *dir = (OSW32FileWatchDirectory *)event->context; 364 dispatch_file_watch(beamformer, current_time, dir, scratch); 365 zero_struct(&dir->overlapped); 366 ReadDirectoryChangesW(dir->handle, dir->buffer, OSW32_FileWatchDirectoryBufferSize, 0, 367 FILE_NOTIFY_CHANGE_LAST_WRITE, 0, &dir->overlapped, 0); 368 }break; 369 } 370 } 371 } 372 373 BASE_IMPORT void 374 entry_point(i32 argc, char *argv[]) 375 { 376 os_w32_context.io_completion_handle = CreateIoCompletionPort(INVALID_FILE, 0, 0, 0); 377 378 BeamformerInput *input = &beamformer_input_main; 379 input->shared_memory = allocate_shared_memory(OS_SHARED_MEMORY_NAME, OS_SHARED_MEMORY_SIZE, 380 &input->shared_memory_size); 381 if (input->shared_memory) { 382 input->shared_memory_name = str8(OS_SHARED_MEMORY_NAME).data; 383 input->shared_memory_name_length = str8(OS_SHARED_MEMORY_NAME).length; 384 } 385 386 os_push_input_event(input, (BeamformerInputEvent){ 387 .kind = BeamformerInputEventKind_ExecutableReload, 388 }); 389 390 load_platform_libraries(); 391 392 void *beamformer = beamformer_init(input); 393 394 while (!WindowShouldClose() && !beamformer_should_close(beamformer, input)) { 395 os_build_frame_input(input); 396 397 Temp scratch; 398 DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) 399 DeferLoop(scratch = temp_begin(os_w32_context.arena), temp_end(scratch)) 400 { 401 clear_io_queue(beamformer, scratch.arena); 402 } 403 404 beamformer_frame_step(beamformer, input); 405 406 // NOTE(rnp): this must happen at the end of frame to allow the pre loop events through 407 // TODO(rnp): hack: until raylib is removed this happens in ui since raylib will cause 408 // glfw to call the input callbacks in during EndDrawing() 409 //input->event_count = 0; 410 } 411 412 beamformer_terminate(beamformer, input); 413 }