Process-wide cache for decoded (and GPU-uploaded) file and immutable embedded images (SkImage). More...
Go to the source code of this file.
Process-wide cache for decoded (and GPU-uploaded) file and immutable embedded images (SkImage).
Embedded images use a NUL-prefixed synthetic key derived from their static address and size, so they cannot collide with a filesystem path.
Inline encoded bytes (a data:image/* source, a retained scene image blob) live in a separate pool keyed by the caller's SHA-256 of the bytes, never their address, because a freed buffer's address is reused for different bytes. That pool is bounded by bytes (the retained encoded copy plus the decoded or uploaded pixels), not by entry count, so a panel with many distinct inline images cannot evict file images or pin unbounded memory; an image larger than the whole budget is built and drawn uncached.
The path pool is bounded by entry count and by bytes (decoded or uploaded pixels), but it never evicts for bytes an entry drawn in the frame being painted, nor its most recent entry. A frame whose file images exceed the budget (an imported panel drawn as several full-frame layers, one backdrop larger than the whole budget) overshoots the budget instead of evicting each layer just before it is drawn again, which would decode and upload every layer on every frame. Entries left over from earlier frames are evicted down to the budget on the next build. Frames are announced per backend with begin_frame(); a backend that never announces one is evicted strictly by recency, keeping only its newest entry. The inline pool keeps its own rule: an image larger than its budget is built and drawn uncached.
Both pools remember a build that returned null for kFailureRetryDelay: lookups of that key inside the window return null without building, so a missing or corrupt asset is not read and decoded on every paint. A path key also records the file's size and modification time, and is retried as soon as either changes, so a file written or fixed inside the window shows on the next repaint. clear() and clear_backend() drop the remembered failures with the entries.
SkiaCanvas::draw_image_from_file used to re-read the PNG/JPEG bytes off disk (SkData::MakeFromFileName), re-decode them (SkImages::DeferredFromEncodedData), AND re-upload a fresh GPU texture (ensure_gpu_image → SkImages::TextureFromImage) on every draw. For a file-image drawn every frame — a dragged sprite-strip knob, a static background bitmap repainted at the display refresh rate — that is a disk read plus a full raster decode plus a texture upload per frame, all to produce a pixel-identical result.
This cache stores the fully-prepared SkImage keyed on the file path plus a backend identity token (the Graphite recorder / Ganesh context the canvas draws into, or null for CPU raster). The backend token is part of the key because a GPU-uploaded texture is bound to the context that created it: an image uploaded for recorder A must never be handed to a canvas drawing through recorder B. A CPU raster canvas (null token) caches the decoded raster image; a GPU canvas caches the texture-backed image.
─── Deliberate semantic change ─────────────────────────────────────────── The key is the file PATH, not the file CONTENTS. Unlike SvgDomCache (which keys on the exact document bytes, so a render-patched string re-parses and a dragged knob stays live), this cache CANNOT cheaply notice that the bytes on disk changed — reading + hashing the file every frame is exactly the cost being eliminated. Consequence: mutating an image file in place while the app holds it will NOT live-update the on-screen image. Callers that need a hot-reload story must call clear() (or a future per-path invalidation hook) when they know a file changed. This is an acceptable trade for interactive UIs, where image assets are effectively immutable for the session; the previous per-frame re-read only "worked" for live editing by paying the full cost on every static repaint too.
Painting is on the UI thread (not the realtime audio thread), so a small mutex is acceptable; the cache is process-global with LRU caps. Values are shared sk_sp<SkImage> — immutable once built and safe to share across the per-frame SkiaCanvas instances that reuse the same recorder.