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.

  1. State Tracking: The SharedImage backing tracks which APIs are currently reading from or writing to the memory.
  2. Automatic Barriers: During BeginAccess() and EndAccess(), the SharedImage system automatically inserts the necessary API-specific memory barriers, layout transitions (in Vulkan/D3D12), or keyed mutex acquisitions.
  3. 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.