#54113: fix: persist GPU shaders compiled by Skia between launches
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 ❌"]
content/browser/gpu/gpu_disk_cache_factory.ccmapskDisplayCompositorGpuDiskCacheHandle→GetShaderDiskCacheDirectory(),kGrShaderGpuDiskCacheHandle→GetGrShaderDiskCacheDirectory(),kGraphiteDawnGpuDiskCacheHandle→GetGraphiteDawnDiskCacheDirectory();gpu_process_host.ccusesGetGPUPersistentCacheDirectory()wherefeatures::kGpuPersistentCacheis on (non-Windows).gpu/ipc/service/gpu_channel_manager.cc:StoreShaderalways targetskGrShaderGpuDiskCacheHandle. The per-rendererGPUCacheElectron apps do have isgles2::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 atsessionDatanext toGPUCache,Cacheand the rest. Like those,sessionDatahas to be set beforeready; the factory is created inBrowserMainLoopafter 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
- I have built and tested this change — Windows build in progress; the "after" column will be filled in from it
- I have filled out the PR description
- I have reviewed and verified the changes — drafted with Claude (see commit trailer), reviewed line by line against the Chromium 155
ContentBrowserClientsignatures andChromeContentBrowserClient's implementations -
npm testpasses - relevant API documentation, tutorials, and examples are updated and follow the documentation style guide —
sessionDatanow mentions compiled GPU shaders - PR release notes describe the change in a way relevant to app developers, and are capitalized, punctuated, past tense, and no longer than 80 characters.
Release Notes
Notes: Fixed GPU shaders compiled by Skia not being cached between launches.
Backports
Semver Impact
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