Skip to content

hk: read large vertex and fragment constant tables from a blob on AGX2 - #45

Open
jargnar wants to merge 1 commit into
omacom:integration/aurorafrom
jargnar:hk-agx2-large-constants
Open

jargnar wants to merge 1 commit into
omacom:integration/aurorafrom
jargnar:hk-agx2-large-constants

Conversation

@jargnar

@jargnar jargnar commented Nov 13, 2026 •

Copy link
Copy Markdown

Fixes #44.

One commit on integration/aurora at b7b03c5b3b8a. It runs the existing vertex and fragment nir_opt_large_constants branch on the AGX2 chips too, so a large constant table is read from the shader's constant blob instead of being stored to scratch by every invocation. Skyrim Special Edition through DXVK 3 goes from 2.8 fps to 43 fps.

The change

What stays as it was

  • The native AGX3 and M5 paths: the same passes with the same thresholds for the same stages.
  • AGX2 compute. Only hk_dispatch_m5_native() and hk_dispatch_g17p_native() publish cs.constant_data_addr, so the pass is not run for it.
  • An array the shader writes to, and any shader without an array above the scratch threshold.
  • The upload reuse in hk_cmd_upload_constants(), which is still native-only. On AGX2 a bound blob is uploaded again at each shader flush; for the 16 KB table here that is one upload a frame. It is left out to keep this to the gate, and can go in here if wanted.

MEASURED

On one AGX2 machine, kernel linux-aurora 7.2.11.aurora14-1. Two builds: b7b03c5b3b8a as it is, and this head, aecd98f83e8f. Vulkan driver only, debugoptimized with assertions, tools stage with LLVM 23, loaded with VK_DRIVER_FILES. Nothing was installed and no boot stack was touched.

  • The standalone reproducer in Honeykrisp on AGX2 fills a large constant table in scratch for every pixel: Skyrim SE at 2.8 fps through DXVK 3 #44, every case PASS with no wrong pixel in both builds:

    Case base this head
    table in the fragment shader, 1920x1080 421.09 ms 0.30 ms
    the same, declared as DXVK declares it 421.24 ms 0.35 ms
    a table the fragment shader writes to, 256x256 14.99 ms 15.05 ms
    table in the vertex shader, 1005 points 0.95 ms 0.27 ms
    the same through a pass-through geometry shader 0.80 ms 0.34 ms
  • What the driver compiled: the fragment shader goes from 3680 instructions, 1893 spills and 19696 bytes of scratch to 15 instructions; the vertex shader from 3701 instructions to 27; the shader that writes to its table keeps the same hash.

  • Skyrim Special Edition (Proton Experimental ARM64, DXVK v3.1.1-188-gcf0a0d90f896514, 1920x1080, Ultra preset): 2.8 fps in two runs of the base, 43.4, 42.5 and 42.5 fps in three runs with the change. The shadow mask pass goes from 339 ms to 0.49 ms, its shader from 5987 instructions with 16080 bytes of scratch to 578 with none. These runs used a build of the same tree made before the commit existed.

  • No GPU timeout or fault in the kernel log in any run. The kernel does log an occasional Job canceled on DRM scheduler teardown when one of the reproducer's short cases exits. Over 96 runs per driver it logged 18 such lines with the system package, 15 with the unpatched build and 32 with this head, whose short cases finish sooner; every run passed. It is there without this change, and which job it cancels was not looked into.

  • python3 tools/siliconforest/check.py passes on this head.

  • The CI probe .github/mesa-ci/pipeline.c passes on the drm-shim profiles g13g, g13x, g14g, g15s, g15c, g15g and g17p, with its own three shaders and with the table shaders in place of the interpolation pair. Run without the validation layer, which is not installed here.

NOT measured

  • ci / result: on a first pull request from a fork it waits for a maintainer to start it.
  • Vulkan CTS.
  • Any other AGX2 chip on hardware, and the native AGX3 and M5 paths on hardware.
  • Tessellation.
  • Other games.

