mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 18:01:03 +00:00
7.6 KiB
7.6 KiB
Knowledge base
Before investigating a symptom, or trusting a PR that "fixes" it, look for it here. Search by what the player sees (black, magenta, flicker, slow motion, missing, stale, streaks) and by the object (sky, grass, shadow, fog, HUD).
Invariants of recent changes
A PR that touches these areas must keep them.
- Queue order. When anything is submitted, every earlier part is already on the queue. Present, uploads, readbacks
from guest threads and the
vkDeviceWaitIdlesites rely on it. The recorder thread now begins, ends and submits the parts it records; the submitting thread still waits for that submission, so the order holds.TOTK_REC_END=0restores the old split. - GPU-written memory is never frozen. Storage-buffer stores, copies and fills land in imported guest memory without a fault or a write-watch bit, so a region only the GPU writes looks idle. Such regions are never frozen read-only, and a frozen region is thawed before a GPU write. LAS Recomp never changes the protection of imported memory because of NVIDIA. A suspected cause of NVIDIA "write invalid" device faults (#788, #802), not proven.
- VRAM pressure. Under pressure, released textures whose storage the game has given to another texture go first:
30 s after release and once no finalized slot has bound them for 600 submits. Clean mirrored entries wait 5 s.
Both stop once use is back under the line. A PR touching texture lifetime is tested with
TOTK_VRAM_LIMIT_MIB. - Texture pins are freed. Each pin counts the recording copies naming it and is freed 1200 submits after its last use unless the replay holds it as a render target. Before that, pins leaked ~44 MiB a game minute (#670).
Fixed bug classes
If a PR touches this code, confirm the fix still holds.
| Symptom | Cause | Where the fix is |
|---|---|---|
| lime silhouettes in grass; rectangles in the sky | a target outside the output map must keep its texel; zero-filling it wiped the half-res depth | vk_pipeline.c target_wmask |
| stale grass maps in the far terrain for a second after a hit | write-back of a texture the CPU had rewritten | vk_texture.c write_back |
| cut grass stays standing; fire does not spread (#132) | CPU reads of GPU-rendered textures without an invalidate | vk_texture.c vk_texture_write_back_small, nvn_ext.c nvn_pool_invalidate |
| cloud and fog volumes empty for the session | a one-shot draw skipped instead of waiting for its pipeline | vk_exec_draw.c prepare_draw |
| tile rectangles | storage buffers bound by Ryujinx first-use order, not the c0 slot | vk_exec_draw.c ssbo_slot |
| black dialog background and loading map | 3D targets rejected, the grading LUT never written | vk_exec.c resolve_target |
| streaks on cliffs | primitive restart ignored | vk_exec_draw.c restart_for |
| streaky map screen | the newest texture picked at shared storage | vk_texture.c vk_texture_by_id |
| dark 16x16 blocks on the ground, sage spirit not drawn | G-buffer feedback loop | vk_exec_draw.c, TOTK_FEEDBACK |
| magenta cave walls, brown sky | CPU-filled textures without a flush | write-watched mirrors, vk_memory.c, vk_texture.c |
| black 3D scene | deferred texture binds | nvn_api_cmd.c Bind*Deferred |
| HUD prompt boxes, black stamina wheel | nvnTextureCompare ignored the channel mapping |
nvn_api_obj.c |
| crash on world load | GetCommandMemoryUsed did not follow the driver's sizes; the game sizes model heaps from it |
nvn_api_cmd.c |
| 20 fps | a yield back-off starting before 2 ms | kernel.c SVC 0x0B: no back-off |
| weapons locked after a Fuse (#18) | timer remainder in (0, FLT_EPSILON] with the real-time step | patch under the realtime setting |
| magenta/blue garbage or device lost on RDNA1/2 (#5) | writes into 3D BC block views | in dev |
| black window after a driver update | a pipeline cache from another driver | vk_pipeline.c drops the cache by UUID |
Known open risks
A PR touching these needs stronger proof.
- Buffer textures are not replayed and bind a stand-in: light and decal lists may read 0.
- New GPU-only colour images start undefined; feedback effects may show garbage.
- Clears with a partial channel or stencil mask are skipped (
W_CLEAR_MASK,W_STENCIL_MASK). - 8-bit indices are widened on the CPU at record time; a copy in the same submission gives stale indices.
UpdateUniformBufferis a copy between full barriers and ends the render pass (fps outdoors).- Report timestamps carry host time at fence completion: the engine's GPU time is wrong.
- The game's SVC-free spin loops burn a host core (843 in
main). - Images aliased onto one GPU-only pool allocation do not see each other's data on AMD.
Past PR decisions
| PR | Decision | Why |
|---|---|---|
| #690 shrine snapshots | rejected | a pass restart per copy, a VkPipeState copy per draw, broken bind_sig skip; fps loss |
| #674 | rejected | a global STXR counter on the hot path |
| #660 | rejected | the preview loses anti-aliasing |
| #713, #576 | rejected | low memory block 10 GiB for everyone; should be 6 GiB plus the extra where needed |
| #573 | rejected | dynamic resolution off by default for everyone, the 1.4.3 site never run |
| #568, #569 | rejected | fake FSR4 and frame generation |
| #487 | rejected | wrong metrics: percentiles over window means, counters over the wrong set |
| #451, #380 | rejected | not painless to take |
| #566, #594, #534 DLSS | taken with changes | #594's shared paths gated on DLSS being on; nothing changes without DLSS |
| #489 | partly taken | the runtime part checked by a frame dump; the installer's DLL download was the owner's call, its pinned hash re-checked |
| #519, #582 amiibo | rewritten by us | took the data-format fixes; left out the features and the Linux event rework |
| #483 vs #585 | #585's mapping kept | #483's stick glyph mapping was rewritten unasked while #585 already had it right |
| #528 rain | taken after a check | code review fine, merged only after a run on a rain save |
| #767 | taken with our fix | the default was skipped with prompts = xbox|pc when those folders did not exist |
| #625 frame queue | taken, off by default | a new frame queue, one field run with it on |
| #743 UB overlay | waiting | +25% in a Depths fight on an RTX 4080; needs review, fight runs x2 and shots on AMD |
Our mistakes, not to repeat
- "-11%" was an A/B artifact of a moving scene. Whole runs off/on showed zero.
- Micro-optimizations from a flat profile (1-3% of samples each) gave 26.2 against 26.4 ms on the weak laptop: nothing. Nobody had computed a ceiling.
- A scheduler patch merged on run statistics added 50-75 ms hitches. Slow frames are a hard bar.
- Ten game runs on lock theories where one Ghidra read of the game's code narrowed it at once.
- 45 minutes of builds and benches on an idea its own first thread census had refuted: the game pins 53 of 56 threads itself. The cheapest refuting check goes first; when it refutes, stop and write the idea down.
- Tests of a bug that could not appear on the hardware at hand (NVIDIA paging on an AMD machine): 245k tokens and 37 minutes for nothing. Ask first where the bug reproduces.
- Turning the recorder off for a vendor instead of finding its bug. Stopped: the feature gets fixed, not silenced.
- Bisecting code when every build crashed the same way at once. The cause was a bad page of a game file in the OS file cache. When all builds fail identically, suspect inputs and caches first.
- A duplicate issue closed with a bare "Same as #N" lost the logs and links. Move everything useful to the issue that stays open first: hardware, driver, version, when it happens, workarounds, links from comments.