Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
65bc267
feat(renderdoctools): add package scaffold and core execution engine
Kim2091 Apr 4, 2026
b17e204
feat(renderdoctools): add events command and CLI entry point
Kim2091 Apr 4, 2026
b2512cc
renderdoctools: add pipeline command (Task 3)
Kim2091 Apr 4, 2026
6dd52b2
renderdoctools: add textures command (Task 4)
Kim2091 Apr 4, 2026
f25ba7f
renderdoctools: add shaders command (Task 5)
Kim2091 Apr 4, 2026
353e7c6
renderdoctools: add mesh command (Task 6)
Kim2091 Apr 4, 2026
f77d5ec
renderdoctools: add counters command (Task 7)
Kim2091 Apr 4, 2026
2960f08
renderdoctools: add analyze command (Task 8)
Kim2091 Apr 4, 2026
2a309d3
renderdoctools: add info and capture utility commands (Task 9)
Kim2091 Apr 4, 2026
eee7541
feat(renderdoctools): add renderdoc-analysis skill document
Kim2091 Apr 4, 2026
cdca461
fix(renderdoctools): integration fixes from real capture testing
Kim2091 Apr 4, 2026
36d9aaf
test(renderdoctools): add 18 integration tests against real FO4 captures
Kim2091 Apr 4, 2026
dc10d12
fix(tests): use tests/test_rdc/ for integration test captures instead…
Kim2091 Apr 4, 2026
2c85aae
fix: renderdoctools bugs, test coverage, and documentation gaps
Kim2091 Apr 4, 2026
0e99226
feat(renderdoctools): add 11 new analysis commands, fix API mismatche…
Kim2091 Apr 4, 2026
6020f38
fix(renderdoctools): API audit fixes + skill doc improvements
Kim2091 Apr 5, 2026
a9fc588
fix(renderdoctools): api_calls SDObjectData.str -> .string
Kim2091 Apr 5, 2026
d72f6f8
Merge branch 'Ekozmaster:master' into renderdoc
Kim2091 Apr 6, 2026
b687e83
Sync .github prompts with .claude: add subagent-workflow, tool-dispat…
Kim2091 Apr 6, 2026
bf05f61
Merge branch 'renderdoc' of https://github.com/Kim2091/Vibe-Reverse-E…
Kim2091 Apr 6, 2026
7506b0b
docs(renderdoc-skill): add DX9 capture support and DDS export guidance
Kim2091 Apr 13, 2026
737ff5d
feat: switch RenderDoc to DX9-enabled build from Kim2091/renderdoc
Kim2091 Apr 13, 2026
445dffa
Update download path for renderdoc
Kim2091 Apr 13, 2026
3d4058a
docs(renderdoc-skill): document CLI capture as primary method, fix DX…
Kim2091 Apr 13, 2026
1e3fe35
fix(install): support latest RenderDoc package layout
Kim2091 Apr 14, 2026
4c1727d
docs(agent): expand workspace DX9 customization guidance
Kim2091 Apr 14, 2026
4117c61
Add RenderDoc install version tracking
Kim2091 Apr 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ These directories are **shared tooling and templates**. Do not modify them for g
- `retools/` — static analysis toolkit (shared tooling)
- `livetools/` — Frida-based dynamic analysis (shared tooling)
- `graphics/` — DX9 tracer framework (shared tooling)
- `renderdoctools/` — RenderDoc capture analysis toolkit (shared tooling)

**Per-game work goes in `patches/<GameName>/`.** When starting a new game, copy `rtx_remix_tools/dx/remix-comp-proxy/` (excluding `build/`) to `patches/<GameName>/` and edit the copy. If the user says "edit remix-comp-proxy code" without specifying, ask whether they mean the template or a game copy.

Expand Down Expand Up @@ -105,3 +106,4 @@ When working on any of the following — invoke the **`dx9-ffp-port` skill** imm
- **Subagent workflow and delegation rules**: @.claude/rules/subagent-workflow.md
- **DX9 FFP proxy porting for RTX Remix**: `.claude/skills/dx9-ffp-port/SKILL.md` (invoke `dx9-ffp-port` skill, not auto-loaded)
- **Frida-based dynamic analysis**: `/dynamic-analysis` skill
- **RenderDoc capture analysis**: `/renderdoc-analysis` skill
11 changes: 11 additions & 0 deletions .claude/rules/tool-dispatch.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ Under `rtx_remix_tools/dx/scripts/`. Use BEFORE retools for D3D9 questions. Run
- `find_blend_states.py $B` — D3DRS_VERTEXBLEND / INDEXEDVERTEXBLENDENABLE + WORLDMATRIX transforms
- `scan_d3d_region.py $B 0xSTART 0xEND` — D3D calls in code region

