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.
| 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 |
Since v1.0.10 iter7, all three modes generally display Blink's rendering output in the GTK window.
--headless was forced onto the browser
process, producing a white screenThe following were still being tracked as of iter9:
gtk_scale=2), thewl_subsurface size does not fully follow the parent widget
allocationblink_web_view_load_uri() on the same WebView accumulates a
resource leak (two layers mitigated insoftware)Behavior:
viz::SoftwareOutputDevice writes the bitmap into the
framebufferCopyFromSurface and displays
it through GtkPicture / GtkGLArea--disable-gpu-compositing is added internallyStrengths:
Weaknesses:
THREE.WebGLRenderer: Error creating WebGL context orFailed to create WebGPU Context Provider. If your
JavaScript depends on GPU acceleration, chooseswiftshader or egl.Recommended for:
swiftshader)Behavior:
--use-gl=angle --use-angle=swiftshader is setStrengths:
Weaknesses:
vk_swiftshader_icd.json),
libvk_swiftshader.so, and the Vulkanlibvulkan.so.1). Official tarballs and RPMs from
v1.0.10 iter7 onward bundle these underlib/chromium/ (packaging fixed in--disable-vulkanRecommended for:
egl)Behavior:
--use-gl=angle --use-angle=gl --ignore-gpu-blocklist is
set--headless is not added (fixed in Issue #47)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, soBLINKGTK_GPU_MODE=eglalone is enough. On earlier versions,
please usesoftware.
What it aims to provide:
Current limitations:
Current status:
BLINKGTK_GPU_MODE=egl alone (from the next
release)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
# 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-zygoteSelecting 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).
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() |
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 processTo 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.
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.
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.
FATAL: gl_factory_ozone NOTREACHED in EGL modeCause: 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).
VK_ERROR_INITIALIZATION_FAILED in SwiftShader modeCause: 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.
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.
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.
This can still occur under some conditions even on v1.0.10 iter7 and later. Check the following:
pkg-config --modversion blinkgtk-0.1
reports 1.0.10 or laterblink_web_view_new() directly (recommended): any
GtkContainer works as the parentgtk_window_set_child(window, view) is the simplest)blink_web_view_new_container(): a GtkOverlay +
GtkPicture is built internally.blink_web_view_new() with your own
GtkOverlaydata:text/html,<body style='background:red'>TEST</body>
as a test pageDidFinishNavigation() and
BLINK_LOAD_FINISHED appearSee Troubleshooting for details.
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_compareWhen 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_compareThree WebViews are displayed side by side so you can compare FPS and rendering quality.
new_with_gpu_mode)