🤖 Generated with Claude Code

On the AGX2 chips a vertex or fragment shader that indexes a large
constant array stored the whole array to scratch in every invocation
before it read one entry of it. nir_opt_large_constants ran only on the
native AGX3 paths and the M5, so on AGX2 the scratch lowering in
agx_preprocess_nir() claimed the array, and scratch is private to one
invocation.

DXVK 3 writes a D3D11 immediate constant buffer as such an array, a
Private, NonWritable variable with a constant initializer, and on
Honeykrisp does not move it to a buffer itself. Skyrim Special Edition's
sun-shadow mask pass has a table of 1005 vec4. Its fragment shader
compiled to 5987 instructions with 1054 spills and 16080 bytes of
scratch, and its one full-screen draw took 339 ms at 1920x1080, about
165 ns per pixel. The game's world ran at 2.8 fps.

Run the existing vertex and fragment branch on every chip. Nothing after
it is particular to the native paths: lower_load_constant reads the
blob's address from draw.vs_constant_data_addr or
draw.fs_constant_data_addr, hk_compile_nir() keeps nir->constant_data
with the shader, and hk_flush_shaders() uploads the bound blobs and
publishes both addresses, whatever the chip. The same shader becomes 578
instructions with no spills and no scratch, the pass takes 0.49 ms and
the game runs at 43 fps.

Unchanged: an array the shader writes to is not constant and stays in
scratch. A shader with no array above the scratch threshold is left
alone, as on the native paths. AGX2 compute keeps its scratch arrays,
because only the native dispatch paths publish cs.constant_data_addr.
The upload reuse in hk_cmd_upload_constants() stays native-only, so AGX2
uploads a bound blob again at each shader flush. The native AGX3 and M5
conditions are the same as before.

Measured on one AGX2 machine with two builds of b7b03c5, with and
without this change (debugoptimized, assertions on):

- A full-screen 1920x1080 draw whose fragment shader reads one entry of
  a 1005-entry vec4 table per pixel: 421 ms to 0.31 ms a frame, every
  pixel as expected in both. The shader goes from 3680 instructions,
  1893 spills and 19696 bytes of scratch to 15 instructions. With the
  table declared as DXVK declares it: 421 ms to 0.44 ms.
- 1005 points whose vertex shader reads the table, drawn directly and
  through a pass-through geometry shader: every point as expected in
  both, the vertex shader from 3701 instructions to 27.
- A fragment shader that writes one entry of the table first: the same
  shader hash and the same output in both.
- Skyrim Special Edition, Proton Experimental (ARM64), DXVK v3.1.1-188,
  1920x1080, Ultra preset: 2.8 fps to 43 fps.
- The table shaders and the three CI probe shaders compile on the
  drm-shim profiles g13g, g13x, g14g, g15s, g15c, g15g and g17p.

Not measured: Vulkan CTS, any other AGX2 chip on hardware, tessellation,
and the native AGX3 and M5 paths on hardware.

Closes: omacom#44
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@jargnar jargnar closed this Nov 13, 2026
@jargnar jargnar changed the title hk: read large vertex and fragment constant tables from a blob on AGX2 Withdrawn Nov 13, 2026
@jargnar
jargnar deleted the hk-agx2-large-constants branch November 13, 2026 10:12
@greptile-apps

greptile-apps Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issues were found.

Summary

Extends large constant-table lowering to AGX2 vertex and fragment shaders.

  • AGX2 vertex and fragment shaders read large tables from constant blobs.

Reviews (2) · Last reviewed commit: "hk: read large vertex and fragment const..." · Reviewed by Greptile

@jargnar
jargnar restored the hk-agx2-large-constants branch November 13, 2026 10:22
@jargnar jargnar reopened this Nov 13, 2026
@jargnar jargnar changed the title Withdrawn hk: read large vertex and fragment constant tables from a blob on AGX2 Nov 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant