BlinkGTK GPU Modes

BlinkGTK runs in one of three GPU modes, selected with the BLINKGTK_GPU_MODE environment variable.

About this page (updated 2026-08-16)
Parts of chapter 6 (troubleshooting) record symptoms observed on earlier
releases; some are resolved in the current release. Chapter 5 (switching)
is up to date. For the authoritative list of environment variables, see the
environment variables reference.


1. The three modes

Mode BLINKGTK_GPU_MODE Rendering Compositing Intended use

|

Software | software (default) | CPU | Software compositor | Headless, CI, servers, low-spec machines |
|

SwiftShader | swiftshader | Software GL (ANGLE → Vulkan → SwiftShader) | Software compositor | Virtual machines, stability first |
|

Native EGL | egl | Hardware GPU (Wayland EGL) | GPU compositor | Desktop, production |

On-screen rendering

Since v1.0.10 iter7, all three modes generally display Blink's rendering output in the GTK window.

The following were still being tracked as of iter9:


2. Mode details

2.1 Software (software)

Behavior:

Strengths:

Weaknesses:

Recommended for:

2.2 SwiftShader (swiftshader)

Behavior:

Strengths:

Weaknesses:

Recommended for:

2.3 Native EGL (egl)

Behavior:

On v1.2.0-build2 and earlier this shows a white screen.

The setup this path needs (GTK4 integration, manual viewport, device scale,
and the receiving widget) was left to the application, so nothing appeared
unless you knew to configure it. From the next release the engine does
this
, so BLINKGTK_GPU_MODE=egl alone is enough. On earlier versions,
please use software.

What it aims to provide:

Current limitations:

Current status:


3. Choosing a mode

What is the application's runtime environment?
├─ GPU + desktop + Wayland         -> egl
├─ No GPU, need WebGL              -> swiftshader
├─ No GPU, no WebGL                -> software (default)
└─ Headless / CI                   -> software or swiftshader

4. Launch examples

# Software mode (default)
./myapp --no-sandbox --no-zygote

# SwiftShader mode
BLINKGTK_GPU_MODE=swiftshader ./myapp --no-sandbox --no-zygote

# Native EGL mode
BLINKGTK_GPU_MODE=egl ./myapp --no-sandbox --no-zygote

Selecting the mode from code (v1.0.2 and later):

GtkWidget* view = blink_web_view_new_with_gpu_mode(BLINK_GPU_MODE_EGL);

Note that blink_web_view_new_with_gpu_mode() sets BLINKGTK_GPU_MODE for the process and therefore
affects renderer processes launched afterwards; it is not per-WebView isolation. For deterministic
behavior, set the environment variable before blink_gtk_init(&argc, &argv).


5. Switching modes and paths — what can change at runtime

There are two different axes, with different capabilities.

Axis Example Switchable at runtime?
GPU mode (software / swiftshader / egl on this page) software → egl No — requires a process restart
Delivery path (inside software mode) P1 (direct) ↔︎ P2 (mojo shared memory) Yes (v1.2.0-build9+) — blink_web_view_switch_render_path()

5.1 Switching the GPU mode (restart required)

If your application offers a UI (for example a menu item) that lets the user
pick a GPU mode, changing the mode requires restarting the process.

// When the user selects a different mode
g_setenv("BLINKGTK_GPU_MODE", "egl", TRUE);
execv(argv[0], argv);  // restart the process

To carry state (URL, scroll position, display scale) across the restart, use
the handoff meta API (blink_web_view_export_handoff_meta() /
import_handoff_meta()). It carries the display scale in canonical units, so
the restarted view does not come back undersized.

5.2 Live delivery-path switching (v1.2.0-build9+)

Software mode has more than one internal delivery path (P1 = direct,
P2 = mojo shared memory), and these can be switched without a restart:

int seq = blink_web_view_switch_render_path(view, "P2");
/* Completion arrives on the "render-path-changed" signal. If the new path
 * fails its render check, the previous path is restored automatically
 * (result="rollback" — the screen is never broken). */

A switch takes a few hundred milliseconds in our test environment. See the
render-path-changed section of the
Signals API reference for details
(sequence numbers, result values).

Scope: this API switches software-side delivery paths ("P1" / "P2") only.
It does not switch the GPU mode itself (software ↔︎ egl) — use the restart
procedure in 5.1 for that.

5.3 Stopping presentation instead (presentation policy)

If what you actually want is "this view need not be presented right now"
(an audio-only mode, for example) rather than a different path, the proper
tool is the presentation policy:
blink_web_view_set_presentation_policy(). It skips only the cost of
presenting frames; layout and rasterization (read-ahead) continue.


6. Troubleshooting

6.1 FATAL: gl_factory_ozone NOTREACHED in EGL mode

Cause: the Wayland EGL driver is not found, or the process is falling back to an X11 display.
Fix: use v1.0.10 iter7 or later (fixed for v1.0.1 in
Issue #47, hardened further for v1.0.10 iter7 in
Issue #53).

6.2 VK_ERROR_INITIALIZATION_FAILED in SwiftShader mode

Cause: ANGLE's Vulkan backend fails to initialize against the environment's Vulkan driver
configuration.
Fix: use v1.0.10 iter7 or later. In iter7 the SwiftShader path was changed to explicitly avoid
ANGLE Vulkan (Issue #53). Upgrade if you are on
iter6 or earlier.

6.3 SwiftShader renders, but pixels never reach GTK

Symptom: BLINK_LOAD_FINISHED is reached and FPS is around 60, yet the GTK window stays black or
shows only the background color.
Cause: the CopyFromSurface guard from Issue #54 may be over-applied to SwiftShader
([Issue #54 follow-up]).
Fix: wait for the fix in v1.0.10 iter10 or later. In the meantime switch to software or egl.

6.4 EGL rendering is clipped to the top-left corner (HiDPI)

Symptom: in EGL mode with gtk_scale=2, the parent widget is 1536×785 logical px but web content
appears only in roughly the top-left 400×360 physical px.
Cause: the Chromium Chromium view (RenderWidgetHostView) is updated correctly through size_allocate, but the destination size of
wl_subsurface / wp_viewport does not follow
(Issue #58).
Fix: wait for the fix in iter10 or later. As a workaround, keep the window at 800×600 or smaller so
that the whole surface is visible.

6.5 White screen in every mode

This can still occur under some conditions even on v1.0.10 iter7 and later. Check the following:

See Troubleshooting for details.


7. Benchmarking

The triple_gpu_compare sample (examples/triple_gpu_compare.c) compares all three modes side by side
within a single application.

Running from a Chromium build tree:

cd $CHROMIUM_SRC/out/Release_Component
LD_LIBRARY_PATH=$PWD ./triple_gpu_compare

When installed from a tarball or RPM:

# The executable lives in $prefix/bin; runtime .so files in $prefix/lib/chromium
cd /usr/local/lib/chromium   # or adjust to your prefix
/usr/local/bin/triple_gpu_compare

Three WebViews are displayed side by side so you can compare FPS and rendering quality.