Skip to content

feat(remote): support texture/RT/depth export in remote replay mode#237

Merged
BANANASJIM merged 11 commits into
masterfrom
feat/236-remote-texture-export
Jun 10, 2026
Merged

feat(remote): support texture/RT/depth export in remote replay mode#237
BANANASJIM merged 11 commits into
masterfrom
feat/236-remote-texture-export

Conversation

@BANANASJIM

@BANANASJIM BANANASJIM commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Closes #236.

Problem

rdc texture, rdc rt, and depth export returned "not supported in remote mode" when connected to a remote replayserver (--proxy / split / android), blocking CI pipelines that replay on a remote GPU target. Root cause: these handlers used SaveTexture(), which writes to the remote replayserver's filesystem, unreachable by the local daemon.

Solution

In remote mode, use controller.GetTextureData() (raw pixels over the JSON-RPC wire) and decode locally with numpy + Pillow. Local mode is unchanged (still SaveTexture).

  • Table-driven _decode_dtype / _decode_texture_png: handles Float16/32, UNorm8/16, UNormSRGB8, SNorm8/16 (signed-remapped), UInt8/16, Depth16/32, BGRA channel swap, 1–4 components, 3D textures (depth slices tiled), NaN-safe HDR clamp + sRGB OETF.
  • Unsupported formats (Typeless, SInt, UScaled, SScaled, packed/block/combined depth-stencil, MSAA, length mismatch, empty) return a clean -32002 error instead of a corrupt image.
  • rt_overlay stays remote-blocked (needs GPU compositing). rdc snapshot now surfaces skipped targets in remote mode instead of silently dropping them.

Testing

  • Real remote-replay GPU test (renderdoccmd remoteserver + --proxy) on a vkcube capture: texture and rt exports are pixel-identical to local SaveTexture output; D16 depth renders a correct grayscale gradient. Verified end-to-end through the normal rdc open --proxy CLI path.
  • ~30 new/updated unit tests (format matrix, reject cases, 3D, snapshot). Full unit suite green; ruff + mypy clean.

Known limitation

Depth visualization differs between local (RenderDoc renders depth into the red channel via SaveTexture) and remote (grayscale min-max stretch). Tracked for a follow-up to unify.

Summary by CodeRabbit

  • New Features

    • Texture and render-target exports now work in remote mode, producing PNGs with automatic format conversion, channel reordering, depth-to-grayscale mapping, and 3D-slice tiling.
  • Bug Fixes

    • Export process continues past unsupported targets, emitting clear skip/warning indications and stopping only for absent assets.
  • Tests

    • Expanded unit and regression tests covering many remote export formats and edge cases.
  • Documentation

    • Added proposal, task plan, and test plan documenting remote export behavior and validation steps.

