main_w32.c (13234B)
1 /* See LICENSE for license details. */ 2 #include "compiler.h" 3 4 // NOTE(rnp): for test compilations on linux (we don't use headers from windows) */ 5 #if OS_LINUX 6 #undef OS_WINDOWS 7 #undef OS_LINUX 8 #undef __declspec 9 #undef __stdcall 10 #define OS_WINDOWS 1 11 #define OS_LINUX 0 12 #define __declspec(x) 13 #define __stdcall 14 #define _WIN32 15 #endif 16 17 #if !OS_WINDOWS 18 #error This file is only meant to be compiled for Win32 19 #endif 20 21 #ifdef BEAMFORMER_DEBUG 22 #define BEAMFORMER_IMPORT __declspec(dllexport) 23 #else 24 #define BEAMFORMER_IMPORT static 25 #define BEAMFORMER_EXPORT static 26 #endif 27 28 #include "beamformer.c" 29 #include "os_win32.c" 30 31 typedef struct { 32 u32 reserved1; 33 u32 reserved2; 34 u64 Reserved3[2]; 35 u32 reserved4; 36 u32 reserved5; 37 } w32_synchronization_barrier; 38 39 W32(u64) CreateThread(iptr, uz, iptr, iptr, u32, u32 *); 40 W32(b32) EnterSynchronizationBarrier(w32_synchronization_barrier *, u32); 41 W32(b32) FreeLibrary(u64); 42 W32(void *) GetModuleHandleA(const c8 *); 43 W32(void *) GetProcAddress(u64, const c8 *); 44 W32(b32) InitializeSynchronizationBarrier(w32_synchronization_barrier *, i32, i32); 45 W32(void *) LoadLibraryA(const c8 *); 46 W32(i32) SetThreadDescription(u64, u16 *); 47 48 #define OS_SHARED_MEMORY_SIZE GB(2) 49 50 #define OS_DEBUG_LIB_NAME ".\\beamformer.dll" 51 #define OS_DEBUG_LIB_TEMP_NAME ".\\beamformer_temp.dll" 52 53 #define OS_CUDA_LIB_NAME "external\\cuda_toolkit.dll" 54 #define OS_CUDA_LIB_TEMP_NAME "external\\cuda_toolkit_temp.dll" 55 56 #define OS_RENDERDOC_SONAME "renderdoc.dll" 57 58 enum {OSW32_FileWatchDirectoryBufferSize = KB(4)}; 59 typedef enum { 60 OSW32_FileWatchKindPlatform, 61 OSW32_FileWatchKindUser, 62 } OSW32_FileWatchKind; 63 64 typedef struct { 65 OSW32_FileWatchKind kind; 66 u64 hash; 67 u64 update_time; 68 void * user_context; 69 } OSW32_FileWatch; 70 71 typedef struct { 72 u64 hash; 73 iptr handle; 74 s8 name; 75 76 OSW32_FileWatch *data; 77 iz count; 78 iz capacity; 79 80 w32_overlapped overlapped; 81 w32_io_completion_event event; 82 83 void *buffer; 84 } OSW32_FileWatchDirectory; 85 DA_STRUCT(OSW32_FileWatchDirectory, OSW32_FileWatchDirectory); 86 87 typedef struct { 88 Arena arena; 89 i32 arena_lock; 90 iptr error_handle; 91 iptr io_completion_handle; 92 93 OSW32_FileWatchDirectoryList file_watch_list; 94 95 OSSystemInfo system_info; 96 } OSW32_Context; 97 global OSW32_Context os_w32_context; 98 99 BEAMFORMER_IMPORT OSSystemInfo * 100 os_system_info(void) 101 { 102 return &os_w32_context.system_info; 103 } 104 105 BEAMFORMER_IMPORT OSThread 106 os_create_thread(const char *name, void *user_context, os_thread_entry_point_fn *fn) 107 { 108 OSThread result = {(u64)CreateThread(0, 0, (iptr)fn, (iptr)user_context, 0, 0)}; 109 if (result.value[0]) { 110 DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) 111 { 112 Arena arena = os_w32_context.arena; 113 SetThreadDescription(result.value[0], s8_to_s16(&arena, c_str_to_s8((c8 *)name)).data); 114 } 115 } else { 116 result.value[0] = OSInvalidHandleValue; 117 } 118 return result; 119 } 120 121 BEAMFORMER_IMPORT OSBarrier 122 os_barrier_alloc(u32 count) 123 { 124 OSBarrier result = {0}; 125 DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) 126 { 127 w32_synchronization_barrier *barrier = push_struct(&os_w32_context.arena, w32_synchronization_barrier); 128 InitializeSynchronizationBarrier(barrier, (i32)count, -1); 129 result.value[0] = (u64)barrier; 130 } 131 return result; 132 } 133 134 BEAMFORMER_IMPORT void 135 os_barrier_enter(OSBarrier barrier) 136 { 137 w32_synchronization_barrier *b = (w32_synchronization_barrier *)barrier.value[0]; 138 if (b) EnterSynchronizationBarrier(b, 0); 139 } 140 141 BEAMFORMER_IMPORT void 142 os_console_log(u8 *data, i64 length) 143 { 144 os_write_file(os_w32_context.error_handle, data, length); 145 } 146 147 BEAMFORMER_IMPORT void 148 os_fatal(u8 *data, i64 length) 149 { 150 os_write_file(os_w32_context.error_handle, data, length); 151 os_exit(1); 152 unreachable(); 153 } 154 155 BEAMFORMER_IMPORT void * 156 os_lookup_symbol(OSLibrary library, const char *symbol) 157 { 158 void *result = 0; 159 if ValidHandle(library) result = GetProcAddress(library.value[0], symbol); 160 return result; 161 } 162 163 function void * 164 allocate_shared_memory(char *name, iz requested_capacity, u64 *capacity) 165 { 166 u64 rounded_capacity = round_up_to(requested_capacity, os_w32_context.system_info.page_size); 167 void *result = 0; 168 iptr h = CreateFileMappingA(-1, 0, PAGE_READWRITE, (rounded_capacity >> 32u), 169 (rounded_capacity & 0xFFFFFFFFul), name); 170 if (h != INVALID_FILE) { 171 result = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, rounded_capacity); 172 if (result) *capacity = rounded_capacity; 173 } 174 return result; 175 } 176 177 function OSW32_FileWatchDirectory * 178 os_lookup_file_watch_directory(OSW32_FileWatchDirectoryList *ctx, u64 hash) 179 { 180 OSW32_FileWatchDirectory *result = 0; 181 for (iz i = 0; !result && i < ctx->count; i++) 182 if (ctx->data[i].hash == hash) 183 result = ctx->data + i; 184 return result; 185 } 186 187 function void 188 os_w32_add_file_watch(s8 path, void *user_context, OSW32_FileWatchKind kind) 189 { 190 s8 directory = path; 191 directory.len = s8_scan_backwards(path, '\\'); 192 assert(directory.len > 0); 193 194 OSW32_FileWatchDirectoryList *fwctx = &os_w32_context.file_watch_list; 195 196 u64 hash = u64_hash_from_s8(directory); 197 OSW32_FileWatchDirectory *dir = os_lookup_file_watch_directory(fwctx, hash); 198 if (!dir) { 199 assert(path.data[directory.len] == '\\'); 200 201 dir = da_push(&os_w32_context.arena, fwctx); 202 dir->hash = hash; 203 dir->name = push_s8(&os_w32_context.arena, directory); 204 dir->handle = CreateFileA((c8 *)dir->name.data, GENERIC_READ, FILE_SHARE_READ, 0, 205 OPEN_EXISTING, 206 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, 0); 207 208 dir->event.tag = W32IOEvent_FileWatch; 209 dir->event.context = (iptr)dir; 210 CreateIoCompletionPort(dir->handle, os_w32_context.io_completion_handle, (uptr)&dir->event, 0); 211 212 dir->buffer = arena_alloc(&os_w32_context.arena, .size = OSW32_FileWatchDirectoryBufferSize); 213 ReadDirectoryChangesW(dir->handle, dir->buffer, OSW32_FileWatchDirectoryBufferSize, 0, 214 FILE_NOTIFY_CHANGE_LAST_WRITE, 0, &dir->overlapped, 0); 215 } 216 217 OSW32_FileWatch *fw = da_push(&os_w32_context.arena, dir); 218 fw->user_context = user_context; 219 fw->hash = u64_hash_from_s8(s8_cut_head(path, dir->name.len + 1)); 220 fw->kind = kind; 221 } 222 223 BEAMFORMER_IMPORT void 224 os_add_file_watch(const char *path, int64_t path_length, void *user_context) 225 { 226 s8 path_str = {.data = (u8 *)path, .len = path_length}; 227 os_w32_add_file_watch(path_str, user_context, OSW32_FileWatchKindUser); 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(BeamformerInput *input) 257 { 258 local_persist OSLibrary beamformer_library_handle = {OSInvalidHandleValue}; 259 260 if ValidHandle(beamformer_library_handle) { 261 beamformer_debug_hot_release(input); 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(s8("[os] failed to load: " OS_DEBUG_LIB_NAME "\n")); 269 270 if ValidHandle(new_handle) { 271 beamformer_debug_hot_reload(new_handle, input); 272 beamformer_library_handle = new_handle; 273 } 274 } 275 #endif /* BEAMFORMER_DEBUG */ 276 277 function void 278 load_platform_libraries(BeamformerInput *input) 279 { 280 #if BEAMFORMER_DEBUG 281 debug_library_reload(input); 282 os_w32_add_file_watch(s8(OS_DEBUG_LIB_NAME), (void *)BeamformerInputEventKind_ExecutableReload, 283 OSW32_FileWatchKindPlatform); 284 #endif 285 286 input->cuda_library_handle = load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME); 287 288 #if BEAMFORMER_RENDERDOC_HOOKS 289 local_persist OSLibrary renderdoc_handle = {OSInvalidHandleValue}; 290 renderdoc_handle = get_module(OS_RENDERDOC_SONAME); 291 if ValidHandle(renderdoc_handle) { 292 renderdoc_get_api_fn *get_api = os_lookup_symbol(renderdoc_handle, "RENDERDOC_GetAPI"); 293 if (get_api) { 294 RenderDocAPI *api = 0; 295 if (get_api(10600, (void **)&api)) { 296 input->renderdoc_start_frame_capture = RENDERDOC_START_FRAME_CAPTURE(api); 297 input->renderdoc_end_frame_capture = RENDERDOC_END_FRAME_CAPTURE(api); 298 } 299 } 300 } 301 #endif 302 } 303 304 function void 305 dispatch_file_watch(BeamformerInput *input, Arena arena, u64 current_time, OSW32_FileWatchDirectory *fw_dir) 306 { 307 TempArena save_point = {0}; 308 i64 offset = 0; 309 310 w32_file_notify_info *fni = (w32_file_notify_info *)fw_dir->buffer; 311 do { 312 end_temp_arena(save_point); 313 save_point = begin_temp_arena(&arena); 314 315 Stream e = {.data = arena_commit(&arena, KB(1)), .cap = KB(1)}; 316 317 if (fni->action != FILE_ACTION_MODIFIED) { 318 stream_append_s8(&e, s8("[os] unknown file watch event: ")); 319 stream_append_u64(&e, fni->action); 320 stream_append_byte(&e, '\n'); 321 os_write_file(os_w32_context.error_handle, e.data, e.widx); 322 stream_reset(&e, 0); 323 } 324 325 s8 file_name = s16_to_s8(&arena, (s16){.data = fni->filename, .len = fni->filename_size / 2}); 326 u64 hash = u64_hash_from_s8(file_name); 327 for (u32 i = 0; i < fw_dir->count; i++) { 328 OSW32_FileWatch *fw = fw_dir->data + i; 329 if (fw->hash == hash) { 330 // NOTE(rnp): avoid multiple updates in a single frame 331 if (fw->update_time < current_time) { 332 BeamformerInputEvent input_event = {0}; 333 if (fw->kind == OSW32_FileWatchKindPlatform) { 334 assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload); 335 #if BEAMFORMER_DEBUG 336 if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload) 337 debug_library_reload(input); 338 #endif 339 input_event.kind = (u64)fw->user_context; 340 } else { 341 input_event.kind = BeamformerInputEventKind_FileEvent; 342 input_event.file_watch_user_context = fw->user_context; 343 } 344 input->event_queue[input->event_count++] = input_event; 345 } 346 fw->update_time = current_time; 347 break; 348 } 349 } 350 351 offset = fni->next_entry_offset; 352 fni = (w32_file_notify_info *)((u8 *)fni + offset); 353 } while (offset); 354 } 355 356 function void 357 clear_io_queue(BeamformerInput *input, Arena arena) 358 { 359 iptr handle = os_w32_context.io_completion_handle; 360 w32_overlapped *overlapped; 361 u32 bytes_read; 362 uptr user_data; 363 364 u64 current_time = os_timer_count(); 365 366 while (GetQueuedCompletionStatus(handle, &bytes_read, &user_data, &overlapped, 0)) { 367 w32_io_completion_event *event = (w32_io_completion_event *)user_data; 368 switch (event->tag) { 369 case W32IOEvent_FileWatch:{ 370 OSW32_FileWatchDirectory *dir = (OSW32_FileWatchDirectory *)event->context; 371 dispatch_file_watch(input, arena, current_time, dir); 372 zero_struct(&dir->overlapped); 373 ReadDirectoryChangesW(dir->handle, dir->buffer, OSW32_FileWatchDirectoryBufferSize, 0, 374 FILE_NOTIFY_CHANGE_LAST_WRITE, 0, &dir->overlapped, 0); 375 }break; 376 } 377 } 378 } 379 380 extern i32 381 main(void) 382 { 383 os_w32_context.error_handle = GetStdHandle(STD_ERROR_HANDLE); 384 os_w32_context.io_completion_handle = CreateIoCompletionPort(INVALID_FILE, 0, 0, 0); 385 os_w32_context.system_info.timer_frequency = os_timer_frequency(); 386 os_w32_context.system_info.path_separator_byte = '\\'; 387 { 388 w32_system_info info = {0}; 389 GetSystemInfo(&info); 390 391 os_w32_context.system_info.page_size = info.page_size; 392 os_w32_context.system_info.logical_processor_count = info.number_of_processors; 393 } 394 395 Arena program_memory = os_alloc_arena(MB(16) + MB(2)); 396 os_w32_context.arena = sub_arena(&program_memory, MB(2), os_w32_context.system_info.page_size); 397 398 BeamformerInput *input = push_struct(&program_memory, BeamformerInput); 399 input->memory = program_memory.beg; 400 input->memory_size = program_memory.end - program_memory.beg; 401 input->shared_memory = allocate_shared_memory(OS_SHARED_MEMORY_NAME, OS_SHARED_MEMORY_SIZE, 402 &input->shared_memory_size); 403 if (input->shared_memory) { 404 input->shared_memory_name = s8(OS_SHARED_MEMORY_NAME).data; 405 input->shared_memory_name_length = s8(OS_SHARED_MEMORY_NAME).len; 406 } 407 408 input->event_queue[input->event_count++] = (BeamformerInputEvent){ 409 .kind = BeamformerInputEventKind_ExecutableReload, 410 }; 411 412 load_platform_libraries(input); 413 414 beamformer_init(input); 415 416 while (!WindowShouldClose() && !beamformer_should_close(input)) { 417 DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) 418 { 419 clear_io_queue(input, os_w32_context.arena); 420 } 421 422 Vector2 new_mouse = GetMousePosition(); 423 input->last_mouse_x = input->mouse_x; 424 input->last_mouse_y = input->mouse_y; 425 input->mouse_x = new_mouse.x; 426 input->mouse_y = new_mouse.y; 427 428 beamformer_frame_step(input); 429 430 input->event_count = 0; 431 } 432 433 beamformer_terminate(input); 434 }