Repository navigation
perf(agc): prove cached image tables from a write index behind APS5_TABLE_INDEX - #12745
EagleBearerYS wants to merge 14 commits into
Conversation
…ABLE_INDEX A cached draw over an image table proves every sampled texture on every Revalidate: one write-watch collect, one depth-plane flush and one generation compare per element, so the proof grows with the table. With APS5_TABLE_INDEX=1 the build subscribes its watched textures and watched T# table spans to a write index over 64 KiB blocks. Writers bump the subscriptions over the blocks they touch: tracker stamps (CPU and driver), recorded storage writes, unwatch, allocation changes, and two sources main's template audit covers today, a texture leaving the cache and a host import over a texture's range. Revalidate then sweeps the subscription's spans once, scans the pending registry only when its serial moved, and proves only the bumped elements, the depth, DCC and storage-backed elements, and the elements outside the write watch; a bumped texture of a draw build is looked up again, from the same first-layer words the build and the full walk use for a 2D read of a 2D array, and re-bound without a rebuild. Everything else, the audit's periodic full walk included, is unchanged, and with the switch off the only added work is one relaxed load per stored tracker stamp. APS5_TABLE_INDEX_BREAK drops one source's events for the tests. Port of BF-041 (0063b98). Two tests assert main's full walk: agc_draw_descriptors' first-layer case now asserts the path the switch implies (the full walk, or the index comparing its unwatched element), and agc_driver_lookup_log_fits_build, which measures the lookup log after a full walk, pins the switch off. The first-layer case also syncs the recorder and clears its textures when it leaves early, as the file's other image cases do: a failed assertion destroyed its own texture detiler before the recorder ran the detile dispatch it had recorded, and the test crashed instead of reporting the failure.
agc_driver_table_index_validation draws an 8-entry T# table over watched textures with APS5_TABLE_INDEX=1, settles each case on the index's O(1) proof, then drives one event source: a CPU write, a T# word change, a pending storage write, a remap, an element outside the write watch, a driver store, an unwatch, textures leaving the cache and a host import over a texture. The "array" case reads a 2D array as 2D and checks that a write to another slice leaves the element bound. Each case runs alone by name and goes red under APS5_TABLE_INDEX_BREAK for its source; "outcome" keeps only what the draw samples and whether the O(1) proof was taken. The pixel program's key is user data plus a term that is zero at run time but not known when the program is planned, so the table is bound whole instead of narrowed to the one record a constant key selects.
…dless T# table Taken from exp/bf046-main b7d6d98 without the GPU mutex hold times, which need the lock-holder totals of the draw-record harness commit. After the split it prints the process's VmRSS and VmHWM.
The table mode loaded each T# with s_load_dwordx8 at a register offset, which the SRT walker does not plan, so every draw was an address-based build and no template formed. The T#s now come from s_buffer_load_dwordx8 through a T# table V# at root+0x220. The key is still the object V#'s index, plus a word of frame data the program reads at run time (zero), so the planner cannot narrow the table to the records a known key selects: every draw binds the whole table, as a bindless title does, and draws of one program reuse one template. VILLAGE_WRITE_PERCENT rewrites that share of the table's textures with their own bytes before each frame, so the proof sees writes without the sampled texels changing.
|
This pull request is labeled |
|
Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.
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.
|
✅ NVIDIA GPU test: passedPR head
Failing on main too (3, not counted against this PR)Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)Validated but not compared (2, not in the main baseline's validation pass)Validation IDs already present on main (4 IDs in 398 tests)Skipped (40)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
# Conflicts: # core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
✅ NVIDIA GPU test: passedPR head
Unstable on main (2, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (2, not counted against this PR)Validated but not compared (2, not in the main baseline's validation pass)Validation IDs already present on main (4 IDs in 390 tests)Skipped (40)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
|
An AMD RDNA 4 run on Windows of Windows 11 Pro 10.0.22631, AMD Radeon RX 9070 XT (
AI-assisted: yes (Claude Code) |
With APS5_TABLE_INDEX=1 the write index's own fast proof counted a storage image's collect refusal, its fast-fail reason and a depth-guard refusal, and the ordinary fast proof the index falls back to counted them again. The index's proof now counts none of them, so the fallback counts each once, as without the index. No proof outcome changes. agc_driver_resource_cache_collect_refusals_index runs the collect refusal test with the index on.
With APS5_TABLE_INDEX=1 a Pending failure of the write index's own fast proof counted its pending images (foreign, snapshot and T1 eligible), and the ordinary fast proof the index falls back to counted them again. countPendingFail now counts nothing inside the index's proof, as the collect refusal and fast-fail counts already do, so the fallback counts each once, as without the index. No proof outcome changes. ShaderResources::PendingFailCounts reads the three counters beside CollectRefusalCounts. agc_driver_resource_cache_pending_fail_count and its _index twin prove a template over a host-imported surface, whose binding views its storage image, while a smaller storage image over the same memory has results pending: each proof counts one foreign and one T1-eligible pending fast-fail, with the index on and off. The imports keep the write watch (APS5_WRITE_WATCH_IMPORTS=watch); where the surface leaves it, the test skips.
✅ NVIDIA GPU test: passedPR head
Unstable on main (2, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validated but not compared (5, not in the main baseline's validation pass)Validation IDs already present on main (4 IDs in 421 tests)Skipped (42)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
The conflict in core/libs/prx/libSceAgcDriver/CMakeLists.txt from boykopovar#12862's split is resolved with tools/agc_test_split.py resolve, which moves this PR's test registrations into tests/cmake/.
✅ NVIDIA GPU test: passedPR head
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (4, not counted against this PR)Validated but not compared (3, not in the main baseline's validation pass)Validation IDs already present on main (5 IDs in 476 tests)Skipped (44)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
|
@EagleBearerYS two points on
A concrete sequence for point 1: fill the 1,024-entry history, overflow at generation G, take the fallback baseline G, finish that proof, then bump another block at G+1 before the receipt trims G. The G+1 bump isn't recorded, trimming G clears the overflow marker, and on the next call This also changes |
…e trimmed baseline TrimSubscriptionBumps cleared an overflowed subscription's marker and its whole history whenever the overflow was at or before the trimmed generation. A bump between the proof's baseline read and the trim is newer than the baseline and went unrecorded because of the overflow, so the next partial proof listed no block for it. Clear the marker only while the subscription's generation still equals the baseline, under the index mutex; otherwise keep it and drop only the bumps at or before the baseline.
…d skip it without a write watch The default execution-test registration already runs the test. Its fragment appends APS5_TABLE_INDEX=1 to that registration's environment, and the test returns 77 when guest memory has no write watch, so such a machine reports a skip instead of a pass.
|
@oneandonlydean thanks. Done in 2537d44 and cdcb984:
|
|
Thanks, A correction to my point 2: I was wrong that |
✅ AMD GPU test: passedPR head
Unstable on main (2, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (8, not counted against this PR)Validated but not compared (4, not in the main baseline's validation pass)Validation IDs already present on main (4 IDs in 516 tests)Skipped (58)ReproduceAutomated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
✅ NVIDIA GPU test: passedPR head
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validated but not compared (3, not in the main baseline's validation pass)Validation IDs already present on main (5 IDs in 484 tests)Skipped (45)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
Texture.cpp conflicts at the top of StorageTexture::markLayersPending, where boykopovar#10256 records the image's layout write and this branch notes the guest range as GPU-pending. The two are independent, and the merge keeps both, main's first.
✅ NVIDIA GPU test: passedPR head
Unstable on main (3, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validated but not compared (4, not in the main baseline's validation pass)Validation IDs already present on main (13 IDs in 543 tests)Skipped (50)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
✅ AMD GPU test: passedPR head
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (8, not counted against this PR)Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)Validated but not compared (3, not in the main baseline's validation pass)Validation IDs already present on main (8 IDs in 533 tests)Skipped (59)ReproduceAutomated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
ShaderResources.cpp conflicts at the end of PrepareDrawBindings, where boykopovar#13858 moves the descriptor-set writes into writeDrawSets and this branch keeps the bindings when no snapshot is selected but a texture was rebound. The merge takes boykopovar#13858's split and keeps this branch's condition on the early return.
✅ NVIDIA GPU test: passedPR head
Unstable on main (4, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validated but not compared (3, not in the main baseline's validation pass)Validation IDs already present on main (13 IDs in 551 tests)Skipped (49)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
✅ AMD GPU test: passedPR head
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (8, not counted against this PR)Validated but not compared (3, not in the main baseline's validation pass)Validation IDs already present on main (8 IDs in 551 tests)Skipped (60)ReproduceAutomated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
✅ AMD GPU test: passedPR head
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (9, not counted against this PR)Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)Validated but not compared (4, not in the main baseline's validation pass)Validation IDs already present on main (4 IDs in 557 tests)Skipped (60)ReproduceAutomated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
✅ NVIDIA GPU test: passedPR head
Unstable on main (4, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validated but not compared (4, not in the main baseline's validation pass)Validation IDs already present on main (9 IDs in 557 tests)Skipped (55)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
What
A cached draw over an image table proves every sampled texture on every Revalidate: one write-watch collect, one depth-plane flush and one generation compare per element. So the proof grows with the table. On
main, the Village harness spends 1,607 µs of lookup per draw at 4,096 entries, 1,288 µs of it in Revalidate.With
APS5_TABLE_INDEX=1, the build subscribes its watched textures and watched T# table spans to a write index over 64 KiB blocks.APS5_DRAW_PIPELINE, and everything else.AnyPendingCpuWrites(perf(agc): skip an address space's pending CPU write scan when none are pending #8895) finds no writer.AgcDriver::Mutex, asGuestMemory's are.Village harness (lavapipe, 850 draws per frame, the whole bindless table bound per draw, 256×256). Each figure is the median of 3 runs on one machine, with main → index → off in each round. Lookup is find + Revalidate per draw, on the draw pipeline's worker; wall is the harness's frame time per draw.
With the index off on the same binary, lookup is −4.3% to +4.7% of main and wall −1.4% to +5.7%. That's inside main's own spread between runs (6–16%), except for one off run at 4,096 / 2%.
Two tests change with it:
agc_draw_descriptors' first-layer case now asserts the proof path the switch implies. It also syncs its recorder when it leaves early, since a failed assertion crashed it.agc_driver_lookup_log_fits_build, which measures the lookup log after a full walk, pins the switch off.Resident Evil Village (PPSA01556)
Resident Evil Village (PPSA01556), static menu. Two blocks of four unprofiled runs: B A A B, then A B B A.
main319621b plus fix(shader): load and store 16-bit data of converted packed images #11580 and fix(agc): keep buffer owners when a staged address space dissolves #10304, with a localscePadOpenanswer for port types 3 and 4 (commit 103015a). B is the same tree plus this PR's four commits (e079ecb, the same added and removed lines as this head; commit e23a2e325e), run withAPS5_TABLE_INDEX=1.APS5_LAZY_SHADER_PREPARE=1 APS5_NO_GPL=1 APS5_HOST_IMPORT_MIB=6400 APS5_TRACE_SUMMARY_EVERY=10, with scripted pad input. B addsAPS5_TABLE_INDEX=1; nothing else differs.Driver.Packetis 10-27 % lower (3,303, 3,643, 3,343 and 3,922 against 3,847, 4,525, 4,712 and 4,466), and so isGraphics.Draw(662-780 against 820-979). Private memory is the same in both, 14.0-14.3 GB at menu +15. There were no aborts and no failed host imports.APS5_PROFILE_DRAW=1, menu +10..15, 31 ten-second[rescache]blocks, one boot per tree, so this isn't a pair.diag/village-combined-on-103015a9cac9cff8c, the A tree above plus perf(agc): bypass the draw cache while its hit rate is under 5% #12478, perf(agc): prove cached image tables from a write index behind APS5_TABLE_INDEX #12745 and perf(agc): keep an untracked fast proof across collect epochs on Windows while its write-watch hint has not moved #12921, run withAPS5_TABLE_INDEX=1.Driver.Packetper frame: 3,822 and 3,568 ms against 5,187 and 5,301 ms.memory_wait: 2,199 and 2,032 ms against 3,090 and 3,167 ms.APS5_TABLE_INDEX=1:agc_untracked_revalidateandagc_untracked_hint_proof, expect a full walk after a collect epoch. The index proves those draws by looking the elements up again, which re-reads the memory, so the content still follows every store.test/untracked-proof-index-on-12745, for perf(agc): prove cached image tables from a write index behind APS5_TABLE_INDEX #12745 if perf(agc): keep an untracked fast proof across collect epochs on Windows while its write-watch hint has not moved #12921 lands first;test/untracked-proof-index-on-12921, for perf(agc): keep an untracked fast proof across collect epochs on Windows while its write-watch hint has not moved #12921 if perf(agc): prove cached image tables from a write index behind APS5_TABLE_INDEX #12745 lands first.ctest -R '^agc'gives 1,048 passed, 73 skipped and 0 failed in both modes.Summary lines at menu +10 of the first block's A and B runs (main 103015a, this change e23a2e325e)
{"commit":"103015a9ca66","dirty":false,"env":{"APS5_HOST_IMPORT_MIB":"6400","APS5_LAZY_SHADER_PREPARE":"1","APS5_NO_GPL":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_DIR":"<path>","APS5_TRACE_SUMMARY_EVERY":"10"},"gpu":"NVIDIA GeForce RTX 3070 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26300","cpu":"AMD Ryzen 9 5900HS with Radeon Graphics","cpu_threads":16,"memory_mib":15776,"title_id":"PPSA01556","title":"Resident Evil Village","content_version":"01.210.000","resolution":"3840x2160","window":{"spec":"summary_every 10","first_present":3606,"ms":11000.454},"interval_start_s":1034.493,"interval_end_s":1045.494,"presents":8,"frame_ms":{"p50":1373.495,"p90":1562.555,"p99":1562.555,"max":1562.555},"gpu_busy_ms_per_present":238.978,"worker_busy_pct":{"queue worker 0x0":99.569,"queue worker 0x38":0.065,"queue worker 0x20":88.370,"queue worker 0x28":94.164},"top_phases_ms_per_frame":{"Driver.Packet":3875.450,"Driver.Packet.memory_wait":2227.222,"Driver.Packet.draw":989.142,"Driver.Draw":988.349,"Graphics.Draw":831.033,"Graphics.Draw.draw_and_resource_release":812.519,"Driver.Packet.dispatch":620.045,"Driver.Dispatch":566.327},"trace_dropped":false,"trace":""} {"commit":"e23a2e325e57","dirty":false,"env":{"APS5_HOST_IMPORT_MIB":"6400","APS5_LAZY_SHADER_PREPARE":"1","APS5_NO_GPL":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_TABLE_INDEX":"1","APS5_TRACE_DIR":"<path>","APS5_TRACE_SUMMARY_EVERY":"10"},"gpu":"NVIDIA GeForce RTX 3070 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26300","cpu":"AMD Ryzen 9 5900HS with Radeon Graphics","cpu_threads":16,"memory_mib":15776,"title_id":"PPSA01556","title":"Resident Evil Village","content_version":"01.210.000","resolution":"3840x2160","window":{"spec":"summary_every 10","first_present":1778,"ms":10373.574},"interval_start_s":835.524,"interval_end_s":845.898,"presents":8,"frame_ms":{"p50":1309.928,"p90":1551.612,"p99":1551.612,"max":1551.612},"gpu_busy_ms_per_present":241.686,"worker_busy_pct":{"queue worker 0x0":99.548,"queue worker 0x38":0.076,"queue worker 0x20":88.449,"queue worker 0x28":93.505},"top_phases_ms_per_frame":{"Driver.Packet":3645.606,"Driver.Packet.memory_wait":2102.811,"Driver.Packet.draw":935.046,"Driver.Draw":934.113,"Graphics.Draw":746.266,"Graphics.Draw.draw_and_resource_release":726.113,"Driver.Packet.dispatch":554.988,"Driver.Dispatch":491.246},"trace_dropped":false,"trace":""}trace_digest.py of A (103015a)
seconds 960:30 from present 2901: 21 presents, frame p50 1497.6 / p90 1783.2 / p99 1903.7 / max 1903.7 ms, GPU busy 230.6 ms per present
NVIDIA GeForce RTX 3070 Laptop GPU, NVIDIA 617.42, Windows 10.0.26300, 3840x2160, commit 103015a, dropped: no
{"commit":"103015a9ca66","dirty":false,"env":{"APS5_HOST_IMPORT_MIB":"6400","APS5_LAZY_SHADER_PREPARE":"1","APS5_NO_GPL":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_SECONDS":"960:30"},"gpu":"NVIDIA GeForce RTX 3070 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26300","cpu":"AMD Ryzen 9 5900HS with Radeon Graphics","cpu_threads":16,"memory_mib":15776,"title_id":"PPSA01556","title":"Resident Evil Village","content_version":"01.210.000","resolution":"3840x2160","window":{"spec":"seconds 960:30","first_present":2901,"ms":31778.408},"presents":21,"frame_ms":{"p50":1497.603,"p90":1783.23,"p99":1903.699,"max":1903.699},"gpu_busy_ms_per_present":230.559,"worker_busy_pct":{"queue worker 0x0":99.554,"queue worker 0x38":0.057,"queue worker 0x28":93.25,"queue worker 0x20":89.065},"top_phases_ms_per_frame":{"Driver.Packet":4261.674,"Driver.Packet.memory_wait":2505.215,"Driver.Packet.draw":1156.599,"Driver.Draw":1155.503,"Graphics.Draw":928.353,"Graphics.Draw.draw_and_resource_release":907.06,"Driver.Packet.dispatch":560.368,"Driver.Dispatch":510.148},"trace_dropped":false,"trace":"aps5-trace-103015a9ca66-33724.json.gz"}Slowest frames (largest slices inside each, inclusive)
Threads (ms per frame)
Largest slices (ms per frame, inclusive, all threads): Driver.Packet 4250.6, Driver.Packet.memory_wait 2505.1, Driver.WorkerWait 1594.9, Driver.WorkerWait.dequeue_order 1338.7, Driver.Packet.draw 1155.6, Driver.Draw 1154.5, Graphics.Draw 927.6, Graphics.Draw.draw_and_resource_release 906.3, Driver.Packet.dispatch 559.1, Driver.Dispatch 508.7
Longest waits
Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)
trace_digest.py of B (e23a2e325e, APS5_TABLE_INDEX=1)
seconds 960:30 from present 3839: 20 presents, frame p50 1518.3 / p90 2022.3 / p99 2080.3 / max 2080.3 ms, GPU busy 241.8 ms per present
NVIDIA GeForce RTX 3070 Laptop GPU, NVIDIA 617.42, Windows 10.0.26300, 3840x2160, commit e23a2e325e57, dropped: no
{"commit":"e23a2e325e57","dirty":false,"env":{"APS5_HOST_IMPORT_MIB":"6400","APS5_LAZY_SHADER_PREPARE":"1","APS5_NO_GPL":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_TABLE_INDEX":"1","APS5_TRACE_SECONDS":"960:30"},"gpu":"NVIDIA GeForce RTX 3070 Laptop GPU","driver":"NVIDIA 617.42","os":"Windows 10.0.26300","cpu":"AMD Ryzen 9 5900HS with Radeon Graphics","cpu_threads":16,"memory_mib":15776,"title_id":"PPSA01556","title":"Resident Evil Village","content_version":"01.210.000","resolution":"3840x2160","window":{"spec":"seconds 960:30","first_present":3839,"ms":30625.661},"presents":20,"frame_ms":{"p50":1518.318,"p90":2022.26,"p99":2080.313,"max":2080.313},"gpu_busy_ms_per_present":241.793,"worker_busy_pct":{"queue worker 0x0":99.506,"queue worker 0x38":0.138,"queue worker 0x28":93.491,"queue worker 0x20":89.008},"top_phases_ms_per_frame":{"Driver.Packet":4317.648,"Driver.Packet.memory_wait":2512.91,"Driver.Packet.draw":1135.088,"Driver.Draw":1133.775,"Graphics.Draw":856.639,"Graphics.Draw.draw_and_resource_release":832.467,"Driver.Packet.dispatch":624.885,"Driver.Dispatch":563.075},"trace_dropped":false,"trace":"aps5-trace-e23a2e325e57-31916.json.gz"}Slowest frames (largest slices inside each, inclusive)
Threads (ms per frame)
Largest slices (ms per frame, inclusive, all threads): Driver.Packet 4306.1, Driver.Packet.memory_wait 2512.7, Driver.WorkerWait 1637.1, Driver.WorkerWait.dequeue_order 1382.7, Driver.Packet.draw 1134.2, Driver.Draw 1132.9, Graphics.Draw 856.1, Graphics.Draw.draw_and_resource_release 831.8, Driver.Packet.dispatch 624.3, Driver.Dispatch 562.4
Longest waits
Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)Graphics/src/ShaderResources.cppand conflict there, and perf(agc): keep an untracked fast proof across collect epochs on Windows while its write-watch hint has not moved #12921 takes the second merge with the test-only update ontest/untracked-proof-index-on-12921)Related:
mainand in this branch since fa7e8e3. It composes with the index's import source: its audit runs only after the host import set changes, unlessAPS5_TEMPLATE_AUDIT_ALWAYSis set, so while imports hold, the index has no periodic full walk.APS5_GPU_HEAP, the index's in-place re-bind must also refresh a heap stage's slots.