Skip to content

feat(agc): remap a viewport depth range the device cannot express into the vertex shader - #4650

Merged
oneandonlydean merged 17 commits into
boykopovar:mainfrom
Mouad-Scriptz-0:feat/agc-depth-range-remap
Oct 19, 2026
Merged

oneandonlydean merged 17 commits into
boykopovar:mainfrom
Mouad-Scriptz-0:feat/agc-depth-range-remap

Conversation

@Mouad-Scriptz-0

Copy link
Copy Markdown
Contributor

What

Intel's ANV has no VK_EXT_depth_range_unrestricted, so a viewport depth range outside [0, 1] threw "viewport depth [-1, 1] out of range". Minecraft (PPSA17221) draws its whole world that way: PA_CL_VPORT_ZSCALE 1 and ZOFFSET 0 in negative-one-to-one clip space, so the console's depth is the raw z/w in [-1, 1], clamped by PA_SC_VPORT_ZMIN/ZMAX to [0, 1] on write. Clamping the viewport to [0, 1] instead maps the range linearly and writes every depth value wrong.

The remap folds the transform into the vertex shader on such a device, generic for any scale and offset:

  • Graphics::ApplyDepthRemap (after DecodeState, with the device context) leaves a range inside [0, 1] or an unrestricted device alone. Otherwise it records the viewport's scale, offset and clip-space mode in State::depthRemap, gives the Vulkan viewport the console's clamp bounds, turns depth clamping on (the console's ZMIN/ZMAX clamp) and clip control off. It requires the vertex path and a device with depth clamping; mesh and tessellation draws with such a range still throw.
  • The vertex shader (RemapPositionDepth, emitted when SpirvTarget::depthRangeRemap) computes z' = scale·z + offset·w, so z'/w is the console's depth before its clamp, and writes two extra gl_ClipDistance elements, w + z (or z in zero-to-one clip space) and w − z, restoring the near and far planes depth clamping removes. Mode, scale and offset are specialization constants (PipelineSpecialization::DepthRemapBase), filled from the draw's ShaderVertexStageInfo in both the per-draw and the registration-module path; mode 0 folds to the untouched position, so shaders of in-range draws are unchanged.
  • The cache key, the request serializer (version 18) and the draw cache's vertex info comparison carry the new fields; VulkanDevice sets the target flag from the missing extension.
  • Tests: agc_depth_range_remap (new) checks the emitted module declares the three specialization constants with the draw's values, two clip distances and the scale, and that an unrestricted target declares none; agc_driver_graphics gains DepthRemapTests for ApplyDepthRemap: the [-1, 1] case, an unrestricted device, a device without depth clamping, an in-range transform and a zero-to-one transform with narrower clamp bounds.

Tested

Fedora 44 (kernel 7.2.8, GCC 16.2.1, Ninja, Release, BUILD_TESTING=ON, Intel Arc Meteor Lake, Mesa 26.2.3, no VK_EXT_depth_range_unrestricted), worktree on main ffa1984: agc_depth_range_remap, agc_driver_graphics, agc_position_invariance, agc_shader_disk_cache pass; tools/check_conventions.py --base origin/main and git diff --check pass. Minecraft (PPSA17221): the title's world draws have PA_SC_VPORT_ZMIN/ZMAX at [-1, 1] on a float depth target, so the remap clips the clamp to [0, 1] and reports it once; worlds render with the remap, with water and fog at their expected depths. Without the remap the same draws threw. Not tested on Windows or on a device with the extension (where the remap stays off).

Checklist

  • Based on current main; no other open PR implements the same functions
  • 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 in TechnicalDebt
  • 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
  • AI-assisted: yes (Claude Code)

🤖 Generated with Claude Code

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Needs a rebase: it conflicts with main in core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp, PipelineSpecialization.hpp and Recompiler.hpp, and docs/dev/TechnicalDebt.md has moved to docs/dev/debt/<library>.md. Take the next free request version when you do.

@johnmaia

Copy link
Copy Markdown
Contributor

Windows run, since the description says Windows is untested. Setup: Windows 11 (26300), WinLibs GCC 15.2.0 r7, Release, BUILD_TESTING=ON, head 13b626a merged into main 1e7f496, ctest -j4 --timeout 300. Two GPUs: an RTX 4080 (NVIDIA 617.42, has VK_EXT_depth_range_unrestricted) and an Intel UHD 770 (driver 101.6129, does not).

  • agc_depth_range_remap, agc_driver_graphics, agc_position_invariance and agc_shader_disk_cache pass on both GPUs.

  • The full suite passes 1072 of 1073 on both. The failure is agc_shader_memory:

    new requests did not use serialization version 21
    

    It passes on main 1e7f496 on the same machine. This PR raises the request version to 23, but tests/ShaderMemory.cpp (line 1829 on main) still expects the version 21 prefix NVNQQRUAAAA=. With version 23 the prefix is NVNQQRcAAAA=. The description also still says "the request serializer (version 18)".

Not tested: a title.

AI-assisted: yes (Claude Code)

nikowolf123 and others added 12 commits October 18, 2026 14:27
…ing it

ApplyDepthRemap gives the Vulkan viewport the console's clamp bounds
clipped to [0, 1]. A zero-to-one viewport maps the shader's z/w linearly
onto [minDepth, maxDepth], so with bounds narrower than [0, 1] the remap
rescaled the depth into them instead of clamping it there; only bounds
of [0, 1] or wider (Minecraft's [-1, 1]) came out right. Scale and offset
are now divided by the clamp range and the offset moved by its minimum,
so the viewport and the depth clamp together give scale * z/w + offset
clamped to the bounds.

agc_driver_depth_range_remap_draw draws through the remap path and,
when the device has VK_EXT_depth_range_unrestricted, through the
unrestricted path, then checks the stored depth with probe draws and
that the two clip distances cut at the near and far planes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The depth remap mode, scale and offset change the pipeline a draw creates, so the
pipeline journal's recipe carries them. The file format is bumped and the State
layout guard follows the new size.
The depth remap fields moved to request format 33 when this branch was rebased onto
main's format 32. The replay tests that rebuild older streams now drop and append the
remap tail on top of the linked call fallbacks and the spanned waves flag, and the
depth remap test downgrades to format 32.
…t run time

The remap mode stays a specialization constant and cache key input. Scale and
offset are two push words at byte 104, patched per draw, so draws that differ
only in depth range share one pipeline. The recipe, journal and serializer
carry the mode only.
A remapped vertex shader reads the scale and offset from the push block, but
the pipeline's push constant stages only counted programs with push data of
their own, so a shader with none got no stage in a non-GPL layout and its
scale and offset never arrived. The result now says it reads them, and the
push stages include the vertex stage for it. The draw test gains a fetchless
remapped vertex shader with no push data.
…words

The flag that adds the vertex stage to the push constant stages was not part
of the encoded result, so a journalled remapped vertex shader replayed
without it. Encode and decode it with the invocation and cover it in the
result round trip and the push stage test.
…heck

The SPIR-V interface check only allowed a push constant block for a program
with push data of its own, so a remapped vertex shader without any was
rejected as having an invalid push constant interface.
On a device without VK_EXT_depth_range_unrestricted the target remaps every
vertex shader, so an in-range draw carries the remap's push block too. Draw
it from a direct and from a prepared build; both must validate and store the
right depth.
@milescoviello

Copy link
Copy Markdown
Contributor

@nikowolf123 sorry, I missed this request on 10-14. I'll run your help/S6-depth-range head on the Intel Arrow Lake iGPU (Mesa ANV), with depth_range_remap|agc_shader_memory|agc_driver_graphics, plus some of the homebrew titles from my earlier run, and post the results here. If the branch has moved since, tell me which head to use.

AI-assisted: yes (Claude Code)

@milescoviello

Copy link
Copy Markdown
Contributor

@nikowolf123 results for your help/S6-depth-range head 64fb88a, built on its own base. Intel Arrow Lake iGPU, Mesa 26.1.6 ANV, Debian 13 VM, no VK_EXT_depth_range_unrestricted:

  • Tests, 2 runs of 2: agc_depth_range_remap, agc_driver_depth_range_remap_draw ("13 cases, unrestricted path absent"), agc_driver_graphics and agc_shader_memory all pass.
  • Titles: the 22 catalog titles from my earlier run, 40 s each. 19 run to the limit. The other 3 stop for the same unrelated reasons as on 10-13:
    • ProsperoPuzzles (PPSA99006) stops at 0 s on the multisampled descriptor shape.
    • Homebrew UI Lab and Surround Sound Studio (PPSA99050, PPSA99051) exit 1 at 3 s. Their bundled GL runtime gives up after its 2 s submission wait.
  • Cloud Play (PPSA99600), which stopped on getservbyname in my earlier run, now runs to the limit.

AI-assisted: yes (Claude Code)

@MarcZX10

Copy link
Copy Markdown
Contributor

New issue #4762 adds a direct Windows Intel Iris Xe reproduction of the same ValidateViewport failure with depthRangeUnrestricted=0. This gives the existing remap PR a concrete Windows title case in addition to the ANV runs reported here; I did not find a separate implementation gap to split out.

@oneandonlydean
oneandonlydean force-pushed the feat/agc-depth-range-remap branch from 60b467c to 5392c27 Compare October 18, 2026 22:13
# Conflicts:
#	core/shader/recompiler/ShaderDiskCache.cpp
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 18, 2026
# Conflicts:
#	core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
#	core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp
#	core/shader/recompiler/tests/RequestSerializerTests.cpp
# Conflicts:
#	core/shader/recompiler/ShaderDiskCache.cpp
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@oneandonlydean
oneandonlydean merged commit 3ddbc4a into boykopovar:main Oct 19, 2026
10 checks passed
oneandonlydean pushed a commit that referenced this pull request Oct 21, 2026
#4650 took request serialization version 35 for its depth remap flag, so this branch's sample order moves to 36. The serialization tests drop #4650's trailing byte from every older version they build, as main's do, along with the iteration fields and the sample order.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* build docs shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants