Skip to content

fix(shader): write cache files of 2 GiB and more in chunks - #7127

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
tibzejoker:fix/cache-file-write-over-2gib-v2
Oct 17, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
tibzejoker:fix/cache-file-write-over-2gib-v2

Conversation

@tibzejoker

Copy link
Copy Markdown
Contributor

What

On Windows (MinGW libstdc++), WriteFileAtomically (core/shader/recompiler/ShaderCacheDirectory.hpp) never returns when it writes 2 GiB or more. It does one std::ofstream::write of bytes.size(). The whole temporary file reaches the disk (2 GiB + 4096 bytes in the test), but the call never comes back. The temporary file is then never renamed over the entry and is never removed.

Where it happens: in GTA V (PPSA04264) the Vulkan pipeline cache file (vk-pipelines-*.bin) grew to 2.1 GB. After that:

  • every periodic save hung in its thread;
  • each run left a 2 GB .tmp.* file next to the cache (36 GB of them before we noticed);
  • every launch read the stale cache file and recompiled the missing pipelines again (about 100 s of compile stalls while Story loads).

Fix: write in chunks of at most 1 GiB and stop after the first chunk that fails. The existing file.close() / file.fail() check still decides the result. On Windows, reads already go in MAXDWORD chunks (ReadWholeFile, af2b869), so this PR only changes the write side.

Related: #6771 (keeps the pipeline cache file with its shader cache version) slows how fast that file grows, but any cache file that reaches 2 GiB within one version still hangs its save without this change. #6958 edits PipelineCache.cpp; this PR does not.

Test: agc_shader_disk_cache_large_file (agc_shader_disk_cache_tests --large-file) writes 2 GiB + 4096 bytes through WriteFileAtomically on a worker thread and waits up to 120 s. It then checks the file size, six sampled bytes on both sides of the 1 GiB and 2 GiB boundaries, and that no temporary file is left, then deletes the file. If the write does not return in 120 s, the test fails at once instead of hanging ctest (ctest TIMEOUT 240 as a backstop). It returns 77 (skip) when it cannot allocate the buffer or when there is less than 3 GiB of free disk space in the temp directory.

Tested

Windows 11 Pro, WinLibs GCC 15.2.0 (posix-seh, ucrt), Release, i9-13900K / 64 GB, rebased on main c9e6c45:

  • current main (fix reverted): agc_shader_disk_cache_large_file fails with WriteFileAtomically did not return within 120 s for a file of more than 2 GiB, with a full-size pipelines.bin.tmp.* left on disk;
  • this PR: passes in a few seconds (the 2 GiB write takes ~1.5 s), and the existing agc_shader_disk_cache test passes too;
  • GTA V (PPSA04264), with the same change on our test branch: the 2.1 GB pipeline cache saves again, no .tmp files left, and Story loads in ~30 s with no pipeline compile stalls.

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
  • Fixes: none
  • Unblocks: PPSA04264 Grand Theft Auto V (PPSA04264) #1203 (pipeline cache saves once it passes 2 GiB)
  • GPUs tested: n/a (file I/O only; the test does not use the GPU)
  • AI-assisted: yes, AI-assisted (Claude Code)

🤖 Generated with Claude Code

https://claude.ai/code/session_01XBXM4pgkMHYEtqN1EQni2P

On Windows (MinGW libstdc++) one std::ofstream::write of 2 GiB or more
never returns: the whole temporary file is written but WriteFileAtomically
hangs, the entry is never replaced and the temporary file stays behind.
A Vulkan pipeline cache of 2.1 GB hung every save and left a 2 GB .tmp
per run. Write in chunks of at most 1 GiB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XBXM4pgkMHYEtqN1EQni2P
@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 6249 core/libs/prx/libSceAgcDriver/CMakeLists.txt L1596
PR 6982 core/libs/prx/libSceAgcDriver/CMakeLists.txt L1596

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 1341 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 4339 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 4650 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 4891 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5426 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5431 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5499 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5609 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5958 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6044 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6176 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6177 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6235 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
PR 6259 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6320 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6373 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6412 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
PR 6503 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6535 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6539 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
PR 6718 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6747 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6856 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6924 core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
PR 6937 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6946 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
PR 6947 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
PR 6973 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6987 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6989 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6995 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7002 core/libs/prx/libSceAgcDriver/CMakeLists.txt

@oneandonlydean
oneandonlydean merged commit 47a266c into boykopovar:main Oct 17, 2026
11 checks passed
@jotitadev

Copy link
Copy Markdown
Contributor

The new agc_shader_disk_cache_large_file test uses a fixed temporary directory and removes it on start, so two copies running at once on the same host (as on the shared anyps5-2-linux-* runners) can delete each other's file mid-write and fail with cannot write a cache file of more than 2 GiB. This has turned the Linux job of several dispatched Build runs red since this merged. Details, a local reproduction and a one-line fix (random suffix, as the other temp dirs in the file already use) are in #7221.

Written with AI assistance (Claude Code).

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.

3 participants