Commit: c52cee3a74e9ea982371d75b58c36f78bb3e05fb
Parent: f7b0fceef843d8de624144ea8ff454848a239ed2
Author: Randy Palamar
Date: Wed, 5 Mar 2025 11:17:32 -0700
core: integrate renderdoc frame capturing in debug build
There is still a caveat that renderdoc is unable to capture only a
single OpenGL context when multiple are present even when you tell
it which one to track. This will at least capture all dispatch
compute calls but they will interleaved with a bunch of garbage
from the other context that we don't really care about. Also this
sometimes misses the raw and decoded data buffers but its better
than what we had before.
closes: #23
Diffstat:
8 files changed, 148 insertions(+), 47 deletions(-)
diff --git a/beamformer.c b/beamformer.c
@@ -4,6 +4,14 @@
static f32 dt_for_frame;
static u32 cycle_t;
+#ifndef _DEBUG
+#define start_renderdoc_capture(...)
+#define end_renderdoc_capture(...)
+#else
+#define start_renderdoc_capture(p, gl) if ((p)->start_frame_capture) (p)->start_frame_capture(gl, 0)
+#define end_renderdoc_capture(p, gl) if ((p)->end_frame_capture) (p)->end_frame_capture(gl, 0)
+#endif
+
static size
decoded_data_size(ComputeShaderCtx *cs)
{
@@ -612,6 +620,8 @@ DEBUG_EXPORT BEAMFORMER_COMPLETE_COMPUTE_FN(beamformer_complete_compute)
} break;
case BW_COMPUTE: {
atomic_store(&cs->processing_compute, 1);
+ start_renderdoc_capture(&ctx->platform, gl_context);
+
BeamformFrame *frame = work->frame;
if (ctx->params->upload) {
glNamedBufferSubData(cs->shared_ubo, 0, sizeof(ctx->params->raw),
@@ -656,6 +666,8 @@ DEBUG_EXPORT BEAMFORMER_COMPLETE_COMPUTE_FN(beamformer_complete_compute)
frame->ready_to_present = 1;
cs->processing_compute = 0;
+
+ end_renderdoc_capture(&ctx->platform, gl_context);
} break;
case BW_SAVE_FRAME: {
BeamformFrame *frame = work->output_frame_ctx.frame;
diff --git a/beamformer.h b/beamformer.h
@@ -342,7 +342,7 @@ typedef struct BeamformerCtx {
BeamformerInput *input)
typedef BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step_fn);
-#define BEAMFORMER_COMPLETE_COMPUTE_FN(name) void name(iptr user_context, Arena arena)
+#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 BEAMFORM_WORK_QUEUE_PUSH_FN(name) BeamformWork *name(BeamformWorkQueue *q)
diff --git a/main_linux.c b/main_linux.c
@@ -10,13 +10,15 @@
#define OS_DEBUG_LIB_NAME "./beamformer.so"
#define OS_DEBUG_LIB_TEMP_NAME "./beamformer_temp.so"
-#define OS_CUDA_LIB_NAME "./external/cuda_toolkit.so"
-#define OS_CUDA_LIB_TEMP_NAME "./external/cuda_toolkit_temp.so"
+#define OS_CUDA_LIB_NAME "./external/cuda_toolkit.so"
+#define OS_CUDA_LIB_TEMP_NAME "./external/cuda_toolkit_temp.so"
-#define OS_PIPE_NAME "/tmp/beamformer_data_fifo"
-#define OS_SMEM_NAME "/ogl_beamformer_parameters"
+#define OS_RENDERDOC_SONAME "librenderdoc.so"
-#define OS_PATH_SEPERATOR "/"
+#define OS_PIPE_NAME "/tmp/beamformer_data_fifo"
+#define OS_SMEM_NAME "/ogl_beamformer_parameters"
+
+#define OS_PATH_SEPERATOR "/"
#include "static.c"
diff --git a/main_w32.c b/main_w32.c
@@ -24,13 +24,15 @@ typedef struct {
#define OS_DEBUG_LIB_NAME ".\\beamformer.dll"
#define OS_DEBUG_LIB_TEMP_NAME ".\\beamformer_temp.dll"
-#define OS_CUDA_LIB_NAME "external\\cuda_toolkit.dll"
-#define OS_CUDA_LIB_TEMP_NAME "external\\cuda_toolkit_temp.dll"
+#define OS_CUDA_LIB_NAME "external\\cuda_toolkit.dll"
+#define OS_CUDA_LIB_TEMP_NAME "external\\cuda_toolkit_temp.dll"
-#define OS_PIPE_NAME "\\\\.\\pipe\\beamformer_data_fifo"
-#define OS_SMEM_NAME "Local\\ogl_beamformer_parameters"
+#define OS_RENDERDOC_SONAME "renderdoc.dll"
-#define OS_PATH_SEPERATOR "\\"
+#define OS_PIPE_NAME "\\\\.\\pipe\\beamformer_data_fifo"
+#define OS_SMEM_NAME "Local\\ogl_beamformer_parameters"
+
+#define OS_PATH_SEPERATOR "\\"
#include "static.c"
diff --git a/os_unix.c b/os_unix.c
@@ -15,6 +15,20 @@
#include <sys/stat.h>
#include <unistd.h>
+#ifdef _DEBUG
+static void *
+os_get_module(char *name, Stream *e)
+{
+ void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD);
+ if (!result && e) {
+ s8 errs[] = {s8("os_get_module(\""), c_str_to_s8(name), s8("\"): "),
+ c_str_to_s8(dlerror()), s8("\n")};
+ stream_append_s8_array(e, errs, ARRAY_COUNT(errs));
+ }
+ return result;
+}
+#endif
+
static PLATFORM_WRITE_FILE_FN(os_write_file)
{
while (raw.len) {
@@ -184,36 +198,33 @@ os_load_library(char *name, char *temp_name, Stream *e)
if (os_copy_file(name, temp_name))
name = temp_name;
}
- void *res = dlopen(name, RTLD_NOW|RTLD_LOCAL);
- if (!res && e) {
- s8 errs[] = {s8("WARNING: os_load_library("), c_str_to_s8(name), s8("): "),
+
+ void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL);
+ if (!result && e) {
+ s8 errs[] = {s8("os_load_library(\""), c_str_to_s8(name), s8("\"): "),
c_str_to_s8(dlerror()), s8("\n")};
stream_append_s8_array(e, errs, ARRAY_COUNT(errs));
- os_write_err_msg(stream_to_s8(e));
- e->widx = 0;
}
if (temp_name)
unlink(temp_name);
- return res;
+ return result;
}
static void *
os_lookup_dynamic_symbol(void *h, char *name, Stream *e)
{
- if (!h)
- return 0;
- void *res = dlsym(h, name);
- if (!res && e) {
- s8 errs[] = {s8("WARNING: os_lookup_dynamic_symbol("), c_str_to_s8(name), s8("): "),
- c_str_to_s8(dlerror()), s8("\n")};
- stream_append_s8_array(e, errs, ARRAY_COUNT(errs));
- os_write_err_msg(stream_to_s8(e));
- e->widx = 0;
+ void *result = 0;
+ if (h) {
+ result = dlsym(h, name);
+ if (!result && e) {
+ s8 errs[] = {s8("os_lookup_dynamic_symbol(\""), c_str_to_s8(name),
+ s8("\"): "), c_str_to_s8(dlerror()), s8("\n")};
+ stream_append_s8_array(e, errs, ARRAY_COUNT(errs));
+ }
}
-
- return res;
+ return result;
}
static void
@@ -274,3 +285,12 @@ static PLATFORM_WAKE_THREAD_FN(os_wake_thread)
{
sem_post((sem_t *)sync_handle);
}
+
+/* TODO(rnp): what do if not X11? */
+iptr glfwGetGLXContext(iptr);
+
+static iptr
+os_get_native_gl_context(iptr window)
+{
+ return glfwGetGLXContext(window);
+}
diff --git a/os_win32.c b/os_win32.c
@@ -99,6 +99,7 @@ W32(b32) FreeLibrary(void *);
W32(i32) GetFileAttributesA(c8 *);
W32(b32) GetFileInformationByHandle(iptr, w32_file_info *);
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);
@@ -114,6 +115,21 @@ W32(b32) WriteFile(iptr, u8 *, i32, i32 *, void *);
W32(void *) VirtualAlloc(u8 *, size, u32, u32);
W32(b32) VirtualFree(u8 *, size, u32);
+#ifdef _DEBUG
+static void *
+os_get_module(char *name, Stream *e)
+{
+ void *result = GetModuleHandleA(name);
+ if (!result && e) {
+ s8 errs[] = {s8("os_get_module(\""), c_str_to_s8(name), s8("\"): ")};
+ stream_append_s8_array(e, errs, ARRAY_COUNT(errs));
+ stream_append_i64(e, GetLastError());
+ stream_append_byte(e, '\n');
+ }
+ return result;
+}
+#endif
+
static PLATFORM_WRITE_FILE_FN(os_write_file)
{
i32 wlen;
@@ -252,37 +268,34 @@ os_load_library(char *name, char *temp_name, Stream *e)
name = temp_name;
}
- void *res = LoadLibraryA(name);
- if (!res && e) {
- s8 errs[] = {s8("WARNING: os_load_library("), c_str_to_s8(name), s8("): ")};
+ void *result = LoadLibraryA(name);
+ if (!result && e) {
+ s8 errs[] = {s8("os_load_library(\""), c_str_to_s8(name), s8("\"): ")};
stream_append_s8_array(e, errs, ARRAY_COUNT(errs));
stream_append_i64(e, GetLastError());
stream_append_byte(e, '\n');
- os_write_err_msg(stream_to_s8(e));
- e->widx = 0;
}
if (temp_name)
DeleteFileA(temp_name);
- return res;
+ return result;
}
static void *
os_lookup_dynamic_symbol(void *h, char *name, Stream *e)
{
- if (!h)
- return 0;
- void *res = GetProcAddress(h, name);
- if (!res && e) {
- s8 errs[] = {s8("WARNING: os_lookup_dynamic_symbol("), c_str_to_s8(name), s8("): ")};
- stream_append_s8_array(e, errs, ARRAY_COUNT(errs));
- stream_append_i64(e, GetLastError());
- stream_append_byte(e, '\n');
- os_write_err_msg(stream_to_s8(e));
- e->widx = 0;
+ void *result = 0;
+ if (h) {
+ result = GetProcAddress(h, name);
+ if (!result && e) {
+ s8 errs[] = {s8("os_lookup_dynamic_symbol(\""), c_str_to_s8(name), s8("\"): ")};
+ stream_append_s8_array(e, errs, ARRAY_COUNT(errs));
+ stream_append_i64(e, GetLastError());
+ stream_append_byte(e, '\n');
+ }
}
- return res;
+ return result;
}
static void
@@ -352,3 +365,11 @@ static PLATFORM_WAKE_THREAD_FN(os_wake_thread)
{
ReleaseSemaphore(sync_handle, 1, 0);
}
+
+iptr glfwGetWGLContext(iptr);
+
+static iptr
+os_get_native_gl_context(iptr window)
+{
+ return glfwGetWGLContext(window);
+}
diff --git a/static.c b/static.c
@@ -33,6 +33,9 @@ static FILE_WATCH_CALLBACK_FN(debug_reload)
DEBUG_ENTRY_POINTS
#undef X
+ if (err.widx)
+ os_write_err_msg(stream_to_s8(&err));
+
os_write_err_msg(s8("Reloaded Main Executable\n"));
input->executable_reloaded = 1;
@@ -44,6 +47,24 @@ debug_init(Platform *p, iptr input, Arena *arena)
{
p->add_file_watch(p, arena, s8(OS_DEBUG_LIB_NAME), debug_reload, input);
debug_reload(p, (s8){0}, input, *arena);
+
+ Arena tmp = *arena;
+ Stream err = arena_stream(&tmp);
+ 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)) {
+ p->start_frame_capture = RENDERDOC_START_FRAME_CAPTURE(api);
+ p->end_frame_capture = RENDERDOC_END_FRAME_CAPTURE(api);
+ stream_append_s8(&err, s8("loaded: " OS_RENDERDOC_SONAME "\n"));
+ }
+ }
+ }
+
+ if (err.widx)
+ os_write_err_msg(stream_to_s8(&err));
}
#endif /* _DEBUG */
@@ -218,6 +239,9 @@ static FILE_WATCH_CALLBACK_FN(load_cuda_lib)
#define X(name) cl->name = os_lookup_dynamic_symbol(cl->lib, #name, &err);
CUDA_LIB_FNS
#undef X
+
+ if (err.widx)
+ os_write_err_msg(stream_to_s8(&err));
}
#define X(name) if (!cl->name) cl->name = name ## _stub;
@@ -233,18 +257,18 @@ void glfwWindowHint(i32, i32);
iptr glfwCreateWindow(i32, i32, char *, iptr, iptr);
void glfwMakeContextCurrent(iptr);
-#include <stdio.h>
static PLATFORM_THREAD_ENTRY_POINT_FN(compute_worker_thread_entry_point)
{
GLWorkerThreadContext *ctx = (GLWorkerThreadContext *)_ctx;
glfwMakeContextCurrent(ctx->window_handle);
+ ctx->gl_context = os_get_native_gl_context(ctx->window_handle);
for (;;) {
ctx->asleep = 1;
os_sleep_thread(ctx->sync_handle);
ctx->asleep = 0;
- beamformer_complete_compute(ctx->user_context, ctx->arena);
+ beamformer_complete_compute(ctx->user_context, ctx->arena, ctx->gl_context);
}
unreachable();
diff --git a/util.h b/util.h
@@ -27,9 +27,11 @@
#else
#define DEBUG_EXPORT
#endif
+ #define DEBUG_DECL(a) a
#define ASSERT(c) do { if (!(c)) debugbreak(); } while (0);
#else
#define DEBUG_EXPORT static
+ #define DEBUG_DECL(a)
#define ASSERT(c)
#endif
@@ -210,6 +212,7 @@ typedef struct {
Arena arena;
iptr handle;
iptr window_handle;
+ iptr gl_context;
iptr sync_handle;
iptr user_context;
b32 asleep;
@@ -285,6 +288,20 @@ typedef PLATFORM_THREAD_ENTRY_POINT_FN(platform_thread_entry_point_fn);
X(write_new_file) \
X(write_file)
+#define RENDERDOC_GET_API_FN(name) b32 name(u32 version, void **out_api)
+typedef RENDERDOC_GET_API_FN(renderdoc_get_api_fn);
+
+#define RENDERDOC_START_FRAME_CAPTURE_FN(name) void name(iptr gl_context, iptr window_handle)
+typedef RENDERDOC_START_FRAME_CAPTURE_FN(renderdoc_start_frame_capture_fn);
+
+#define RENDERDOC_END_FRAME_CAPTURE_FN(name) b32 name(iptr gl_context, iptr window_handle)
+typedef RENDERDOC_END_FRAME_CAPTURE_FN(renderdoc_end_frame_capture_fn);
+
+typedef __attribute__((aligned(16))) u8 RenderDocAPI[216];
+#define RENDERDOC_API_FN_ADDR(a, offset) (*(iptr *)((*a) + offset))
+#define RENDERDOC_START_FRAME_CAPTURE(a) (renderdoc_start_frame_capture_fn *)RENDERDOC_API_FN_ADDR(a, 152)
+#define RENDERDOC_END_FRAME_CAPTURE(a) (renderdoc_end_frame_capture_fn *) RENDERDOC_API_FN_ADDR(a, 168)
+
#define X(name) platform_ ## name ## _fn *name;
struct Platform {
PLATFORM_FNS
@@ -292,6 +309,9 @@ struct Platform {
iptr os_context;
iptr error_file_handle;
GLWorkerThreadContext compute_worker;
+
+ DEBUG_DECL(renderdoc_start_frame_capture_fn *start_frame_capture);
+ DEBUG_DECL(renderdoc_end_frame_capture_fn *end_frame_capture);
};
#undef X