ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

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 }