// RenderDoc in-app capture (debug tool). TOTK_RDOC=[,...] captures those presented frames; TOTK_RDOC_DIR is the // output directory (default build/captures); TOTK_RDOC_DLL overrides the library's path (renderdoc.dll, librenderdoc.so). // Only the API fields used are declared here (layout of RENDERDOC_API_1_4_1, renderdoc_app.h, MIT), so no RenderDoc header // is vendored. #include #include #include #include #include "cfg.h" #include "host.h" #include "vk.h" #include "platform/platform.h" #define RDOC_API_1_4_1 10401 #define RDOC_MAX_FRAMES 64 typedef struct { void *GetAPIVersion; uint32_t (*SetCaptureOptionU32)(int opt, uint32_t val); void *fn2_10[9]; // SetCaptureOptionF32 .. UnloadCrashHandler void (*SetCaptureFilePathTemplate)(const char *); void *GetCaptureFilePathTemplate; uint32_t (*GetNumCaptures)(void); uint32_t (*GetCapture)(uint32_t idx, char *filename, uint32_t *pathlength, uint64_t *timestamp); void *TriggerCapture, *IsTargetControlConnected, *LaunchReplayUI, *SetActiveWindow; void (*StartFrameCapture)(void *device, void *window); uint32_t (*IsFrameCapturing)(void); uint32_t (*EndFrameCapture)(void *device, void *window); } RdocApi; static RdocApi *api; static uint64_t targets[RDOC_MAX_FRAMES]; static int n_targets; static int capturing; int rdoc_active(void) { return api != NULL; } void rdoc_init(void) { const char *e = cfg_get("TOTK_RDOC"); if (!e || !*e) return; for (const char *p = e; *p && n_targets < RDOC_MAX_FRAMES;) { char *end; unsigned long long v = strtoull(p, &end, 10); if (end == p) break; targets[n_targets++] = v; p = *end == ',' ? end + 1 : end; if (*end != ',') break; } if (!n_targets) { rt_log("rdoc: TOTK_RDOC=%s has no frame numbers", e); return; } const char *dll = cfg_get("TOTK_RDOC_DLL"); #ifdef _WIN32 const char *def = "C:/Program Files/RenderDoc/renderdoc.dll"; #else const char *def = "librenderdoc.so"; #endif void *m = plat_lib_open(dll && *dll ? dll : def); if (!m) { rt_log("rdoc: cannot load %s; set TOTK_RDOC_DLL", dll && *dll ? dll : def); return; } int (*get_api)(int, void **) = (int (*)(int, void **))plat_lib_sym(m, "RENDERDOC_GetAPI"); void *a = NULL; if (!get_api || get_api(RDOC_API_1_4_1, &a) != 1 || !a) { rt_log("rdoc: RENDERDOC_GetAPI(1.4.1) failed"); return; } api = a; // Option ids of eRENDERDOC_CaptureOption: 8 RefAllResources, 9 SaveAllInitials, 12 AllowUnsupportedVendorExtensions. // The back end imports guest pages with VK_EXT_external_memory_host, which RenderDoc hides unless 12 is set. rt_log("rdoc: AllowUnsupportedVendorExtensions -> %u", api->SetCaptureOptionU32(12, 1)); api->SetCaptureOptionU32(8, 1); api->SetCaptureOptionU32(9, 1); const char *dir = cfg_get("TOTK_RDOC_DIR"); char full[PLAT_PATH_MAX], tmpl[PLAT_PATH_MAX + 16]; if (!plat_full_path(dir && *dir ? dir : "build/captures", full, sizeof full)) return; plat_file_mkdir_p(full); snprintf(tmpl, sizeof tmpl, "%s/totk", full); api->SetCaptureFilePathTemplate(tmpl); rt_log("rdoc: ready, %d frame(s), captures %s", n_targets, full); } // Called by vk_be_present after frame `frame` was queued and the present drained. A capture of frame N starts after frame N-1's // present finished and ends after frame N's present, so it holds frame N's submits and its blit/present. void rdoc_after_present(uint64_t frame) { if (!api) return; if (capturing) { api->EndFrameCapture(NULL, NULL); capturing = 0; char path[PLAT_PATH_MAX]; uint32_t len = sizeof path, n = api->GetNumCaptures(); if (n && api->GetCapture(n - 1, path, &len, NULL)) rt_log("rdoc: frame %llu captured: %s", (unsigned long long)frame, path); else rt_log("rdoc: frame %llu capture failed", (unsigned long long)frame); } for (int i = 0; i < n_targets; i++) if (targets[i] == frame + 1) { api->StartFrameCapture(NULL, NULL); capturing = 1; rt_log("rdoc: capture of frame %llu started", (unsigned long long)(frame + 1)); } }