Skip to content

fix(agc): look up and attach draw recipes on the relocated donor entry - #7369

Open
Flamefusion wants to merge 3 commits into
boykopovar:mainfrom
Flamefusion:fix/agc-relocated-draw-recipes
Open

Flamefusion wants to merge 3 commits into
boykopovar:mainfrom
Flamefusion:fix/agc-relocated-draw-recipes

Conversation

@Flamefusion

Copy link
Copy Markdown
Contributor

What

A draw that misses on its own register key and is served by a relocated draw entry (relocateDrawEntry, #4943) never uses or keeps a draw recipe, so every such draw is recorded in full (Graphics::Draw) instead of from the cached draw's recipe (DrawFromRecipe).

In Driver::draw (Execution/src/Driver/Draw/Draw.cpp):

  • relocateDrawEntry builds a fresh DrawEntry with per-draw copies of the donor's variants. That entry is not stored in drawCache under the draw's key.
  • findDrawRecipe and, after recording, attachDrawRecipe still look up the draw's own key in drawCache. They find nothing and return, so a relocated hit never finds the donor's recipe, and a recipe built while recording it is dropped. With APS5_TRACE_DRAW_CACHE this shows as "attaches 0".
  • The recipe records also compare stage identity against the per-draw copies, which are new objects on every draw, so even a stored record could never match.

Fix:

  • DrawEntry and DispatchVariant gain a donor pointer. relocateDrawEntry sets it on the relocated entry and on every copied variant, always to the original cached entry or variant (a copy of a copy points at the root), so the chain never grows.
  • Driver::draw keys the recipe stages on the donor variant when a matched variant is a relocation copy. It hands the donor entry to findDrawRecipe and attachDrawRecipe, also through commitDraw on the pipelined path. With a donor, both read and update the donor entry's recipe list directly instead of looking up the key; without one they behave as before.
  • Graphics::DrawWithRecipe rebuilds the push constant block from the current shaders (AssemblePushConstants plus the recorded patches) when the recipe has push constants. Relocation moves user words that land in push constants, so the donor's block must not be replayed as-is.

Found while profiling Hades (PPSA03355, discussion #3234), whose sprite draws move per-draw pointers on almost every draw. There, recipe attaches now succeed, but few of its draws build a recipe at all: most hold an address-based build's lease, so they are not reusable. This PR therefore claims no frame-rate change; it makes the relocated path consistent with the exact-key path.

Test: agc_driver_draw_cache_relocation gains RelocatedDrawsReuseTheirRecipe.

  • Its draws read a constant through a user pointer that moves on every draw to identical contents, so after four teaching draws every further draw is a relocated hit.
  • They render into a 64 KiB guest block with the SW_64KB_R_X swizzle mode. The 1 KiB linear target the other cases use is never resident, and a draw into a non-resident target is not recorded, so it would never build a recipe.
  • The case requires all six later draws to be draw-cache hits and all six to be recorded from the recipe.
  • VulkanDevice::DrawRecipeHits() exposes the draw recipe hit counter the [recipe] profile line already keeps, so the test can read it without APS5_PROFILE_DRAW.

Tested

Windows 11 (build 26300), WinLibs GCC 15.2.0 (posix-seh, ucrt), Release, -DBUILD_TESTING=ON, Ryzen 7 7435HS / 16 GB, NVIDIA GeForce RTX 3050 Laptop GPU (driver 617.42, graphics pipeline libraries available), rebased on main b39d90bf1:

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none (draw recipes on relocated draw-cache hits; found on PPSA03355 Hades (PPSA03355) #3234, no stop there)
  • GPUs tested: NVIDIA GeForce RTX 3050 Laptop GPU, driver 617.42, Windows 11 (build 26300)
  • AI-assisted: yes

std::shared_ptr<DrawEntry> entry;
{
void Driver::attachDrawRecipe(std::uint64_t key, const std::vector<std::shared_ptr<DispatchVariant>>& stages, std::shared_ptr<const DrawRecipe> recipe, const std::shared_ptr<DrawEntry>& donor) {
std::shared_ptr<DrawEntry> entry = donor;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This donor path lets relocated entries from different draw keys attach recipes to the same DrawEntry. The recipe update below is a load-copy-store sequence: if two completions load the same old vector, each builds a vector with only its own new record, and the later store drops the other attachment. AtomicSharedPtr makes each operation safe, but not this compound update. Could this use a compare-and-retry loop (or per-entry serialization) so parallel relocated draws do not discard each other’s cached recipes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thanks. The race was already possible on one key, but sharing the donor entry across keys makes it much more likely, and pipelined commits can attach in parallel.

Fixed in 090d66e: attachDrawRecipe now uses a compare_exchange_strong loop. If another attach replaced the list after the load, the new list is rebuilt from the list now stored and the exchange is retried, so both records are kept (still capped at dispatchVariants()). Both the AtomicSharedPtr fallback and std::atomic<std::shared_ptr> provide compare_exchange_strong.

The draw cache relocation and reuse tests (all variants), address passes, mesh draw relocation and linear target resident tests still pass.

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 1191 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 1368 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 1395 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 1896 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
PR 2434 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4040 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4072 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 4472 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4847 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5021 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5431 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 5441 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 5609 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Dispatch/DispatchCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRelocate.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 5925 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6146 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6188 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6202 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6249 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6290 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6292 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6501 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 6539 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Dispatch/DispatchCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRelocate.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/execution/DrawCacheRelocation.cpp
PR 6612 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6615 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6656 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Dispatch/DispatchCache.hpp
PR 6782 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6788 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6804 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6959 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6960 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6977 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7085 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7201 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7256 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7275 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7292 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7307 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7310 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7446 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
PR 7449 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7464 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7484 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7508 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRelocate.cpp
PR 7618 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7622 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7642 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Dispatch/DispatchCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRelocate.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7643 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7644 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 7647 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7668 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7691 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawCache.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
PR 7888 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7922 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7930 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7978 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 8004 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8014 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8105 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8180 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8212 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8223 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 8246 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 8278 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8336 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8339 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
PR 8372 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
PR 8380 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8432 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8489 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8564 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 8612 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8658 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8735 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8740 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 8968 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 8999 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9020 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9050 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9129 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp
PR 9176 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9177 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
PR 9193 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9202 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9243 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp
PR 9248 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Dispatch/DispatchCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRelocate.cpp
PR 9287 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9289 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9335 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9348 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9356 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9373 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9387 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9396 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRelocate.cpp
core/libs/prx/libSceAgcDriver/tests/execution/DrawCacheRelocation.cpp
PR 9400 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9424 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9431 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9432 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9436 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9445 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9450 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9489 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9508 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Dispatch/DispatchCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawCache.hpp
core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRelocate.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9533 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9564 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9597 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9599 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
PR 9602 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9670 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9672 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9673 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9674 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9675 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9676 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9685 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9712 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9717 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9742 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9748 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9756 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9842 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9852 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9875 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9879 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9881 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9888 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
PR 9891 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9892 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9893 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9895 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9897 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9898 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9915 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9934 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 9978 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 10043 core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 10046 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 10062 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 10069 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 10103 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp

@MarcZX10

Copy link
Copy Markdown
Contributor

Thanks for the fix. The compare-and-retry rebuilds from the latest recipe list on contention, which addresses the lost-attachment race I flagged. The updated head is clean; I have not run the draw-cache tests locally.

@github-actions github-actions Bot added agc libSceAgc* unblocks title Names a title ID on the Unblocks line needs rebase Conflicts with main, rebase needed labels Oct 18, 2026
@blawesom

Copy link
Copy Markdown
Contributor

Rebase handoff — rebased onto current main (all three commits, authorship unchanged). Test-file conflicts unioned (main's newer SRT-walker tests kept alongside your recipe-coverage tests).

Verified on Linux (CachyOS, kernel 7.2.9, GCC 16.2.1, Ninja, Release, BUILD_TESTING=ON, AMD RX 9070 XT / RADV): builds; the draw-cache tests pass (5/5); conventions pass; full suite green apart from the four known main regressions (#5444 Mesa, #7584 BDA, #7766, #7784), none from this PR.

To pick it up:

git fetch https://github.com/blawesom/AnyPS5 rebase/7369
git checkout <your branch>
git merge --ff-only FETCH_HEAD

Head is f08cae59a.

@adriadam10 adriadam10 self-assigned this Oct 19, 2026
@adriadam10
adriadam10 force-pushed the fix/agc-relocated-draw-recipes branch from 090d66e to 403651e Compare October 19, 2026 14:22
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@Flamefusion

Copy link
Copy Markdown
Contributor Author

@adriadam10 do you need anything from my side for this pr ?

@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed unblocks title Names a title ID on the Unblocks line labels Oct 19, 2026
A draw that misses on its own register key and is served by a relocated
draw entry (relocateDrawEntry, boykopovar#4943) never got a draw recipe:

- relocateDrawEntry builds a fresh DrawEntry with per-draw copies of the
  donor's variants. That entry is not stored in drawCache under the draw's
  key.
- Driver::draw still passes the draw's own key to findDrawRecipe and, after
  recording, to attachDrawRecipe. Both look the key up in drawCache, find
  nothing and return, so a recipe built while recording such a draw is
  dropped and the next relocated draw of the same shape records in full
  again ("attaches 0" with APS5_TRACE_DRAW_CACHE).
- The recipe records also compare stage identity against the per-draw
  copies, which are new objects on every draw, so even a stored record
  could never match.

Changes:

- DrawEntry and DispatchVariant gain a donor pointer. relocateDrawEntry
  sets it on the relocated entry and on every copied variant, always to
  the original cached entry/variant (a copy of a copy points at the
  root), so the chain never grows.
- Driver::draw keys the recipe stages on the donor variant when a matched
  variant is a relocation copy, and hands the donor entry to
  findDrawRecipe/attachDrawRecipe (also through commitDraw on the
  pipelined path). With a donor, both read and update the donor entry's
  recipe list directly instead of looking the key up; without one they
  behave as before.
- DrawWithRecipe rebuilds the push constant block from the current shaders
  and patches it through the recorded resources when the recipe has push
  stages, instead of replaying the bytes captured with the recipe. A
  relocated draw moves user words that end up in push constants, so the
  captured block would carry the first draw's values.

Draws that are not relocated take the same path as before: their matched
variants have no donor and recipe lookup still goes through drawCache.

Measured on Hades (PPSA03355, discussion boykopovar#3234), where nearly every sprite
draw is served through a relocated entry: lookups and attaches now
succeed, but Graphics::Draw builds a recipe for only ~15 of ~100,000 draws
per 10 s because the draws' ShaderResources are not Reusable(), so the
frame rate does not change yet. The fix is what lets recipes apply to
relocated draws once that is addressed.
Adds RelocatedDrawsReuseTheirRecipe to the draw cache relocation test.
It covers the case the previous commit fixes: a draw that misses on its
own key, is served by a relocated draw entry and should then be recorded
from the recipe of the cached draw it was relocated from.

- The draws read a constant through a user pointer that moves on every
  draw to identical contents, like MovedDataHitsTheCache, so after four
  teaching draws every further draw is a relocated hit.
- They render into a 64 KiB guest block with the SW_64KB_R_X swizzle
  mode. The linear 1 KiB target the other cases use is never resident,
  and a draw into a non-resident target is not recorded, so it never
  builds a recipe and could not show the bug.
- The case checks that all six relocated hits are draw-cache hits and
  that all six were recorded through DrawFromRecipe.

VulkanDevice::DrawRecipeHits() returns the draw kind's recipe hit
counter that the [recipe] profile line already keeps, so the test can
read it without APS5_PROFILE_DRAW.

Without the previous commit the case fails with "0 of 6 draws served by
a relocated entry were recorded from the cached draw's recipe": the
lookup uses the draw's own key, which has no draw cache entry. With it,
agc_driver_draw_cache_relocation, agc_driver_draw_cache_reuse (and its
_off, _verify and _inline variants), agc_driver_address_passes,
agc_driver_mesh_draw_relocation and agc_driver_linear_target_resident
pass.
attachDrawRecipe builds a new recipe list from the entry's current one
and stores it: it loads the old list, copies the still-valid records
behind the new one and stores the result. Each AtomicSharedPtr operation
is atomic, but the sequence is not. If two attaches load the same old
list, each stores a list holding only its own new record, and the later
store drops the earlier attachment.

With recipes attached through the relocated donor entry, draws with
different register keys that relocate from the same cached draw attach
to one DrawEntry, and on the pipelined path their commits run apart from
the worker. Concurrent attaches to the same entry are therefore no
longer limited to one key.

The update is now a compare_exchange_strong loop. When another attach
has replaced the list since it was loaded, the new list is rebuilt from
the list now stored and the exchange is retried, so both records are
kept, within the dispatchVariants() limit as before. The
AtomicSharedPtr fallback and std::atomic<std::shared_ptr> both provide
compare_exchange_strong.

agc_driver_draw_cache_relocation, agc_driver_draw_cache_reuse (and its
_off, _verify and _inline variants), agc_driver_address_passes,
agc_driver_mesh_draw_relocation and agc_driver_linear_target_resident
pass.
@Flamefusion
Flamefusion force-pushed the fix/agc-relocated-draw-recipes branch from 403651e to 562f910 Compare October 20, 2026 05:44
@Flamefusion

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (87a32d145). New head is 562f9107f; the three commits are unchanged in intent.

Thanks @blawesom for the earlier rebase handoff. main had moved again since then, so I redid it on the latest tip:

Verified on Windows 11, MinGW GCC 15.2, Ninja, Release, BUILD_TESTING=ON, NVIDIA RTX 3050 Laptop (driver 617.42):

  • builds
  • all 32 AGC draw-path tests pass, including the draw cache relocation/reuse variants, mesh draw relocation, linear target resident and pipelined indirect draws
  • check_conventions.py passes

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* needs rebase Conflicts with main, rebase needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants