main_w32.c (16853B)
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 typedef struct { 36 u32 reserved1; 37 u32 reserved2; 38 u64 Reserved3[2]; 39 u32 reserved4; 40 u32 reserved5; 41 } w32_synchronization_barrier; 42 43 W32(u64) CreateThread(iptr, u64, iptr, iptr, u32, u32 *); 44 W32(b32) EnterSynchronizationBarrier(w32_synchronization_barrier *, u32); 45 W32(b32) FreeLibrary(u64); 46 W32(void *) GetModuleHandleA(const c8 *); 47 W32(void *) GetProcAddress(u64, const c8 *); 48 W32(b32) InitializeSynchronizationBarrier(w32_synchronization_barrier *, i32, i32); 49 W32(void *) LoadLibraryA(const c8 *); 50 W32(i32) SetThreadDescription(u64, u16 *); 51 52 #define OS_SHARED_MEMORY_SIZE GB(2) 53 54 #define OS_DEBUG_LIB_NAME ".\\beamformer.dll" 55 #define OS_DEBUG_LIB_TEMP_NAME ".\\beamformer_temp.dll" 56 57 #define OS_CUDA_LIB_NAME "external\\cuda_toolkit.dll" 58 #define OS_CUDA_LIB_TEMP_NAME "external\\cuda_toolkit_temp.dll" 59 60 #define OS_RENDERDOC_SONAME "renderdoc.dll" 61 62 #define OS_VULKAN_SONAME_LIST \ 63 X("vulkan-1.dll") \ 64 65 enum {OSW32_FileWatchDirectoryBufferSize = KB(4)}; 66 67 typedef struct OSW32Entity OSW32Entity; 68 typedef struct { 69 void *handle; 70 OSW32Entity *prev, *next; 71 } OSW32Window; 72 73 typedef enum { 74 OSW32FileWatchKind_Platform, 75 OSW32FileWatchKind_User, 76 } OSW32FileWatchKind; 77 78 typedef struct OSW32FileWatchDirectory OSW32FileWatchDirectory; 79 typedef struct OSW32FileWatch OSW32FileWatch; 80 struct OSW32FileWatch { 81 OSW32FileWatchKind kind; 82 u64 hash; 83 u64 update_time; 84 void *user_context; 85 86 OSW32FileWatchDirectory *parent; 87 OSW32FileWatch *prev, *next; 88 }; 89 90 struct OSW32FileWatchDirectory { 91 u64 hash; 92 i64 handle; 93 str8 name; 94 95 OSW32FileWatch *first_child; 96 OSW32FileWatch *last_child; 97 OSW32FileWatchDirectory *prev, *next; 98 99 w32_overlapped overlapped; 100 w32_io_completion_event event; 101 102 void *buffer; 103 }; 104 105 typedef enum { 106 OSW32EntityKind_Window, 107 OSW32EntityKind_FileWatch, 108 OSW32EntityKind_FileWatchDirectory, 109 } OSW32EntityKind; 110 111 struct OSW32Entity { 112 OSW32EntityKind kind; 113 union { 114 OSW32FileWatch file_watch; 115 OSW32FileWatchDirectory file_watch_directory; 116 OSW32Window window; 117 } as; 118 OSW32Entity *next; 119 }; 120 121 typedef struct { 122 Arena *arena; 123 i32 arena_lock; 124 i64 error_handle; 125 i64 io_completion_handle; 126 127 BeamformerInput *input; 128 129 struct { 130 OSW32FileWatchDirectory *first; 131 OSW32FileWatchDirectory *last; 132 } file_watch_directories; 133 134 struct { 135 OSW32Entity *first; 136 OSW32Entity *last; 137 } windows; 138 139 OSW32Entity *entity_freelist; 140 } OSW32_Context; 141 global OSW32_Context os_w32_context; 142 143 function OSW32Entity * 144 os_entity_allocate(OSW32EntityKind kind) 145 { 146 OSW32Entity *result = 0; 147 DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) 148 { 149 result = SLLPopFreelist(os_w32_context.entity_freelist); 150 if (!result) result = push_struct_no_zero(os_w32_context.arena, OSW32Entity); 151 } 152 153 zero_struct(result); 154 result->kind = kind; 155 return result; 156 } 157 158 BEAMFORMER_IMPORT OSThread 159 os_create_thread(const char *name, void *user_context, os_thread_entry_point_fn *fn) 160 { 161 OSThread result = {(u64)CreateThread(0, 0, (iptr)fn, (iptr)user_context, 0, 0)}; 162 if (result.value[0]) { 163 Temp scratch; 164 DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) 165 DeferLoop(scratch = temp_begin(os_w32_context.arena), temp_end(scratch)) 166 { 167 SetThreadDescription(result.value[0], str16_from_str8(scratch.arena, str8_from_c_str((c8 *)name)).data); 168 } 169 } else { 170 result.value[0] = OSInvalidHandleValue; 171 } 172 return result; 173 } 174 175 BEAMFORMER_IMPORT OSBarrier 176 os_barrier_alloc(u32 count) 177 { 178 OSBarrier result = {0}; 179 DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) 180 { 181 w32_synchronization_barrier *barrier = push_struct(os_w32_context.arena, w32_synchronization_barrier); 182 InitializeSynchronizationBarrier(barrier, (i32)count, -1); 183 result.value[0] = (u64)barrier; 184 } 185 return result; 186 } 187 188 BEAMFORMER_IMPORT void 189 os_barrier_enter(OSBarrier barrier) 190 { 191 w32_synchronization_barrier *b = (w32_synchronization_barrier *)barrier.value[0]; 192 if (b) EnterSynchronizationBarrier(b, 0); 193 } 194 195 BEAMFORMER_IMPORT void 196 os_console_log(u8 *data, i64 length) 197 { 198 os_write_file(os_w32_context.error_handle, data, length); 199 } 200 201 BEAMFORMER_IMPORT void 202 os_fatal(u8 *data, i64 length) 203 { 204 os_write_file(os_w32_context.error_handle, data, length); 205 os_exit(1); 206 unreachable(); 207 } 208 209 BEAMFORMER_IMPORT void 210 os_release_handle(OSHandle h) 211 { 212 if ValidHandle(h) 213 CloseHandle(h.value[0]); 214 } 215 216 BEAMFORMER_IMPORT void * 217 os_lookup_symbol(OSLibrary library, const char *symbol) 218 { 219 void *result = 0; 220 if ValidHandle(library) result = GetProcAddress(library.value[0], symbol); 221 return result; 222 } 223 224 function void * 225 allocate_shared_memory(char *name, i64 requested_capacity, u64 *capacity) 226 { 227 u64 rounded_capacity = round_up_to(requested_capacity, win32_system_info.page_size); 228 void *result = 0; 229 iptr h = CreateFileMappingA(-1, 0, PAGE_READWRITE, (rounded_capacity >> 32u), 230 (rounded_capacity & 0xFFFFFFFFul), name); 231 if (h != INVALID_FILE) { 232 result = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, rounded_capacity); 233 if (result) *capacity = rounded_capacity; 234 } 235 return result; 236 } 237 238 function OSW32FileWatchDirectory * 239 os_lookup_file_watch_directory(u64 hash) 240 { 241 OSW32FileWatchDirectory *result = 0; 242 for (OSW32FileWatchDirectory *fwd = os_w32_context.file_watch_directories.first; fwd; fwd = fwd->next) { 243 if (fwd->hash == hash) { 244 result = fwd; 245 break; 246 } 247 } 248 return result; 249 } 250 251 function void 252 os_w32_add_file_watch(str8 path, void *user_context, OSW32FileWatchKind kind) 253 { 254 str8 directory = path; 255 directory.length = str8_scan_backwards(path, '\\'); 256 assert(directory.length > 0); 257 258 u64 hash = u64_hash_from_str8(directory); 259 OSW32FileWatchDirectory *dir = os_lookup_file_watch_directory(hash); 260 if (!dir) { 261 assert(path.data[directory.length] == '\\'); 262 263 OSW32Entity *fwd = os_entity_allocate(OSW32EntityKind_FileWatchDirectory); 264 dir = &fwd->as.file_watch_directory; 265 DLLInsert(0, os_w32_context.file_watch_directories.first, 266 os_w32_context.file_watch_directories.last, dir, next, prev); 267 268 dir->hash = hash; 269 dir->name = push_str8(os_w32_context.arena, directory); 270 dir->handle = CreateFileA((c8 *)dir->name.data, GENERIC_READ, FILE_SHARE_READ, 0, 271 OPEN_EXISTING, 272 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, 0); 273 274 dir->event.tag = W32IOEvent_FileWatch; 275 dir->event.context = (iptr)dir; 276 CreateIoCompletionPort(dir->handle, os_w32_context.io_completion_handle, (uptr)&dir->event, 0); 277 278 dir->buffer = arena_alloc(os_w32_context.arena, .size = OSW32_FileWatchDirectoryBufferSize); 279 ReadDirectoryChangesW(dir->handle, dir->buffer, OSW32_FileWatchDirectoryBufferSize, 0, 280 FILE_NOTIFY_CHANGE_LAST_WRITE, 0, &dir->overlapped, 0); 281 } 282 283 OSW32Entity *fwe = os_entity_allocate(OSW32EntityKind_FileWatch); 284 OSW32FileWatch *fw = &fwe->as.file_watch; 285 DLLInsert(0, dir->first_child, dir->last_child, fw, next, prev); 286 fw->user_context = user_context; 287 fw->hash = u64_hash_from_str8(str8_cut_head(path, dir->name.length + 1)); 288 fw->kind = kind; 289 fw->parent = dir; 290 } 291 292 BEAMFORMER_IMPORT void 293 os_add_file_watch(const char *path, int64_t path_length, void *user_context) 294 { 295 str8 path_str = {.data = (u8 *)path, .length = path_length}; 296 os_w32_add_file_watch(path_str, user_context, OSW32FileWatchKind_User); 297 } 298 299 function void 300 os_window_resize_callback(void *window, i32 width, i32 height) 301 { 302 OSWindow event_window = {0}; 303 for (OSW32Entity *we = os_w32_context.windows.first; we; we = we->as.window.next) { 304 if (we->as.window.handle == window) { 305 event_window.value[0] = (u64)we; 306 break; 307 } 308 } 309 310 os_push_input_event(beamformer_input, (BeamformerInputEvent){ 311 .kind = BeamformerInputEventKind_WindowResize, 312 .window_resize = { 313 .width = (u32)width, .height = (u32)height, 314 .window = event_window, 315 }, 316 }); 317 318 raylib_window_resize(window, width, height); 319 } 320 321 BEAMFORMER_IMPORT OSWindow 322 os_window_create(u8 *title, i64 title_length, i32 width, i32 height) 323 { 324 OSW32Entity *we = os_entity_allocate(OSW32EntityKind_Window); 325 OSWindow result = {(u64)we}; 326 DLLInsert(0, os_w32_context.windows.first, os_w32_context.windows.last, we, as.window.next, as.window.prev); 327 328 SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN); 329 330 str8 name = {.data = title, .length = title_length}; 331 Temp scratch; 332 DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) 333 DeferLoop(scratch = temp_begin(os_w32_context.arena), temp_end(scratch)) 334 { 335 str8 title_string = push_str8(scratch.arena, name); 336 InitWindow(width, height, (char *)title_string.data); 337 } 338 339 we->as.window.handle = GetPlatformWindowHandle(); 340 os_window_equip_common(os_w32_context.input, we->as.window.handle); 341 raylib_window_resize = glfwSetWindowSizeCallback(we->as.window.handle, os_window_resize_callback); 342 343 /* NOTE: do this after initing so that the window starts out floating in tiling wm */ 344 SetWindowState(FLAG_WINDOW_RESIZABLE); 345 SetWindowMinSize(320, 240); 346 347 return result; 348 } 349 350 BEAMFORMER_IMPORT u8 * 351 os_get_clipboard_text(i64 *length) 352 { 353 str8 result = str8_from_c_str((char *)GetClipboardText()); 354 if (length) *length = result.length; 355 return result.data; 356 } 357 358 BEAMFORMER_IMPORT void 359 os_set_clipboard_text(u8 *data, i64 length) 360 { 361 Temp scratch; 362 DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) 363 DeferLoop(scratch = temp_begin(os_w32_context.arena), temp_end(scratch)) 364 { 365 str8 string = push_str8(scratch.arena, (str8){.data = data, .length = length}); 366 SetClipboardText((char *)string.data); 367 } 368 } 369 370 #if BEAMFORMER_RENDERDOC_HOOKS 371 function OSLibrary 372 get_module(char *name) 373 { 374 OSLibrary result = {(u64)GetModuleHandleA(name)}; 375 if (result.value[0] == 0) result.value[0] = OSInvalidHandleValue; 376 return result; 377 } 378 #endif 379 380 function OSLibrary 381 load_library(char *name, char *temp_name) 382 { 383 if (temp_name && os_copy_file(name, temp_name)) 384 name = temp_name; 385 386 OSLibrary result = {(u64)LoadLibraryA(name)}; 387 if (result.value[0] == 0) result.value[0] = OSInvalidHandleValue; 388 389 if (temp_name) DeleteFileA(temp_name); 390 391 return result; 392 } 393 394 #if BEAMFORMER_DEBUG 395 function void 396 debug_library_reload(void *beamformer, BeamformerInput *input) 397 { 398 local_persist OSLibrary beamformer_library_handle = {OSInvalidHandleValue}; 399 400 if ValidHandle(beamformer_library_handle) { 401 beamformer_debug_hot_release(beamformer, input); 402 FreeLibrary(beamformer_library_handle.value[0]); 403 beamformer_library_handle = (OSLibrary){OSInvalidHandleValue}; 404 } 405 406 OSLibrary new_handle = load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME); 407 if (InvalidHandle(beamformer_library_handle) && InvalidHandle(new_handle)) 408 fatal(str8("[os] failed to load: " OS_DEBUG_LIB_NAME "\n")); 409 410 if ValidHandle(new_handle) { 411 beamformer_debug_hot_reload(new_handle); 412 beamformer_library_handle = new_handle; 413 } 414 } 415 #else 416 #define debug_library_reload(a, b) (void)(a), (void)(b) 417 #endif /* BEAMFORMER_DEBUG */ 418 419 function void 420 load_platform_libraries(BeamformerInput *input) 421 { 422 #if BEAMFORMER_DEBUG 423 debug_library_reload(0, input); 424 os_w32_add_file_watch(str8(OS_DEBUG_LIB_NAME), (void *)BeamformerInputEventKind_ExecutableReload, 425 OSW32FileWatchKind_Platform); 426 #endif 427 428 input->vulkan_library_handle = (OSLibrary){OSInvalidHandleValue}; 429 #define X(name) \ 430 if InvalidHandle(input->vulkan_library_handle) \ 431 input->vulkan_library_handle = load_library(name, 0); 432 OS_VULKAN_SONAME_LIST 433 #undef X 434 435 if InvalidHandle(input->vulkan_library_handle) 436 fatal(str8("[os] fatal error: failed to find valid vulkan library\n")); 437 438 input->cuda_library_handle = load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME); 439 440 #if BEAMFORMER_RENDERDOC_HOOKS 441 local_persist OSLibrary renderdoc_handle = {OSInvalidHandleValue}; 442 renderdoc_handle = get_module(OS_RENDERDOC_SONAME); 443 load_renderdoc_functions(input, renderdoc_handle); 444 #endif 445 } 446 447 function void 448 dispatch_file_watch(void *beamformer, BeamformerInput *input, u64 current_time, OSW32FileWatchDirectory *fw_dir, Arena *scratch) 449 { 450 i64 offset = 0; 451 w32_file_notify_info *fni = (w32_file_notify_info *)fw_dir->buffer; 452 Stream e = stream_alloc(scratch, KB(1)); 453 do { 454 if (fni->action != FILE_ACTION_MODIFIED) { 455 stream_append_str8(&e, str8("[os] unknown file watch event: ")); 456 stream_append_u64(&e, fni->action); 457 stream_append_byte(&e, '\n'); 458 os_write_file(os_w32_context.error_handle, e.data, e.widx); 459 stream_reset(&e, 0); 460 } 461 462 str8 file_name = str8_from_str16(scratch, (str16){.data = fni->filename, .length = fni->filename_size / 2}); 463 u64 hash = u64_hash_from_str8(file_name); 464 for (OSW32FileWatch *fw = fw_dir->first_child; fw; fw = fw->next) if (fw->hash == hash) { 465 // NOTE(rnp): avoid multiple updates in a single frame 466 if (fw->update_time < current_time) { 467 BeamformerInputEvent input_event = {0}; 468 if (fw->kind == OSW32FileWatchKind_Platform) { 469 assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload); 470 if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload) 471 debug_library_reload(beamformer, input); 472 input_event.kind = (u64)fw->user_context; 473 } else { 474 input_event.kind = BeamformerInputEventKind_FileEvent; 475 input_event.file_watch_user_context = fw->user_context; 476 } 477 os_push_input_event(input, input_event); 478 } 479 fw->update_time = current_time; 480 break; 481 } 482 483 offset = fni->next_entry_offset; 484 fni = (w32_file_notify_info *)((u8 *)fni + offset); 485 } while (offset); 486 } 487 488 function void 489 clear_io_queue(void *beamformer, BeamformerInput *input, Arena *scratch) 490 { 491 iptr handle = os_w32_context.io_completion_handle; 492 w32_overlapped *overlapped; 493 u32 bytes_read; 494 uptr user_data; 495 496 u64 current_time = os_timer_count(); 497 498 while (GetQueuedCompletionStatus(handle, &bytes_read, &user_data, &overlapped, 0)) { 499 w32_io_completion_event *event = (w32_io_completion_event *)user_data; 500 switch (event->tag) { 501 case W32IOEvent_FileWatch:{ 502 OSW32FileWatchDirectory *dir = (OSW32FileWatchDirectory *)event->context; 503 dispatch_file_watch(beamformer, input, current_time, dir, scratch); 504 zero_struct(&dir->overlapped); 505 ReadDirectoryChangesW(dir->handle, dir->buffer, OSW32_FileWatchDirectoryBufferSize, 0, 506 FILE_NOTIFY_CHANGE_LAST_WRITE, 0, &dir->overlapped, 0); 507 }break; 508 } 509 } 510 } 511 512 BASE_IMPORT void 513 entry_point(i32 argc, char *argv[]) 514 { 515 os_w32_context.arena = arena_create(.name = "Platform Arena"); 516 os_w32_context.error_handle = GetStdHandle(STD_ERROR_HANDLE); 517 os_w32_context.io_completion_handle = CreateIoCompletionPort(INVALID_FILE, 0, 0, 0); 518 519 BeamformerInput *input = push_struct(os_w32_context.arena, BeamformerInput); 520 os_w32_context.input = input; 521 input->shared_memory = allocate_shared_memory(OS_SHARED_MEMORY_NAME, OS_SHARED_MEMORY_SIZE, 522 &input->shared_memory_size); 523 if (input->shared_memory) { 524 input->shared_memory_name = str8(OS_SHARED_MEMORY_NAME).data; 525 input->shared_memory_name_length = str8(OS_SHARED_MEMORY_NAME).length; 526 } 527 528 os_push_input_event(input, (BeamformerInputEvent){ 529 .kind = BeamformerInputEventKind_ExecutableReload, 530 }); 531 532 load_platform_libraries(input); 533 534 void *beamformer = beamformer_init(input); 535 536 while (!WindowShouldClose() && !beamformer_should_close(beamformer, input)) { 537 os_build_frame_input(input); 538 539 Temp scratch; 540 DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) 541 DeferLoop(scratch = temp_begin(os_w32_context.arena), temp_end(scratch)) 542 { 543 clear_io_queue(beamformer, input, scratch.arena); 544 } 545 546 beamformer_frame_step(beamformer, input); 547 548 // NOTE(rnp): this must happen at the end of frame to allow the pre loop events through 549 // TODO(rnp): hack: until raylib is removed this happens in ui since raylib will cause 550 // glfw to call the input callbacks in during EndDrawing() 551 //input->event_count = 0; 552 } 553 554 beamformer_terminate(beamformer, input); 555 }