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


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