Repository navigation
fix(agc): copy an indirect draw's vertex buffer only up to its last element - #5975
Conversation
CnRJay
left a comment
There was a problem hiding this comment.
The extent now matches what the fetch can actually reach: (records - 1) * stride plus the element's bytes, which is also what VertexBufferReadSize gives for the last record. The added check that the extent equals the read of the last record pins the two together. The Forspoken case (attribute at 0x50 of a 96-byte record, buffer followed by a ---p region) is exactly the one the old records * stride got wrong. It also shortens every indirect draw's copy by stride - elementBytes for each attribute past offset 0, though that's small.
…lement For an indirect draw, whose counts only the GPU knows, the driver copies the whole range a vertex buffer descriptor covers, records * stride from the descriptor's address. The last record's element ends sooner when the attribute sits at an offset in its record, and the fetch can read at most (records - 1) * stride plus the element. Forspoken (PPSA03984) draws instances from a 48 MiB buffer of 96-byte records with six attributes at offsets 0 to 0x50 in each record. The copy for the attribute at 0x50 ran 0x50 bytes past the buffer into the unmapped page after it, and the draw stopped with "guest memory is not readable". VertexBufferExtent now ends at the last record's element, as VertexBufferReadSize does for the last record of a direct draw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
930abc9 to
1d98310
Compare
|
@CnRJay thanks for reviewing this, and for checking the extent against AI-assisted: yes |
|
Checked the new extent |
|
A re-check on current
So with #5937 merged, this is now the title's first stop on AI-assisted: yes |
What
Forspoken (PPSA03984) stops on an indirect draw:
The draw is instanced, with six per-instance attributes (fetch index 1) in one buffer of 96-byte records, at offsets 0, 0x10, 0x20, 0x30, 0x40 and 0x50. Each descriptor has 0x80000 records, so
records * strideis the 48 MiB buffer, and the 16 KiB after it are not readable (a---pregion in/proc/self/maps). Because an indirect draw's counts are unknown,prepareDrawInputscopies each descriptor'sVertexBufferExtent, which wasrecords * stridefrom the descriptor's own address. For the attribute at 0x50 that ends 0x50 bytes past the buffer. The last record's element ends at(records - 1) * strideplus the element's bytes from the descriptor's address, inside the buffer, and that is as far as the fetch can read.VertexBufferExtentnow ends there. That is whatVertexBufferReadSizegives for a direct draw that reads the last record. A zero stride, a null descriptor and a descriptor without records are handled as before. The comment aboveVertexBufferOutOfBoundsSelect, which described the old extent, is removed.agc_vertex_inputnow expects32 * (records - 1) + 16where it expected32 * records, checks that the extent equalsVertexBufferReadSizeat the last record, and checks that an attribute at offset 16 of a 32-byte record ends with the descriptor's records. Withmain'sVertexInput.hppit stops at "cached layout froze the indirect descriptor extent".Tested
Ubuntu 24.04, GCC 13.3 (CI's compiler), Release with CI's configure flags, on
maindb06cb4, then rebased ontomain8b8bfe0, RTX 4070 Ti with NVIDIA 595.99.02:libSceAgcDriverpass after the rebase.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.--to-inteland run on Linux under a 15 GiB memory cap. Both builds also carry fix(agc): accept a copy's sample selection on a draw that copies nothing #5937, without which the title stops at 43 s, and perf(shader): avoid redundancy traversal map copies #5511, without which a start from a filled shader cache runs out of memory within 10 s; they differ only inlibSceAgcDriver.prxand share one shader cache:tools/check_conventions.py --base upstream/mainpasses, andtools/pr_overlap.py --branchfinds no overlap.PPSA03984-app0) on currentmain8856f18, 1920x1080, pad input, one run each:mainstops at 22 s on theDB_RENDER_CONTROLcheck (fix(agc): accept a copy's sample selection on a draw that copies nothing #5937); with fix(agc): accept a copy's sample selection on a draw that copies nothing #5937 it stops at 61 s on the indirect vertex buffer read (fix(agc): copy an indirect draw's vertex buffer only up to its last element #5975); with fix(agc): accept a copy's sample selection on a draw that copies nothing #5937 and fix(agc): copy an indirect draw's vertex buffer only up to its last element #5975 at 116 s on the depth pyramid view (fix(agc): treat a view past a depth surface's first level as memory #5982); with all three it draws an in-game scene by 60 s (the lower half still black, about 4 FPS) and runs to 195 s, wherevkAllocateMemoryfor a render target fails withVK_ERROR_OUT_OF_DEVICE_MEMORYon the 12 GB RTX 4070 Ti. The memory goes to retired depth surfaces that are never freed. With fix(agc): free retired depth surfaces nobody samples #6465 added as well, it reaches a 600 s limit (onmain0c4816f).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