Latte: mirror small 1D-tiled render targets back to guest memory#2007
Open
NKR0ne wants to merge 1 commit into
Open
Latte: mirror small 1D-tiled render targets back to guest memory#2007NKR0ne wants to merge 1 commit into
NKR0ne wants to merge 1 commit into
Conversation
Titles that reduce a frame down to a tiny surface on the GPU and then read the result back on the CPU never received that data, because enableReadback was only set for TM_LINEAR_ALIGNED surfaces. Disney Infinity does this for auto exposure. It reduces the frame through 1152x600 -> 284x150 -> 96x48 -> 17x16 -> 4x8 -> a final 1x8 R32_FLOAT holding average scene luminance, then reads that value on the CPU to derive an exposure scalar which it uploads as a shader uniform. The chain is TM_1D_TILED_THIN1, so it was never mirrored back, the game read stale zeros and uploaded an exposure of 0, and the lighting shader (which ends in colour * uf_remappedPS[13].x) rendered the entire scene black. UI is drawn after that multiply and stayed visible, so the symptom was a black scene with working audio, menus and HUD on both the Vulkan and OpenGL backends. Also enable readback for TM_1D_TILED_THIN1 surfaces of at most 64x64. Full size render targets are unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Member
|
Appreciate the pull requests, but they fail to meet our AI contribution guidelines |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Disney Infinity renders the entire scene black. Audio, menus, HUD and input all work normally, and
the black scene reproduces identically on both the Vulkan and OpenGL backends.
Cause
The game implements auto-exposure on the GPU. It reduces the frame through a chain of render
targets — 1152x600 → 284x150 → 96x48 → 17x16 → 4x8 → a final 1x8 R32_FLOAT holding average
scene luminance — and then reads that value back on the CPU to compute an exposure scalar, which it
uploads as a shader uniform.
LatteTextureonly setsenableReadbackwhentileMode == TM_LINEAR_ALIGNED. That chain isTM_1D_TILED_THIN1, so the result was never mirrored back to guest memory. The game read stalezeros, computed an exposure of
0, and uploaded it. Cemu then passed that zero through perfectlycorrectly — the lighting shader ends in
so every lit pixel became black. UI is drawn after that multiply, which is why it stayed visible
and made this look like a render-target or presentation bug rather than a readback one.
How it was diagnosed
Captured with RenderDoc:
(
passed=1) while writingshaderOut=(0.0000, 0.0000, 0.0000, 0.0000)— so geometry rasterizescorrectly and the pixel shader itself produces black.
uf_remappedPS[13] = (0, 0, 0, 0)while every other entry waspopulated, and while the same frame's G-buffer pass had fully populated uniforms.
For completeness, these were ruled out along the way: depth-
EQUALrejection (disproved by forcingEQUAL→ALWAYS), texture-cache format aliasing, scissor/viewport, colour write masks, swapchainacquisition, and shader position invariance.
Fix
Also enable readback for
TM_1D_TILED_THIN1targets of at most 64x64. Full-size render targets areuntouched, so the common case is unaffected.
The size limit is a heuristic — it's meant to cover the "GPU reduces something to a tiny surface
that the CPU then reads" pattern while avoiding readback cost on real render targets. Happy to
change the bound, key it on something other than dimensions, or narrow the condition further if
you'd prefer something more targeted.
Testing
0005000010132900), Vulkan, NVIDIA RTX 3080 — scene renders correctlywith the fix, black without it. Verified on a clean build with all debug instrumentation removed.
introduces a GPU→CPU sync, so I'd appreciate a second opinion on whether the 64x64 bound is
conservative enough, and I'm glad to run any specific title you'd like checked before merge.