Skip to content

fix(shader): lower hulls with one input or one output control point (fork CI) - #2

Closed
EagleBearerYS wants to merge 5 commits into
mainfrom
fix/shader-hull-single-control-point-on-4222
Closed

EagleBearerYS wants to merge 5 commits into
mainfrom
fix/shader-hull-single-control-point-on-4222

Conversation

@EagleBearerYS

Copy link
Copy Markdown
Owner

Fork-internal PR to run the full Build workflow before the upstream PR. Based on boykopovar#4222's head; the last two commits are the change.

Celegans12 and others added 5 commits October 13, 2026 13:50
A wave64 hull program narrows EXEC with s_and_saveexec_b64 and restores it
later, so its exec words reach the lane-bit test as ANDs with the host
ballot, as loop or join phis, and through separate ballots for the two
halves. Tessellation lowering only recognised a ballot word directly and
refused the rest with "uses a lane mask other than its own lane's bit".

The own-lane bit of a mask pair is now rebuilt from the words' operations:
a ballot gives its predicate, bitwise and/or/xor/not become logical ones,
selects and phis become boolean selects and phis, all-zero or all-ones
words become constants. Both halves must have the same shape and ballot
the same predicate. Hull values that only feed one another, such as a
dead saved-exec loop, are removed before the remaining-ballot check, so
a mask whose other lanes are read (s_bcnt1_i32_b64 exec) is still refused.

The rewrite runs in hull programs only; local and domain programs keep
their exec words, which the half-wave reduction matcher needs.
… data

Hull programs whose relative patch index (v1 bits 7:0) reached anything
but a tessellation ring or LDS address threw, because the lowering runs
every Vulkan patch as relative patch 0. The quad terrain hull 0x80002fc700
in Ghost of Yotei is one, and so is the domain 0x8068fbd900 (v7).

Such hulls and domains now take (PrimitiveID mod GE_CNTL.PRIM_GRP_SIZE)
mod VGT_LS_HS_CONFIG.NUM_PATCHES, as if hull threadgroups took NUM_PATCHES
patches at a time out of each primitive group; both stages see the same
PrimitiveID for a patch. This holds only when every instance starts a new
threadgroup, so it needs GE_CNTL.BREAK_WAVE_AT_EOI (bit 18); with the bit
clear the instances share threadgroups and such programs still throw.
PRIM_GRP_SIZE and BREAK_WAVE_AT_EOI join the tessellation configuration
(draw keys, hull and domain cache keys, request version 23). A missing
GE_CNTL throws, and so do local programs, whose relative vertex index
depends on vertex reuse that Vulkan does not expose.
Every hull program with a BDA access was refused with "BDA fault
termination requires a barrier-safe tessellation-control execution
protocol". The refusal predates BdaInvocationsMayStop: a program with
s_barrier no longer stops a faulting invocation; the fault is recorded,
the read returns zero and every invocation reaches the barrier. In a
hull, s_barrier is the only control barrier the backend emits after the
guest code starts, so that rule already covers it, and a hull without
one can stop an invocation without stranding a barrier.

The tessellation test draws hulls that scale their levels by a BDA read,
with and without s_barrier, and requires an unmapped read to report a
tessellation-control fault (not on llvmpipe, which runs hull loads that
the unmapped-address check skips). The contract test now accepts a hull
with s_barrier.
…eads one control point

derive() learned the LS-to-HS stride from the control-point term of hull LDS reads, or from the patch term of reads at a computed control-point index. A hull with one input control point reads it at constant offsets, so neither applies and the stride stays unknown ('the hull shader reads no LDS per control point, so the local stride is unknown'). Every hull read already has a patch term of exactly inputControlPoints * localStride, so take the stride from it for reads without a control-point term too. Resident Evil Village's quad patches have one input control point.
A hull with one output control point writes its control point at the
patch record offset alone, so the write has no control-point term and
was taken for a patch constant with no control stride ('the hull shader
writes off-chip memory outside its control points and patch constants').
Take the control stride from the patch term of such writes when the
write fits in one record (its offset is at least 0 and its end within
the patch term), the rule domain programs already apply to control-point
reads; patch-constant records lie outside that, below the top of the
off-chip block or above every control point. With one invocation per
patch, per-patch outputs also need no control-point-0 guard. Resident
Evil Village's quad patches have one output control point.
@github-actions github-actions Bot added the open PR limit Closed over the open PR limit: 1 without merged PRs, 20 with at least 1, none from 20 label Oct 13, 2026
@github-actions

Copy link
Copy Markdown

Thank you for your contribution! Authors with no pull requests merged into main can have 1 open pull request at a time, authors with at least 1 merged can have 20, and from 20 merged there is no limit. This PR has been automatically closed and labeled "open PR limit" because that limit was reached. This is not a judgment of the quality of your changes: reopen it once one of your open pull requests is merged or closed.

@github-actions github-actions Bot closed this Oct 13, 2026
@EagleBearerYS EagleBearerYS reopened this Oct 13, 2026
@EagleBearerYS

Copy link
Copy Markdown
Owner Author

CI done (run 38455894201); the upstream PR is boykopovar#5457. The branch stays: it is that PR's head.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

open PR limit Closed over the open PR limit: 1 without merged PRs, 20 with at least 1, none from 20

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants