Repository navigation
A registered texture the CPU rewrites after its first use is kept as the GPU's only once the GPU has written it (Ocarina of Time's flat blue terrain holes) - #1558
Conversation
|
Independent corroboration of the CPU-upload ownership problem on Ocarina of Time v131072, macOS 27.0, Apple M5 Ultra. In Kokiri Forest we captured two sampled 260×260 R32Float height maps whose Metal backing contained 67,600 zeros while the corresponding guest storage held nonzero heights around 0.134–0.175. Our downstream fix tracks actual GPU writes and lets unrelated textures sharing a GPU-written pool retain CPU upload ownership. Afterward the two captured streamed height maps matched the deswizzled guest storage byte-for-byte. This tested a separate implementation; direct validation of #1558 and #1447 remains pending. Two regression cases proved useful beyond usage-flag classification:
Those cases may be useful alongside this PR's coverage of a texture bound before its first CPU fill. Our 106-check storage suite includes real GPU readback for these transitions. |
…il cached view, on the test stack seedds#1604 uncompressed textures are taken from their storage unless the GPU wrote that storage, by address range, not their pool: the terrain no longer opens holes after walking a distance (streamed tiles came out zero once any texture in their pool had been a render target; the 260x260 tera_tile special case goes with it). Hand-merged with the stack's own ownership code: serial GPU writes (seedds#1558) and the BC render-target shadows (seedds#1590) stay; the image bind records the range through mtl_tex_gpu_written beside them; mtl_pool_gpu_written is gone with its last caller. seedds#1607 a texture ID with no texture drops its cached view: nil == nil kept a released texture valid on the pool tables' fast path, and the fast paths gave the shaders its resource ID after the host had released it (GPU page faults). The nil guard keeps the alias-view parent match. Tested in game on an M3 Max: a walk between areas holds the ground whole, no page faults, no regression of the earlier fixes (upscaler lines, load-time holes, stutters). Front-ends: host change only, chain unchanged (2027aa29b1b7).
…the GPU's only when the GPU wrote its storage after the CPU: every uncompressed GPU-only texture is made with render-target use, so the poll dropped every such rewrite, and Ocarina of Time's terrain tiles (height, blend and hole maps rewritten as tile slots are reused) opened flat blue holes in the ground after an hour nvn_ft_poll asks mtl_tex_storage_gpu_owned, the storage spans tex_cpu_content keeps (gws_owned: the GPU wrote some of the storage and the CPU has not rewritten it since), instead of the host texture's usage. Loading a Kokiri Forest save: 127-263 textures uploaded in the first 3-4 minutes where none was, 4 GPU-drawn normal tiles kept (the CPU only zeroes their level 0 before the GPU draws them). t_cpu_storage checks the three cases; reference entry, game note and symptom index.
022558e to
0ed3c57
Compare
…age by the first-use rule: with tex_dirty_upload the poll asked the host texture's usage, which every uncompressed GPU-only texture has as a render target, so Ocarina of Time's terrain tile maps kept their old content (flat blue holes in the ground) (PR #1558) nvn_ft_poll asks mtl_tex_cpu_rewritten, which applies tex_cpu_content's rule without its non-zero check: the same flags and formats (tex_storage_ok, split out of tex_cpu_content), and an uncompressed 2D texture not while the GPU has written its storage and the CPU has not rewritten it since (gws_owned). An uncompressed texture it uploads joins g_cpu_unc (cpu_unc_take), so cpu_unc_drop makes it a GPU texture again when the GPU draws into it. A block-compressed texture is uploaded whatever the GPU wrote: GPU copies into a registered one land in its storage too, so a GPU-written span over it does not make the storage stale. A texture left to the GPU because the GPU wrote after the CPU stays registered with a new stamp (its pages protected before the question), so the CPU's next rewrite is still seen; textures the rule leaves out keep the usage test. Ocarina A/B (M4 Pro, Kokiri autosave and a walk onto the village floor, 160 s, both hosts on chain d866f9700d78 for the build's libraries): about 250 textures uploaded by the poll against none, 17 of them made GPU textures again when drawn into, picture whole at 60 fps, 36 long frames against 57. t_cpu_storage 56 checks, t_cpu_tex 73 (the poll itself, with a tex_dirty_upload profile), t_clear_tex 51, t_readback 154, test_docs: pass. Tears of the Kingdom not run (no build on hand). Co-authored-by: Romen Adama Caetano Ramírez <56840314+Romen-Adama-Dev@users.noreply.github.com>
|
Landed as b547f1e, with you as co-author: thank you for the diagnosis. The usage test could never answer the question, and your log lines and reference entry are kept. The commit reworks the poll in three places:
On an M4 Pro (Kokiri autosave and a walk onto the village floor, 160 s): about 250 textures were uploaded by the poll against none before, and 17 of them became GPU textures again when drawn into. The picture was whole at 60 fps. @dderiso1, thanks for the independent check. Both of your cases hold, because the spans are by address: an unrelated GPU write in the same pool doesn't take a texture away from the CPU, and a second texture object over GPU-written storage is the GPU's too. The second one is now a test. Still open: an eye check at the exact spots where the holes appeared after an hour of play. |
…age by the first-use rule, and Ocarina of Time turns on tex_flush_upload / tex_dirty_upload: its terrain tile maps kept their old content (flat blue holes in the ground) (from upstream b547f1e, PR seedds#1558, on this tree's GPU-written ranges)
…age by the first-use rule: with tex_dirty_upload the poll asked the host texture's usage, which every uncompressed GPU-only texture has as a render target, so Ocarina of Time's terrain tile maps kept their old content (flat blue holes in the ground) (PR seedds#1558) nvn_ft_poll asks mtl_tex_cpu_rewritten, which applies tex_cpu_content's rule without its non-zero check: the same flags and formats (tex_storage_ok, split out of tex_cpu_content), and an uncompressed 2D texture not while the GPU has written its storage and the CPU has not rewritten it since (gws_owned). An uncompressed texture it uploads joins g_cpu_unc (cpu_unc_take), so cpu_unc_drop makes it a GPU texture again when the GPU draws into it. A block-compressed texture is uploaded whatever the GPU wrote: GPU copies into a registered one land in its storage too, so a GPU-written span over it does not make the storage stale. A texture left to the GPU because the GPU wrote after the CPU stays registered with a new stamp (its pages protected before the question), so the CPU's next rewrite is still seen; textures the rule leaves out keep the usage test. Ocarina A/B (M4 Pro, Kokiri autosave and a walk onto the village floor, 160 s, both hosts on chain d866f9700d78 for the build's libraries): about 250 textures uploaded by the poll against none, 17 of them made GPU textures again when drawn into, picture whole at 60 fps, 36 long frames against 57. t_cpu_storage 56 checks, t_cpu_tex 73 (the poll itself, with a tex_dirty_upload profile), t_clear_tex 51, t_readback 154, test_docs: pass. Tears of the Kingdom not run (no build on hand). Co-authored-by: Romen Adama Caetano Ramírez <56840314+Romen-Adama-Dev@users.noreply.github.com>
What
With
tex_dirty_upload, a registered texture that the CPU writes after its first use is uploaded from guest memory bynvn_ft_poll, unless its host texture "is the GPU's" (mtl_tex_gpu_owned), so that a CPU write to pages it shares does not replace what the GPU drew. That test was the texture's usage: render-target or shader-writable. But NVN has no flag for a render target, sotex_descgives every uncompressed texture made without a CPU fill render-target use from the start, and the test held for every such texture whether or not the GPU had ever written it. A texture bound before the CPU filled it, then filled, was never uploaded; only block-compressed textures (no render-target use) came through, which is why the poll worked for Tears of the Kingdom's BC4 atlas and ASTC arrays.Ocarina of Time's terrain tiles (260×260, one
CPU_CACHED | GPU_CACHEDpool: R32F height, RGBA8 and 2-layer RGBA8 blend and hole maps, block-compressed detail, RG8 normals with 9 levels) are bound at creation and written from the CPU as tile slots are reused while the player moves. Every uncompressed one was dropped, and after about an hour the ground opened flat blue holes with grass and rocks floating over them (Kokiri Forest, the village floor, the Deku Tree), the same look the profile's keys had fixed for tiles written before their first use.Fix
nvn_mtl.m:mtl_tex_storage_gpu_owned: whether the GPU wrote the texture's storage and the CPU has not rewritten it since, through the storage spanstex_cpu_contentkeeps since e2b76d4 (gws_owned, stamped when a GPU write is recorded).nvn_ft_pollasks it instead of the host texture's usage: a texture the GPU never wrote, or wrote before the CPU's write, is uploaded; one the GPU wrote after the CPU's write stays the GPU's.mtl_tex_gpu_owned(usage) stays fortex_storage_dst, so packaged textures keep their render-pass readbacks.t_cpu_storage: storage the GPU never wrote is the CPU's; the GPU's once it writes it; the CPU's again once it rewrites it.Measured
Mac (M3 Pro, 18 GB), loading a Kokiri Forest save and turning the camera, two runs:
The 4 kept are right: the diagnostic showed the CPU zeroing their level 0 (every byte 0 at the poll) before the GPU draws every level. The uploaded ones are the tiles' height, blend and hole maps and a few small lookup textures (4×1 RGBA8, 32×32 RGBA8, 4×4×6 R32F, 64³ BC4) that had the same fate. Frame time unchanged on this walk (p50 16.9–18.9 ms); the uploads happen as tiles stream in.
Not yet eye-checked at the exact spots of the reported holes (they appeared after an hour of play on the previous host); the mechanism and the textures match the profile note's description of the symptom, and the fixed host is now in the play build for that check.
Tests
t_cpu_storage(45 → 48 checks),t_cpu_tex(68),t_clear_tex(51),t_readback(153): pass.dm/tools/tests/test_docs.py: pass.regress.py check --gamescannot run on this Mac: Ocarina of Time is the only local game. Reach: every profile withtex_dirty_upload(Tears of the Kingdom, FINAL FANTASY TACTICS, The Great Ace Attorney Chronicles, Kirby Air Riders, Ocarina of Time), whose block-compressed cases are unchanged; uncompressed textures in those games that were bound before the CPU filled them now upload too.