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