Skip to content

feat(recompiler): add an uncached compile entry point and entry key helpers for offline tools - #6296

Closed
SaloQT wants to merge 3 commits into
boykopovar:mainfrom
SaloQT:feat/uncached-compile-entry
Closed

SaloQT wants to merge 3 commits into
boykopovar:mainfrom
SaloQT:feat/uncached-compile-entry

Conversation

@SaloQT

@SaloQT SaloQT commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

What

Two small recompiler additions that offline shader tooling needs, split out of #6032 as requested there:

  • CompileShaderUncached: compiles a request to a CompiledVariant without reading or writing the disk cache, with optional frontend/backend timings. Runtime behaviour is untouched; nothing in the emulator calls it yet.
  • Encoded entry helpers: EntryHeaderBytes (48, static-asserted against the on-disk header), EntryKey (extract and hash-check the key stored in an encoded entry), and IsCurrentEntryHeader (cheap current-format check for a header plus file size). These let tools build and inspect cache entries in the exact on-disk format without duplicating the layout.

The offline shader archive tool that consumes these will follow as its own PR once this lands and its real-run hit rate is measured.

Tested

Windows 11: agc_shader_disk_cache and shader_cache_file_read pass, including new cases: entry-key round trip, truncated/damaged headers rejected, uncached compile deterministic across two calls and leaving the disk-cache totals (hits, misses, writes, bytes) unchanged.

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 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. Split from feat(tools): add an offline shader archive tool that publishes prebuilt packs #6032, which is closed in favour of this order.
  • Unblocks: the offline shader archive tool follow-up
  • GPUs tested: n/a (no GPU path changes)
  • AI-assisted: yes

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for splitting it out. Small and well tested (determinism and untouched disk-cache totals are the right checks). Two things to fix, then a reply to what you wrote on Discord, so it's all in one place.

Changes

  1. Reuse unwrapEntry's validation. EntryKey and IsCurrentEntryHeader repeat the header checks of unwrapEntry (ShaderDiskCache.cpp), and EntryKey skips one of them: the zero-padding check between key and payload. So EntryKey can return a key for an entry that DecodeEntry then rejects, which is exactly what a tool must not believe. Please move the header/size/padding validation out of unwrapEntry into one helper and have all three use it, so there is a single definition of "a valid current entry".
  2. One header for both helpers. EntryKey is declared in ShaderCachePack.hpp and IsCurrentEntryHeader in ShaderDiskCache.hpp, both defined in ShaderDiskCache.cpp. Neither is about packs; please declare both in ShaderDiskCache.hpp.

Before merging we'll run the full Build (the PR's linux/windows jobs are skipping) and agc_shader_disk_cache / shader_cache_file_read on Linux here, since it was only tested on Windows.

About your message on Discord

I'm limited to submitting more than a few PR's due to the rules.

Not anymore: since #5792 merged you have one merged PR, so the limit is 20 open PRs, not 1 (the bot's text: "at least 1 merged can have 20"). You didn't need to close #6032. Reopen the tool as its own PR whenever you want, marked "Depends on: #6296", and send the other prerequisites in parallel too, each one small and with its own tests.

There is no shader compilation done at all... all 60,000 are done before the game opens.

That's the goal, and we want it too. The reason we keep asking for numbers is that what happens on your local copy isn't what this series does upstream:

  • What upstream already removed. Since #639 the RDNA → SPIR-V translation runs at shader registration, not at the first draw. What's left at the first draw is per-draw specialization (spec constants, vertex input types, binding plan) plus the driver's pipeline compile. A pack of CompiledVariants doesn't skip the driver compile, and nothing loads pipeline-cache.bin upstream. So even with 100 % hits, "no compilation at all" doesn't follow from this series as it stands.
  • Exact keys. A variant's key includes runtime state: target formats, binding layout, stage configuration, vertex inputs and the host subgroup size. Variants built only from the archives have to guess that state. If they guess wrong, every lookup misses, silently. That's also why packs are per GPU class, which is likely the "it wouldn't work for all platforms" you ran into. So, for the tool PR, the number that matters is still [shader-prebuilt] hits/misses from a real run, with a pack built by build --paks only, plus load time with and without it.
  • Where this meets our work. Phase B of #2242 (shader objects, vertex fetch in the shader, buffer descriptors read at run time) is about removing that per-draw specialization. The less runtime state ends up in the key, the closer a statically built pack gets to always hitting. So your work and ours meet in the middle. What would help most: tell us, here or in #2242, what your fork does to make the keys predictable before the game runs (fixed targets? a capture pass? defaults for vertex formats?) and how you handle driver pipelines (pipeline cache, GPL, shader objects?). Then we can line up the upstream steps so the pack can actually reach "nothing compiled in game", instead of porting the fork's pieces one by one without the part that makes them hit.