## RenderDoc capture analysis (main agent)

- `python -m renderdoctools events <rdc>` — list events/draw calls
- `python -m renderdoctools analyze <rdc> --summary` — capture overview
- `python -m renderdoctools pipeline <rdc> --event EID` — pipeline state
- `python -m renderdoctools textures <rdc> --event EID` — bound textures
- `python -m renderdoctools shaders <rdc> --event EID` — shader disassembly
- `python -m renderdoctools mesh <rdc> --event EID` — vertex data
- `python -m renderdoctools counters <rdc>` — GPU counters
- `python -m renderdoctools open <rdc>` — launch RenderDoc GUI

## dx9tracer

- Capture (main agent): `python -m graphics.directx.dx9.tracer trigger --game-dir <DIR>`
Expand Down
12 changes: 12 additions & 0 deletions .claude/skills/dynamic-analysis/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,3 +394,15 @@ python -m livetools analyze scene.jsonl --export-csv scene.csv
7. **Use modules to find DLL bases.** Before hooking a DLL function (e.g. D3D9 vtable), use `modules` to find the actual loaded base address.

8. **Composable pipeline.** `trace` captures raw records. `collect` streams them to disk. `analyze` aggregates offline. Chain them for any investigation.

9. **Hook the game's CALL instruction, not the DLL function.** To trace a D3D9 method (or any API call), find the `call [reg+offset]` or `call <addr>` instruction *in the game's code* via `xrefs.py` or `vtable.py calls`. Hook THAT address. Do NOT compute the target address inside d3d9.dll and hook there — the arguments are arranged at the caller, and the DLL entry point is shared across all callers.

10. **Zero hits means something is wrong — diagnose, don't give up.** If trace/collect returns 0 samples: (a) Ask the user: is the game window focused and actively rendering? (b) Verify the address: `disasm <addr>` in livetools — confirm real code exists there. (c) Try a known-hot address: `dipcnt callers 10` finds confirmed active call sites; trace one to prove the hook pipeline works. (d) Only after all three pass should you reconsider whether the original address is actually called during gameplay.

---

## Anti-Patterns

**Do NOT chase the "real" device pointer.** When working with D3D9, do NOT: read a device pointer from a global, dereference its vtable, compute `d3d9.dll_base + slot_offset`, and hook that address. This hooks inside the DLL where arguments are not in the expected layout and proxy/wrapper DLLs break the vtable chain. Hook the game's CALL instruction instead (pattern #9).

**Do NOT explain away zero data.** If a trace returns 0 samples, the answer is "I got no data and need to troubleshoot" — not "the game doesn't appear to use this code path." Follow the escalation in pattern #10.
251 changes: 251 additions & 0 deletions .claude/skills/renderdoc-analysis/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
---
name: renderdoc-analysis
description: RenderDoc-based GPU capture analysis toolkit for reverse engineering. Use when loading .rdc capture files, inspecting draw calls, examining pipeline state, viewing textures/shaders, decoding mesh data, analyzing GPU counters, or performing any graphics debugging task on a captured frame.
---

# RenderDoc Analysis with renderdoctools

Programmatic GPU capture analysis. Analyze .rdc files headlessly or launch the RenderDoc GUI for manual inspection.

All commands: `python -m renderdoctools <command> [args]`
All commands support `--json` for raw JSON and `--output FILE`.

## Capturing

A capture (`.rdc`) is a snapshot of one frame's entire GPU command stream: every draw call, state change, resource binding, and buffer/texture content at that point in time.

### CLI capture (preferred)

Launch a game with RenderDoc injection and capture — no GUI required:
```
python -m renderdoctools capture <exe> # launch + capture
python -m renderdoctools capture <exe> --output out.rdc # specify output filename
python -m renderdoctools capture <exe> -- --arg1 --arg2 # pass args to the game
```

