How SharedImages Replace Legacy GPU Mailboxes in Chromium
In the Chromium web browser, rendering content securely and efficiently requires sharing graphics resources across multiple isolated processes, such as the renderer, media, and GPU processes. Historically, Chromium relied on a legacy system known as GPU mailboxes to share OpenGL textures across these boundaries. However, as modern graphics APIs like Vulkan, Metal, and Direct3D 12 emerged, the OpenGL-centric mailbox system became inadequate. This article explains how the SharedImage architecture replaces legacy GPU mailboxes to provide a unified, API-agnostic, and secure mechanism for sharing graphical textures across processes in Chromium.
The Limitations of Legacy GPU Mailboxes
To understand the role of SharedImages, it is necessary to look at why the legacy mailbox system failed to scale. In the legacy system, a mailbox was a 16-byte cryptographic identifier mapped directly to an OpenGL texture ID inside the GPU process.
This approach had several critical flaws:
- OpenGL Dependency: The system assumed everything was an OpenGL texture. Sharing resources with Vulkan, Metal, Direct3D, or the Skia rendering engine required complex, slow, and fragile conversion steps.
- Manual Synchronization: Processes had to manually
manage synchronization using
SyncTokens. It was easy for a developer to accidentally read from a texture in one context while another context was still writing to it, leading to visual corruption and security vulnerabilities. - Ambiguous Lifetimes: Determining when a texture was safe to delete across multiple asynchronous processes was difficult and frequently caused memory leaks or use-after-free bugs.
What is a SharedImage?
A SharedImage is an abstraction layer in Chromium’s GPU process that
represents a platform-specific chunk of graphical memory (called a
"backing"). Instead of tying an identifier directly to an OpenGL
texture, a SharedImage ties the identifier to the underlying OS memory
primitive—such as an IOSurface on macOS, an
AHardwareBuffer on Android, a DXGI shared
resource on Windows, or a DMA-BUF on Linux/ChromeOS.
Note: The SharedImage system still uses the
gpu::Mailbox data structure as a unique 16-byte name to
identify the resource across processes, which often causes confusion.
However, what that identifier points to has completely changed from a
raw OpenGL texture to a managed SharedImage object.
API-Agnostic Access via Representations
The primary role of a SharedImage is to decouple the underlying memory from the graphics API used to access it. When a process creates a SharedImage, it specifies how the image will be used (e.g., for WebGL, 2D Canvas, video decoding, or the display compositor) using usage flags.
Once the SharedImage backing is allocated in the GPU process, different components can access the exact same memory through Representations. A Representation is a lightweight wrapper that translates the SharedImage backing into an API-specific object:
- GLTextureRepresentation: Exposes the memory as an OpenGL texture (used by WebGL).
- SkiaRepresentation: Exposes the memory as a Skia surface or image (used by the renderer for 2D Canvas and the Viz display compositor).
- DawnRepresentation: Exposes the memory as a WebGPU texture.
- OverlayRepresentation: Allows the memory to be handed directly to the OS window manager for hardware overlay composition.
Because the SharedImage backing is built on native OS buffers, a video decoder can write frames into a SharedImage using Direct3D 11, and WebGPU can immediately read that frame via Dawn without ever copying the pixel data.
Enforced Synchronization and Access Control
Legacy mailboxes relied entirely on the caller to remember to insert synchronization primitives before using a texture. SharedImages solve this by enforcing strict read/write locks at the architectural level.
To use a SharedImage Representation, the consumer must call
BeginAccess() before reading or writing, and
EndAccess() when finished.
- State Tracking: The SharedImage backing tracks which APIs are currently reading from or writing to the memory.
- Automatic Barriers: During
BeginAccess()andEndAccess(), the SharedImage system automatically inserts the necessary API-specific memory barriers, layout transitions (in Vulkan/D3D12), or keyed mutex acquisitions. - Validation: If a component attempts to write to a SharedImage while another component has an active read lock, the SharedImage system detects the violation and safely fails, preventing race conditions and GPU crashes.
Unified Lifetime Management
SharedImages also replace the ad-hoc lifetime management of legacy
mailboxes with a dedicated SharedImageInterface. When a
client process creates a SharedImage, it holds a reference. It can pass
the mailbox identifier to another process (like the display compositor),
which then registers its own access.
The underlying memory backing is strictly reference-counted inside the GPU process. Even if a renderer process crashes or destroys its reference early, the SharedImage backing remains alive and valid until the display compositor finishes drawing the current frame and releases its own reference. This guarantees safe resource cleanup across asynchronous process boundaries.