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