Repository navigation
perf(agc): free a registered shader's decoded program once it is planned - #5910
Conversation
CnRJay
left a comment
There was a problem hiding this comment.
Checked: on main, RegisteredPreparation::decoded is only read by the UnresolvableSwappcTarget call in PlanRegistered, and the preparation decodes again in AcquireFrontend. So every queued shader held a decoded copy it never used, and decoding into a local keeps behaviour identical. Clean change with a clear heap profile behind it.
It should also help #5872, which reports the same registration burst pushing Demon's Souls out of memory.
|
A second title: Forspoken (PPSA03984) on Linux (RTX 4070 Ti, 595.99.02, GCC 13.3, Release). The base is
The total barely moves because the run with this PR got further before the cap stopped it: its last profile came after 82 dumps instead of 61. Its resource plans ( AI-assisted: yes |
|
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.
|
Since registered shaders are prepared in the background (boykopovar#2583), PlanRegistered keeps the decoded RDNA program in the RegisteredPreparation it queues for the preparation pool. Only PlanRegistered reads it, for the s_swappc_b64 check, so each queued shader holds its decoded program until a pool worker has prepared it. During the registration burst of Borderlands 4 (PPSA01494) these programs held 638 MiB. Decode into a local again, as before boykopovar#2583. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
01b4ca8 to
54f5c41
Compare
Main brings the pipelined-draw stack (boykopovar#1193/boykopovar#1201/boykopovar#1202), boykopovar#6316 after boykopovar#6060, boykopovar#5893, boykopovar#5910 and depth/HTILE work (boykopovar#454, boykopovar#5496, boykopovar#5554, boykopovar#5524, boykopovar#5627, 626cff7). Our SharedSpirv / cold-SPIR-V storage is replaced by main's mapped cache entries (CreateFileMappingW on Windows), so cached variants' words are views into the cache files instead of heap copies; APS5_COLD_SPIRV, APS5_COLD_SPIRV_FILE and APS5_CACHE_SPIRV_BACKING are gone. Kept local: lane hang fixes, boykopovar#5815, boykopovar#6227, table stores and storage tables, option 1 substitutes (.slots), stencil remap and alias path (a written storage view our alias path serves seeds from the depth surface instead of retiring it), 10_11_11 blending next to main's reversed-order blending, VRAM share/spill/fallbacks, diagnostics. Versions: FormatVersion 43, RuntimeAbi 17, request serializer v31. Notes in work/sync-20261013/*-4.md. AI-assisted: yes
What
Borderlands 4 (PPSA01494) registers shaders in a burst about 190 s into a warm run, and its anonymous resident memory grows from about 1 GB to 8–9 GB within six seconds. A jemalloc heap profile taken in that burst shows 638 MiB held by RDNA programs that
PlanRegistereddecoded (RdnaInstructionDecoder::Decode←PlanRegistered←Driver::RegisterShader←sceAgcCreateShader).Since #2583 prepares registered shaders in the background,
PlanRegistereddecodes the program into theRegisteredPreparationit queues for the registration prepare pool. OnlyPlanRegisteredreads that decoded program, for thes_swappc_b64check;PrepareRegistereddoes not use it, and the preparation decodes the program again on its own (ShaderPreparationContext::AcquireFrontend). So every shader waiting in the pool's queue holds a decoded copy of its program until a worker has prepared it.PlanRegistereddecodes into a local again, as it did before #2583, and the queued preparation no longer carries it.Tested
Ubuntu 24.04, GCC 13.3 (CI's compiler), Release with CI's configure flags, on
main84b01f9, then rebased ontomaina453108 (which changes onlylibSceVoicesince):All 541 tests in
libSceAgcDriverpass.agc_driver_stencil_ref_exportis skipped because the device lacksVK_EXT_shader_stencil_export, andagc_host_import_sparseandagc_driver_sparse_dma_buf_importbecause/dev/udmabufis not accessible here.Borderlands 4 (PPSA01494), relinked with
--to-inteland run on Linux (RTX 4070 Ti, NVIDIA 580.142) with a warm shader cache under a 15 GB memory cap, once per build under jemalloc 5.3 with sampled heap profiles (prof:true,lg_prof_interval:32,lg_prof_sample:19). The two runs differ only inlibSceAgcDriver.prx. Live heap in the last profile before the run reached the cap (about 181 s):mainPlanRegisteredAcquireFrontend(preparations in progress)Most of the rest of the heap at that point is resource plans decoded from the shader cache (1.7–1.9 GB) and SPIR-V optimization in progress, which this change does not touch.
Without jemalloc the title still reaches the 15 GB cap in that burst, at 201–209 s, with and without this change: 3 of 3 runs with this PR, and 2 of 3 on
main, where the third stopped earlier at a glibc heap check that this machine's memory produces with and without changes (see perf(agc): return freed memory when shader preparation drains #5777). So this change alone does not let it through on that budget.tools/check_conventions.py --base upstream/mainpasses, andtools/pr_overlap.py --branchfinds no overlap.Not checked on Windows or on a console.
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