Skip to content

fix(shader): evaluate a control point masked out of the packed hull register - #9804

Closed
frankea wants to merge 1 commit into
boykopovar:mainfrom
frankea:fix/hull-masked-control-point
Closed

frankea wants to merge 1 commit into
boykopovar:mainfrom
frankea:fix/hull-masked-control-point

Conversation

@frankea

@frankea frankea commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

What

The tessellation lowering evaluates every ring and LDS address as vertex * control point + relative * relative patch + constant. A v_and_b32 with a constant was accepted only when the mask kept the low bits, like 0xff or 0x1fff. A hull program's v1 packs the control point into bits 8-12, above the relative patch in bits 0-7. A mask like 0x1f00, which keeps only the control point, was refused with the tessellation ring or LDS address is not affine in the control point and relative patch.

  • A mask that keeps one contiguous run of bits, from bit lo up to bit hi, is now evaluated as (x mod 2^hi) - (x mod 2^lo).
    • Both remainders come from the existing split. It proves each one exact: the part below the boundary must stay inside the boundary for every control point and relative patch.
    • So v1 & 0x1f00 is control point * 256, as long as the relative patch stays below 256 and the control point below 32.
  • A mask that cuts through a term is still refused. For example, 0x1e00 drops the control point's low bit, and 0x1f0f has a gap.

Why: Gran Turismo 7 (PPSA01317) has nine hull programs that address their control-point records in the off-chip ring this way: 0x1280676200, 0x1280ada900, 0x1280c98400, 0x1280d71100, 0x1280ed4e00, 0x1280ed8d00, 0x1280f63d00, 0x12810bce00 and 0x12810d5900. Their control-point records are 256 bytes.

  • The address is (v1 & 0x1f00) + relative patch * patch stride. The patch stride is << 10 (4 control points) in eight of them and * 0x300 (3 control points) in 0x1280ed8d00.
  • In 0x1280676200, for example: v_and_b32 v0, 0x1f00, v1, then v_lshl_add_u32 v0, v46, 10, v0 with v46 = v1 & 0xff, then buffer_store_dwordx4 ... offen offset:0x68 at pc 0x2d0.
  • GT7 creates their pipelines on the way past fix(shader): read other control points' off-chip outputs in hull programs #9743's stop, and the driver deferred all nine with this refusal.

Tested

Gran Turismo 7, on a local stack of main with the open PRs it needs before these hulls (#7243, #7256, #7550, #7978, #8564, #9020, #9335, #9068, #9700, #9721, #9743) and this commit:

  • The nine hull programs, replayed with that stack's agc_shader_replay from the requests the driver dumped when it deferred them (APS5_DUMP_SHADERS).
  • GT7 runs with scripted pad presses, with its diagnostic switches that skip refused compute dispatches:
    • With fix(shader): read other control points' off-chip outputs in hull programs #9743 alone (one run), the World Map load gets past fix(shader): read other control points' off-chip outputs in hull programs #9743's stop. On the way, GT7 creates these nine pipelines, and the driver defers each one with this refusal.
    • With this commit as well (two runs), one run takes the same path: it logs the same 29 skipped compute dispatches, and no hull program is refused or deferred. It stops on the same next refusal, a texture sampled with unnormalized coordinates (guest texture sampled with unnormalized coordinates is not a single-layer 1D or 2D view ...).
    • The other run stops earlier, on the one-sample depth rule's other conditions (... PS_ITER_SAMPLE, a sample index or coverage input, a sample-mask export ...), before GT7 creates any tessellation pipeline.

Windows 11 (build 26200), NVIDIA GeForce RTX 4090 (driver 616.92), MinGW GCC Release build with BUILD_TESTING=ON, all on one machine.

New tests:

  • agc_tessellation_lowering has three new IR cases.
    • Lowered: a hull LDS read at ((packed & 0x1f00) >> 3) + relative patch * stride, where packed is control point << 8 | relative patch as in v1, reads the invocation's own input control point.
    • Refused: packed & 0x1e00, which drops the control point's low bit, and packed & 0x1f0f, which has a gap.
  • agc_driver_tessellation (GPU) adds HullMaskedControlPoint. It is HullCode with its off-chip record address 16 * control point replaced by (v1 & 0x1f00) >> 4. The domain program takes its positions from those records, so a wrong address moves or drops the patches. The test draws it and checks each patch like HullCode.

Runs:

  • Focused set: the 52 ctest entries of 8 targets: agc_tessellation_lowering_tests, agc_tessellation_factor_ring_tests, agc_driver_tessellation_tests, agc_driver_graphics_tests, agc_vector_window_tests, agc_vertex_input_validation_tests, agc_driver_bda_device_tests and agc_shader_memory_tests.
  • This PR (d39285d on main 8b3a6ba): 52/52 passed, each test run three times (ctest --repeat until-fail:3).
  • main control: main 8b3a6ba with only this PR's two test files. Both tests fail. The new lowered case is refused with not affine in the control point and relative patch, and the GPU test stops on HullMaskedControlPoint's store at pc 0x60 with the same refusal. The two refusal cases pass on both.
  • Validation layer: VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation (Vulkan SDK 1.4.363.0), ANYPS5_NO_SHADER_CACHE=1 and ANYPS5_REQUIRE_VULKAN=1 with ctest -V on this PR: 52/52. The log has no was not found but was requested by env var VK_INSTANCE_LAYERS and no VUID- line.
  • Not run on Linux. tools/check_conventions.py --base origin/main reports no findings.

Checklist

  • Based on current main; no other open PR implements the same functions. No open PR changes the address evaluator's mask. This PR and fix(shader): read other control points' off-chip outputs in hull programs #9743 both add a helper before Hull::merge and cases at the end of tests/TessellationLowering.cpp; whichever lands second keeps both.
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: PPSA01317 Gran Turismo 7 (PPSA01317) #4697 (with fix(shader): read other control points' off-chip outputs in hull programs #9743, GT7 creates these nine hull pipelines; its next stop is the texture refusal under Tested)
  • GPUs tested: NVIDIA GeForce RTX 4090, driver 616.92, Windows 11 build 26200
  • AI-assisted: yes (Claude Code)

@github-actions github-actions Bot added unblocks title Names a title ID on the Unblocks line open PR limit Closed over the open PR limit: 1 without merged PRs, 20 with at least 1, none from 20 labels Oct 22, 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.

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 unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant