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


      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 W32(b32)    FreeLibrary(u64);
     36 W32(void *) GetModuleHandleA(const c8 *);
     37 W32(void *) GetProcAddress(u64, const c8 *);
     38 W32(void *) LoadLibraryA(const c8 *);
     39 
     40 #define OS_SHARED_MEMORY_SIZE  GB(2)
     41 
     42 #define OS_DEBUG_LIB_NAME      ".\\beamformer.dll"
     43 #define OS_DEBUG_LIB_TEMP_NAME ".\\beamformer_temp.dll"
     44 
     45 #define OS_CUDA_LIB_NAME       "external\\cuda_toolkit.dll"
     46 #define OS_CUDA_LIB_TEMP_NAME  "external\\cuda_toolkit_temp.dll"
     47 
     48 #define OS_RENDERDOC_SONAME    "renderdoc.dll"
     49 
     50 #define OS_VULKAN_SONAME_LIST \
     51 	X("vulkan-1.dll") \
     52 
     53 global BeamformerInput beamformer_input_main;
     54 
     55 BEAMFORMER_IMPORT void
     56 os_console_log(u8 *data, i64 length)
     57 {
     58 	os_write_file(os_w32_context.error_handle, data, length);
     59 }
     60 
     61 BEAMFORMER_IMPORT void
     62 os_fatal(u8 *data, i64 length)
     63 {
     64 	os_write_file(os_w32_context.error_handle, data, length);
     65 	os_exit(1);
     66 	unreachable();
     67 }
     68 
     69 BEAMFORMER_IMPORT void
     70 os_release_handle(OSHandle h)
     71 {
     72 	if ValidHandle(h)
     73 		CloseHandle(h.value[0]);
     74 }
     75 
     76 BEAMFORMER_IMPORT void *
     77 os_lookup_symbol(OSLibrary library, const char *symbol)
     78 {
     79 	void *result = 0;
     80 	if ValidHandle(library) result = GetProcAddress(library.value[0], symbol);
     81 	return result;
     82 }
     83 
     84 function void *
     85 allocate_shared_memory(char *name, i64 requested_capacity, u64 *capacity)
     86 {
     87 	u64 rounded_capacity = round_up_to(requested_capacity, os_w32_context.system_info.page_size);
     88 	void *result = 0;
     89 	iptr h = CreateFileMappingA(-1, 0, PAGE_READWRITE, (rounded_capacity >> 32u),
     90 	                            (rounded_capacity & 0xFFFFFFFFul), name);
     91 	if (h != INVALID_FILE) {
     92 		result = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, rounded_capacity);
     93 		if (result) *capacity = rounded_capacity;
     94 	}
     95 	return result;
     96 }
     97 
     98 function OSW32FileWatchDirectory *
     99 os_lookup_file_watch_directory(u64 hash)
    100 {
    101 	OSW32FileWatchDirectory *result = 0;
    102 	for (OSW32FileWatchDirectory *fwd = os_w32_context.file_watch_directories.first; fwd; fwd = fwd->next) {
    103 		if (fwd->hash == hash) {
    104 			result = fwd;
    105 			break;
    106 		}
    107 	}
    108 	return result;
    109 }
    110 
    111 function void
    112 os_w32_add_file_watch(str8 path, void *user_context, OSW32FileWatchKind kind)
    113 {
    114 	str8 directory   = path;
    115 	directory.length = str8_scan_backwards(path, '\\');
    116 	assert(directory.length > 0);
    117 
    118 	u64 hash = u64_hash_from_str8(directory);
    119 	OSW32FileWatchDirectory *dir = os_lookup_file_watch_directory(hash);
    120 	if (!dir) {
    121 		assert(path.data[directory.length] == '\\');
    122 
    123 		OSW32Entity *fwd = os_entity_allocate(OSW32EntityKind_FileWatchDirectory);
    124 		dir = &fwd->as.file_watch_directory;
    125 		DLLInsert(0, os_w32_context.file_watch_directories.first,
    126 		          os_w32_context.file_watch_directories.last, dir, next, prev);
    127 
    128 		dir->hash   = hash;
    129 		dir->name   = push_str8(os_w32_context.arena, directory);
    130 		dir->handle = CreateFileA((c8 *)dir->name.data, GENERIC_READ, FILE_SHARE_READ, 0,
    131 		                          OPEN_EXISTING,
    132 		                          FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, 0);
    133 
    134 		dir->event.tag     = W32IOEvent_FileWatch;
    135 		dir->event.context = (iptr)dir;
    136 		CreateIoCompletionPort(dir->handle, os_w32_context.io_completion_handle, (uptr)&dir->event, 0);
    137 
    138 		dir->buffer = arena_alloc(os_w32_context.arena, .size = OSW32_FileWatchDirectoryBufferSize);
    139 		ReadDirectoryChangesW(dir->handle, dir->buffer, OSW32_FileWatchDirectoryBufferSize, 0,
    140 		                      FILE_NOTIFY_CHANGE_LAST_WRITE, 0, &dir->overlapped, 0);
    141 	}
    142 
    143 	OSW32Entity    *fwe = os_entity_allocate(OSW32EntityKind_FileWatch);
    144 	OSW32FileWatch *fw  = &fwe->as.file_watch;
    145 	DLLInsert(0, dir->first_child, dir->last_child, fw, next, prev);
    146 	fw->user_context = user_context;
    147 	fw->hash         = u64_hash_from_str8(str8_cut_head(path, dir->name.length + 1));
    148 	fw->kind         = kind;
    149 	fw->parent       = dir;
    150 }
    151 
    152 BEAMFORMER_IMPORT void
    153 os_add_file_watch(const char *path, int64_t path_length, void *user_context)
    154 {
    155 	str8 path_str = {.data = (u8 *)path, .length = path_length};
    156 	os_w32_add_file_watch(path_str, user_context, OSW32FileWatchKind_User);
    157 }
    158 
    159 function void
    160 os_window_resize_callback(void *window, i32 width, i32 height)
    161 {
    162 	OSWindow event_window = {0};
    163 	for (OSW32Entity *we = os_w32_context.windows.first; we; we = we->as.window.next) {
    164 		if (we->as.window.handle == window) {
    165 			event_window.value[0] = (u64)we;
    166 			break;
    167 		}
    168 	}
    169 
    170 	os_push_input_event(beamformer_input, (BeamformerInputEvent){
    171 		.kind      = BeamformerInputEventKind_WindowResize,
    172 		.window_resize = {
    173 			.width = (u32)width, .height = (u32)height,
    174 			.window = event_window,
    175 		},
    176 	});
    177 
    178 	raylib_window_resize(window, width, height);
    179 }
    180 
    181 BEAMFORMER_IMPORT OSWindow
    182 os_window_create(u8 *title, i64 title_length, i32 width, i32 height)
    183 {
    184 	OSW32Entity *we = os_entity_allocate(OSW32EntityKind_Window);
    185 	OSWindow result = {(u64)we};
    186 	DLLInsert(0, os_w32_context.windows.first, os_w32_context.windows.last, we, as.window.next, as.window.prev);
    187 
    188 	SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN);
    189 
    190 	str8 name = {.data = title, .length = title_length};
    191 	Temp scratch;
    192 	DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock))
    193 	DeferLoop(scratch = temp_begin(os_w32_context.arena), temp_end(scratch))
    194 	{
    195 		str8 title_string = push_str8(scratch.arena, name);
    196 		InitWindow(width, height, (char *)title_string.data);
    197 	}
    198 
    199 	we->as.window.handle = GetPlatformWindowHandle();
    200 	os_window_equip_common(&beamformer_input_main, we->as.window.handle);
    201 	raylib_window_resize = glfwSetWindowSizeCallback(we->as.window.handle, os_window_resize_callback);
    202 
    203 	/* NOTE: do this after initing so that the window starts out floating in tiling wm */
    204 	SetWindowState(FLAG_WINDOW_RESIZABLE);
    205 	SetWindowMinSize(320, 240);
    206 
    207 	return result;
    208 }
    209 
    210 BEAMFORMER_IMPORT u8 *
    211 os_get_clipboard_text(i64 *length)
    212 {
    213 	str8 result = str8_from_c_str((char *)GetClipboardText());
    214 	if (length) *length = result.length;
    215 	return result.data;
    216 }
    217 
    218 BEAMFORMER_IMPORT void
    219 os_set_clipboard_text(u8 *data, i64 length)
    220 {
    221 	Temp scratch;
    222 	DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock))
    223 	DeferLoop(scratch = temp_begin(os_w32_context.arena), temp_end(scratch))
    224 	{
    225 		str8 string = push_str8(scratch.arena, (str8){.data = data, .length = length});
    226 		SetClipboardText((char *)string.data);
    227 	}
    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(void *beamformer)
    257 {
    258 	local_persist OSLibrary beamformer_library_handle = {OSInvalidHandleValue};
    259 
    260 	if ValidHandle(beamformer_library_handle) {
    261 		beamformer_debug_hot_release(beamformer, &beamformer_input_main);
    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(str8("[os] failed to load: " OS_DEBUG_LIB_NAME "\n"));
    269 
    270 	if ValidHandle(new_handle) {
    271 		beamformer_debug_hot_reload(new_handle);
    272 		beamformer_library_handle = new_handle;
    273 	}
    274 }
    275 #else
    276 #define debug_library_reload(a) (void)(a)
    277 #endif /* BEAMFORMER_DEBUG */
    278 
    279 function void
    280 load_platform_libraries(void)
    281 {
    282 	#if BEAMFORMER_DEBUG
    283 		debug_library_reload(0);
    284 		os_w32_add_file_watch(str8(OS_DEBUG_LIB_NAME), (void *)BeamformerInputEventKind_ExecutableReload,
    285 		                      OSW32FileWatchKind_Platform);
    286 	#endif
    287 
    288 	BeamformerInput *input = &beamformer_input_main;
    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(str8("[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(void *beamformer, u64 current_time, OSW32FileWatchDirectory *fw_dir, Arena *scratch)
    310 {
    311 	i64 offset = 0;
    312 	w32_file_notify_info *fni = (w32_file_notify_info *)fw_dir->buffer;
    313 	Stream e = stream_alloc(scratch, KB(1));
    314 	do {
    315 		if (fni->action != FILE_ACTION_MODIFIED) {
    316 			stream_append_str8(&e, str8("[os] unknown file watch event: "));
    317 			stream_append_u64(&e, fni->action);
    318 			stream_append_byte(&e, '\n');
    319 			os_write_file(os_w32_context.error_handle, e.data, e.widx);
    320 			stream_reset(&e, 0);
    321 		}
    322 
    323 		str8 file_name = str8_from_str16(scratch, (str16){.data = fni->filename, .length = fni->filename_size / 2});
    324 		u64  hash      = u64_hash_from_str8(file_name);
    325 		for (OSW32FileWatch *fw = fw_dir->first_child; fw; fw = fw->next) if (fw->hash == hash) {
    326 			// NOTE(rnp): avoid multiple updates in a single frame
    327 			if (fw->update_time < current_time) {
    328 				BeamformerInputEvent input_event = {0};
    329 				if (fw->kind == OSW32FileWatchKind_Platform) {
    330 					assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload);
    331 					if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload)
    332 						debug_library_reload(beamformer);
    333 					input_event.kind = (u64)fw->user_context;
    334 				} else {
    335 					input_event.kind = BeamformerInputEventKind_FileEvent;
    336 					input_event.file_watch_user_context = fw->user_context;
    337 				}
    338 				os_push_input_event(&beamformer_input_main, input_event);
    339 			}
    340 			fw->update_time = current_time;
    341 			break;
    342 		}
    343 
    344 		offset = fni->next_entry_offset;
    345 		fni    = (w32_file_notify_info *)((u8 *)fni + offset);
    346 	} while (offset);
    347 }
    348 
    349 function void
    350 clear_io_queue(void *beamformer, Arena *scratch)
    351 {
    352 	iptr handle = os_w32_context.io_completion_handle;
    353 	w32_overlapped *overlapped;
    354 	u32  bytes_read;
    355 	uptr user_data;
    356 
    357 	u64 current_time = os_timer_count();
    358 
    359 	while (GetQueuedCompletionStatus(handle, &bytes_read, &user_data, &overlapped, 0)) {
    360 		w32_io_completion_event *event = (w32_io_completion_event *)user_data;
    361 		switch (event->tag) {
    362 		case W32IOEvent_FileWatch:{
    363 			OSW32FileWatchDirectory *dir = (OSW32FileWatchDirectory *)event->context;
    364 			dispatch_file_watch(beamformer, current_time, dir, scratch);
    365 			zero_struct(&dir->overlapped);
    366 			ReadDirectoryChangesW(dir->handle, dir->buffer, OSW32_FileWatchDirectoryBufferSize, 0,
    367 			                      FILE_NOTIFY_CHANGE_LAST_WRITE, 0, &dir->overlapped, 0);
    368 		}break;
    369 		}
    370 	}
    371 }
    372 
    373 BASE_IMPORT void
    374 entry_point(i32 argc, char *argv[])
    375 {
    376 	os_w32_context.io_completion_handle = CreateIoCompletionPort(INVALID_FILE, 0, 0, 0);
    377 
    378 	BeamformerInput *input = &beamformer_input_main;
    379 	input->shared_memory   = allocate_shared_memory(OS_SHARED_MEMORY_NAME, OS_SHARED_MEMORY_SIZE,
    380 	                                                &input->shared_memory_size);
    381 	if (input->shared_memory) {
    382 		input->shared_memory_name        = str8(OS_SHARED_MEMORY_NAME).data;
    383 		input->shared_memory_name_length = str8(OS_SHARED_MEMORY_NAME).length;
    384 	}
    385 
    386 	os_push_input_event(input, (BeamformerInputEvent){
    387 		.kind = BeamformerInputEventKind_ExecutableReload,
    388 	});
    389 
    390 	load_platform_libraries();
    391 
    392 	void *beamformer = beamformer_init(input);
    393 
    394 	while (!WindowShouldClose() && !beamformer_should_close(beamformer, input)) {
    395 		os_build_frame_input(input);
    396 
    397 		Temp scratch;
    398 		DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock))
    399 		DeferLoop(scratch = temp_begin(os_w32_context.arena), temp_end(scratch))
    400 		{
    401 			clear_io_queue(beamformer, scratch.arena);
    402 		}
    403 
    404 		beamformer_frame_step(beamformer, input);
    405 
    406 		// NOTE(rnp): this must happen at the end of frame to allow the pre loop events through
    407 		// TODO(rnp): hack: until raylib is removed this happens in ui since raylib will cause
    408 		// glfw to call the input callbacks in during EndDrawing()
    409 		//input->event_count = 0;
    410 	}
    411 
    412 	beamformer_terminate(beamformer, input);
    413 }