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 (16529B)


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