Hona

#54113: fix: persist GPU shaders compiled by Skia between launches

Merged
Created: Sep 18, 2026, 11:54:11 PM
Merged: Sep 21, 2026, 2:40:04 AM
6 comments
Target: main

Description of Change

Fixes #54112.

Here's a demo where the only change is this PR applied.
-400ms saved from the launch time!

side-by-side.mp4

Content asks the embedder where to keep the GPU shader disk caches and only registers a cache for the handles that have a directory. ElectronBrowserClient never answered, so every shader Skia compiles for an app's UI is dropped on its way to disk and compiled again on the next launch.

flowchart LR
  A["GrShaderCache::store"] --> B["StoreBlobToDisk(kGrShaderGpuDiskCacheHandle)"]
  B --> C{"GetGrShaderDiskCacheDirectory()"}
  C -->|"Chrome: User Data\\GrShaderCache"| D["persisted, loaded next launch ✅"]
  C -->|"Electron: default, empty"| E["no cache for the handle → dropped ❌"]
Loading
  • content/browser/gpu/gpu_disk_cache_factory.cc maps kDisplayCompositorGpuDiskCacheHandleGetShaderDiskCacheDirectory(), kGrShaderGpuDiskCacheHandleGetGrShaderDiskCacheDirectory(), kGraphiteDawnGpuDiskCacheHandleGetGraphiteDawnDiskCacheDirectory(); gpu_process_host.cc uses GetGPUPersistentCacheDirectory() where features::kGpuPersistentCache is on (non-Windows).
  • gpu/ipc/service/gpu_channel_manager.cc: StoreShader always targets kGrShaderGpuDiskCacheHandle. The per-renderer GPUCache Electron apps do have is gles2::ProgramCache (WebGL programs) — which is why VS Code's has ~1,200 entries and a non-WebGL app's has 0.
  • This returns the same four directories Chrome does (chrome_content_browser_client.cc), rooted at sessionData next to GPUCache, Cache and the rest. Like those, sessionData has to be set before ready; the factory is created in BrowserMainLoop after the main script has run.

Prior art: Chrome has shipped exactly these directories for years; the on-disk format, prefix keying, corruption handling (kClearGrShaderDiskCacheOnInvalidPrefix, crbug 1442633) and clearStorageData({ storages: ['shadercache'] }) all already exist and apply unchanged.

Measured on Windows 11 / Intel Iris Xe / ANGLE D3D11, first raster of a window that paints text, rounded rects and shadows, --trace-startup:

GPU main thread, per launch measured today expected with this change (Chrome's behaviour)
GrShaderCache::load 31, all miss hits from the second launch on
Program::MainLinkLoadEvent::wait (ANGLE → D3DCompile) 31 × ~15 ms ≈ 480 ms, serialised ~0
frames presented meanwhile 0
sessionData/GrShaderCache never exists populated
base::FilePath ElectronBrowserClient::GetGrShaderDiskCacheDirectory() {
  return GpuCacheDirectory(FILE_PATH_LITERAL("GrShaderCache"));
}

Repro: https://gist.github.com/Hona/0ba82a4c26cc576cb4caeebc3f4ec746 (loads a page, waits 10 s, prints which shader caches exist under sessionData and their entry counts).

Checklist

Release Notes

Notes: Fixed GPU shaders compiled by Skia not being cached between launches.

Backports

44-x-y
In-flight
PR Number
#54161
Waiting to be merged
45-x-y
Merged
PR Number
#54156
Merged At
Sep 21, 2026, 3:48:53 AM
Released In
Not yet
Release Date
Not yet

Semver Impact

Major
Breaking changes
Minor
New features
Patch
Bug fixes
None
Docs, tests, etc.

Semantic Versioning helps users understand the impact of updates:

  • Major (X.y.z): Breaking changes that may require code modifications
  • Minor (x.Y.z): New features that maintain backward compatibility
  • Patch (x.y.Z): Bug fixes that don't change the API
  • None: Changes that don't affect using facing parts of Electron