Why This Design
rdc-cli exists because RenderDoc captures GPU data but traps it behind a GUI. Scripts, CI pipelines, and AI agents cannot access this data programmatically. rdc-cli solves this by exposing capture data through a Unix-friendly CLI with pipe-friendly output.
The Problem
RenderDoc is the industry-standard open-source GPU debugger. It captures every API call, shader, texture, buffer, and render target in a single .rdc file. But all this data is locked inside a GUI application. There is no built-in way to:
- Query capture data from a script or CI pipeline
- Diff two captures programmatically
- Assert rendering correctness in automated tests
- Let an AI agent explore GPU state iteratively
rdc-cli bridges this gap: it turns .rdc files into streams of tab-separated text that Unix tools can process.
Why a Daemon
A 1 GB capture takes 30–60 seconds to load via
OpenCapture(). Reloading for every command is impractical.
rdc-cli uses a daemon architecture:
rdc open capture.rdcstarts a background daemon that loads the capture once- Subsequent commands (
draws,pipeline,shader, etc.) send JSON-RPC requests over TCP localhost - The daemon holds the ReplayController in memory — queries complete in milliseconds
rdc closeshuts down the daemon
Why TCP instead of Unix sockets? TCP works identically on Linux, macOS, and Windows — zero platform-specific code.
Why single-threaded? RenderDoc's ReplayController must be called from the thread that created it. The daemon uses an asyncio event loop and serializes all requests.
Why TSV by Default
Every command outputs tab-separated values by default. This means:
- Pipe to
grep,awk,cut,sort,diff,join— they all just work - Add
--jsonfor structured output when you need it (jq, CI assertions, AI agents) - Add
--jsonlfor streaming JSON (one object per line) - stderr carries metadata and summaries — stdout is always clean data
Composability in practice:
rdc draws --sort tri_count | tail -5 # heaviest 5 draws
rdc resources --type texture | wc -l # count textures
rdc shader-map | grep "a1b2" | cut -f1 # find EIDs using a shader
Exit codes follow diff(1) semantics: 0 = success/no difference,
1 = difference found, 2 = error.
Why VFS Paths
Capture data is organized as a virtual filesystem. Two layers with strict separation:
Navigation layer (ls, cat, tree) — pure addressing, no business logic:
rdc ls /draws/142/shader # → vs ps
rdc cat /draws/142/shader/ps # → shader disassembly
rdc tree / --depth 2 # → full structure Query layer (draws, resources, pipeline, etc.) — business logic with filtering:
rdc draws --sort tri_count --limit 10
rdc resources --type texture --sort size Why not FUSE? FUSE adds kernel module complexity, mount/unmount management, and platform dependencies. Path-string addressing with ls/cat/tree covers every use case without any of that overhead.
Every piece of data has a stable, deterministic path like
/draws/142/shader/ps/disasm. AI agents can navigate iteratively
— ls to discover, cat to read.
Why CI Assertions
Five purpose-built assertion commands cover the most common CI checks:
- assert-pixel — verify pixel color at (x,y) within tolerance
- assert-image — pixel-by-pixel image comparison
- assert-clean — no validation errors above severity threshold
- assert-count — numeric metric assertions (draws, triangles, etc.)
- assert-state — pipeline state value at EID matches expected
For anything else, compose with Unix tools:
test "$(rdc count draws)" -ge 10 # at least 10 draws
rdc log | grep -i error && exit 1 # fail on errors Why AI-Agent Friendly
rdc-cli is designed for programmatic consumption:
- Deterministic paths —
/draws/142/shader/psalways means the same thing - Machine-parseable output — TSV and JSON, never mixed formats on stdout
- Iterative exploration —
lsto discover structure,catto read data, just like a real filesystem - Escape hatch —
rdc scriptexecutes arbitrary Python inside the daemon when the 60 built-in commands aren't enough
The Claude Code skill (rdc install-skill) teaches AI agents the
full command vocabulary and VFS namespace.
Key Trade-offs
Design decisions have trade-offs:
| Decision | Trade-off |
|---|---|
| Single-threaded daemon | No parallel queries, but zero race conditions and simpler code |
| TSV default (not JSON) | Requires --json flag for structured data, but pipes work out of the box |
| GLSL-only shader edit | SPIR-V text assembly causes segfaults in RenderDoc; safety over coverage |
| No FUSE | Cannot mount as real filesystem, but no kernel dependencies |
| Session token auth | Security relies on file permissions (0600), not encryption |
| TCP localhost | No TLS currently; remote capture support is planned for a future phase |