You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
AGC: since #6291, Stray (PPSA02101) stops on Intel Arc with "a pixel shader that reads barycentrics … needs a vertex shader before it" on a Geometry-path draw #7693
On maina373e85e5, Stray (PPSA02101) stops about 15 s after start, at its first frames, on Intel Arc (no VK_KHR_fragment_shader_barycentric):
[gpu] driver failure: AGC driver: a pixel shader that reads barycentrics without VK_KHR_fragment_shader_barycentric needs a vertex shader before it
terminate called after throwing an instance of 'std::runtime_error'
It starts with #6291. A bisect over main's first-parent history from 2da2284de^1 to fea306dff gives 18bd70080 (#6291's merge) as the first bad commit:
c820102bd, its parent, reaches 64 presents in 75 s with no error;
on a373e85e5 with git revert -m 1 18bd70080 alone, the title runs normally again (63 presents in 90 s).
#7681's fix was applied at every bisect step from #6270 on, because the indirect register sentinel abort (#6270) hid this stop on the same range.
After that, HostInterpolationChecker::Lower no longer accepts some of Stray's pixel shaders. Recompiler.cpp then translates them again with barycentric emulation (barycentricEmulation = true).
In Draw.cpp, that emulation needs ShaderPath::Vertex with two programs. A local log just before the require shows Stray's draws:
rectList=0 path=0 programs=2 stages=2: a Vertex-path draw, which goes through;
rectList=0 path=1 programs=2 stages=2: a Geometry-path draw, which throws.
I haven't checked which part of the interpolation pattern the WQM mask breaks in the checker, so that step is inferred from the bisect and the revert, not shown.
The bisect and single-commit revert make the regression attribution to #6291 strong. Before choosing a fix, could you capture the specific reason HostInterpolationChecker::Lower rejects the affected pixel shader (for example, its existing diagnostic/trace at the rejection point) and, if possible, a sanitized replay request? That would distinguish a checker false negative caused by the WQM EXEC mask from an interpolation pattern that actually needs geometry-path emulation. The current report establishes the failing draw path, but not which of those code paths is safe to change, so I don't have a validated standalone patch yet.
@ThatsLeo a data point that narrows this down, without the Stray shader. I checked whether #6291's WQM mask on EXEC copies stops host interpolation from lowering for the usual pixel prologues. I recompiled three variants through agc_driver_pixel_barycentric_emulation's draw with fragmentShaderBarycentricEnabled = false (RTX 4070 Ti, Linux), on #6291's parent c820102 and on main8a526d4:
the first one, restoring EXEC with s_mov_b64 exec, s[0:1].
All three lower to host interpolation on both commits (barycentricEmulation.active 0), and the interpolated pixels match, in both the geometry and the tessellation emulation. So the shader that fails in Stray needs some other pattern; the plain save, WQM and restore around v_interp isn't enough. A dumped request of the failing pixel shader (APS5_DUMP_SHADERS=1, the shader_<address>.req of the program that turns on the emulation) would let someone replay it and see which part of HostInterpolationChecker refuses it.
I reproduced a checker false negative with EXEC save/restore betweenv_interp_p1_f32 and v_interp_p2_f32. The P1 result retains a select guarded by the pre-copy EXEC predicate, while P2 uses a reconstructed predicate. partialOf in HostInterpolationChecker.cpp cannot peel that select because the predicates are different IR values. The PR extends the proof for nested/intersected entry WQM masks, allowing this pair to use host interpolation while preserving #6291's helper-lane behavior. A counterexample that actually widens EXEC after P1 still requires emulation.
Verified on Windows 11 / AMD RX 6700 XT: all nine focused tests pass with Vulkan required and the Khronos validation layer enabled. On main with the regression tests added, the two new regression checks fail; with the fix, both pass. Neither validation log contains a VUID- error. The original 12 mip-selection cases also pass with zero incorrect pixels.
Stray recovery on Intel Arc remains unverified. Testing #7811 on the affected setup would help confirm whether this is the same pattern; a sanitized failing pixel-shader .req capture (APS5_DUMP_SHADERS=1) would let us check it directly.
@santhoshkumar-dev#7811 fixes it on the Arc: Stray runs again on mainc74aeb2f4 + 9b45e784b, with 62 presents in 90 s and no error, the same as with #6291 reverted.
Checker rejections. I logged every HostInterpolationChecker::Lower rejection in one 90 s Stray run with no shader cache, using a local log at each return false:
The 54 that #6291 adds all fail at the same place: exact accepts every interpolation, but a barycentric GetBuiltin is still live after lowering. They are not rejected at partialOf.
What keeps the barycentric live. On the smallest of them (0x2011160000, APS5_DUMP_IR, after the first fold):
P1 and P2 share %74 here, so the pair itself is accepted. What keeps %30 alive is the select on the v_interp_p2 result (%87). Its condition is now the WQM of the entry EXEC (%28/%29, from Ballot(GetBuiltin 0x12 == 0)) ANDed with the WQM of every lane. MaskedSelectEliminator no longer reduces that to "every lane", so the inactive-lane arm, the raw barycentric, survives the lowering. Before #6291 the same copy was entry & Ballot(true), which it removed.
Which guest instructions produce that EXEC is my reading of the IR, not of a disassembly. #7811's nested/intersected entry WQM masks match it, and with it this shader lowers.
The dumped requests are on ThatsLeo:evidence/7693-stray-requests. They were written by a local RequestSerializer dump at the rejection, from mainc74aeb2f4, and replay with agc_shader_replay:
Activity
MarcZX10 commentedon Oct 19, 2026
The bisect and single-commit revert make the regression attribution to #6291 strong. Before choosing a fix, could you capture the specific reason
HostInterpolationChecker::Lowerrejects the affected pixel shader (for example, its existing diagnostic/trace at the rejection point) and, if possible, a sanitized replay request? That would distinguish a checker false negative caused by the WQM EXEC mask from an interpolation pattern that actually needs geometry-path emulation. The current report establishes the failing draw path, but not which of those code paths is safe to change, so I don't have a validated standalone patch yet.milescoviello commentedon Oct 19, 2026
@ThatsLeo a data point that narrows this down, without the Stray shader. I checked whether #6291's WQM mask on EXEC copies stops host interpolation from lowering for the usual pixel prologues. I recompiled three variants through
agc_driver_pixel_barycentric_emulation's draw withfragmentShaderBarycentricEnabled = false(RTX 4070 Ti, Linux), on #6291's parent c820102 and onmain8a526d4:s_mov_b64 s[0:1], exec,s_wqm_b64 exec, exec,v_interp_p1/p2,s_and_b64 exec, exec, s[0:1];s_wqm_b64 exec, execand the interpolation only;s_mov_b64 exec, s[0:1].All three lower to host interpolation on both commits (
barycentricEmulation.active0), and the interpolated pixels match, in both the geometry and the tessellation emulation. So the shader that fails in Stray needs some other pattern; the plain save, WQM and restore aroundv_interpisn't enough. A dumped request of the failing pixel shader (APS5_DUMP_SHADERS=1, theshader_<address>.reqof the program that turns on the emulation) would let someone replay it and see which part ofHostInterpolationCheckerrefuses it.AI-assisted: yes (Claude Code)
santhoshkumar-dev commentedon Oct 19, 2026
Follow-up: opened #7811, currently at
9b45e784b.I reproduced a checker false negative with EXEC save/restore between
v_interp_p1_f32andv_interp_p2_f32. The P1 result retains a select guarded by the pre-copy EXEC predicate, while P2 uses a reconstructed predicate.partialOfinHostInterpolationChecker.cppcannot peel that select because the predicates are different IR values. The PR extends the proof for nested/intersected entry WQM masks, allowing this pair to use host interpolation while preserving #6291's helper-lane behavior. A counterexample that actually widens EXEC after P1 still requires emulation.Verified on Windows 11 / AMD RX 6700 XT: all nine focused tests pass with Vulkan required and the Khronos validation layer enabled. On
mainwith the regression tests added, the two new regression checks fail; with the fix, both pass. Neither validation log contains aVUID-error. The original 12 mip-selection cases also pass with zero incorrect pixels.Stray recovery on Intel Arc remains unverified. Testing #7811 on the affected setup would help confirm whether this is the same pattern; a sanitized failing pixel-shader
.reqcapture (APS5_DUMP_SHADERS=1) would let us check it directly.AI-assisted: yes (Codex).
ThatsLeo commentedon Oct 19, 2026
@santhoshkumar-dev #7811 fixes it on the Arc: Stray runs again on
mainc74aeb2f4+9b45e784b, with 62 presents in 90 s and no error, the same as with #6291 reverted.Checker rejections. I logged every
HostInterpolationChecker::Lowerrejection in one 90 s Stray run with no shader cache, using a local log at eachreturn false:mainmainwithout #6291'sExecMask.cppchangemain+ #7811The 54 that #6291 adds all fail at the same place:
exactaccepts every interpolation, but a barycentricGetBuiltinis still live after lowering. They are not rejected atpartialOf.What keeps the barycentric live. On the smallest of them (
0x2011160000,APS5_DUMP_IR, after the firstfold):P1 and P2 share
%74here, so the pair itself is accepted. What keeps%30alive is the select on thev_interp_p2result (%87). Its condition is now the WQM of the entry EXEC (%28/%29, fromBallot(GetBuiltin 0x12 == 0)) ANDed with the WQM of every lane.MaskedSelectEliminatorno longer reduces that to "every lane", so the inactive-lane arm, the raw barycentric, survives the lowering. Before #6291 the same copy wasentry & Ballot(true), which it removed.Which guest instructions produce that EXEC is my reading of the IR, not of a disassembly. #7811's nested/intersected entry WQM masks match it, and with it this shader lowers.
The dumped requests are on
ThatsLeo:evidence/7693-stray-requests. They were written by a localRequestSerializerdump at the rejection, frommainc74aeb2f4, and replay withagc_shader_replay:shader_2011160000.req, the one above;added-by-6291/, 53 of the 54 shaders that fix(shader): preserve pixel helper lanes in EXEC copies #6291 adds to the rejections.AI-assisted: yes