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 |
In the current release (BlinkGTK 1.2.3 / Chromium 154), the
software and EGL modes
display Blink's rendering output in the GTK window. Up to
1.2.3-build1 the
swiftshader mode showed nothing on screen (issue #151;
the capture API also failed
because no frame arrived). This is fixed as of 1.2.3-build2: pages
appear as in software
and EGL, and WebGL output is visible too. On 1.2.3-build1 and earlier,
use software or EGL.
On machines without a usable GPU, asking for EGL falls back
to software
automatically (since v1.2.1-build2). It switches when no frame
arrives within
five seconds and writes the reason to standard error; it does not switch
where
the GPU path works. Set BLINKGTK_EGL_AUTO_FALLBACK=0 to
disable it (the screen
may then stay white).
| Version | Item |
|---|---|
| v0.9 series | --headless was forced onto the browser process,
producing a white screen |
| v1.0.1 | Issue #47 — EGL mode fix |
| v1.0.10 iter2–iter7 | Issue #53 — SwiftShader mode fix |
| v1.0.10 iter5 | Issue #54 — EGL mode viz Skia readback fix |
| v1.0.10 iter7 | Issue #55 — EGL mode context-lost fix |
| v1.0.x (resolved 2026-04-20) | Issue #58 — did not follow the parent container's size on
HiDPI (fixed by implementing the size_allocate
vfunc) |
| v1.1.x (resolved 2026-06-21) | Issue #59 — repeated blink_web_view_load_uri()
accumulated resources |
| since v1.2.0-build3 | GPU rendering works with a single environment variable |
| since v1.2.1-build2 | Automatic fallback on machines without a GPU |
software)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.
Falls back automatically on machines without a usable GPU:
You may select egl on a machine where the GPU path does
not work (no driver,
no GPU visible inside a virtual machine, and so on). Previously the
window
stayed blank with nothing to explain why.
Now, if no frame reaches the EGL path within five seconds, BlinkGTK
switches to
the software path so drawing continues, and writes the reason to
standard error.
The fallback does not trigger where the GPU path works. Disable it
with
BLINKGTK_EGL_AUTO_FALLBACK=0 (the window then stays blank).
See
Environment
variables for details.
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 path from code (v1.2.2-build5 and later):
blink_gtk_set_gpu_mode(BLINK_GPU_MODE_EGL); /* before blink_gtk_init */
blink_gtk_init(&argc, &argv);Note: blink_web_view_new_with_gpu_mode() cannot
select the path. The
rendering path is fixed at blink_gtk_init(), and so are the
launch flags handed
to the GPU process. By the time a WebView is created it can no longer
change, so
passing a mode there has no effect — it only emits a warning. (Found
on
2026-09-14 from an external report; until then it was possible to
measure the
software path while believing EGL was in use.)
There is one path per process. It cannot differ between WebViews in
the same
process.
Confirm what took effect with
blink_web_view_get_gpu_mode(); the startup log
also prints the path. Setting BLINKGTK_GPU_MODE before
launch still works.
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 (how frames reach the screen) | P1 (direct) ↔︎ P2 (mojo shared memory) ↔︎ P3 (EGL dmabuf) | Yes (P1↔︎P2 since v1.2.0-build9, P3 since
v1.2.1-build3) — 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.
There is more than one delivery path (P1 = direct, P2 = mojo shared
memory,
P3 = EGL dmabuf), and these can be switched without a
restart:
BLINKGTK_GPU_MODE=egl. A
process started underint 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.
While presentation is stopped, a screenshot request
(blink_web_view_capture_screenshot_async()
and similar) still gets exactly one frame, after which presentation
stays stopped (1.2.3-build2 and later).
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.
This is resolved as of 1.2.3-build2 (first fixed on
2026-04-19 under Issue #54;
it came back in later versions, issue #151).
BLINK_LOAD_FINISHED is
reached and FPS is around 60, yet theCopyFromSurface
guard was applied to SwiftShader too.CopyFromSurface is theThis is resolved (Issue #58, 2026-04-20).
gtk_scale=2, the parent
widget is 1536×785 logical px butsize_allocate, but the wl_subsurface /
wp_viewport destination size didsize_allocate vfunc fixed
itgtk_scale. We check for these two symptoms
automatically on every updateThis 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)