Repository navigation
fix(agc): apply the raw OOB_SELECT range rules to zero-stride vertex buffers - #1769
Conversation
…buffers With stride 0, OOB_SELECT 2 puts a fetch out of range only when NUM_RECORDS is 0, and OOB_SELECT 3 only when the offset is not below NUM_RECORDS (GFX10 table in Mesa ac_descriptors.c). A one-record descriptor therefore returns its whole element: Unreal's per-draw PrimitiveId stream is such a descriptor, and refusing it skipped the draw. A wholly out-of-range fetch reads a zero buffer.
|
Linux results, since this was tested on Windows only. Everything passes here, including Nobara 44 (Linux 7.2), GCC 16.2.1, Release,
CPU tests ( |
|
Linux run on AMD, since the description lists it as not tested: Ubuntu 24.04.5 (kernel 6.8), GCC 13.3, Release, AMD Radeon RX 7600 (RADV NAVI33, Mesa 25.2.8). This PR's head |
|
Tested this PR's commit (c17c5eb) in Poppy Playtime: Chapter 4 (PPSA25063, Unreal Engine 5), applied with the other open PRs that title needs. Windows 11, RTX 4070. It binds the same kind of V#: stride 0, |
|
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.
|
fix(agc): apply the raw OOB_SELECT range rules to zero-stride vertex buffers
What
A vertex attribute whose V# has stride 0 and fewer
NUM_RECORDSbytes than one element made the draw throw ("vertex fetch exceeds descriptor byte range"), so the draw was skipped.With stride 0 the range check depends on
OOB_SELECT(V# word 3, bits 28-29). The GFX10 table in Mesaac_descriptors.cgives:OOB_SELECT 2: out of range only whenNUM_RECORDS == 0.OOB_SELECT 3(swizzle off): out of range whenoffset >= NUM_RECORDS.This change:
VertexFetchOutOfRange(VertexInput.hpp) is true for a stride-0OOB_SELECT2 or 3 V# with no records.Drawbinds a zeroed buffer of one element for such an attribute instead of planning a guest copy.ZeroStrideAvailableBytesmakesVertexBufferReadSizeandVertexBufferExtent(the indirect-draw path) read the whole element for a stride-0OOB_SELECT 2V# with nonzero records. This goes through the normal guest copy, so pending GPU writes are flushed and the render-target alias check runs.OOB_SELECT 3with a fetch that only partly fits still throws, because the source does not say whether each dword is checked separately.OOB_SELECT0 and 1 keep the current structured checks.Why it matters: Unreal Engine binds its per-draw PrimitiveId vertex stream as a stride-0, one-record,
OOB_SELECT 2V# (V# xxxxxxxx 0000001e 00000001 20014204in PPSA16064). The vertex shader uses the id to fetch the primitive's transform. With the draw skipped, or with the id read as zero, every mesh is placed with primitive 0's transform.Relation to #1426: #1426 handles the same short zero-stride V#s. Its first version kept only the whole dwords below
NUM_RECORDS(records & ~3), so a one-record PrimitiveId stream read as 0. A downstream branch with that rule drew every terrain chunk of PPSA16064's title scene on top of the first one: the depth prepass covered about 13% of the frame and the base pass showed see-through blocks. Reading the whole element, asOOB_SELECT 2defines, brought depth coverage to about 37% of the 2848x1600 target. Since 7a8926c, #1426 also reads the whole element forOOB_SELECT 2, so both PRs give the same result for that case and for zero records. They differ only where no source settles the behaviour: a stride-0OOB_SELECT 3fetch that only partly fits, and shortOOB_SELECT0 and 1 buffers. #1426 keeps the whole dwords belowNUM_RECORDSand zero-fills the rest, recorded as unmeasured in its TechnicalDebt entry; this PR leaves those cases throwing. Both PRs change the same lines ofDraw.cpp,tests/Graphics.cppandTechnicalDebt.md, so whichever merges second needs rework.Tested
Windows 11, MinGW-w64 GCC 15.2.0, Release,
BUILD_TESTING=ON, NVIDIA RTX 5070.agc_driver_zero_stride_vertex_fetch. It draws through a fetch-shader vertex attribute and stores the fetched registers. Cases:OOB_SELECT 2with one record reads the whole element;OOB_SELECT 2with none reads zeros;OOB_SELECT 3with a whole element in range reads it;OOB_SELECT 3with none reads zeros;OOB_SELECT 0with a whole element in range is unchanged. It fails onmainwith "vertex fetch exceeds descriptor byte range" and passes with this change.agc_driver_graphicshas new unit checks forVertexBufferReadSize,VertexBufferExtentandVertexFetchOutOfRangeacross theOOB_SELECTmodes.agc_drivertests: 196/196 pass.Checklist
main; no other open PR implements the same functions (fix(agc): fetch zero-stride vertex buffers shorter than their element #1426 implements the sameOOB_SELECT 2rule and also covers the cases this PR leaves throwing; see "Relation to fix(agc): fetch zero-stride vertex buffers shorter than their element #1426")NotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository (attach them to this PR)