Skip to content

perf(agc): take the HTILE words pending fills leave instead of reading them - #6505

Merged
adriadam10 merged 3 commits into
boykopovar:mainfrom
keravyn-ops:fix/htile-pending-fill
Oct 19, 2026
Merged

adriadam10 merged 3 commits into
boykopovar:mainfrom
keravyn-ops:fix/htile-pending-fill

Conversation

@keravyn-ops

@keravyn-ops keravyn-ops commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

What

A depth target's bind (storeBackClearedHtile) reads the target's HTILE words to find a fast clear, then stores them back expanded. When a recorded fill over the HTILE is still pending, that read makes the flush hook submit the open batch and wait for the GPU while the
aps5-trace-0c4816f6119e-896.json.gz
aps5-trace-3dc3324c60ed-17292.json.gz
GPU mutex is held. A title that clears its depth targets with buffer fill kernels (the fill HLE) pays this wait at every clear. In #6045 that was 1271 syncs per 10 s at about 4.3 ms each, 5.5 s per 10 s with the lock held.

The change follows the DCC key path (KeysAfterPendingWrites / StoreUncompressedOnGpu):

  • VulkanDevice::FillBuffer notes a uniform fill with its 32-bit word (Recorder::NotePendingWordFill). PendingWritesOver reports the word next to the byte value, and the byte value the DCC key paths read is unchanged.
  • storeBackClearedHtile: when a write is pending over the HTILE, it works out the landed word from the notes. That needs every pending write over the range to be a noted fill, and the bytes outside them to hold the same word. When the landed word is a clear, the expanded words are recorded as a GPU fill behind the pending fills, instead of being read and stored on the CPU. Any other pending write keeps the read, as before.
  • APS5_NO_PENDING_HTILE_FILLS=1 always reads, as before.

DepthFastClearSubmit gets a case for this. A fill is recorded over a bound target's HTILE, then the target is drawn. The case checks that the depth is cleared, that the bind did not wait for the GPU (Recorder::ThreadHookWaits), that the HTILE holds the expanded words afterwards, and that a second draw keeps its depth.

Tested

Windows 11 Home 10.0.26100, AMD Radeon 780M (Ryzen Z1 Extreme), AMD driver 25.10.20.01, WinLibs GCC 15.2.0 r7, Release. The change was measured on main 0c4816f (commit 3dc3324c60ed). It has since been rebased onto 03c4cb5 without conflicts, and both depth fast clear tests below pass 3 of 3 there.

  • agc_driver_depth_fast_clear_submit_tests: passes 3 of 3. The new case fails with APS5_NO_PENDING_HTILE_FILLS=1 (the old path) at "the bind of the depth target waited for the GPU to run the fill recorded over its HTILE".
  • agc_driver_depth_fast_clear_tests passes 3 of 3, and agc_driver_dcc_keys_first_write_tests passes.
  • These agc_driver_recorder_tests cases pass: --pending-key-stores-only, --shadowed-key-stores-only, --pending-write-snapshot-only, --space-pending-writes-only and --storage-cpu-snapshot-only. The full run stops at "(9) a scan under a pending write was kept", on main as well as with this change.
  • PPSA12544 (Hollow Knight: Silksong), the scene from perf: PPSA12544 on Legion Go (Ryzen Z1 Extreme, Windows): queue worker 99 % busy, mostly import comparisons and GpuMutex waits #6045 (standing at a bench after a save loads): 60-61 fps in the title's 60 Hz mode and 69-73 fps with the display at 144 Hz (44-46 on main, which stays below 60 either way). Ten runs had no crash and no error in the log: the trace run below on exactly this commit, and nine runs of this change together with an unrelated local patch (APS5_STORE_QUIET_AT_FLIP, see Hollow Knight: Silksong (PPSA12544) #726). The inventory opens and closes back to the world, and the frames dumped at the bench look the same as on main.

Summary lines from the same machine and scene, main first and this PR second (APS5_THREAD_PRIORITY=high on both):

{"commit":"0c4816f6119e","dirty":false,"env":{"APS5_PAD_INPUT_FILE":"<path>","APS5_THREAD_PRIORITY":"high","APS5_TRACE_SECONDS":"90:20"},"gpu":"AMD Radeon Graphics","driver":"AMD proprietary driver 25.10.20.01 (LLPC)","os":"Windows 10.0.26100","resolution":"1920x1080","window":{"spec":"seconds 90:20","first_present":5620,"ms":20003.059},"presents":906,"frame_ms":{"p50":22.061,"p90":22.812,"p99":23.258,"max":23.637},"gpu_busy_ms_per_present":14.563,"worker_busy_pct":{"queue worker 0x0":98.792},"top_phases_ms_per_frame":{"Driver.Packet":21.787,"Driver.Packet.device_lock":9.602,"Driver.Packet.draw":8.813,"Driver.Draw":8.742,"Driver.Draw.draw_enqueued":6.876,"Driver.Present":3.671,"Driver.Packet.dispatch":2.054,"Driver.Present.gpu_mutex_wait":1.752},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":15.254,"events_lost":82203,"totals_full":false}},"trace":"aps5-trace-0c4816f6119e-896.json.gz"}
{"commit":"3dc3324c60ed","dirty":false,"env":{"APS5_PAD_INPUT_FILE":"<path>","APS5_THREAD_PRIORITY":"high","APS5_TRACE_SECONDS":"90:20"},"gpu":"AMD Radeon Graphics","driver":"AMD proprietary driver 25.10.20.01 (LLPC)","os":"Windows 10.0.26100","resolution":"1920x1080","window":{"spec":"seconds 90:20","first_present":6964,"ms":20009.982},"presents":1222,"frame_ms":{"p50":16.119,"p90":20.329,"p99":21.218,"max":22.043},"gpu_busy_ms_per_present":13.082,"worker_busy_pct":{"queue worker 0x0":86.160},"top_phases_ms_per_frame":{"Driver.Packet":14.085,"Driver.Present":9.074,"Driver.Packet.draw":7.632,"Driver.Draw":7.530,"Driver.Draw.draw_enqueued":5.061,"Driver.Present.acquire_image":4.782,"Vulkan.Acquire":4.780,"Vulkan.Acquire.acquire_fence_wait":4.623},"trace_dropped":true,"trace_dropped_threads":{"queue worker 0x0":{"events_full_after_s":8.539,"events_lost":352876,"totals_full":false}},"trace":"aps5-trace-3dc3324c60ed-17292.json.gz"}

In this capture the presented rate sits at about 60 fps, with most of the frame time in Vulkan.Acquire.acquire_fence_wait. A run started while the display was at 144 Hz, where the title can take its 119.88 Hz output mode, reaches 69-73 fps in the same scene. With APS5_PROFILE_DRAW=1, the [hooksync] line is gone and the untagged [lock] line reports locked GPU waits 0 / 0 ms. On main that line read 2161 / 5424 ms per 10 s.

Traces: aps5-trace-0c4816f6119e-896.json.gz and aps5-trace-3dc3324c60ed-17292.json.gz (attached).

Checklist

@keravyn-ops
keravyn-ops force-pushed the fix/htile-pending-fill branch from 60f9861 to d6b25f2 Compare October 15, 2026 18:16
@github-actions

github-actions Bot commented Oct 15, 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 1368 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 1395 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 2434 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
PR 4472 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4891 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/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
docs/dev/ENVIRONMENT.md
PR 5441 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5480 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5499 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 5609 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 5925 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6177 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6185 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6186 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6188 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6202 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6249 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6292 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6300 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 6360 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6503 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 6539 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6615 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 6639 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6647 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6683 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 6718 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6727 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 6763 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6786 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6788 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6801 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6804 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6808 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 6820 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6924 docs/dev/ENVIRONMENT.md
PR 6946 core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
docs/dev/ENVIRONMENT.md
PR 6947 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
docs/dev/ENVIRONMENT.md
PR 6960 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6977 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 6989 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6995 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7002 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7201 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 7233 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 7256 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 7292 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7332 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
docs/dev/ENVIRONMENT.md
PR 7340 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
docs/dev/ENVIRONMENT.md
PR 7369 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7371 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 7390 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7427 docs/dev/ENVIRONMENT.md
PR 7445 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7484 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 7604 docs/dev/ENVIRONMENT.md
PR 7618 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7622 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 7642 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
docs/dev/ENVIRONMENT.md

@SoRadGaming

Copy link
Copy Markdown
Contributor

Windows / AMD results for this head (d6b25f2d6): on ASTRO BOT it recovers what #5627 cost.

Host: Windows 11 Pro 26200, Ryzen 7 5800X3D, Radeon RX 9070 XT (AMD proprietary driver 32.0.23033.1002), 32 GB, WinLibs GCC 15.2.0 r7, Release.

What the regression looked like. ASTRO BOT (PPSA21564 01.007.000), -lvl hub_crashsite, on main 5d9dcc4 (with #6188, #6266, #6273 and #6292 on top). The hub dropped from about 6.6 to 5.4 FPS after the overnight merges. APS5_SAMPLE_WORKER on queue worker 0, over 80 s in the hub, attributes 16 % of the worker's samples to one path:

DepthSurfaceView -> storeBackClearedHtile -> GuestMemory::Read -> FlushForAccess -> Recorder::SyncThrough -> Recorder::Sync / finish

The recorder syncs grew from 6.5 % to 26 % of the worker.

As a diagnostic I made storeBackClearedHtile return without reading when a write is pending over the HTILE. That gave 6.91 / 6.93 FPS, and hub frame dumps matched the unchanged build. It is not a fix: it would miss a clear made by a pending kernel.

This PR. Same tree with this PR merged (clean), one relinked executable, toggling only APS5_NO_PENDING_HTILE_FILLS. No input, warm shader cache (1,904 hits, 0 misses), APS5_NO_SNAPSHOT_CHECK=1 APS5_RESOURCE_CACHE_ENTRIES=4096, pipelined draws on (main's default). FPS is the window's present counter over 120 s, after a 120 s settle once the level has started. Runs alternate.

hub FPS 10 s samples after the settle
APS5_NO_PENDING_HTILE_FILLS=1 (reads, as main) 5.33 / 5.35 5.4-5.7
this PR 6.88 / 6.64 (+27 %) 6.5-7.0

So this title's pending writes over its depth targets' HTILE are noted fills, and the bind no longer waits for the GPU.

Frames. GPU frame dumps (APS5_DUMP_FRAMES, every 100th present) through the hub with this PR show the same frames as the build without it: Astro idling by the pool, with depth, shadows and occlusion intact.

No stops or errors in the runs above.

AI-assisted (Claude Code); the builds and runs are on my machine.

@EagleBearerYS

Copy link
Copy Markdown
Contributor

Data point from Planet of Lana (PPSA19450) on Windows with NVIDIA. With this PR, the first meadow checkpoint goes from 7.06 to 8.89 fps. Before #5627, the same spot ran 12.7.

Host: Windows 11 Home 10.0.26300, AMD Ryzen 9 5900HS, GeForce RTX 3070 Laptop GPU (NVIDIA 617.42), 15.4 GB RAM, WinLibs GCC 15.2.0 r7, Release.

Trees:

Scene: a save in the first meadow, loaded through PLAY / Slot 1 / Resume, then standing with no input. APS5_THREAD_PRIORITY=high. The fps is the median from 3 to 8 minutes after the load, unprofiled, on a warm shader cache.

libs fps
base 7.06
base + this PR 8.89 (7.69–9.43)
10-11 main plus fixes, before #5627 12.7

Runs with APS5_PROFILE_DRAW=1 at the same spot, medians per 10 s over windows 40–70 (the run with this PR was its build's first boot):

line base + this PR
presents 64 96
[draws] prepare 6,152 ms 4,236 ms
[hooksync] pending-write syncs 263, waiting 1,610 ms none (no line printed)
[fill] uniform buffer fills, stored on the GPU 263 384
[guestmem] read 21.3 GB 19.7 GB

At the checkpoint, the frame matches the base's: no stale depth and no missing clears.

The reads that remain look like the bind-time store-back reading the whole HTILE after a fill's batch has been reaped. On Windows, this title's 640 KiB HTILE import is under the 1 MiB settle floor (settleFloorBytes), so it stays out of the write watch. GuestMemory::CollectWrites then returns 0 over it, and the store-back's "unchanged since the last read" check can't hold.

@keravyn-ops

Copy link
Copy Markdown
Contributor Author

Added a depth and stencil case to RecordedFillClearsWithoutWait (7918fd4): a D32_SFLOAT_S8_UINT target, a recorded SMem 0 fill (0xf0) over its HTILE, bound without a GPU wait, stored back as expanded (0xfffff3ff), and the stencil checked cleared to 0.

  • agc_driver_depth_fast_clear_submit passes on the Radeon 780M (AMD driver 25.10.20.01, Windows 11 26100), compute fill branch included.
  • The stencil case alone (run first) fails with APS5_NO_PENDING_HTILE_FILLS=1 at "the bind of the depth target waited for the GPU to run the fill recorded over its HTILE".
  • check_conventions.py passes.

Note for reviewers: #6764 implements the same idea (taking the fast clear from the pending HTILE fill instead of reading it).

@moong15

moong15 commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Tested this on Windows (RX 9070 XT, AMD 26.8.1) with Stellar Blade, merged on top of current main together with other draw-pipeline work. The game binds a 2564x1444 depth target every frame, so before this PR every bind read back its 288 KiB of HTILE on the CPU and waited for the GPU to do it. Measured with APS5_PROFILE_DRAW=1. Numbers are per 10 s, scenes are lined up by load start, and "before" is the same tree without this PR:

before after
288 KiB HTILE readback syncs at DRAW_INDEX_OFFSET_2, menu ~500 (4.2-4.4 s waited) 0
Same, early gameplay 245-288 (2.1-2.6 s waited) 0
Menu frame time p50 38.6 ms (259 presents) 31.3 ms (305 presents)
Early gameplay frame time p50 ~70 ms 66 ms
Open area, presents per 10 s (one dip window excluded) 66-99, avg ~75 81-110, avg ~87

That's about 18% more frames in the menu and about 15% more in gameplay, and the game felt clearly smoother while playing. One ~8 minute run, no pipeline failures, and nothing looked wrong on screen. The PR's depth fast clear test passes on the merged tree and fails with APS5_NO_PENDING_HTILE_FILLS=1, as expected.

🤖 Generated with Claude Code

@tibzejoker

tibzejoker commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Tested on GTA V (PPSA04264) in the open world (Los Santos, a story save resumed), Windows 11, RTX 4090 (driver 616.92), i9-13900K. Both HTILE PRs were measured on the same base, one run each, in the same session: main 47a266c + #6488 + #6848, which GTA needs to render. APS5_PROFILE_DRAW=1, last 12 windows of 10 s (two minutes in the world, warm shader cache).

Build [hooksync] pending-write syncs / 10 s waited / 10 s committer µs per draw
base (3 runs) 595 / 622 / 708 555 / 591 / 666 ms 41.8 / 45.5 / 36.6
+ #6505 (7918fd4) 502 138 ms 41.7
+ #6764 (d9621ed) 617 354 ms 40.6

A committer profile of the base (sampling the DrawPipeline thread) puts DepthSurfaceView → storeBackClearedHtile → GuestMemory::Read → FlushForAccess → Recorder::Sync at about 5 % of the committer's samples, so GTA hits the case both PRs handle. In this scene #6505 removes about 75 % of the hook wait time and #6764 about 40 %. FPS and the committer cost per draw stay within run-to-run noise: the committer is ~84 % busy with other per-draw work. Neither run crashed. I checked the rendering on the #6764 run's screenshots (correct); the #6505 run's were overwritten before I looked.

AI-assisted (Claude Code).

@mrfelek

mrfelek commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Data point from Linux / NVIDIA: on Diablo II: Resurrected (PPSA03912), this PR removes every HTILE wait, giving 47 → 60 presents per 10 s (+28%).

Host: Ubuntu 26.10 (kernel 7.3), Ryzen 9 5950X, RTX 3080 Ti (NVIDIA 615.71.09 open), GCC 15.3.0, Release.

Trees: main 1b0c7cf, and the same base with this PR's head 7918fd4 merged (clean merge). I also ran #6764 on the same base before it was closed, and it gave the same result (60 presents, 0 HTILE waits).

Scene: in game with a saved character loaded, no input. APS5_PROFILE_DRAW=1, measured over 120 s starting 60 s after the character loads.

main + #6505
presents / 10 s 46–48 58–61
[hooksync] HTILE syncs (DRAW_INDEX_2, 714K range) / 10 s 228, 4505 ms waited 0
hook waits inside draws / 10 s 4.4 s 0.0 s
committer µs per draw 70 31.8
GPU busy (nvidia-smi) ~69% ~88%
  • I checked where the old wait came from with a local probe in DepthSurfaceView. About 230 binds per 10 s took the HTILE read under a pending fill, at about 20 ms each (max 60 ms). The other ~110k binds per 10 s took the cached path at almost no cost.
  • With the PR applied, a different sync shows up that is not this PR's path: 60 per 10 s, about 2.95 s waited, at DRAW_INDEX_INDIRECT indirect-args. The bytes are unchanged after every one of those waits (unchanged 60, in flight unsignaled). It is about one per frame, and it is why the gain is smaller than the 4.5 s removed. I'll follow it up separately.
  • I did not compare the rendering.

AI-assisted; the runs are from my machine(s).

@Pacuka

Pacuka commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Linux / AMD data point on a Unity title: Subnautica (PPSA02453). CachyOS kernel 7.2.2, Ryzen 9 5950X, RX 6650 XT / RADV Mesa 26.2.4, Release, no env switches. Same spot in both runs: the burning lifepod right after the intro, standing still, APS5_PROFILE_DRAW=1.

Before this PR, gdb samples of the committer thread (DrawPipeline::run) sat in a GPU wait about 70% of the time, every time on this path:
commitDraw → VulkanDevice::Draw → Graphics::Draw → DepthSurfaceView → GuestMemory::Read → Recorder::SyncThrough → Recorder::Sync → Recorder::finish → drmSyncobjWait

build presents / 10 s commit per draw GPU busy / idle per frame
main 61d3d30 + #4449 75-78 44 us 57 / 67 ms
+ this PR (7918fd4) 146-155 30 us 35 / 22 ms

About 2x at this spot (~18k draws per frame). Both PRs merged cleanly onto main 61d3d30. The image is unchanged (fire, lifepod interior, ocean), no crash, clean exit. #4449 was in both builds; without it this title also loses time on failed host imports.

AI-assisted (Claude Code).

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@adriadam10 adriadam10 self-assigned this Oct 19, 2026
keravyn-ops and others added 3 commits October 19, 2026 05:16
…g them

A depth target's bind (storeBackClearedHtile) reads its HTILE words to find a fast clear and
stores them back expanded. When a recorded fill over the HTILE was still pending, that read
made the flush hook submit the open batch and wait for the GPU, under the GPU mutex. A title
that clears its depth targets with buffer fill kernels pays that wait at every clear.

Uniform fills now note their 32-bit word with the pending write (NotePendingWordFill; the byte
value the DCC key paths use is unchanged). When every pending write over the HTILE is such a
fill and the rest of the range already holds the same word, the bind takes the landed word
from the notes, and stores the expanded words as a GPU fill recorded behind the pending ones.
Any other pending write keeps the read. APS5_NO_PENDING_HTILE_FILLS=1 always reads.

PPSA12544 on a Radeon 780M (Windows): 1271 pending-write syncs per 10 s waited 5.5 s at the
bind, about 4.3 ms each; with this change there are none, and the scene runs at 60-61 fps
instead of 44-46.
…ll leaves

RecordedFillClearsWithoutWait now also runs with a D32_SFLOAT_S8_UINT target: a
recorded SMem 0 fill (0xf0) over the HTILE clears depth and stencil at the bind
without a GPU wait, and is stored back as expanded (0xfffff3ff). The stencil case
alone fails with APS5_NO_PENDING_HTILE_FILLS=1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@adriadam10
adriadam10 force-pushed the fix/htile-pending-fill branch from 7918fd4 to b04f7b8 Compare October 19, 2026 03:37
@github-actions github-actions Bot added docs and removed needs rebase Conflicts with main, rebase needed labels Oct 19, 2026
@adriadam10
adriadam10 merged commit 3904ce6 into boykopovar:main Oct 19, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* docs perf Performance change unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants