Repository navigation
Conversation
|
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.
|
cdb140f to
727a810
Compare
|
Rebased onto The memo still compares:
ctest AI-assisted: yes (Claude Code) |
727a810 to
341a085
Compare
…re unchanged A table snapshot read every record of every column again on each materialization: locate its dwords, answer page accessibility, read the runs, assemble a descriptor per record and index it. In 007 First Light's menu, with columns of up to 19k records, snapshots took 7.78 s of every 10 s (5,209 snapshots, 1.49 ms each). With the memo they take 0.62 ms each and the menu runs at 1.31 frames/s instead of 0.71 (10,490 hits and 0 misses per 10 s, 38 columns and 29.8 MiB kept). A column now keeps a memo of its last read: the column itself (TableColumn), the narrowed record states it started from, the layout (which records are read whole or by dword, the runs, the states of the records left unread, and every page accessibility query the layout made with its answer), the words it read, and their distinct descriptors with their available-dword masks. The memo is stored under the shader's code address, the column's descriptor source, the table's base, size and key count, and a hash of the narrowed states. That key only finds a candidate. The candidate is used only when its column equals the column being read under TableColumn's operator== (every field that places or reads a record; the key bound and the key domains act only through the key count and the narrowed states), its narrowed states equal the current ones element by element, and every recorded accessibility query gives the same answer again, in order. The layout is then reused. When the words read through it are also equal, the records take the stored descriptors: one index lookup per distinct descriptor instead of one per record. The words are always read, so a changed table is never reused. A column is kept only from the second time its key is seen: the last 4096 keys read without a memo are remembered, so a table read once (or whose base moves every frame) costs one plain read and no memory. Up to 512 columns and an estimated 256 MiB are kept, least recently used out. A memo that only refreshes the words shares the layout of the memo it replaces, and a query that repeats the one recorded just before it is recorded once. The [image-table] profile line counts hits, reused layouts and misses with the columns and MiB kept, and ResourceMaterializer::ColumnMemoCounts() returns the totals. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
341a085 to
81c1a5f
Compare
adriadam10
left a comment
There was a problem hiding this comment.
The memo looks sound: the key plus TableColumn::operator==, the narrowed states and the replayed accessibility checks cover what the layout reads, and the words are always read again. The tests assert hit / layout reused / miss on each read, and they would fail without the change.
Two things before this can merge:
docs/dev/ARCHITECTURE.md: this adds a cache to the image table snapshot, but theTableSnapshotterparagraph (the one starting "At every invocation,TableSnapshotter... reads each column's records") doesn't mention it. Please add a sentence under it: the per-column memo, what it is keyed and verified on, that the words are always re-read, and its bounds (512 columns, 256 MiB, LRU, admitted on the second sighting).- Conflict with #7395: it also rewrites the column read in
ResourceMaterializer.cpp(merge-treeconflicts in that file). Whichever lands second needs a rebase, so please rebase once the other is onmain.
What
An image table snapshot reads every record of every column again on each materialization: it locates each record's dwords, answers page accessibility, reads the runs, assembles a descriptor per record and indexes it. In 007 First Light's menu (PPSA11386), with columns of up to 19k records, snapshots take 7.78 s of every 10 s on
main(5,209 snapshots, 1.49 ms each).Each column now keeps a memo of its last read: the column itself (
TableColumn), the narrowed record states it started from, the layout, the words it read, and their distinct descriptors with their available-dword masks. The layout covers which records are read whole or by dword, the runs, the states of the records left unread, and every page accessibility query the layout made with its answer.TableColumn::operator==), its narrowed states equal the current ones element by element, and every recorded accessibility query gives the same answer again, in order. The layout is then reused.[image-table]profile line counts hits, reused layouts and misses with the columns and MiB kept.ResourceMaterializer::ColumnMemoCounts()returns the totals, for the tests.Related:
[image-table]report line,TableCountersand the snapshot reader; whichever lands second rebases. It also widensTableColumnand itsoperator==, which keeps the comparison here complete.GuestMemory's verify path (fix(agc): cache inaccessible guest pages #6184, perf(agc): answer small guest read checks without /proc/self/maps #6179) are covered by the replay.Tested
agc_shader_memory: new cases forEach read asserts whether it was a hit, a reused layout or a miss.
ctest
agc|shader|recompileron lavapipe (Mesa 26.2.4, LLVM 23.1.1; Arch Linux container, GCC 16.2.1), this change onmain4186f06: 590 of 599 pass, the same 9 failures asmainbuilt the same way (5 known ones plus 4 that need write watching, which the container lacks).Rebased onto
mainf1af6c5 (2026-10-18). Onlytests/ShaderMemory.cppconflicted, where fix(recompiler): fold chained immediate addends of image-table record offsets #7325 addedverifyChainedTableOffsetat the same spot, and both tests are kept; the other files are unchanged. ctest^agcon lavapipe, in an Arch Linux container with the same Mesa, LLVM and GCC and also without write watching: 663 of 676 pass. The 13 failures are the same as onmainf1af6c5 built the same way: the 9 above, plusagc_driver_msaa_dcc_clear_2,_4,_8andagc_driver_native_depth_bda_clear, which need write watching.RTX 3080 (NVIDIA 615.78.08), CachyOS (Linux 7.2.10), GCC 16.2.1, Release, ctest
agc|shader|recompiler: 599 of 600 pass. The failure,agc_driver_store_over_untracked, fails on Linuxmaintoo (reported on fix(agc): save pending storage results in every 64 KiB block that leaves the write watch #5671).A local benchmark (200 captures of one shader with a 19k-entry image table; not part of this PR), 6 runs per side alternated on
main4186f06, on a Ryzen 7 5700X:maincapture (µs)Materialization time and peak RSS were equal to
mainin all three.007 First Light (PPSA11386), main menu, no input, warm caches, RTX 3080 as above.
Both arms are
main4186f06 plus the title's start-up fixes (fix(shader): apply cube output modifiers whenever IEEE mode is off #5799, fix(shader): leave out the all-zero position distance when guest distances fill all eight #6317, fix(shader): repeat phi folding until no phi folds #6332, an earlier revision of perf(agc): stores beside a range in its 64 KiB block leave the range unchanged #6346, and local commits: input scripting, an FPS logger and fault diagnostics (off), a registered-range join, a volume-sampling workaround). Arm A adds this change; arm B is the same tree with it reverted.mainalone stops before this menu.APS5_TRACE_FRAMES=200:40and no other trace or profile switch, presents 200–239, runs alternated ABBAAB:Menu rate, frames 215–255: A 1.325 / 1.312 / 1.307, B 0.680 / 0.714 / 0.724 (medians 1.312 and 0.714, 1.84×).
[image-table]per 10 s, A: 9,177 snapshots in 5.72 s (0.62 ms each), 10,490 memo hits, 0 misses, 38 columns and 29.8 MiB kept. B: 5,209 snapshots in 7.78 s (1.49 ms each).Summary lines (A1–A3, then B1–B3):
{"commit":"699643f682cc","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_FRAMES":"200:40"},"gpu":"NVIDIA GeForce RTX 3080","driver":"NVIDIA 615.78.08","os":"Linux 7.2.10-1-cachyos","resolution":"3840x2160","window":{"spec":"frames 200:40","first_present":200,"ms":30545.996},"presents":40,"frame_ms":{"p50":771.669,"p90":796.05,"p99":808.454,"max":808.454},"gpu_busy_ms_per_present":150.099,"worker_busy_pct":{"queue worker 0x0":99.952,"queue worker 0x20":98.291,"queue worker 0x38":98.295,"queue worker 0x28":98.843,"queue worker 0x30":98.844},"top_phases_ms_per_frame":{"Driver.Packet":3771.015,"Driver.Packet.memory_wait":2809.317,"Driver.Packet.draw":669.359,"Driver.Draw":669.227,"Driver.Draw.shader_compile_and_link":638.826,"Shader.DrawStage":622.789,"Shader.DrawStage.capture_resources":491.826,"Driver.Packet.dispatch":282.911},"trace_dropped":false,"trace":"aps5-trace-699643f682cc-2.json.gz"} {"commit":"699643f682cc","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_FRAMES":"200:40"},"gpu":"NVIDIA GeForce RTX 3080","driver":"NVIDIA 615.78.08","os":"Linux 7.2.10-1-cachyos","resolution":"3840x2160","window":{"spec":"frames 200:40","first_present":200,"ms":30478.711},"presents":40,"frame_ms":{"p50":763.671,"p90":785.542,"p99":804.787,"max":804.787},"gpu_busy_ms_per_present":154.606,"worker_busy_pct":{"queue worker 0x0":99.954,"queue worker 0x20":97.463,"queue worker 0x28":98.561,"queue worker 0x38":97.461,"queue worker 0x30":98.563},"top_phases_ms_per_frame":{"Driver.Packet":3746.547,"Driver.Packet.memory_wait":2792.384,"Driver.Packet.draw":667.504,"Driver.Draw":667.38,"Driver.Draw.shader_compile_and_link":634.349,"Shader.DrawStage":623.035,"Shader.DrawStage.capture_resources":496.436,"Driver.Packet.dispatch":277.717},"trace_dropped":false,"trace":"aps5-trace-699643f682cc-2.json.gz"} {"commit":"699643f682cc","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_FRAMES":"200:40"},"gpu":"NVIDIA GeForce RTX 3080","driver":"NVIDIA 615.78.08","os":"Linux 7.2.10-1-cachyos","resolution":"3840x2160","window":{"spec":"frames 200:40","first_present":200,"ms":30755.968},"presents":40,"frame_ms":{"p50":770.945,"p90":797.25,"p99":803.52,"max":803.52},"gpu_busy_ms_per_present":155.539,"worker_busy_pct":{"queue worker 0x0":99.955,"queue worker 0x20":97.574,"queue worker 0x28":98.892,"queue worker 0x30":98.894,"queue worker 0x38":97.936},"top_phases_ms_per_frame":{"Driver.Packet":3789.414,"Driver.Packet.memory_wait":2817.807,"Driver.Packet.draw":669.362,"Driver.Draw":669.231,"Driver.Draw.shader_compile_and_link":639.041,"Shader.DrawStage":629.266,"Shader.DrawStage.capture_resources":499.374,"Driver.Packet.dispatch":294.376},"trace_dropped":false,"trace":"aps5-trace-699643f682cc-2.json.gz"} {"commit":"17b7e9e8b233","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_FRAMES":"200:40"},"gpu":"NVIDIA GeForce RTX 3080","driver":"NVIDIA 615.78.08","os":"Linux 7.2.10-1-cachyos","resolution":"3840x2160","window":{"spec":"frames 200:40","first_present":200,"ms":58820.01},"presents":40,"frame_ms":{"p50":1463.749,"p90":1498.044,"p99":1542.307,"max":1542.307},"gpu_busy_ms_per_present":162.081,"worker_busy_pct":{"queue worker 0x0":99.975,"queue worker 0x20":97.526,"queue worker 0x28":98.61,"queue worker 0x38":97.625,"queue worker 0x30":98.614},"top_phases_ms_per_frame":{"Driver.Packet":7238.663,"Driver.Packet.memory_wait":5568.945,"Driver.Packet.draw":1364.972,"Driver.Draw":1364.833,"Driver.Draw.shader_compile_and_link":1340.145,"Shader.DrawStage":1333.044,"Shader.DrawStage.capture_resources":1199.99,"Driver.Packet.dispatch":296.511},"trace_dropped":false,"trace":"aps5-trace-17b7e9e8b233-2.json.gz"} {"commit":"17b7e9e8b233","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_FRAMES":"200:40"},"gpu":"NVIDIA GeForce RTX 3080","driver":"NVIDIA 615.78.08","os":"Linux 7.2.10-1-cachyos","resolution":"3840x2160","window":{"spec":"frames 200:40","first_present":200,"ms":56026.433},"presents":40,"frame_ms":{"p50":1397.877,"p90":1433.603,"p99":1447.574,"max":1447.574},"gpu_busy_ms_per_present":160.538,"worker_busy_pct":{"queue worker 0x0":99.972,"queue worker 0x20":97.533,"queue worker 0x28":98.63,"queue worker 0x30":98.635,"queue worker 0x38":97.646},"top_phases_ms_per_frame":{"Driver.Packet":6894.023,"Driver.Packet.memory_wait":5289.778,"Driver.Packet.draw":1289.66,"Driver.Draw":1289.518,"Driver.Draw.shader_compile_and_link":1266.412,"Shader.DrawStage":1258.559,"Shader.DrawStage.capture_resources":1123.283,"Driver.Packet.dispatch":301.492},"trace_dropped":false,"trace":"aps5-trace-17b7e9e8b233-2.json.gz"} {"commit":"17b7e9e8b233","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_FRAMES":"200:40"},"gpu":"NVIDIA GeForce RTX 3080","driver":"NVIDIA 615.78.08","os":"Linux 7.2.10-1-cachyos","resolution":"3840x2160","window":{"spec":"frames 200:40","first_present":200,"ms":55599.282},"presents":40,"frame_ms":{"p50":1388.475,"p90":1440.71,"p99":1476.353,"max":1476.353},"gpu_busy_ms_per_present":161.73,"worker_busy_pct":{"queue worker 0x0":99.974,"queue worker 0x20":97.688,"queue worker 0x28":98.752,"queue worker 0x30":98.753,"queue worker 0x38":97.689},"top_phases_ms_per_frame":{"Driver.Packet":6848.013,"Driver.Packet.memory_wait":5254.326,"Driver.Packet.draw":1276.462,"Driver.Draw":1276.318,"Driver.Draw.shader_compile_and_link":1254.896,"Shader.DrawStage":1244.992,"Shader.DrawStage.capture_resources":1112.862,"Driver.Packet.dispatch":308.044},"trace_dropped":false,"trace":"aps5-trace-17b7e9e8b233-2.json.gz"}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)🤖 Generated with Claude Code