fix(vk): terrain tiles the GPU drew are written into guest memory (the ground garbling after minutes in one area) #2545
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2545
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/drawn-tile-writeback"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
After some minutes of play inside one area the ground can turn into a woven or smeared pattern and stay that way until a
restart: Dodongo's Cavern's floor, the sand outside Gerudo Valley, Hyrule Field. The terrain's normal tiles are all zero
there. Their texels were drawn on the GPU and never reached guest memory, and the game copies that memory.
Refs #1717 (and the reports folded into it: #2213, #2276, #2297). Tracking: #2508.
What goes wrong
copies. On the console the drawn texels are in that memory. Here they are only in the Vulkan image, so the copy of a
drawn tile is all zero, and the terrain samples a zero normal tile.
vk: upload over a drawn image: ... 260x260x1 x1 NVN 0xd ... finalizedis a second way into the samefault: the game writes a finalized tile's memory (zeros, before the slot is used again) and that memory is uploaded
over the drawn image.
What changes
the textures due in a frame are recorded with the present's uploads, as the VRAM compaction's copies are. Nothing
waits for the GPU: a later frame, once the GPU is past that recording, writes the texels into memory. A first version
submitted the batch on its own and waited: 3 to 8 ms of the frame per batch, and 232 ms once behind an area load's
uploads.
the CPU's bytes. A texture destroyed, drawn or uploaded over between the copy and the write, or whose storage another
image was drawn over, is dropped. An image with a copy outstanding is kept until the GPU is past it.
has to be all zero (the game clears a tile's slot before it draws the tile) or still hold the texture's own last
write-back. The page generations know a CPU write only once its capture was taken, so this check does not rely on
them. A texture left out this way is counted in the log line.
against the memory the check read, the memory is compared with it once more, and each word goes in only where it
still holds that value. If a guest thread wrote a word meanwhile, the words this write put in are taken out again the
same way. Either all of the texture's texels are in its memory or the memory is as the game has it.
theirs (from any start address) was drawn in that frame or since (that memory is the newer image's), ones the GPU
only cleared (a font cache, whose glyphs come from the CPU), compressed, depth and multisampled ones, and anything
flushable()already refuses.gpu/vk/drawn_writeback.h.RX_VK_NO_DRAWN_WRITEBACK=1turns the change off.Credit and overlap
write-back two frames after the drawing holds for 80 minutes. The two-frame rule here follows that result. Their case
of a tile finalized the frame after its drawing is covered: a finalize does not stop the write-back.
my own play (the woven sand outside Gerudo Valley, then Dodongo's Cavern) and its logs (the
upload over a drawn imageline in three sessions with the woven ground and in none without), and I only learned of the claim thisafternoon, when the fix was already built and in testing. I am not on Discord, where claims are made, so I had no way
to claim it there. Sorry for the overlap, Joey: I am
opened this as a draft first so the work and the numbers were in one place, not to step on yours. I am happy to compare
notes, fold the best parts of both into one PR, test yours on my PC, or close this one if yours lands first.
Testing
python3 -I -B tests/test_drawn_writeback.pypasses (Linux, g++): the rule's cases, the compare andwrite with a guest write before, during and after it and its undo, and that
vk_texture.cppstill notes a texture when its GPU contents begin, asks the rule each frame and checks the memorybefore the write.
tests/test_compressed_over_rt.pyandtests/test_astc_bc_policy.pypass too.tests/run_all.pyon Linux: 361 passed, 62skipped (no Vulkan headers or .NET here), 1 failed:
test_bake_portability.py, which needs the zstandard module thismachine does not have.
Hyrule Field as adult, the same input both times, on this branch's first version (it waited for the GPU; the rule and
the memory check as here), with a local debug line (not part of the change) for each small mip-chained texture made
from guest memory.
all (5000 textures made from memory, none of them such a tile), so none could be zero, and the run says nothing
about the fix itself.
failed, no crash. That version's time on the frame is in What changes.
PC, outside Gerudo Valley, where I had seen the sand turn into the woven pattern before; these figures are from the
session's first 7 minutes, the start and the way there included. The ground looked right from the start, with no bare sand first and no woven pattern. In that time the game
made 122 of the 260 x 260 normal tiles from memory, every one with real texels and none all zero, and one tile
uploaded over a drawn image held real texels too. 78 textures were written back (16 MB, 15 ms in all, at most 1.9 ms
for a batch, finished 1 frame after their copies), none left out, dropped or failed, no "slow present" beside a
write-back, no crash.
Not tested
test only. Both go by content, so a guest store of the value already there cannot be told from no store: zero over
zero while the texels go in, or the texel's own value over a word that had gone in before an undo (that word goes
back to what the check saw). The undo also goes a word at a time, so a guest store into part of a word that had gone
in leaves the rest of that word's texels beside it. Closing that would take stopping the game's threads for the write. The write-backs at
eviction and remap are unchanged; they go by the page generations alone.
github-sync referenced this pull request2026-11-12 06:22:54 +00:00