…de (#236)

Remote sessions previously rejected tex_export, rt_export and rt_depth
because they call SaveTexture, which writes to the remote daemon host.
In remote mode, fetch raw pixels with GetTextureData (already wired over
JSON-RPC) and decode them locally with numpy + Pillow:

- Add _decode_texture_png helper handling Regular formats (UNorm/SRGB
  8/16-bit, Float16/32 with clip + sRGB OETF, BGRA swap, 1-4 channels)
  and contrast-stretched grayscale for depth targets.
- Reject block-compressed/packed/MSAA formats and length mismatches with
  a clear error instead of producing a corrupt image.
- Keep the local SaveTexture path unchanged and rt_overlay still blocked
  in remote mode (needs GPU compositing).
- Extend mock_renderdoc with ResourceFormatType for unit coverage.
…ote decode (#236)

- decode 3D textures (depth>1) by tiling depth slices vertically instead
  of rejecting them; depth==1 stays byte-for-byte identical
- sanitize NaN/Inf in float HDR via nan_to_num before clip (no black
  output, no numpy RuntimeWarning)
- wrap GetTextureData, write_bytes/stat, and empty raw guard in tex_raw
  so remote I/O failures surface as descriptive -32002 instead of -32603
Add call_with_code() to distinguish a missing resource (-32001) from an
unsupported operation (-32002). snapshot now stops the color loop only on
-32001 (target absent) and skip-and-warns on -32002 (decode unsupported)
instead of silently truncating the bundle. Depth export emits a stderr
warning when remote decode is unsupported (e.g. D24S8/MSAA).
Add coverage for the remote-decode and snapshot fixes: 3D depth-slice
tiling, NaN HDR rendering black without warning, cc=2/cc=3 channel
expansion, R16G16_SNORM signed remap, B8G8R8A8_SRGB BGRA swap pixel
values, and snapshot skip-and-warn behavior on -32002 color/depth
decode failures.
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f0d44080-dd8e-4c79-ad0a-5cc6733c1fce

📥 Commits

Reviewing files that changed from the base of the PR and between fc797a2 and f3cf4a6.

📒 Files selected for processing (5)
  • openspec/changes/2026-06-09-remote-texture-export/proposal.md
  • openspec/changes/2026-06-09-remote-texture-export/test-plan.md
  • src/rdc/handlers/_helpers.py
  • src/rdc/handlers/texture.py
  • tests/unit/test_tex_stats_handler.py
✅ Files skipped from review due to trivial changes (2)
  • openspec/changes/2026-06-09-remote-texture-export/test-plan.md
  • openspec/changes/2026-06-09-remote-texture-export/proposal.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/rdc/handlers/_helpers.py
  • src/rdc/handlers/texture.py
  • tests/unit/test_tex_stats_handler.py

📝 Walkthrough

Walkthrough

Replaces remote SaveTexture with controller.GetTextureData + client-side decode to produce PNGs; adds call_with_code RPC helper, format-aware decode helpers, routes tex_export/rt_export/rt_depth through remote decode, updates mocks/tests, and documents the design and test plan.

Changes

Remote Texture Export via GetTextureData

Layer / File(s) Summary
Specification, tasks, and test plan
openspec/changes/2026-06-09-remote-texture-export/{proposal,tasks,test-plan}.md
OpenSpec documents and task plan define remote export flow via GetTextureData, decode rules (BGRA→RGBA, sRGB OETF, dtype mappings), explicit -32002 rejection cases, retained rt_overlay rejection, and phased test/validation steps.
JSON-RPC helper and snapshot export flow
src/rdc/commands/_helpers.py, src/rdc/commands/snapshot.py
Adds call_with_code returning (result, error_code) and refactors snapshot exports to handle -32001 (absent) vs -32002 (unsupported decode), skipping unsupported targets while continuing and halting on absent.
Mock ResourceFormatType
tests/mocks/mock_renderdoc.py
Adds ResourceFormatType IntEnum used by decode/rejection tests.
PNG decode and color-space helpers
src/rdc/handlers/_helpers.py
Implements _srgb_encode, _decode_dtype, and _decode_texture_png to validate formats, map comp types→NumPy dtypes, handle float/snorm/uint16/uint8 paths, BGRA→RGBA reordering, 3D tiling, depth normalization, and explicit unsupported returns.
Remote texture export handlers
src/rdc/handlers/texture.py
Adds _export_remote to call GetTextureData, decode via _decode_texture_png, write PNG to temp path, and routes _handle_tex_export, _handle_rt_export, and _handle_rt_depth through this remote path; _handle_tex_raw now errors on empty data.
Unit tests and CLI tests
tests/unit/test_tex_stats_handler.py, tests/unit/test_snapshot_command.py
Adds tests validating remote tex_export/rt_export/rt_depth PNG outputs across many formats (RGBA, BGR(A) swap, R8/R16, float16/32 clipping, SNorm/UNorm mapping, 3D tiling, NaN handling) and snapshot CLI behavior for RPC error codes and local SaveTexture regression.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Daemon
  participant Decoder
  participant Filesystem
  Client->>Daemon: GetTextureData(resourceId, subresource)
  Daemon-->>Client: raw texel bytes OR JSON-RPC error(code)
  Client->>Decoder: _decode_texture_png(raw, format, is_depth)
  Decoder-->>Client: PNG bytes OR None (unsupported)
  Client->>Filesystem: write PNG to caller path
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • BANANASJIM/rdc-cli#172: Removes the "not supported in remote mode" guard that this PR replaces with GetTextureData-based export logic.
  • BANANASJIM/rdc-cli#207: Both PRs modify texture handler request flows in texture.py around GetTextureData/SaveTexture paths used by remote export.
  • BANANASJIM/rdc-cli#130: Related edits around rt_overlay remote-mode behavior and surrounding handler adjustments.

Suggested labels

Review effort 2/5

🐰 From far-off GPU hills I came to play,
I fetched the texels, byte by byte, hooray!
NumPy nudged colors, Pillow drew the scene,
BGRA flipped right, and depth turned to grey.
A tiny rabbit clap — PNGs saved today.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely summarizes the main change: adding remote replay mode support for texture/RT/depth export via controller.GetTextureData and local decoding.
Linked Issues check ✅ Passed All coding objectives from issue #236 are met: remote guard removed, GetTextureData fetching implemented, local numpy+Pillow decoding with format handling added, rt_overlay kept blocked, and error handling for unsupported formats included.
Out of Scope Changes check ✅ Passed All changes are directly aligned with remote texture export objectives; proposal/task/test plan documents, decode helpers, snapshot CLI updates, and test coverage are all in scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/236-remote-texture-export

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
openspec/changes/2026-06-09-remote-texture-export/test-plan.md (1)

105-109: ⚡ Quick win

Run command is environment-specific; provide a portable default.

The command hardcodes a local interpreter path/version, which is brittle for other contributors. Consider documenting a generic command first (e.g., pytest ...) and keeping env-specific variants as optional examples.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openspec/changes/2026-06-09-remote-texture-export/test-plan.md` around lines
105 - 109, The test-run command hardcodes an environment-specific Python path
(RENDERDOC_PYTHON_PATH and ./.venv-236/bin/python); change the documentation to
show a portable default first (e.g., a plain pytest invocation targeting
tests/unit/test_tex_stats_handler.py) and then add the environment-specific
variant as an optional example—mention RENDERDOC_PYTHON_PATH and the venv
command as alternate examples rather than the primary command so contributors
can run `pytest tests/unit/test_tex_stats_handler.py -q` by default.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openspec/changes/2026-06-09-remote-texture-export/proposal.md`:
- Around line 94-97: The Scope section currently lists 3D textures as "Out of
scope" but the implementation actually supports depth > 1 via slice tiling;
update the proposal text so it matches the code: either remove "3D textures
(`depth > 1`)" from the Out of scope list or change it to "In scope" and add a
short note describing the supported slice-tiling behavior (reference the
`tex_export`, `rt_export`, `rt_depth` features and the implementation's handling
of `depth > 1` / slice tiling). Ensure the wording clarifies any limitations
(e.g., max depth or tiling rules) so the spec and the decode implementation
remain consistent.

In `@openspec/changes/2026-06-09-remote-texture-export/test-plan.md`:
- Around line 105-107: The fenced code block containing the command starting
with "RENDERDOC_PYTHON_PATH=/usr/lib/python3.14/site-packages
./.venv-236/bin/python -m pytest tests/unit/test_tex_stats_handler.py -q" should
include a language tag (e.g., bash) after the opening backticks to satisfy
markdown linting and improve readability; update the fence from ``` to ```bash
so the block is recognized as bash/sh.

In `@src/rdc/handlers/_helpers.py`:
- Around line 356-360: The float RGBA export path currently applies _srgb_encode
to all channels (in the branch where ct == int(rd.CompType.Float)), which
incorrectly gamma-encodes alpha; change the logic in that branch so you apply
_srgb_encode only to the RGB channels of f (e.g., f[..., :3]) and leave the
alpha channel (f[..., 3] if present) in linear space, then combine the encoded
RGB and linear alpha, multiply by 255.0, round, and cast to uint8 to produce
rgba8; ensure you still handle arrays without alpha safely and preserve the
existing clipping/sanitization steps (sanitized, f) and use the same dtype
conversions.

In `@src/rdc/handlers/texture.py`:
- Around line 154-156: Wrap the call to controller.GetTextureData inside
_handle_tex_raw (the raw_data = controller.GetTextureData(...) line) in a
try/except; if GetTextureData raises, catch the exception and return the
JSON-RPC error using _error_response(request_id, -32002, f"GetTextureData error:
{e}") (or similar) and True so the handler returns a structured error instead of
letting the exception propagate. Ensure you reference the same request_id and
preserve the existing empty-data check path.

---

Nitpick comments:
In `@openspec/changes/2026-06-09-remote-texture-export/test-plan.md`:
- Around line 105-109: The test-run command hardcodes an environment-specific
Python path (RENDERDOC_PYTHON_PATH and ./.venv-236/bin/python); change the
documentation to show a portable default first (e.g., a plain pytest invocation
targeting tests/unit/test_tex_stats_handler.py) and then add the
environment-specific variant as an optional example—mention
RENDERDOC_PYTHON_PATH and the venv command as alternate examples rather than the
primary command so contributors can run `pytest
tests/unit/test_tex_stats_handler.py -q` by default.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 46c1f031-7af2-4b9f-b170-87d59fcfbfa1

📥 Commits

Reviewing files that changed from the base of the PR and between 4576962 and fc797a2.

📒 Files selected for processing (10)
  • openspec/changes/2026-06-09-remote-texture-export/proposal.md
  • openspec/changes/2026-06-09-remote-texture-export/tasks.md
  • openspec/changes/2026-06-09-remote-texture-export/test-plan.md
  • src/rdc/commands/_helpers.py
  • src/rdc/commands/snapshot.py
  • src/rdc/handlers/_helpers.py
  • src/rdc/handlers/texture.py
  • tests/mocks/mock_renderdoc.py
  • tests/unit/test_snapshot_command.py
  • tests/unit/test_tex_stats_handler.py

Comment thread openspec/changes/2026-06-09-remote-texture-export/proposal.md Outdated
Comment thread openspec/changes/2026-06-09-remote-texture-export/test-plan.md
Comment thread src/rdc/handlers/_helpers.py
Comment thread src/rdc/handlers/texture.py Outdated

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@BANANASJIM BANANASJIM merged commit 621528f into master Jun 10, 2026
17 of 18 checks passed
@BANANASJIM BANANASJIM deleted the feat/236-remote-texture-export branch June 10, 2026 03:54
BANANASJIM added a commit that referenced this pull request Jun 10, 2026
…mote export (#240)

* feat(remote): decode R11G11B10F and R9G9B9E5 packed HDR formats in remote export

Both formats are common HDR render-target and IBL storage formats that PR #237
scoped out. They are closed-form bit-unpackable in numpy with no GPU round-trip,
so they follow the existing local-decode path for Regular Float formats.

Add _unpack_r11g11b10 and _unpack_r9g9b9e5 vectorised helpers and branch on them
in _decode_texture_png before the Regular gate, carrying their own MSAA reject and
local width/height/depth/length checks. Decoded RGB feeds the existing Float
display pipeline (nan_to_num, clip, sRGB encode, opaque alpha, depth tiling).

Repurpose the former R11G11B10 rejection test to R5G6B5, which remains an
unsupported packed format after this change.

* docs(spec): correct subnormal decode value annotation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: support texture/RT export in remote replay mode

1 participant