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


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