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 (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 }