This calls `renderdoccmd capture -w <exe>` under the hood. The game launches with RenderDoc hooked in. Press **F12** or **Print Screen** in-game to trigger the capture. The `.rdc` file is written to the working directory (or the `--output` path).

**Use this as the default capture method.** The agent can run this directly — no need to walk the user through the GUI.

### GUI capture (fallback)

If CLI capture fails (e.g. game needs specific launch options the CLI doesn't support):
```
python -m renderdoctools open <rdc> # open existing capture in GUI
```
Or launch RenderDoc GUI manually: File > Launch Application. Set executable + working dir. Hit Launch, press F12 in-game.

### Capture tips
- Navigate to the exact game state first, then capture. The captured frame is whatever's rendering at trigger time.
- For games with launchers, use `--opt-hook-children` to capture the child game process.
- D3D11/D3D12/Vulkan/OpenGL/DX9 supported. DX9 requires a custom RenderDoc build with the D3D9 driver.
- If the game crashes on inject, try `--opt-ref-all-resources` (slower but more compatible).
- Capture files can be 100MB-1GB+. Each contains full texture/buffer data for that frame.

## DX9 Captures

DX9 support requires a custom RenderDoc build with the D3D9 replay driver. When working with DX9 captures:

- **Texture export must use DDS format.** PNG export hangs or corrupts on DX9 BC/DXT textures. Always pass `--format dds`:
```
python -m renderdoctools textures capture.rdc --event <EID> --save-all ./dump --format dds
```
- **Supported commands:** `events`, `analyze`, `pipeline`, `textures` (list + DDS export), `mesh`, `tex-data`, `api-calls`, `usage`, `frame-info`
- **Limited/unsupported:** `shaders` (disassembly not available for SM1-3 bytecode), `debug-shader`, `custom-shader`, `counters`, `pixel-history`, `tex-stats`, `pick-pixel`
- **Pipeline state** reports vertex and pixel shader stages with constant buffers and sampler bindings. Hull/domain/geometry/compute stages don't exist in DX9.
- **Mesh data** decodes vertex attributes (POSITION, NORMAL, TEXCOORD, etc.) from the vertex declaration.

## Quick Reference

| Command | Description |
|---------|-------------|
| `events <rdc>` | List all events/draw calls |
| `events <rdc> --draws-only` | Draw calls only |
| `pipeline <rdc> --event EID` | Pipeline state at event |
| `pipeline <rdc> --event EID --stage pixel` | Single stage |
| `textures <rdc> --event EID` | List bound textures |
| `textures <rdc> --event EID --save-all DIR` | Export all textures (PNG) |
| `textures <rdc> --event EID --save-all DIR --format dds` | Export all textures (DDS, required for DX9) |
| `shaders <rdc> --event EID` | Disassemble bound shaders |
| `shaders <rdc> --event EID --cbuffers` | Include constant buffer values |
| `mesh <rdc> --event EID` | Vertex input data |
| `mesh <rdc> --event EID --post-vs` | Post-VS output |
| `descriptors <rdc> --event EID` | All descriptors accessed at event |
| `descriptors <rdc> --event EID --type srv` | Filter: sampler/cbuffer/srv/uav |
| `api-calls <rdc>` | List all API calls with inline params |
| `api-calls <rdc> --event EID` | Detailed params for one event |
| `api-calls <rdc> --filter "Map"` | Filter calls by function name |
| `api-calls <rdc> --range 100 200` | Calls in event ID range |
| `counters <rdc>` | List GPU counters |
| `counters <rdc> --zero-samples` | Find wasted draws |
| `analyze <rdc> --summary` | Capture overview stats |
| `analyze <rdc> --biggest-draws 10` | Top N draws by vertex count |
| `analyze <rdc> --render-targets` | Unique render targets |
| `pixel-history <rdc> --event EID --resource RID --x X --y Y` | What drew to this pixel? |
| `pick-pixel <rdc> --resource RID --x X --y Y` | Read pixel value at (x,y) |
| `pick-pixel <rdc> --resource RID --x X --y Y --comp-type float` | Pick with type override |
| `messages <rdc>` | All API debug/validation messages |
| `messages <rdc> --severity high` | Only high+ severity messages |
| `tex-stats <rdc> --resource RID` | Min/max RGBA values of a texture |
| `tex-stats <rdc> --resource RID --histogram` | Value distribution histogram |
| `custom-shader <rdc> --event EID --source FILE --output FILE` | Apply custom viz shader |
| `tex-data <rdc> --resource RID` | Raw bytes + hex preview |
| `tex-data <rdc> --resource RID --output-file out.bin` | Save raw texture bytes |
| `usage <rdc> --resource RID` | Which events read/write a resource? |
| `usage <rdc> --resource RID --filter read` | Only read usages |
| `usage <rdc> --resource RID --filter write` | Only write usages |
| `frame-info <rdc>` | Frame stats: draws, dispatches, binds, state changes |
| `debug-shader <rdc> --event EID --mode vertex --vertex-index N` | Debug vertex shader |
| `debug-shader <rdc> --event EID --mode pixel --x X --y Y` | Debug pixel shader |
| `debug-shader <rdc> --event EID --mode compute --group 0,0,0 --thread 0,0,0` | Debug compute |
| `open <rdc>` | Launch RenderDoc GUI |
| `capture <exe> [--output FILE] [-- EXE_ARGS]` | Launch game with RenderDoc injection, capture on F12 |

## Verification: Confirm Before You Dig

Always verify you're looking at the right thing before deep analysis.

**Dump and check render targets:**
```
python -m renderdoctools textures capture.rdc --event <EID> --save-all ./verify
python -m renderdoctools textures capture.rdc --event <EID> --save-all ./verify --format dds # DX9 captures
```
Open the images. Confirm the render target matches expected game output. If it's a depth buffer, GBuffer, or intermediate pass you don't recognize — wrong draw.

**Spot-check pixel values:**
```
python -m renderdoctools pick-pixel capture.rdc --resource <RID> --x 100 --y 100
```
Normal map: expect RGB near (0.5, 0.5, 1.0). HDR color: expect values > 1.0. All zeros: resource uninitialized or cleared at that EID.

**Compare before/after:** Dump textures at two EIDs to confirm a draw changes what you expect.

## Finding the Right Draw Call

Core RE question: "which draw call renders X?"

### Strategy 1: Work backwards from render targets
```
python -m renderdoctools analyze capture.rdc --render-targets
```
Dump the most-written RTs, visually identify which contains your target, then:
```
python -m renderdoctools usage capture.rdc --resource <RT_RID> --filter write
```
Lists every draw writing to it. Narrow by EID range.

### Strategy 2: Binary search by EID
`events --draws-only`, pick midpoint EID, dump its RTs. Content there? Search earlier. Not there? Search later. Converge on the exact draw.

### Strategy 3: Filter by name
Many engines annotate draws with debug markers:
```
python -m renderdoctools events capture.rdc --filter "shadow"
python -m renderdoctools events capture.rdc --filter "GBuffer"
```

### Strategy 4: Filter by geometry size
```
python -m renderdoctools analyze capture.rdc --biggest-draws 20
```

## Multi-Pass Analysis

Reconstruct a render pipeline — who writes what, who reads it:

1. `analyze --render-targets` — list all unique RTs
2. For each RT: `usage --resource <RID>` — all reads and writes
3. Write events = pass boundaries. Reads between writes = consumers of that pass.
4. Dump textures at key EIDs to label each pass (shadow, GBuffer, lighting, post, final)

RT written at EID 100, 300, 500. Read at EID 200, 400. Means: Pass A (100) produces, Pass B (200) consumes, Pass C (300) overwrites, etc.

## Interpreting Shader Debug Output

`debug-shader` produces: inputs, constant blocks, per-step variable changes, source locations.

**What to look for:**
- **NaN/Inf:** float values becoming NaN mid-shader = division by zero or bad input. Trace the step that introduced it.
- **Unexpected zeros:** input that should be nonzero reads as 0 = wrong binding or uninitialized resource.
- **Matrix transforms:** check `finalState` output position in vertex shaders. Offscreen or degenerate = bad matrices in constant blocks.
- **Shader discards:** `shaderDiscarded: true` in pixel history. Debug the pixel shader to find the discard condition.

**Combine with cbuffer inspection:**
```
python -m renderdoctools shaders capture.rdc --event <EID> --cbuffers
python -m renderdoctools debug-shader capture.rdc --event <EID> --mode pixel --x X --y Y
```

## When Data Looks Wrong

Checklist:

1. **Correct EID?** All pipeline/texture/resource queries reflect state at the queried EID. Wrong EID = wrong data.
2. **Correct resource?** Resource IDs are per-capture. Re-discover with `textures` or `analyze --render-targets`.
3. **Correct format?** `pick-pixel` with wrong `--comp-type` reads garbage. Check `textures` output for actual format.
4. **Initialized?** All zeros = resource not yet written at that EID. `usage --resource <RID> --filter write` finds the first write.
5. **Mip/slice?** Querying mip 0 of a texture only written at mip 1+ returns stale data. Use `--sub-mip`.

## Falling Back to the GUI

When programmatic analysis can't get you there:

```
python -m renderdoctools open capture.rdc
```

**GUI strengths over CLI:**
- **Texture viewer scrubbing:** step through events watching render targets update live. Fastest way to find "which draw renders X."
- **Mesh viewer:** 3D vertex visualization with rotation/zoom. Essential for understanding vertex transforms.
- **Shader debugger with source:** step through HLSL/GLSL with variable watch, breakpoints, source highlighting. Far richer than JSON trace.
- **Overlay modes:** wireframe, depth, stencil, overdraw heat map.
- **Resource inspector:** browse all textures/buffers with format decoding, mip/slice selection.

**Workflow:** Open in GUI, visually locate the draw/resource, note the EID and resource ID, return to CLI for scripted/batch operations.

## Workflow Recipes

### Quick capture overview
```
python -m renderdoctools analyze capture.rdc --summary
python -m renderdoctools events capture.rdc --draws-only
python -m renderdoctools analyze capture.rdc --biggest-draws 10
```

### Investigate a specific draw
```
python -m renderdoctools pipeline capture.rdc --event <EID>
python -m renderdoctools textures capture.rdc --event <EID> --save-all ./dump
python -m renderdoctools shaders capture.rdc --event <EID> --cbuffers
```

### Shader debugging
```
python -m renderdoctools debug-shader capture.rdc --event <EID> --mode vertex --vertex-index 0
python -m renderdoctools debug-shader capture.rdc --event <EID> --mode pixel --x 512 --y 384
python -m renderdoctools debug-shader capture.rdc --event <EID> --mode compute --group 0,0,0 --thread 0,0,0
```

### Resource tracking
```
python -m renderdoctools usage capture.rdc --resource <RID>
python -m renderdoctools usage capture.rdc --resource <RID> --filter write
```

### Full frame audit
```
python -m renderdoctools analyze capture.rdc --summary
python -m renderdoctools analyze capture.rdc --render-targets
python -m renderdoctools messages capture.rdc --severity high
python -m renderdoctools counters capture.rdc --zero-samples
```

## Thinking Patterns

1. **Broad to narrow.** `analyze --summary` > `events --draws-only` > `pipeline`/`shaders`/`textures` on the target draw.
2. **Verify with texture dumps.** Dump render targets and visually confirm before deep analysis.
3. **Cross-reference with livetools.** Match draw call patterns with function traces from dynamic analysis.
4. **Track dependencies with usage.** `--filter write` = producers. `--filter read` = consumers.
5. **Debug shaders for transforms.** `debug-shader` + `shaders --cbuffers` traces inputs through shader code.
6. **GUI when stuck.** Scrub through events in the texture viewer to visually identify draws.
7. **JSON for automation.** `--json` on any command for scripted pipelines.
11 changes: 11 additions & 0 deletions .cursor/rules/project-workspace.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ alwaysApply: true

# Project Workspace

## Read-Only Templates

Do not modify for game-specific work — per-game changes go in `patches/<GameName>/`.

- `rtx_remix_tools/dx/remix-comp-proxy/` — proxy framework **template** (copy per-game)
- `rtx_remix_tools/dx/scripts/`, `retools/`, `livetools/`, `graphics/`, `renderdoctools/` — shared tooling

Shared tooling can be modified to improve tools themselves — not for game-specific customization.

## Per-Game Work

Use `patches/<project_name>/` (git-ignored) for all project-specific artifacts:
- Knowledge base files (`kb.h`)
- One-off analysis scripts
Expand Down
Loading
Loading