rdc_trigger.c 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. // LD_PRELOAD companion for headless RenderDoc GL captures - no windowing system to send the
  2. // default F12 hotkey to, and no pyrenderdoc target-control setup in this environment, so this
  3. // calls the in-application C API directly instead. On load, waits a few seconds (long enough for
  4. // the target's GL context and first frames to exist), then triggers one capture.
  5. //
  6. // Build: gcc -shared -fPIC -o rdc_trigger.so rdc_trigger.c -I/opt/renderdoc/include -ldl -lpthread
  7. // Use: env -u WAYLAND_DISPLAY DISPLAY=:0 XDG_SESSION_TYPE=x11 \
  8. // LD_PRELOAD="/opt/renderdoc/lib/librenderdoc.so:/path/to/rdc_trigger.so" \
  9. // ./your_gl_app [args]
  10. //
  11. // Renderdoc's official Linux build only supports xlib/XCB windowing, so GLX-via-XWayland is the
  12. // only path that opens a capturable GL context here. XDG_SESSION_TYPE must be forced to "x11" -
  13. // GLFWOpenGLDriver otherwise picks the Wayland/EGL backend whenever the session type says
  14. // "wayland", even with DISPLAY set, and RenderDoc's hooks never see a GLX context to capture.
  15. #define _GNU_SOURCE
  16. #include <stdbool.h>
  17. #include <dlfcn.h>
  18. #include <pthread.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <unistd.h>
  22. #include "renderdoc_app.h"
  23. static void *trigger_thread(void *arg) {
  24. (void) arg;
  25. const char *delay_env = getenv("RDC_TRIGGER_DELAY_SEC");
  26. sleep(delay_env ? (unsigned) atoi(delay_env) : 4);
  27. void *mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
  28. if (!mod) {
  29. fprintf(stderr, "[rdc_trigger] librenderdoc.so not loaded yet\n");
  30. return NULL;
  31. }
  32. pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI) dlsym(mod, "RENDERDOC_GetAPI");
  33. if (!RENDERDOC_GetAPI) {
  34. fprintf(stderr, "[rdc_trigger] no RENDERDOC_GetAPI symbol\n");
  35. return NULL;
  36. }
  37. RENDERDOC_API_1_6_0 *rdoc = NULL;
  38. int ok = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, (void **) &rdoc);
  39. if (!ok || !rdoc) {
  40. fprintf(stderr, "[rdc_trigger] RENDERDOC_GetAPI failed\n");
  41. return NULL;
  42. }
  43. const char *capfile = getenv("RDC_TRIGGER_CAPFILE");
  44. if (capfile) {
  45. rdoc->SetCaptureFilePathTemplate(capfile);
  46. }
  47. fprintf(stderr, "[rdc_trigger] path template: %s\n", rdoc->GetCaptureFilePathTemplate());
  48. fprintf(stderr, "[rdc_trigger] IsFrameCapturing before trigger: %u\n", rdoc->IsFrameCapturing());
  49. fprintf(stderr, "[rdc_trigger] triggering capture now\n");
  50. rdoc->TriggerCapture();
  51. for (int i = 0; i < 10; i++) {
  52. usleep(200000);
  53. fprintf(stderr, "[rdc_trigger] tick %d IsFrameCapturing=%u NumCaptures=%d\n", i, rdoc->IsFrameCapturing(),
  54. rdoc->GetNumCaptures());
  55. }
  56. fprintf(stderr, "[rdc_trigger] done, num captures=%d\n", rdoc->GetNumCaptures());
  57. if (rdoc->GetNumCaptures() > 0) {
  58. char pathbuf[1024] = {0};
  59. uint32_t pathlen = sizeof(pathbuf);
  60. uint64_t ts = 0;
  61. rdoc->GetCapture(0, pathbuf, &pathlen, &ts);
  62. fprintf(stderr, "[rdc_trigger] capture 0 path: %s\n", pathbuf);
  63. }
  64. return NULL;
  65. }
  66. __attribute__((constructor))
  67. static void rdc_trigger_init(void) {
  68. pthread_t t;
  69. pthread_create(&t, NULL, trigger_thread, NULL);
  70. pthread_detach(t);
  71. }