ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: bc3d47016040d28aada1425abd2998afd983a185
Parent: 8d6215f453e9a5b420743dfa44165d474feea45e
Author: Randy Palamar
Date:   Tue, 20 Jan 2026 10:04:33 -0700

os: add os_timer_count() to platform requirements

Since the beamformer has multiple timelines running at different
update rates a single timer tick delta (or even absolute
timestamp) is not sufficient. This adds os_timer_count() to the
platform requirements so that each timeline may track its own time
as necessary.

Diffstat:
Mbeamformer.h | 8+++++---
Mbeamformer_core.c | 4+++-
Mbeamformer_internal.h | 4+++-
Mbuild.c | 4++--
Mmain_linux.c | 15++++++---------
Mmain_w32.c | 19++++++++-----------
Mos_linux.c | 6+++---
Mos_win32.c | 6+++---
Mtests/throughput.c | 6+++---
9 files changed, 36 insertions(+), 36 deletions(-)

diff --git a/beamformer.h b/beamformer.h @@ -81,12 +81,16 @@ typedef struct { uint8_t path_separator_byte; } OSSystemInfo; -BEAMFORMER_IMPORT OSSystemInfo * os_get_system_info(void); +BEAMFORMER_IMPORT OSSystemInfo * os_system_info(void); BEAMFORMER_IMPORT OSThread os_create_thread(const char *name, void *user_context, os_thread_entry_point_fn *fn); BEAMFORMER_IMPORT OSBarrier os_barrier_alloc(uint32_t thread_count); BEAMFORMER_IMPORT void os_barrier_enter(OSBarrier); +/* NOTE(rnp): since the beamformer may spawn threads, which may need to keep time, + * passing in a single timer value with the rest of the input is insufficient. */ +BEAMFORMER_IMPORT uint64_t os_timer_count(void); + BEAMFORMER_IMPORT void os_add_file_watch(const char *path, int64_t path_length, void *user_context); BEAMFORMER_IMPORT int64_t os_read_entire_file(const char *file, void *buffer, int64_t buffer_capacity); @@ -154,8 +158,6 @@ typedef struct { uint8_t * shared_memory_name; uint32_t shared_memory_name_length; - uint64_t timer_ticks; - float mouse_x; float mouse_y; float last_mouse_x; diff --git a/beamformer_core.c b/beamformer_core.c @@ -1533,7 +1533,9 @@ beamformer_frame_step(BeamformerInput *input) { BeamformerCtx *ctx = BeamformerContextMemory(input->memory); - dt_for_frame = (f64)(input->timer_ticks) / os_get_system_info()->timer_frequency; + u64 current_time = os_timer_count(); + dt_for_frame = (f64)(current_time - ctx->frame_timestamp) / os_system_info()->timer_frequency; + ctx->frame_timestamp = current_time; if (IsWindowResized()) { ctx->window_size.h = GetScreenHeight(); diff --git a/beamformer_internal.h b/beamformer_internal.h @@ -19,7 +19,7 @@ #define beamformer_info(s) s8("[info] " s "\n") -#define os_path_separator() (s8){.data = &os_get_system_info()->path_separator_byte, .len = 1} +#define os_path_separator() (s8){.data = &os_system_info()->path_separator_byte, .len = 1} /////////////////////////////// // NOTE: CUDA Library Bindings @@ -311,6 +311,8 @@ typedef struct { void *ui; u32 ui_dirty_parameter_blocks; + u64 frame_timestamp; + BeamformerComputeContext compute_context; /* TODO(rnp): ideally this would go in the UI but its hard to manage with the UI diff --git a/build.c b/build.c @@ -3591,7 +3591,7 @@ metagen_file_direct(Arena arena, char *filename) i32 main(i32 argc, char *argv[]) { - u64 start_time = os_get_timer_counter(); + u64 start_time = os_timer_count(); g_argv0 = argv[0]; b32 result = 1; @@ -3630,7 +3630,7 @@ main(i32 argc, char *argv[]) if (config.debug) result &= build_beamformer_as_library(arena); if (config.time) { - f64 seconds = (f64)(os_get_timer_counter() - start_time) / (f64)os_get_timer_frequency(); + f64 seconds = (f64)(os_timer_count() - start_time) / (f64)os_timer_frequency(); build_log_info("took %0.03f [s]", seconds); } diff --git a/main_linux.c b/main_linux.c @@ -59,7 +59,7 @@ typedef struct { global OSLinux_Context os_linux_context; BEAMFORMER_IMPORT OSSystemInfo * -os_get_system_info(void) +os_system_info(void) { return &os_linux_context.system_info; } @@ -247,13 +247,15 @@ load_platform_libraries(BeamformerInput *input) } function void -dispatch_file_watch_events(BeamformerInput *input, u64 current_time) +dispatch_file_watch_events(BeamformerInput *input) { OSLinux_FileWatchDirectoryList *fwctx = &os_linux_context.file_watch_list; Arena arena = os_linux_context.arena; u8 *mem = arena_alloc(&arena, .size = 4096, .align = 16); struct inotify_event *event; + u64 current_time = os_timer_count(); + iz rlen; while ((rlen = read(os_linux_context.inotify_handle, mem, 4096)) > 0) { for (u8 *data = mem; data < mem + rlen; data += sizeof(*event) + event->len) { @@ -296,7 +298,7 @@ dispatch_file_watch_events(BeamformerInput *input, u64 current_time) extern i32 main(void) { - os_linux_context.system_info.timer_frequency = os_get_timer_frequency(); + os_linux_context.system_info.timer_frequency = os_timer_frequency(); os_linux_context.system_info.logical_processor_count = os_number_of_processors(); os_linux_context.system_info.page_size = ARCH_X64? KB(4) : getauxval(AT_PAGESZ); os_linux_context.system_info.path_separator_byte = '/'; @@ -328,21 +330,16 @@ main(void) fds[0].fd = os_linux_context.inotify_handle; fds[0].events = POLLIN; - u64 last_time = os_get_timer_counter(); while (!WindowShouldClose() && !beamformer_should_close(input)) { - u64 now = os_get_timer_counter(); - poll(fds, countof(fds), 0); if (fds[0].revents & POLLIN) - dispatch_file_watch_events(input, now); + dispatch_file_watch_events(input); Vector2 new_mouse = GetMousePosition(); input->last_mouse_x = input->mouse_x; input->last_mouse_y = input->mouse_y; input->mouse_x = new_mouse.x; input->mouse_y = new_mouse.y; - input->timer_ticks = now - last_time; - last_time = now; beamformer_frame_step(input); diff --git a/main_w32.c b/main_w32.c @@ -95,7 +95,7 @@ typedef struct { global OSW32_Context os_w32_context; BEAMFORMER_IMPORT OSSystemInfo * -os_get_system_info(void) +os_system_info(void) { return &os_w32_context.system_info; } @@ -297,11 +297,13 @@ load_platform_libraries(BeamformerInput *input) } function void -dispatch_file_watch(BeamformerInput *input, Arena arena, u64 current_time, OSW32_FileWatchDirectory *fw_dir) +dispatch_file_watch(BeamformerInput *input, Arena arena, OSW32_FileWatchDirectory *fw_dir) { TempArena save_point = {0}; i64 offset = 0; + u64 current_time = os_timer_count(); + w32_file_notify_info *fni = (w32_file_notify_info *)fw_dir->buffer; do { end_temp_arena(save_point); @@ -349,7 +351,7 @@ dispatch_file_watch(BeamformerInput *input, Arena arena, u64 current_time, OSW32 } function void -clear_io_queue(BeamformerInput *input, Arena arena, u64 current_time) +clear_io_queue(BeamformerInput *input, Arena arena) { iptr handle = os_w32_context.io_completion_handle; w32_overlapped *overlapped; @@ -360,7 +362,7 @@ clear_io_queue(BeamformerInput *input, Arena arena, u64 current_time) switch (event->tag) { case W32IOEvent_FileWatch:{ OSW32_FileWatchDirectory *dir = (OSW32_FileWatchDirectory *)event->context; - dispatch_file_watch(input, arena, current_time, dir); + dispatch_file_watch(input, arena, dir); zero_struct(&dir->overlapped); ReadDirectoryChangesW(dir->handle, dir->buffer, OSW32_FileWatchDirectoryBufferSize, 0, FILE_NOTIFY_CHANGE_LAST_WRITE, 0, &dir->overlapped, 0); @@ -374,7 +376,7 @@ main(void) { os_w32_context.error_handle = GetStdHandle(STD_ERROR_HANDLE); os_w32_context.io_completion_handle = CreateIoCompletionPort(INVALID_FILE, 0, 0, 0); - os_w32_context.system_info.timer_frequency = os_get_timer_frequency(); + os_w32_context.system_info.timer_frequency = os_timer_frequency(); os_w32_context.system_info.path_separator_byte = '\\'; { w32_system_info info = {0}; @@ -405,13 +407,10 @@ main(void) beamformer_init(input); - u64 last_time = os_get_timer_counter(); while (!WindowShouldClose() && !beamformer_should_close(input)) { - u64 now = os_get_timer_counter(); - DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) { - clear_io_queue(input, os_w32_context.arena, now); + clear_io_queue(input, os_w32_context.arena); } Vector2 new_mouse = GetMousePosition(); @@ -419,8 +418,6 @@ main(void) input->last_mouse_y = input->mouse_y; input->mouse_x = new_mouse.x; input->mouse_y = new_mouse.y; - input->timer_ticks = now - last_time; - last_time = now; beamformer_frame_step(input); diff --git a/os_linux.c b/os_linux.c @@ -43,13 +43,13 @@ os_exit(i32 code) } function u64 -os_get_timer_frequency(void) +os_timer_frequency(void) { return 1000000000ULL; } -function u64 -os_get_timer_counter(void) +BEAMFORMER_IMPORT u64 +os_timer_count(void) { struct timespec time = {0}; clock_gettime(CLOCK_MONOTONIC, &time); diff --git a/os_win32.c b/os_win32.c @@ -137,15 +137,15 @@ os_exit(i32 code) } function u64 -os_get_timer_frequency(void) +os_timer_frequency(void) { u64 result; QueryPerformanceFrequency(&result); return result; } -function u64 -os_get_timer_counter(void) +BEAMFORMER_IMPORT u64 +os_timer_count(void) { u64 result; QueryPerformanceCounter(&result); diff --git a/tests/throughput.c b/tests/throughput.c @@ -402,11 +402,11 @@ execute_study(s8 study, Arena arena, Stream path, Options *options) u32 frame = 0; f32 times[32] = {0}; f32 data_size = (f32)(bp.raw_data_dimensions.E[0] * bp.raw_data_dimensions.E[1] * sizeof(*data)); - u64 start = os_get_timer_counter(); - f64 frequency = os_get_timer_frequency(); + u64 start = os_timer_count(); + f64 frequency = os_timer_frequency(); for (;!g_should_exit;) { if (send_frame(data, &bp)) { - u64 now = os_get_timer_counter(); + u64 now = os_timer_count(); f64 delta = (now - start) / frequency; start = now;