ogl_beamforming

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

Commit: 2f2c4fcc35a69489ce9e3e0dead06cad8b247bea
Parent: cca918133a1f5442b1547904c892623fe75eaf66
Author: Randy Palamar
Date:   Tue, 13 Jan 2026 10:57:02 -0700

core: use input event queue for input, beamformer no longer loads libraries

Once raylib is removed we can handle input properly (in the order
it arrived in). To enable that we need to use an input event queue
that gets reset after each frame.

To start replace file watch callbacks with an event so that the
beamformer doesn't have to worry about being called at unexpected
times. This also helps reduce the amount of symbols that the debug
lib needs to export.

Furthermore the beamformer is no longer allowed to load libraries.
It is not the beamformer's responsibility to know what the library
is called on each platform. It also seems bad to allow the
beamformer to just load arbitrary libraries. The beamformer is
still allowed to load symbols since it knows which portion of the
library it supports. Besides vulkan which is a hard requirement
the code should be written to handle the case when the library is
not present (this is already the case with the existing
libraries).

Diffstat:
Mbeamformer.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mbeamformer.h | 110++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mbeamformer_internal.h | 29++++++++++++++++++-----------
Mbeamformer_shared_memory.c | 1-
Mbuild.c | 2++
Mmain_linux.c | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mmain_w32.c | 163+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mos_linux.c | 91+++++++++++++++++++++----------------------------------------------------------
Mos_win32.c | 94++++++++++++++++++++-----------------------------------------------------------
Mstatic.c | 172+++++++++++++++++++++++--------------------------------------------------------
Mui.c | 12+-----------
Mutil.h | 19+++++--------------
12 files changed, 566 insertions(+), 362 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -39,7 +39,7 @@ global f32 dt_for_frame; #define MIN_MAX_MIPS_LEVEL_UNIFORM_LOC 1 #define SUM_PRESCALE_UNIFORM_LOC 1 -#ifndef _DEBUG +#if !BEAMFORMER_RENDERDOC_HOOKS #define start_renderdoc_capture(...) #define end_renderdoc_capture(...) #else @@ -77,6 +77,9 @@ beamformer_compute_plan_for_block(BeamformerComputeContext *cc, u32 block, Arena { assert(block < countof(cc->compute_plans)); BeamformerComputePlan *result = cc->compute_plans[block]; + + assert(!arena && result); + if (!result) { result = SLLPopFreelist(cc->compute_plan_freelist); if (result) zero_struct(result); @@ -1098,14 +1101,17 @@ shader_text_with_header(s8 header, s8 filepath, b32 has_file, BeamformerShaderKi /* NOTE(rnp): currently this function is only handling rendering shaders. * look at load_compute_shader for compute shaders */ -DEBUG_EXPORT BEAMFORMER_RELOAD_SHADER_FN(beamformer_reload_shader) +function void +beamformer_reload_shader(BeamformerCtx *ctx, BeamformerShaderReloadContext *src, Arena arena, s8 shader_name) { - BeamformerCtx *ctx = src->beamformer_context; - BeamformerShaderKind kind = beamformer_reloadable_shader_kinds[src->reloadable_info_index]; + BeamformerShaderKind kind = beamformer_reloadable_shader_kinds[src->reloadable_info_index]; assert(kind == BeamformerShaderKind_Render3D); + s8 path = push_s8_from_parts(&arena, os_path_separator(), s8("shaders"), + beamformer_reloadable_shader_files[src->reloadable_info_index]); + i32 shader_count = 1; - ShaderReloadContext *link = src->link; + BeamformerShaderReloadContext *link = src->link; while (link != src) { shader_count++; link = link->link; } s8 *shader_texts = push_array(&arena, s8, shader_count); @@ -1124,8 +1130,6 @@ DEBUG_EXPORT BEAMFORMER_RELOAD_SHADER_FN(beamformer_reload_shader) glDeleteProgram(*shader); *shader = load_shader(arena, shader_texts, shader_types, shader_count, shader_name); ctx->frame_view_render_context.updated = 1; - - return 1; } function void @@ -1138,23 +1142,6 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena *arena, iptr gl_c while (work) { b32 can_commit = 1; switch (work->kind) { - case BeamformerWorkKind_ReloadShader:{ - u32 reserved_blocks = sm->reserved_parameter_blocks; - for (u32 block = 0; block < reserved_blocks; block++) { - BeamformerComputePlan *cp = beamformer_compute_plan_for_block(cs, block, arena); - for (u32 slot = 0; slot < cp->pipeline.shader_count; slot++) { - i32 shader_index = beamformer_shader_reloadable_index_by_shader[cp->pipeline.shaders[slot]]; - if (beamformer_reloadable_shader_kinds[shader_index] == work->reload_shader) - cp->dirty_programs |= 1 << slot; - } - } - - if (ctx->latest_frame && !sm->live_imaging_parameters.active) { - fill_frame_compute_work(ctx, work, ctx->latest_frame->view_plane_tag, - ctx->latest_frame->parameter_block, 0); - can_commit = 0; - } - }break; case BeamformerWorkKind_ExportBuffer:{ /* TODO(rnp): better way of handling DispatchCompute barrier */ post_sync_barrier(&ctx->shared_memory, BeamformerSharedMemoryLockKind_DispatchCompute, sm->locks); @@ -1469,8 +1456,74 @@ DEBUG_EXPORT BEAMFORMER_RF_UPLOAD_FN(beamformer_rf_upload) } } +function void +beamformer_queue_compute(BeamformerCtx *ctx, BeamformerFrame *frame, u32 parameter_block) +{ + BeamformerSharedMemory *sm = ctx->shared_memory.region; + BeamformerSharedMemoryLockKind dispatch_lock = BeamformerSharedMemoryLockKind_DispatchCompute; + if (!sm->live_imaging_parameters.active && + os_shared_memory_region_lock(&ctx->shared_memory, sm->locks, (i32)dispatch_lock, 0)) + { + BeamformWork *work = beamform_work_queue_push(ctx->beamform_work_queue); + BeamformerViewPlaneTag tag = frame ? frame->view_plane_tag : 0; + if (fill_frame_compute_work(ctx, work, tag, parameter_block, 0)) + beamform_work_queue_push_commit(ctx->beamform_work_queue); + } + os_wake_waiters(&ctx->compute_worker.sync_variable); +} + #include "ui.c" +function void +beamformer_process_input_events(BeamformerCtx *ctx, BeamformerInput *input, + BeamformerInputEvent *events, u32 event_count) +{ + for (u32 index = 0; index < event_count; index++) { + BeamformerInputEvent *event = events + index; + switch (event->kind) { + + case BeamformerInputEventKind_ExecutableReload:{ + ui_init(ctx, ctx->ui_backing_store); + + #if BEAMFORMER_RENDERDOC_HOOKS + start_frame_capture = input->renderdoc_start_frame_capture; + end_frame_capture = input->renderdoc_end_frame_capture; + #endif + }break; + + case BeamformerInputEventKind_FileEvent:{ + BeamformerFileReloadContext *frc = event->file_watch_user_context; + switch (frc->kind) { + case BeamformerFileReloadKind_Shader:{ + BeamformerShaderReloadContext *src = frc->shader_reload_context; + BeamformerShaderKind kind = beamformer_reloadable_shader_kinds[src->reloadable_info_index]; + beamformer_reload_shader(ctx, src, ctx->arena, beamformer_shader_names[kind]); + }break; + case BeamformerFileReloadKind_ComputeShader:{ + BeamformerSharedMemory *sm = ctx->shared_memory.region; + u32 reserved_blocks = sm->reserved_parameter_blocks; + + for (u32 block = 0; block < reserved_blocks; block++) { + BeamformerComputePlan *cp = beamformer_compute_plan_for_block(&ctx->compute_context, block, 0); + for (u32 slot = 0; slot < cp->pipeline.shader_count; slot++) { + i32 shader_index = beamformer_shader_reloadable_index_by_shader[cp->pipeline.shaders[slot]]; + if (beamformer_reloadable_shader_kinds[shader_index] == frc->compute_shader_kind) + atomic_or_u32(&cp->dirty_programs, 1 << slot); + } + } + + if (ctx->latest_frame) + beamformer_queue_compute(ctx, ctx->latest_frame, ctx->latest_frame->parameter_block); + }break; + InvalidDefaultCase; + } + }break; + + InvalidDefaultCase; + } + } +} + DEBUG_EXPORT BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step) { BeamformerCtx *ctx = BeamformerContextMemory(input->memory); @@ -1484,11 +1537,7 @@ DEBUG_EXPORT BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step) coalesce_timing_table(ctx->compute_timing_table, ctx->compute_shader_stats); - if (input->executable_reloaded) { - ui_init(ctx, ctx->ui_backing_store); - DEBUG_DECL(start_frame_capture = ctx->start_frame_capture); - DEBUG_DECL(end_frame_capture = ctx->end_frame_capture); - } + beamformer_process_input_events(ctx, input, input->event_queue, input->event_count); BeamformerSharedMemory *sm = ctx->shared_memory.region; if (atomic_load_u32(sm->locks + BeamformerSharedMemoryLockKind_UploadRF)) diff --git a/beamformer.h b/beamformer.h @@ -6,8 +6,72 @@ #define BEAMFORMER_NAME_STRING "OGL Beamformer" +/////////////////////////////// +// COMPILE TIME CONFIGURATION + +/* NOTE(rnp): By design the beamformer has very little compile time configuration. + * The few options it does have are documented here. + * + * BEAMFORMER_IMPORT + * BEAMFORMER_EXPORT + * The symbol markup for imported and exported symbols. In a typical + * release unity build these are both defined to `static`. + * + * BEAMFORMER_DEBUG + * Compile the beamformer with handling for hot reloading at runtime. + * This requires compiling `beamformer.c` as a dynamic library which the + * platform is required to load at runtime. + * IMPORTANT: When the platform wants to reload the library at runtime it + * MUST NOT unload the old library immediately; the beamformer may still + * be executing code in old library. Instead the platform must first call + * `beamformer_debug_hot_reload` with the program's memory and the new library + * handle. Once that function has returned it is safe to close the old handle. + * + * BEAMFORMER_RENDERDOC_HOOKS + * Add RenderDoc API calls to capture complete compute frames. As compute is performed + * asynchronously from normal rendering it is not possible to capture normally. In this + * configuration the beamformer will use the function pointers provided in the + * BeamformerInput to make these calls. + * IMPORTANT: The renderdoc library will only be visible when the application is started + * through RenderDoc. Furthermore the library has startup code which will halt the program + * if loaded normally. It must be loaded using platform module loading APIs. For example + * GetModuleHandle or dlopen with the RTLD_NOLOAD flag set. + * + */ + +#ifndef BEAMFORMER_IMPORT + #define BEAMFORMER_IMPORT +#endif + +#ifndef BEAMFORMER_EXPORT + #define BEAMFORMER_EXPORT +#endif + +#ifdef BEAMFORMER_DEBUG + #undef BEAMFORMER_DEBUG + #define BEAMFORMER_DEBUG (1) +#else + #define BEAMFORMER_DEBUG (0) +#endif + +#ifdef BEAMFORMER_RENDERDOC_HOOKS + #undef BEAMFORMER_RENDERDOC_HOOKS + #define BEAMFORMER_RENDERDOC_HOOKS (1) +#else + #define BEAMFORMER_RENDERDOC_HOOKS (0) +#endif + /////////////////// // REQUIRED OS API +#define BeamformerInvalidHandle (BeamformerLibraryHandle){-1} +typedef struct { uint64_t value[1]; } BeamformerLibraryHandle; + +#define BEAMFORMER_OS_ADD_FILE_WATCH_FN(name) void name(char *path, int64_t path_length, void *user_context) +BEAMFORMER_IMPORT BEAMFORMER_OS_ADD_FILE_WATCH_FN(os_add_file_watch); + +#define BEAMFORMER_OS_LOOKUP_SYMBOL_FN(name) void *name(BeamformerLibraryHandle library, char *symbol, Stream *error) +BEAMFORMER_IMPORT BEAMFORMER_OS_LOOKUP_SYMBOL_FN(os_lookup_symbol); + function void os_barrier_wait(Barrier); function iptr os_error_handle(void); function s8 os_path_separator(void); @@ -17,6 +81,32 @@ function OS_SHARED_MEMORY_UNLOCK_REGION_FN(os_shared_memory_region_unlock); function OS_WAKE_WAITERS_FN(os_wake_waiters); function OS_WRITE_FILE_FN(os_write_file); +////////////////////////////// +// BEAMFORMER APPLICATION API + +typedef enum { + BeamformerInputEventKind_ButtonPress, + BeamformerInputEventKind_ButtonRelease, + BeamformerInputEventKind_ExecutableReload, + BeamformerInputEventKind_FileEvent, +} BeamformerInputEventKind; + +// TODO: not yet used +typedef enum { + BeamformerButtonID_MouseLeft, + BeamformerButtonID_MouseRight, + BeamformerButtonID_MouseMiddle, + BeamformerButtonID_Count, +} BeamformerButtonID; + +typedef struct { + BeamformerInputEventKind kind; + union { + BeamformerButtonID button_id; + void * file_watch_user_context; + }; +} BeamformerInputEvent; + typedef struct { void * memory; uint64_t memory_size; @@ -29,9 +119,23 @@ typedef struct { float last_mouse_x; float last_mouse_y; - uint32_t executable_reloaded; + uint32_t event_count; + + BeamformerInputEvent event_queue[256]; + + /* NOTE(rnp): the beamformer is not allowed to dynamically load libraries + * itself. Libraries are optional and the beamformer will not use features + * from libraries which have not been provided. */ + BeamformerLibraryHandle cuda_library_handle; + + #if BEAMFORMER_RENDERDOC_HOOKS + void *renderdoc_start_frame_capture; + void *renderdoc_end_frame_capture; + #endif } BeamformerInput; +BEAMFORMER_EXPORT void beamformer_init(BeamformerInput *); + #define BEAMFORMER_FRAME_STEP_FN(name) void name(BeamformerInput *input) typedef BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step_fn); @@ -40,4 +144,8 @@ typedef BEAMFORMER_DEBUG_UI_DEINIT_FN(beamformer_debug_ui_deinit_fn); function void beamformer_invalidate_shared_memory(void *memory); +#if BEAMFORMER_DEBUG +BEAMFORMER_EXPORT void beamformer_debug_hot_reload(BeamformerLibraryHandle, BeamformerInput *); +#endif + #endif /*BEAMFORMER_H */ diff --git a/beamformer_internal.h b/beamformer_internal.h @@ -16,6 +16,8 @@ #include "threads.c" #include "util_gl.c" +#define beamformer_info(s) s8("[info] " s "\n") + /////////////////////////////// // NOTE: CUDA Library Bindings @@ -312,29 +314,34 @@ typedef struct { GLWorkerThreadContext upload_worker; GLWorkerThreadContext compute_worker; - - DEBUG_DECL(renderdoc_start_frame_capture_fn *start_frame_capture;) - DEBUG_DECL(renderdoc_end_frame_capture_fn *end_frame_capture;) } BeamformerCtx; #define BeamformerContextMemory(m) (BeamformerCtx *)align_pointer_up((m), alignof(BeamformerCtx)); -typedef struct ShaderReloadContext ShaderReloadContext; -struct ShaderReloadContext { - BeamformerCtx *beamformer_context; - ShaderReloadContext *link; +typedef enum { + BeamformerFileReloadKind_Shader, + BeamformerFileReloadKind_ComputeShader, +} BeamformerFileReloadKind; + +typedef struct BeamformerShaderReloadContext BeamformerShaderReloadContext; +struct BeamformerShaderReloadContext { + BeamformerShaderReloadContext * link; s8 header; GLenum gl_type; i32 reloadable_info_index; }; +typedef struct { + BeamformerFileReloadKind kind; + union { + BeamformerShaderReloadContext * shader_reload_context; + BeamformerShaderKind compute_shader_kind; + }; +} BeamformerFileReloadContext; + #define BEAMFORMER_COMPLETE_COMPUTE_FN(name) void name(iptr user_context, Arena *arena, iptr gl_context) typedef BEAMFORMER_COMPLETE_COMPUTE_FN(beamformer_complete_compute_fn); #define BEAMFORMER_RF_UPLOAD_FN(name) void name(BeamformerUploadThreadContext *ctx) typedef BEAMFORMER_RF_UPLOAD_FN(beamformer_rf_upload_fn); -#define BEAMFORMER_RELOAD_SHADER_FN(name) b32 name(s8 path, ShaderReloadContext *src, \ - Arena arena, s8 shader_name) -typedef BEAMFORMER_RELOAD_SHADER_FN(beamformer_reload_shader_fn); - #endif /* BEAMFORMER_INTERNAL_H */ diff --git a/beamformer_shared_memory.c b/beamformer_shared_memory.c @@ -7,7 +7,6 @@ typedef enum { BeamformerWorkKind_Compute, BeamformerWorkKind_ComputeIndirect, BeamformerWorkKind_CreateFilter, - BeamformerWorkKind_ReloadShader, BeamformerWorkKind_ExportBuffer, BeamformerWorkKind_UploadBuffer, } BeamformerWorkKind; diff --git a/build.c b/build.c @@ -3563,6 +3563,8 @@ main(i32 argc, char *argv[]) ////////////////// // static portion cmd_append(&arena, &c, options.bake_shaders? "-DBakeShaders=1" : "-DBakeShaders=0"); + if (options.debug) cmd_append(&arena, &c, "-DBEAMFORMER_DEBUG", "-DBEAMFORMER_RENDERDOC_HOOKS"); + iz c_count = c.count; cmd_append(&arena, &c, OS_MAIN, OUTPUT_EXE("ogl")); cmd_pdb(&arena, &c, "ogl"); diff --git a/main_linux.c b/main_linux.c @@ -5,6 +5,11 @@ #error This file is only meant to be compiled for Linux #endif +#ifndef BEAMFORMER_DEBUG + #define BEAMFORMER_IMPORT function + #define BEAMFORMER_EXPORT function +#endif + #include "util.h" #include "beamformer.h" @@ -35,13 +40,94 @@ os_gl_proc_address(char *name) #include "static.c" +#include <dlfcn.h> + +BEAMFORMER_IMPORT BEAMFORMER_OS_ADD_FILE_WATCH_FN(os_add_file_watch) +{ + s8 path_str = {.data = (u8 *)path, .len = path_length}; + os_linux_add_file_watch(path_str, user_context, OSLinux_FileWatchKindUser); +} + +BEAMFORMER_IMPORT BEAMFORMER_OS_LOOKUP_SYMBOL_FN(os_lookup_symbol) +{ + void *result = 0; + if ValidHandle(library) { + result = dlsym((void *)library.value[0], symbol); + if (!result && error) { + stream_append_s8s(error, s8("os_lookup_symbol(\""), c_str_to_s8(symbol), s8("\"): "), + c_str_to_s8(dlerror()), s8("\n")); + } + } + return result; +} + +function BeamformerLibraryHandle +load_library(char *name, char *temp_name, u32 flags) +{ + if (temp_name && os_copy_file(name, temp_name)) + name = temp_name; + + BeamformerLibraryHandle result = {(u64)dlopen(name, flags)}; + if (result.value[0] == 0) result = BeamformerInvalidHandle; + + if (temp_name) unlink(temp_name); + + return result; +} + +#if BEAMFORMER_DEBUG function void -dispatch_file_watch_events(void) +debug_library_reload(BeamformerInput *input) +{ + local_persist BeamformerLibraryHandle beamformer_library_handle = BeamformerInvalidHandle; + BeamformerLibraryHandle new_handle = load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME, RTLD_NOW|RTLD_LOCAL); + + if (!ValidHandle(beamformer_library_handle) && !ValidHandle(new_handle)) + os_fatal(s8("[os] failed to load: " OS_DEBUG_LIB_NAME "\n")); + + if ValidHandle(new_handle) { + beamformer_debug_hot_reload(new_handle, input); + + if ValidHandle(beamformer_library_handle) + dlclose((void *)beamformer_library_handle.value[0]); + beamformer_library_handle = new_handle; + } +} +#endif /* BEAMFORMER_DEBUG */ + +function void +load_platform_libraries(BeamformerInput *input) +{ + #if BEAMFORMER_DEBUG + debug_library_reload(input); + os_linux_add_file_watch(s8(OS_DEBUG_LIB_NAME), (void *)BeamformerInputEventKind_ExecutableReload, + OSLinux_FileWatchKindPlatform); + #endif + + input->cuda_library_handle = load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME, RTLD_NOW|RTLD_LOCAL); + + #if BEAMFORMER_RENDERDOC_HOOKS + local_persist BeamformerLibraryHandle renderdoc_handle = BeamformerInvalidHandle; + renderdoc_handle = load_library(OS_RENDERDOC_SONAME, 0, RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD); + if ValidHandle(renderdoc_handle) { + renderdoc_get_api_fn *get_api = os_lookup_symbol(renderdoc_handle, "RENDERDOC_GetAPI", 0); + if (get_api) { + RenderDocAPI *api = 0; + if (get_api(10600, (void **)&api)) { + input->renderdoc_start_frame_capture = RENDERDOC_START_FRAME_CAPTURE(api); + input->renderdoc_end_frame_capture = RENDERDOC_END_FRAME_CAPTURE(api); + } + } + } + #endif +} + +function void +dispatch_file_watch_events(BeamformerInput *input, u64 current_time) { OSLinux_FileWatchDirectoryList *fwctx = &os_linux_context.file_watch_list; Arena arena = os_linux_context.arena; u8 *mem = arena_alloc(&arena, .size = 4096, .align = 16); - Stream path = stream_alloc(&arena, 256); struct inotify_event *event; iz rlen; @@ -56,13 +142,25 @@ dispatch_file_watch_events(void) s8 file = c_str_to_s8(event->name); u64 hash = u64_hash_from_s8(file); for (u32 j = 0; j < dir->count; j++) { - FileWatch *fw = dir->data + j; + OSLinux_FileWatch *fw = dir->data + j; if (fw->hash == hash) { - stream_append_s8s(&path, dir->name, s8("/"), file); - stream_append_byte(&path, 0); - stream_commit(&path, -1); - fw->callback(stream_to_s8(&path), fw->user_data, arena); - stream_reset(&path, 0); + // NOTE(rnp): avoid multiple updates in a single frame + if (fw->update_time < current_time) { + BeamformerInputEvent input_event = {0}; + if (fw->kind == OSLinux_FileWatchKindPlatform) { + assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload); + #if BEAMFORMER_DEBUG + if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload) + debug_library_reload(input); + #endif + input_event.kind = (u64)fw->user_context; + } else { + input_event.kind = BeamformerInputEventKind_FileEvent; + input_event.file_watch_user_context = fw->user_context; + } + input->event_queue[input->event_count++] = input_event; + } + fw->update_time = current_time; break; } } @@ -86,7 +184,12 @@ main(void) input->memory = program_memory.beg; input->memory_size = program_memory.end - program_memory.beg; input->timer_frequency = os_get_timer_frequency(); - input->executable_reloaded = 1; + input->event_queue[input->event_count++] = (BeamformerInputEvent){ + .kind = BeamformerInputEventKind_ExecutableReload, + }; + + load_platform_libraries(input); + beamformer_init(input); struct pollfd fds[1] = {{0}}; @@ -95,12 +198,13 @@ main(void) u64 last_time = os_get_timer_counter(); while (!WindowShouldClose()) { + u64 now = os_get_timer_counter(); + poll(fds, countof(fds), 0); if (fds[0].revents & POLLIN) - dispatch_file_watch_events(); + dispatch_file_watch_events(input, now); Vector2 new_mouse = GetMousePosition(); - u64 now = os_get_timer_counter(); input->last_mouse_x = input->mouse_x; input->last_mouse_y = input->mouse_y; input->mouse_x = new_mouse.x; @@ -110,7 +214,7 @@ main(void) beamformer_frame_step(input); - input->executable_reloaded = 0; + input->event_count = 0; } beamformer_invalidate_shared_memory(program_memory.beg); diff --git a/main_w32.c b/main_w32.c @@ -17,6 +17,11 @@ #error This file is only meant to be compiled for Win32 #endif +#ifndef BEAMFORMER_DEBUG + #define BEAMFORMER_IMPORT function + #define BEAMFORMER_EXPORT function +#endif + #include "util.h" #include "beamformer.h" @@ -45,8 +50,104 @@ os_gl_proc_address(char *name) #include "static.c" +W32(b32) FreeLibrary(u64); +W32(void *) GetModuleHandleA(c8 *); +W32(void *) GetProcAddress(u64, c8 *); +W32(void *) LoadLibraryA(c8 *); + +BEAMFORMER_IMPORT BEAMFORMER_OS_ADD_FILE_WATCH_FN(os_add_file_watch) +{ + s8 path_str = {.data = (u8 *)path, .len = path_length}; + os_w32_add_file_watch(path_str, user_context, OSW32_FileWatchKindUser); +} + +BEAMFORMER_IMPORT BEAMFORMER_OS_LOOKUP_SYMBOL_FN(os_lookup_symbol) +{ + void *result = 0; + if ValidHandle(library) { + result = GetProcAddress(library.value[0], symbol); + if (!result && error) { + stream_append_s8s(error, s8("os_lookup_symbol(\""), c_str_to_s8(symbol), s8("\"): ")); + stream_append_i64(error, GetLastError()); + stream_append_byte(error, '\n'); + } + } + return result; +} + +#if BEAMFORMER_RENDERDOC_HOOKS +function BeamformerLibraryHandle +get_module(char *name) +{ + BeamformerLibraryHandle result = {(u64)GetModuleHandleA(name)}; + if (result.value[0] == 0) result = BeamformerInvalidHandle; + return result; +} +#endif + +function BeamformerLibraryHandle +load_library(char *name, char *temp_name) +{ + if (temp_name && os_copy_file(name, temp_name)) + name = temp_name; + + BeamformerLibraryHandle result = {(u64)LoadLibraryA(name)}; + if (result.value[0] == 0) result = BeamformerInvalidHandle; + + if (temp_name) DeleteFileA(temp_name); + + return result; +} + +#if BEAMFORMER_DEBUG function void -dispatch_file_watch(OSW32_FileWatchDirectory *fw_dir, Arena arena) +debug_library_reload(BeamformerInput *input) +{ + local_persist BeamformerLibraryHandle beamformer_library_handle = BeamformerInvalidHandle; + BeamformerLibraryHandle new_handle = load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME); + + if (!ValidHandle(beamformer_library_handle) && !ValidHandle(new_handle)) + os_fatal(s8("[os] failed to load: " OS_DEBUG_LIB_NAME "\n")); + + if ValidHandle(new_handle) { + beamformer_debug_hot_reload(new_handle, input); + + if ValidHandle(beamformer_library_handle) + FreeLibrary(beamformer_library_handle.value[0]); + beamformer_library_handle = new_handle; + } +} +#endif /* BEAMFORMER_DEBUG */ + +function void +load_platform_libraries(BeamformerInput *input) +{ + #if BEAMFORMER_DEBUG + debug_library_reload(input); + os_w32_add_file_watch(s8(OS_DEBUG_LIB_NAME), (void *)BeamformerInputEventKind_ExecutableReload, + OSW32_FileWatchKindPlatform); + #endif + + input->cuda_library_handle = load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME); + + #if BEAMFORMER_RENDERDOC_HOOKS + local_persist BeamformerLibraryHandle renderdoc_handle = BeamformerInvalidHandle; + renderdoc_handle = get_module(OS_RENDERDOC_SONAME); + if ValidHandle(renderdoc_handle) { + renderdoc_get_api_fn *get_api = os_lookup_symbol(renderdoc_handle, "RENDERDOC_GetAPI", 0); + if (get_api) { + RenderDocAPI *api = 0; + if (get_api(10600, (void **)&api)) { + input->renderdoc_start_frame_capture = RENDERDOC_START_FRAME_CAPTURE(api); + input->renderdoc_end_frame_capture = RENDERDOC_END_FRAME_CAPTURE(api); + } + } + } + #endif +} + +function void +dispatch_file_watch(BeamformerInput *input, Arena arena, u64 current_time, OSW32_FileWatchDirectory *fw_dir) { TempArena save_point = {0}; i64 offset = 0; @@ -56,30 +157,38 @@ dispatch_file_watch(OSW32_FileWatchDirectory *fw_dir, Arena arena) end_temp_arena(save_point); save_point = begin_temp_arena(&arena); - Stream path = {.data = arena_commit(&arena, KB(1)), .cap = KB(1)}; + Stream e = {.data = arena_commit(&arena, KB(1)), .cap = KB(1)}; if (fni->action != FILE_ACTION_MODIFIED) { - stream_append_s8(&path, s8("unknown file watch event: ")); - stream_append_u64(&path, fni->action); - stream_append_byte(&path, '\n'); - os_write_file(os_w32_context.error_handle, stream_to_s8(&path)); - stream_reset(&path, 0); + stream_append_s8(&e, s8("unknown file watch event: ")); + stream_append_u64(&e, fni->action); + stream_append_byte(&e, '\n'); + os_write_file(os_w32_context.error_handle, stream_to_s8(&e)); + stream_reset(&e, 0); } - stream_append_s8(&path, fw_dir->name); - stream_append_byte(&path, '\\'); - - s8 file_name = s16_to_s8(&arena, (s16){.data = fni->filename, - .len = fni->filename_size / 2}); - stream_append_s8(&path, file_name); - stream_append_byte(&path, 0); - stream_commit(&path, -1); - + s8 file_name = s16_to_s8(&arena, (s16){.data = fni->filename, .len = fni->filename_size / 2}); u64 hash = u64_hash_from_s8(file_name); for (u32 i = 0; i < fw_dir->count; i++) { - FileWatch *fw = fw_dir->data + i; + OSW32_FileWatch *fw = fw_dir->data + i; if (fw->hash == hash) { - fw->callback(stream_to_s8(&path), fw->user_data, arena); + // NOTE(rnp): avoid multiple updates in a single frame + if (fw->update_time < current_time) { + BeamformerInputEvent input_event = {0}; + if (fw->kind == OSW32_FileWatchKindPlatform) { + assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload); + #if BEAMFORMER_DEBUG + if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload) + debug_library_reload(input); + #endif + input_event.kind = (u64)fw->user_context; + } else { + input_event.kind = BeamformerInputEventKind_FileEvent; + input_event.file_watch_user_context = fw->user_context; + } + input->event_queue[input->event_count++] = input_event; + } + fw->update_time = current_time; break; } } @@ -90,7 +199,7 @@ dispatch_file_watch(OSW32_FileWatchDirectory *fw_dir, Arena arena) } function void -clear_io_queue(BeamformerInput *input, Arena arena) +clear_io_queue(BeamformerInput *input, Arena arena, u64 current_time) { iptr handle = os_w32_context.io_completion_handle; w32_overlapped *overlapped; @@ -101,7 +210,7 @@ clear_io_queue(BeamformerInput *input, Arena arena) switch (event->tag) { case W32IOEvent_FileWatch:{ OSW32_FileWatchDirectory *dir = (OSW32_FileWatchDirectory *)event->context; - dispatch_file_watch(dir, arena); + dispatch_file_watch(input, arena, current_time, dir); zero_struct(&dir->overlapped); ReadDirectoryChangesW(dir->handle, dir->buffer, OSW32_FileWatchDirectoryBufferSize, 0, FILE_NOTIFY_CHANGE_LAST_WRITE, 0, &dir->overlapped, 0); @@ -125,19 +234,25 @@ main(void) input->memory = program_memory.beg; input->memory_size = program_memory.end - program_memory.beg; input->timer_frequency = os_w32_context.timer_frequency; - input->executable_reloaded = 1; + input->event_queue[input->event_count++] = (BeamformerInputEvent){ + .kind = BeamformerInputEventKind_ExecutableReload, + }; + + load_platform_libraries(input); + beamformer_init(input); u64 last_time = os_get_timer_counter(); while (!WindowShouldClose()) { + u64 now = os_get_timer_counter(); + DeferLoop(os_take_lock(&os_w32_context.arena_lock, -1), os_release_lock(&os_w32_context.arena_lock)) { - clear_io_queue(input, os_w32_context.arena); + clear_io_queue(input, os_w32_context.arena, now); } Vector2 new_mouse = GetMousePosition(); - u64 now = os_get_timer_counter(); input->last_mouse_x = input->mouse_x; input->last_mouse_y = input->mouse_y; input->mouse_x = new_mouse.x; @@ -147,7 +262,7 @@ main(void) beamformer_frame_step(input); - input->executable_reloaded = 0; + input->event_count = 0; } beamformer_invalidate_shared_memory(program_memory.beg); diff --git a/os_linux.c b/os_linux.c @@ -10,7 +10,6 @@ #include "util.h" -#include <dlfcn.h> #include <fcntl.h> #include <linux/futex.h> #include <poll.h> @@ -22,14 +21,26 @@ #include <sys/sysinfo.h> #include <unistd.h> +typedef enum { + OSLinux_FileWatchKindPlatform, + OSLinux_FileWatchKindUser, +} OSLinux_FileWatchKind; + +typedef struct { + OSLinux_FileWatchKind kind; + u64 hash; + u64 update_time; + void * user_context; +} OSLinux_FileWatch; + typedef struct { - u64 hash; - iptr handle; - s8 name; + u64 hash; + iptr handle; + s8 name; - FileWatch *data; - iz count; - iz capacity; + OSLinux_FileWatch * data; + iz count; + iz capacity; } OSLinux_FileWatchDirectory; DA_STRUCT(OSLinux_FileWatchDirectory, OSLinux_FileWatchDirectory); @@ -43,19 +54,6 @@ typedef struct { } OSLinux_Context; global OSLinux_Context os_linux_context; -#ifdef _DEBUG -function void * -os_get_module(char *name, Stream *e) -{ - void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD); - if (!result && e) { - stream_append_s8s(e, s8("os_get_module(\""), c_str_to_s8(name), s8("\"): "), - c_str_to_s8(dlerror()), s8("\n")); - } - return result; -} -#endif - function OS_WRITE_FILE_FN(os_write_file) { while (raw.len > 0) { @@ -211,48 +209,6 @@ os_copy_file(char *name, char *new) return result; } -function void * -os_load_library(char *name, char *temp_name, Stream *e) -{ - if (temp_name) { - if (os_copy_file(name, temp_name)) - name = temp_name; - } - - void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL); - if (!result && e) { - stream_append_s8s(e, s8("os_load_library(\""), c_str_to_s8(name), s8("\"): "), - c_str_to_s8(dlerror()), s8("\n")); - } - - if (temp_name) - unlink(temp_name); - - return result; -} - -function void * -os_lookup_dynamic_symbol(void *h, char *name, Stream *e) -{ - void *result = 0; - if (h) { - result = dlsym(h, name); - if (!result && e) { - stream_append_s8s(e, s8("os_lookup_dynamic_symbol(\""), c_str_to_s8(name), - s8("\"): "), c_str_to_s8(dlerror()), s8("\n")); - } - } - return result; -} - -function void -os_unload_library(void *h) -{ - /* NOTE: glibc is buggy gnuware so we need to check this */ - if (h) - dlclose(h); -} - function OSLinux_FileWatchDirectory * os_lookup_file_watch_directory(OSLinux_FileWatchDirectoryList *ctx, u64 hash) { @@ -263,7 +219,8 @@ os_lookup_file_watch_directory(OSLinux_FileWatchDirectoryList *ctx, u64 hash) return result; } -function OS_ADD_FILE_WATCH_FN(os_add_file_watch) +function void +os_linux_add_file_watch(s8 path, void *user_context, OSLinux_FileWatchKind kind) { s8 directory = path; directory.len = s8_scan_backwards(path, '/'); @@ -282,10 +239,10 @@ function OS_ADD_FILE_WATCH_FN(os_add_file_watch) dir->handle = inotify_add_watch(os_linux_context.inotify_handle, (c8 *)dir->name.data, mask); } - FileWatch *fw = da_push(&os_linux_context.arena, dir); - fw->user_data = user_data; - fw->callback = callback; - fw->hash = u64_hash_from_s8(s8_cut_head(path, dir->name.len + 1)); + OSLinux_FileWatch *fw = da_push(&os_linux_context.arena, dir); + fw->user_context = user_context; + fw->hash = u64_hash_from_s8(s8_cut_head(path, dir->name.len + 1)); + fw->kind = kind; } function OS_WAIT_ON_VALUE_FN(os_wait_on_value) diff --git a/os_win32.c b/os_win32.c @@ -113,17 +113,13 @@ W32(iptr) CreateThread(iptr, uz, iptr, iptr, u32, u32 *); W32(b32) DeleteFileA(c8 *); W32(b32) EnterSynchronizationBarrier(w32_synchronization_barrier *, u32); W32(void) ExitProcess(i32); -W32(b32) FreeLibrary(void *); W32(i32) GetFileAttributesA(c8 *); W32(b32) GetFileInformationByHandle(iptr, void *); W32(i32) GetLastError(void); -W32(void *) GetModuleHandleA(c8 *); -W32(void *) GetProcAddress(void *, c8 *); W32(b32) GetQueuedCompletionStatus(iptr, u32 *, uptr *, w32_overlapped **, u32); W32(iptr) GetStdHandle(i32); W32(void) GetSystemInfo(w32_system_info *); W32(b32) InitializeSynchronizationBarrier(w32_synchronization_barrier *, i32, i32); -W32(void *) LoadLibraryA(c8 *); W32(void *) MapViewOfFile(iptr, u32, u32, u32, u64); W32(b32) QueryPerformanceCounter(u64 *); W32(b32) QueryPerformanceFrequency(u64 *); @@ -139,14 +135,26 @@ W32(b32) WriteFile(iptr, u8 *, i32, i32 *, void *); W32(void *) VirtualAlloc(u8 *, iz, u32, u32); enum {OSW32_FileWatchDirectoryBufferSize = KB(4)}; +typedef enum { + OSW32_FileWatchKindPlatform, + OSW32_FileWatchKindUser, +} OSW32_FileWatchKind; + +typedef struct { + OSW32_FileWatchKind kind; + u64 hash; + u64 update_time; + void * user_context; +} OSW32_FileWatch; + typedef struct { - u64 hash; - iptr handle; - s8 name; + u64 hash; + iptr handle; + s8 name; - FileWatch *data; - iz count; - iz capacity; + OSW32_FileWatch *data; + iz count; + iz capacity; w32_overlapped overlapped; w32_io_completion_event event; @@ -167,20 +175,6 @@ typedef struct { } OSW32_Context; global OSW32_Context os_w32_context; -#ifdef _DEBUG -function void * -os_get_module(char *name, Stream *e) -{ - void *result = GetModuleHandleA(name); - if (!result && e) { - stream_append_s8s(e, s8("os_get_module(\""), c_str_to_s8(name), s8("\"): ")); - stream_append_i64(e, GetLastError()); - stream_append_byte(e, '\n'); - } - return result; -} -#endif - function OS_WRITE_FILE_FN(os_write_file) { i32 wlen = 0; @@ -350,47 +344,6 @@ os_copy_file(char *name, char *new) return CopyFileA(name, new, 0); } -function void * -os_load_library(char *name, char *temp_name, Stream *e) -{ - if (temp_name && os_copy_file(name, temp_name)) - name = temp_name; - - void *result = LoadLibraryA(name); - if (!result && e) { - stream_append_s8s(e, s8("os_load_library(\""), c_str_to_s8(name), s8("\"): ")); - stream_append_i64(e, GetLastError()); - stream_append_byte(e, '\n'); - } - - if (temp_name) - DeleteFileA(temp_name); - - return result; -} - -function void * -os_lookup_dynamic_symbol(void *h, char *name, Stream *e) -{ - void *result = 0; - if (h) { - result = GetProcAddress(h, name); - if (!result && e) { - stream_append_s8s(e, s8("os_lookup_dynamic_symbol(\""), c_str_to_s8(name), - s8("\"): ")); - stream_append_i64(e, GetLastError()); - stream_append_byte(e, '\n'); - } - } - return result; -} - -function void -os_unload_library(void *h) -{ - FreeLibrary(h); -} - function OSW32_FileWatchDirectory * os_lookup_file_watch_directory(OSW32_FileWatchDirectoryList *ctx, u64 hash) { @@ -401,7 +354,8 @@ os_lookup_file_watch_directory(OSW32_FileWatchDirectoryList *ctx, u64 hash) return result; } -function OS_ADD_FILE_WATCH_FN(os_add_file_watch) +function void +os_w32_add_file_watch(s8 path, void *user_context, OSW32_FileWatchKind kind) { s8 directory = path; directory.len = s8_scan_backwards(path, '\\'); @@ -430,10 +384,10 @@ function OS_ADD_FILE_WATCH_FN(os_add_file_watch) FILE_NOTIFY_CHANGE_LAST_WRITE, 0, &dir->overlapped, 0); } - FileWatch *fw = da_push(&os_w32_context.arena, dir); - fw->user_data = user_data; - fw->callback = callback; - fw->hash = u64_hash_from_s8(s8_cut_head(path, dir->name.len + 1)); + OSW32_FileWatch *fw = da_push(&os_w32_context.arena, dir); + fw->user_context = user_context; + fw->hash = u64_hash_from_s8(s8_cut_head(path, dir->name.len + 1)); + fw->kind = kind; } function OS_WAIT_ON_VALUE_FN(os_wait_on_value) diff --git a/static.c b/static.c @@ -6,85 +6,41 @@ EXPORT i32 AmdPowerXpressRequestHighPerformance = 1; #include "beamformer_internal.h" -#ifndef _DEBUG - +#if !BEAMFORMER_DEBUG #include "beamformer.c" -#define debug_init(...) - #else -global void *debug_lib; - -#define DEBUG_ENTRY_POINTS \ +#define BEAMFORMER_DEBUG_ENTRY_POINTS \ X(beamformer_debug_ui_deinit) \ X(beamformer_complete_compute) \ X(beamformer_frame_step) \ - X(beamformer_reload_shader) \ - X(beamformer_rf_upload) + X(beamformer_rf_upload) \ #define X(name) global name ##_fn *name; -DEBUG_ENTRY_POINTS +BEAMFORMER_DEBUG_ENTRY_POINTS #undef X -struct debug_context { - BeamformerInput *input; - b32 *compute_worker_asleep; - b32 *upload_worker_asleep; -}; - -function FILE_WATCH_CALLBACK_FN(debug_reload) +BEAMFORMER_EXPORT void +beamformer_debug_hot_reload(BeamformerLibraryHandle library, BeamformerInput *input) { - struct debug_context *ctx = (struct debug_context *)user_data; - Stream err = arena_stream(arena); + BeamformerCtx *ctx = BeamformerContextMemory(input->memory); + Stream err = ctx->error_stream; + // TODO(rnp): this will deadlock if live imaging is active /* NOTE(rnp): spin until compute thread finishes its work (we will probably * never reload while compute is in progress but just incase). */ - spin_wait(!atomic_load_u32(ctx->compute_worker_asleep)); - spin_wait(!atomic_load_u32(ctx->upload_worker_asleep)); + spin_wait(atomic_load_u32(&ctx->upload_worker.awake)); + spin_wait(atomic_load_u32(&ctx->compute_worker.awake)); - os_unload_library(debug_lib); - debug_lib = os_load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME, &err); - - #define X(name) name = os_lookup_dynamic_symbol(debug_lib, #name, &err); - DEBUG_ENTRY_POINTS + #define X(name) name = os_lookup_symbol(library, #name, &err); + BEAMFORMER_DEBUG_ENTRY_POINTS #undef X - stream_append_s8(&err, s8("Reloaded Main Executable\n")); - os_write_file(os_error_handle(), stream_to_s8(&err)); - - ctx->input->executable_reloaded = 1; - - return 1; -} - -function void -debug_init(BeamformerCtx *ctx, BeamformerInput *input, Arena *arena) -{ - struct debug_context *dctx = push_struct(arena, struct debug_context); - dctx->input = input; - dctx->compute_worker_asleep = &ctx->compute_worker.asleep; - dctx->upload_worker_asleep = &ctx->upload_worker.asleep; - os_add_file_watch(s8(OS_DEBUG_LIB_NAME), debug_reload, (iptr)dctx); - debug_reload(s8(""), (iptr)dctx, *arena); - - Stream err = arena_stream(*arena); - void *rdoc = os_get_module(OS_RENDERDOC_SONAME, 0); - if (rdoc) { - renderdoc_get_api_fn *get_api = os_lookup_dynamic_symbol(rdoc, "RENDERDOC_GetAPI", &err); - if (get_api) { - RenderDocAPI *api = 0; - if (get_api(10600, (void **)&api)) { - ctx->start_frame_capture = RENDERDOC_START_FRAME_CAPTURE(api); - ctx->end_frame_capture = RENDERDOC_END_FRAME_CAPTURE(api); - stream_append_s8(&err, s8("loaded: " OS_RENDERDOC_SONAME "\n")); - } - } - } - + stream_append_s8(&err, s8("reloaded main executable\n")); os_write_file(os_error_handle(), stream_to_s8(&err)); } -#endif /* _DEBUG */ +#endif /* BEAMFORMER_DEBUG */ function void gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, i32 len, const char *msg, const void *userctx) @@ -175,47 +131,18 @@ load_gl(Stream *err) } } -function FILE_WATCH_CALLBACK_FN(reload_shader) -{ - ShaderReloadContext *ctx = (typeof(ctx))user_data; - BeamformerShaderKind kind = beamformer_reloadable_shader_kinds[ctx->reloadable_info_index]; - return beamformer_reload_shader(path, ctx, arena, beamformer_shader_names[kind]); -} - -typedef struct { - BeamformerCtx *beamformer; - BeamformerShaderKind shader; -} BeamformerShaderReloadIndirectContext; - -function FILE_WATCH_CALLBACK_FN(reload_shader_indirect) -{ - BeamformerShaderReloadIndirectContext *rsi = (typeof(rsi))user_data; - BeamformerCtx *ctx = rsi->beamformer; - BeamformWork *work = beamform_work_queue_push(ctx->beamform_work_queue); - if (work) { - work->kind = BeamformerWorkKind_ReloadShader, - work->reload_shader = rsi->shader; - beamform_work_queue_push_commit(ctx->beamform_work_queue); - os_wake_waiters(&ctx->compute_worker.sync_variable); - } - return 1; -} - -function FILE_WATCH_CALLBACK_FN(load_cuda_library) +function void +beamformer_load_cuda_library(BeamformerCtx *ctx, BeamformerLibraryHandle cuda, Arena arena) { - local_persist void *cuda_library_handle; - /* TODO(rnp): (25.10.30) registering the rf buffer with CUDA is currently * causing a major performance regression. for now we are disabling its use * altogether. it will be reenabled once the issue can be fixed */ - b32 result = 0 && gl_parameters.vendor_id == GLVendor_NVIDIA && os_file_exists((c8 *)path.data); + b32 result = 0 && gl_parameters.vendor_id == GLVendor_NVIDIA && cuda.value[0] != BeamformerInvalidHandle.value[0]; if (result) { Stream err = arena_stream(arena); - stream_append_s8(&err, s8("loading CUDA library: " OS_CUDA_LIB_NAME "\n")); - os_unload_library(cuda_library_handle); - cuda_library_handle = os_load_library((c8 *)path.data, OS_CUDA_LIB_TEMP_NAME, &err); - #define X(name, symname) cuda_## name = os_lookup_dynamic_symbol(cuda_library_handle, symname, &err); + stream_append_s8(&err, beamformer_info("loading CUDA library functions")); + #define X(name, symname) cuda_## name = os_lookup_symbol(cuda, symname, &err); CUDALibraryProcedureList #undef X @@ -225,8 +152,6 @@ function FILE_WATCH_CALLBACK_FN(load_cuda_library) #define X(name, symname) if (!cuda_## name) cuda_## name = cuda_ ## name ## _stub; CUDALibraryProcedureList #undef X - - return result; } function BeamformerRenderModel @@ -281,9 +206,9 @@ worker_thread_sleep(GLWorkerThreadContext *ctx, BeamformerSharedMemory *sm) } /* TODO(rnp): clean this crap up; we shouldn't need two values to communicate this */ - atomic_store_u32(&ctx->asleep, 1); + atomic_store_u32(&ctx->awake, 0); os_wait_on_value(&ctx->sync_variable, 1, (u32)-1); - atomic_store_u32(&ctx->asleep, 0); + atomic_store_u32(&ctx->awake, 1); } } @@ -330,7 +255,7 @@ function OS_THREAD_ENTRY_POINT_FN(beamformer_upload_entry_point) return 0; } -function void +BEAMFORMER_EXPORT void beamformer_init(BeamformerInput *input) { Arena memory = arena_from_memory(input->memory, input->memory_size); @@ -346,14 +271,11 @@ beamformer_init(BeamformerInput *input) ctx->window_size = (iv2){{1280, 840}}; ctx->error_stream = error; ctx->ui_backing_store = ui_arena; - input->executable_reloaded = 1; ctx->compute_worker.arena = compute_arena; - ctx->compute_worker.asleep = 1; ctx->upload_worker.arena = upload_arena; - ctx->upload_worker.asleep = 1; - debug_init(ctx, input, &memory); + beamformer_load_cuda_library(ctx, input->cuda_library_handle, memory); SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN); InitWindow(ctx->window_size.w, ctx->window_size.h, "OGL Beamformer"); @@ -400,13 +322,10 @@ beamformer_init(BeamformerInput *input) upctx->compute_worker_sync = &ctx->compute_worker.sync_variable; upload->window_handle = glfwCreateWindow(1, 1, "", 0, raylib_window_handle); upload->handle = os_create_thread((iptr)upload, beamformer_upload_entry_point); - os_set_thread_name(worker->handle, s8("[upload]")); + os_set_thread_name(upload->handle, s8("[upload]")); glfwMakeContextCurrent(raylib_window_handle); - if (load_cuda_library(s8(OS_CUDA_LIB_NAME), 0, memory)) - os_add_file_watch(s8(OS_CUDA_LIB_NAME), load_cuda_library, 0); - /* NOTE: set up OpenGL debug logging */ Stream *gl_error_stream = push_struct(&memory, Stream); *gl_error_stream = stream_alloc(&memory, 1024); @@ -422,14 +341,11 @@ beamformer_init(BeamformerInput *input) Arena temp = scratch; s8 file = push_s8_from_parts(&temp, s8(OS_PATH_SEPARATOR), s8("shaders"), beamformer_reloadable_shader_files[index]); - - BeamformerShaderReloadIndirectContext *rsi = push_struct(&memory, typeof(*rsi)); - rsi->beamformer = ctx; - rsi->shader = beamformer_reloadable_shader_kinds[index]; - os_add_file_watch(file, reload_shader_indirect, (iptr)rsi); - reload_shader_indirect(file, (iptr)rsi, memory); + BeamformerFileReloadContext *frc = push_struct(&memory, typeof(*frc)); + frc->kind = BeamformerFileReloadKind_ComputeShader; + frc->compute_shader_kind = beamformer_reloadable_shader_kinds[index]; + os_add_file_watch((char *)file.data, file.len, frc); } - os_wake_waiters(&worker->sync_variable); } FrameViewRenderContext *fvr = &ctx->frame_view_render_context; @@ -448,9 +364,9 @@ beamformer_init(BeamformerInput *input) "only a single render shader is currently handled"); i32 render_rsi_index = beamformer_reloadable_render_shader_info_indices[0]; - Arena *arena = BakeShaders? &scratch : &memory; - ShaderReloadContext *render_3d = push_struct(arena, typeof(*render_3d)); - render_3d->beamformer_context = ctx; + // TODO(rnp): leaks when BakeShaders is true + Arena *arena = &memory; + BeamformerShaderReloadContext *render_3d = push_struct(arena, typeof(*render_3d)); render_3d->reloadable_info_index = render_rsi_index; render_3d->gl_type = GL_FRAGMENT_SHADER; render_3d->header = s8("" @@ -498,13 +414,25 @@ beamformer_init(BeamformerInput *input) "\tgl_Position = u_projection * u_view * u_model * vec4(pos, 1);\n" "}\n"); - s8 render_file = {0}; - if (!BakeShaders) { - render_file = push_s8_from_parts(&scratch, s8(OS_PATH_SEPARATOR), s8("shaders"), - beamformer_reloadable_shader_files[render_rsi_index]); - os_add_file_watch(render_file, reload_shader, (iptr)render_3d); + // TODO(rnp): this is probably not expected by the platform, refactor so that all + // needed context (eg. headers) are available outside of here and push initial load + // into ui_init + { + BeamformerFileReloadContext *frc = push_struct(&memory, typeof(*frc)); + frc->kind = BeamformerFileReloadKind_Shader; + frc->shader_reload_context = render_3d; + input->event_queue[input->event_count++] = (BeamformerInputEvent){ + .kind = BeamformerInputEventKind_FileEvent, + .file_watch_user_context = frc, + }; + + s8 render_file = {0}; + if (!BakeShaders) { + render_file = push_s8_from_parts(&scratch, os_path_separator(), s8("shaders"), + beamformer_reloadable_shader_files[render_rsi_index]); + os_add_file_watch((char *)render_file.data, render_file.len, frc); + } } - reload_shader(render_file, (iptr)render_3d, memory); f32 unit_cube_vertices[] = { 0.5f, 0.5f, -0.5f, diff --git a/ui.c b/ui.c @@ -3986,7 +3986,6 @@ function void draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformerFrame *frame_to_draw, BeamformerViewPlaneTag frame_plane) { BeamformerUI *ui = ctx->ui; - BeamformerSharedMemory *sm = ctx->shared_memory.region; ui->latest_plane[BeamformerViewPlaneTag_Count] = frame_to_draw; ui->latest_plane[frame_plane] = frame_to_draw; @@ -4021,16 +4020,7 @@ draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformerFrame *frame_to_dr BeamformerParameterBlockRegion_Parameters); beamformer_parameter_block_unlock(&ctx->shared_memory, selected_block); - BeamformerSharedMemoryLockKind dispatch_lock = BeamformerSharedMemoryLockKind_DispatchCompute; - if (!sm->live_imaging_parameters.active && - os_shared_memory_region_lock(&ctx->shared_memory, sm->locks, (i32)dispatch_lock, 0)) - { - BeamformWork *work = beamform_work_queue_push(ctx->beamform_work_queue); - BeamformerViewPlaneTag tag = frame_to_draw ? frame_to_draw->view_plane_tag : 0; - if (fill_frame_compute_work(ctx, work, tag, selected_block, 0)) - beamform_work_queue_push_commit(ctx->beamform_work_queue); - } - os_wake_waiters(&ctx->compute_worker.sync_variable); + beamformer_queue_compute(ctx, frame_to_draw, selected_block); } } } diff --git a/util.h b/util.h @@ -313,7 +313,9 @@ typedef struct { b32 errors; } Stream; -#define INVALID_FILE (-1) +#define INVALID_FILE (-1) +#define InvalidHandle (Handle){(u64)(-1)} +#define ValidHandle(h) ((h).value[0] != InvalidHandle.value[0]) typedef struct OS OS; @@ -324,24 +326,16 @@ typedef struct { iptr gl_context; iptr user_context; i32 sync_variable; - b32 asleep; + b32 awake; } GLWorkerThreadContext; -#define FILE_WATCH_CALLBACK_FN(name) b32 name(s8 path, iptr user_data, Arena arena) -typedef FILE_WATCH_CALLBACK_FN(file_watch_callback); - -typedef struct { - iptr user_data; - u64 hash; - file_watch_callback *callback; -} FileWatch; - typedef struct { void *region; iptr os_context; } SharedMemoryRegion; typedef struct { u64 value[1]; } Barrier; +typedef struct { u64 value[1]; } Handle; typedef struct { u64 index; @@ -360,9 +354,6 @@ typedef struct { #define OS_ALLOC_ARENA_FN(name) Arena name(iz capacity) typedef OS_ALLOC_ARENA_FN(os_alloc_arena_fn); -#define OS_ADD_FILE_WATCH_FN(name) void name(s8 path, file_watch_callback *callback, iptr user_data) -typedef OS_ADD_FILE_WATCH_FN(os_add_file_watch_fn); - #define OS_WAKE_WORKER_FN(name) void name(GLWorkerThreadContext *ctx) typedef OS_WAKE_WORKER_FN(os_wake_worker_fn);