Suggested order on your side: fix 1–2 here (we'll merge after the full Build), then reopen the tool on top with the hit-rate numbers, and the key/pipeline write-up in parallel.

CompileShaderUncached compiles one request without the process-lifetime
source cache or a resource snapshot, so batch tools can compile shader
archives with bounded worker lifetimes. Optional frontend and backend
timings report where each compilation spent its time.
EntryKey returns the validated key stored in an encoded cache entry
and IsCurrentEntryHeader checks that a file header belongs to this
compiler source and format, so offline tooling can group and screen
existing cache files without decoding them. Both validate against the
aligned payload layout.
EntryKey and IsCurrentEntryHeader round-trip an encoded entry and
reject truncated or damaged headers. CompileShaderUncached produces
the same encoded entry twice without touching the disk-cache totals.
@SaloQT
SaloQT force-pushed the feat/uncached-compile-entry branch from edf3c82 to 721af43 Compare October 15, 2026 11:29
@SaloQT

SaloQT commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

Both fixed, squashed into the commits that own them:

  1. Shared validation. parseEntryHeader (magic/format/source version, nonzero key, payload size arithmetic) and validatedEntry (header plus zero-padding, key hash and payload hash) now sit under all three: unwrapEntry adds only the key-equality check, EntryKey returns the key only for a fully validated entry, and IsCurrentEntryHeader is the header-only form. One behaviour note: a stored zero-length key now reads as Rejected instead of KeyMismatch, since the shared header check requires a nonzero key; wrapEntry never writes one. The test now also damages a padding byte and requires both EntryKey and DecodeEntry to refuse it.
  2. One header. EntryKey moved from ShaderCachePack.hpp to ShaderDiskCache.hpp, next to IsCurrentEntryHeader.

Rebased onto current main; agc_shader_disk_cache and shader_cache_file_read pass locally, conventions clean. The tool PR reopens alongside this one, marked Depends on: #6296 — thanks for the limit correction.

This was referenced Oct 15, 2026
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 3247 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 4072 core/shader/recompiler/Recompiler.cpp
PR 4650 core/shader/recompiler/Recompiler.cpp
PR 5021 core/shader/recompiler/Recompiler.cpp
PR 5315 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
PR 5500 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 5609 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 5656 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 5925 core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 5958 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 6177 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
PR 6178 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp
core/shader/recompiler/Recompiler.cpp
PR 6222 core/shader/recompiler/Recompiler.cpp
PR 6232 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 6235 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
PR 6280 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 6412 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 6454 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 6470 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 6486 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 6488 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 6503 core/shader/recompiler/Recompiler.cpp
PR 6539 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 6611 core/shader/recompiler/Recompiler.cpp
PR 6612 core/shader/recompiler/Recompiler.cpp
PR 6679 core/shader/recompiler/Recompiler.cpp
PR 6683 core/shader/recompiler/ShaderDiskCache.hpp
PR 6733 core/shader/recompiler/Recompiler.cpp
PR 6762 core/shader/recompiler/Recompiler.cpp
PR 6879 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 6924 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 6941 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 6946 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 6947 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 6959 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 6970 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 6977 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 7085 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 7201 core/shader/recompiler/Recompiler.cpp
PR 7223 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 7296 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.cpp
PR 7332 core/shader/recompiler/Recompiler.cpp
PR 7381 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 7390 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 7395 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/ShaderDiskCache.cpp
core/shader/recompiler/ShaderDiskCache.hpp
PR 7410 core/shader/recompiler/Recompiler.cpp
PR 7445 core/shader/recompiler/Recompiler.cpp
PR 7484 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
core/shader/recompiler/Recompiler.cpp
PR 7520 core/shader/recompiler/ShaderDiskCache.cpp
PR 7522 core/shader/recompiler/Recompiler.cpp

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

Labels

agc libSceAgc* needs rebase Conflicts with main, rebase needed shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants