WIP: fix(vk): a remap's write-back no longer puts an older target's texels over an image drawn inside it #941

Draft
Amateurwizrd wants to merge 2 commits from Amateurwizrd/Longshot:fix/writeback-nested-target into main
Contributor

Type: fix · Risk: low · Tested on: Windows 11, RTX 5070 Ti 16 GB · Needs: Linux

Why

When the game remaps virtual pages, the renderer writes back every texture the GPU drew in that range, in the texture
cache's hash order. If an old render target is written after a newer image drawn inside its storage, the old target's
stale texels land over the newer image, the pages are marked written, and the newer image is then uploaded again from
that memory: it shows what was there before, not what was drawn. #323 fixed the same mistake for eviction's write-back
and noted this path; this PR closes it. See #323.

How to reproduce

Seen on: the game-free check below, Windows 11, RTX 5070 Ti, driver 621.01. In the game the path is reached (Hyrule Field under a 6,144 MB allowance: pages 0x11637ca000..0x1163896000 remapped once a run with a GPU-drawn texture in them), but no wrong picture was seen there
Settings and switches: RX_VK_TEST_ALLOWED_MB=6144 for the in-game path; defaults otherwise
Steps: run tests/test_remap_writeback_stale.py against main's gpu/vk/vk_texture.cpp: an old target and an image drawn inside it, listed in both orders, written back over shared stub memory
Expected: the memory holds the newest drawing's texels
Actual: it holds the older target's texels (inner image listed first), or the older image's (target redrawn, listed first)

What changed

A remap's write-back now skips a texture when another image was drawn into its storage since it was last drawn, the
same rule #323 uses for eviction, so both write-back paths treat the newest drawing as the owner of that memory.

  • vk: texture_flush_to_guest in gpu/vk/vk_texture.cpp computes drawn_others(todo) once, when there is anything to
    write back, and skips each texture for which evict_reason::write_back_stale(t->last_written, other[i]) holds. A
    skipped texture keeps gpu_written and its image, as one that cannot be written back does; a texture drawn in the
    same frame as the other image is still written back, as in #323. Its log line counts the skipped ones:
    "N not (another image drawn into their storage since)".
  • tests: tests/test_remap_writeback_stale.py (new) cuts texture_flush_to_guest and drawn_others from the source
    unmodified and runs them over stub textures and guest memory, in both orders.

Measurements

Verdict: it passes: the new check fails on main @ 13e8b6da in both runs and passes on this PR @ 366be509 in both; in
the game the skip fires once a run on the remap it is for, with the HUD clean on both sides.

Hardware: Windows 11 Pro 26200, RTX 5070 Ti 16 GB, driver 621.01, Ryzen 7 7800X3D, 32 GB
Scene: the game-free check; and Hyrule Field (child) by debug teleport, standing 25 s then 5 s in the pause menu, RX_VK_TEST_ALLOWED_MB=6144
Method: the check run 2 times on each side, interleaved, main with this PR's test file copied in. In the game, 3 rounds alternating main and a build of main with this PR and two other open eviction fixes (#943, #944), each a copy of one game folder updated with make_game.py --update
How to reproduce: python -I -B tests/run_all.py remap_writeback_stale on this branch and on main with the test copied in; in the game, the scene above and the remapped: log lines

Before: main @ 13e8b6da After: this PR @ 366be509
tests/test_remap_writeback_stale.py (run 1 / run 2) FAIL / FAIL pass / pass
Remap write-backs skipped, a run (Hyrule Field, 6,144 MB) – 1, 1, 1 (the batch build)
HUD prompt icons and item count clean, clean, clean clean, clean, clean
batch build (this PR with #943 and #944), each round:
vk: virtual pages 0x11637ca000..0x1163896000 remapped: 0 texture(s) the GPU wrote there written back into their memory, 1 not (another image drawn into their storage since) (0 KB, 0.1 ms; ...

Flags and settings

None

Testing

Tested: on Windows 11 with the pinned llvm-mingw, the 38 checks that read this diff, one at a time, on this PR @
366be509 (main @ 13e8b6da): all pass. In the game, the runs above: no device loss, the HUD clean.

Not tested: a picture where the stale write-back showed wrong texels (none found in the scenes tried); the cost of
drawn_others, which walks the texture list once per remap with something to write back (0.1-0.2 ms in the log line
above).

Must be tested:

  • Linux (Test Farm: @Beanstalk linux, build the renderer): the renderer builds with this PR, expecting no error in gpu/vk/vk_texture.cpp

Risks

What could break: a texture skipped here keeps its texels only in VRAM; the skip covers its whole storage, so its
texels outside the newer image are not written back either, as #323 does for eviction.

Turning it off: revert the commit.

Compatibility: Nothing changes.

Issues

See #323

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how.
  • Rebased on current main.
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named).
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

When it applies

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • A game-free test added for the fix, failing on main and passing here.
  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. (Windows here; Linux is in Must be tested)
**Type:** fix · **Risk:** low · **Tested on:** Windows 11, RTX 5070 Ti 16 GB · **Needs:** Linux ## Why When the game remaps virtual pages, the renderer writes back every texture the GPU drew in that range, in the texture cache's hash order. If an old render target is written after a newer image drawn inside its storage, the old target's stale texels land over the newer image, the pages are marked written, and the newer image is then uploaded again from that memory: it shows what was there before, not what was drawn. #323 fixed the same mistake for eviction's write-back and noted this path; this PR closes it. See #323. ## How to reproduce **Seen on:** the game-free check below, Windows 11, RTX 5070 Ti, driver 621.01. In the game the path is reached (Hyrule Field under a 6,144 MB allowance: pages `0x11637ca000..0x1163896000` remapped once a run with a GPU-drawn texture in them), but no wrong picture was seen there **Settings and switches:** `RX_VK_TEST_ALLOWED_MB=6144` for the in-game path; defaults otherwise **Steps:** run `tests/test_remap_writeback_stale.py` against main's `gpu/vk/vk_texture.cpp`: an old target and an image drawn inside it, listed in both orders, written back over shared stub memory **Expected:** the memory holds the newest drawing's texels **Actual:** it holds the older target's texels (inner image listed first), or the older image's (target redrawn, listed first) ## What changed A remap's write-back now skips a texture when another image was drawn into its storage since it was last drawn, the same rule #323 uses for eviction, so both write-back paths treat the newest drawing as the owner of that memory. - vk: `texture_flush_to_guest` in `gpu/vk/vk_texture.cpp` computes `drawn_others(todo)` once, when there is anything to write back, and skips each texture for which `evict_reason::write_back_stale(t->last_written, other[i])` holds. A skipped texture keeps `gpu_written` and its image, as one that cannot be written back does; a texture drawn in the same frame as the other image is still written back, as in #323. Its log line counts the skipped ones: "N not (another image drawn into their storage since)". - tests: `tests/test_remap_writeback_stale.py` (new) cuts `texture_flush_to_guest` and `drawn_others` from the source unmodified and runs them over stub textures and guest memory, in both orders. ## Measurements **Verdict:** it passes: the new check fails on main @ 13e8b6da in both runs and passes on this PR @ 366be509 in both; in the game the skip fires once a run on the remap it is for, with the HUD clean on both sides. Hardware: Windows 11 Pro 26200, RTX 5070 Ti 16 GB, driver 621.01, Ryzen 7 7800X3D, 32 GB Scene: the game-free check; and Hyrule Field (child) by debug teleport, standing 25 s then 5 s in the pause menu, `RX_VK_TEST_ALLOWED_MB=6144` Method: the check run 2 times on each side, interleaved, main with this PR's test file copied in. In the game, 3 rounds alternating main and a build of main with this PR and two other open eviction fixes (#943, #944), each a copy of one game folder updated with `make_game.py --update` How to reproduce: `python -I -B tests/run_all.py remap_writeback_stale` on this branch and on main with the test copied in; in the game, the scene above and the `remapped:` log lines | | Before: main @ 13e8b6da | After: this PR @ 366be509 | |---|---|---| | `tests/test_remap_writeback_stale.py` (run 1 / run 2) | FAIL / FAIL | pass / pass | | Remap write-backs skipped, a run (Hyrule Field, 6,144 MB) | – | 1, 1, 1 (the batch build) | | HUD prompt icons and item count | clean, clean, clean | clean, clean, clean | ``` batch build (this PR with #943 and #944), each round: vk: virtual pages 0x11637ca000..0x1163896000 remapped: 0 texture(s) the GPU wrote there written back into their memory, 1 not (another image drawn into their storage since) (0 KB, 0.1 ms; ... ``` ### Flags and settings None ## Testing **Tested:** on Windows 11 with the pinned llvm-mingw, the 38 checks that read this diff, one at a time, on this PR @ 366be509 (main @ 13e8b6da): all pass. In the game, the runs above: no device loss, the HUD clean. **Not tested:** a picture where the stale write-back showed wrong texels (none found in the scenes tried); the cost of `drawn_others`, which walks the texture list once per remap with something to write back (0.1-0.2 ms in the log line above). **Must be tested:** - Linux (Test Farm: @Beanstalk linux, build the renderer): the renderer builds with this PR, expecting no error in `gpu/vk/vk_texture.cpp` ## Risks **What could break:** a texture skipped here keeps its texels only in VRAM; the skip covers its whole storage, so its texels outside the newer image are not written back either, as #323 does for eviction. **Turning it off:** revert the commit. **Compatibility:** Nothing changes. ## Issues See #323 ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### When it applies - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] A game-free test added for the fix, failing on `main` and passing here. - [ ] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. (Windows here; Linux is in Must be tested)
Fails on main: texture_flush_to_guest writes an older render target's texels over an image drawn inside its storage after it, when the list has the older one last.

Assisted-by: Claude
fix(vk): a remap's write-back no longer puts an older target's texels over an image drawn inside it
Some checks are pending
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
Guide screenshots / Guide images and local UI source review (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request_target) Has been skipped
beanstalk/approval waiting for a BigBean's 👍 on the request
PR description / Description follows the template (pull_request_target) Successful in 3s
366be509c0
texture_flush_to_guest writes every texture the GPU drew in the remapped (or invalidated) range into its memory, in the
texture list's order. When another image was drawn into a texture's storage after its last drawing, that write-back put
stale texels over the newer image's and marked those pages written, so the newer one uploaded from them: the same
mechanism #323 closed for the eviction's write-back (OoT's HUD labels inside an old 1920x1080 target).

It now skips those textures with the same rule, evict_reason::write_back_stale over drawn_others, and its log line counts
them. tests/test_remap_writeback_stale.py fails on main and passes here.

Assisted-by: Claude
Author
Contributor

@Beanstalk linux, build the renderer at this head: it should build with no error in the files this PR changes; no change in play expected from the build alone.

@Beanstalk linux, build the renderer at this head: it should build with no error in the files this PR changes; no change in play expected from the build alone.
Member

@Amateurwizrd only members of BigBeans can start the farm. One of them can approve this request by reacting 👍 to it.

@Amateurwizrd only members of `BigBeans` can start the farm. One of them can approve this request by reacting 👍 to it.
Author
Contributor

Tested in game: Windows 11, RTX 5070 Ti 16 GB, driver 621.01, Hyrule Field (child) by debug teleport, 25 s standing and 5 s in the pause menu at RX_VK_TEST_ALLOWED_MB=6144, 3 rounds alternating main @ 13e8b6da and a build of main with #941, #943 and #944 together. No device loss, the HUD clean on both sides; #941's skip fired once a run on the same remap. Not run in the game alone at this head.

Tested in game: Windows 11, RTX 5070 Ti 16 GB, driver 621.01, Hyrule Field (child) by debug teleport, 25 s standing and 5 s in the pause menu at RX_VK_TEST_ALLOWED_MB=6144, 3 rounds alternating main @ 13e8b6da and a build of main with #941, #943 and #944 together. No device loss, the HUD clean on both sides; #941's skip fired once a run on the same remap. Not run in the game alone at this head.
Some checks are pending
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
Guide screenshots / Guide images and local UI source review (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request_target) Has been skipped
beanstalk/approval waiting for a BigBean's 👍 on the request
PR description / Description follows the template (pull_request_target) Successful in 3s
This pull request is marked as a work in progress.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'fix/writeback-nested-target:Amateurwizrd-fix/writeback-nested-target'
git switch 'Amateurwizrd-fix/writeback-nested-target'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!941
No description provided.