Skip to content

[bug][cef] Linux: WebGPU only offers SwiftShader in tauri-runtime-cef, while plain CEF 152.0.6 (cefsimple) finds the hardware GPU #16203

Description

@toddbartholow

Describe the bug

On Linux, a tauri-runtime-cef app never gets a hardware WebGPU adapter. With the
Chromium switches that make WebGPU work in stock Chrome on the same machine,
navigator.gpu.requestAdapter() returns only SwiftShader (vendor: google,
architecture: swiftshader, isFallbackAdapter: true).

Plain CEF from the same CEF build (cefsimple from the official
cef_binary_152.0.6+g708dc14+chromium-152.0.7977.83_linux64_client
distribution), on the same machine, with the same switches and the same X11
ozone platform, returns the Intel GPU (vendor: intel, architecture: gen-9,
isFallbackAdapter: false). So this looks specific to how tauri-runtime-cef
sets CEF up, not to CEF or Chromium.

Separately, enable-unsafe-webgpu passed through Cef::command_line_arg seems to
have no effect at all: with it, not even the SwiftShader fallback adapter is
offered. Passing the same switch on the process command line (with
Cef::allow_chromium_command_line_args(true)) does make the fallback appear.

Reproduction

Versions: tauri 3.0.0-alpha.4, tauri-runtime-cef 3.0.0-alpha.5 (cef =152.3.0,
CEF 152.0.6, Chromium 152.0.7977.83), tauri-build 3.0.0-alpha.3, CLI 3.0.0-alpha.4.

src/main.rs (the rest is a default tauri.conf.json with one window, frontendDist
pointing at a page that runs the snippet below):

use tauri_runtime_cef::{Cef, RemoteDebugging, SandboxPolicy};

#[tauri_runtime_cef::cef_entry_point]
fn main() {
  let cef = Cef::default()
    // Ubuntu 24.04 restricts unprivileged user namespaces; disabled only so the
    // unbundled binary starts. The AppImage behaves the same with sandbox `auto`
    // once its AppArmor prompt is declined.
    .sandbox(SandboxPolicy::Disabled)
    .enable_features(["Vulkan"])                 // merged, per switches.rs
    .command_line_arg("enable-unsafe-webgpu", None::<String>)
    .remote_debugging(RemoteDebugging::Port { port: 20071, allowed_origins: vec![] });
  tauri::Builder::default()
    .runtime(cef)
    .run(tauri::generate_context!())
    .expect("run");
}

Page:

const a = await navigator.gpu.requestAdapter({ powerPreference: 'high-performance' });
console.log(a ? JSON.stringify({ vendor: a.info.vendor, arch: a.info.architecture, fallback: a.info.isFallbackAdapter }) : 'no adapter');

Results (same machine, same session, X11 via XWayland unless noted)

Host WebGPU switches Adapter
tauri-runtime-cef app none none ("No available adapters")
tauri-runtime-cef app enable_features(["Vulkan"]) + command_line_arg("enable-unsafe-webgpu") none
tauri-runtime-cef app enable_features(["Vulkan"]) + --enable-unsafe-webgpu as argv (allow_chromium_command_line_args(true)) SwiftShader only
cefsimple (CEF 152.0.6 client distribution) none none
cefsimple --enable-unsafe-webgpu --enable-features=Vulkan --ozone-platform=x11 Intel gen-9 (also with --use-alloy-style)
Chrome for Testing 152.0.7977.83 --enable-unsafe-webgpu --enable-features=Vulkan (X11 or Wayland) Intel gen-9

In every tauri-runtime-cef run, SystemInfo.getInfo reports webgpu: enabled,
vulkan: enabled_on, and WebGL2 is hardware
(ANGLE (Intel, Mesa Intel(R) HD Graphics 630 (KBL GT2), OpenGL 4.6)).

One structural difference I noticed (not proven to be the cause)

cefsimple runs a normal --type=gpu-process child, plus --type=renderer and
--type=utility children. The tauri-runtime-cef app shows no
--type=gpu-process or --type=renderer processes at all, only zygote-forked
processes and one utility process. One of those zygote forks maps the system
libvulkan_intel.so (and SwiftShader), so the Intel ICD is loaded, but Dawn does
not offer it.

Expected behavior

With the same switches, tauri-runtime-cef should enumerate the same adapters as
plain CEF from the same distribution. And Cef::command_line_arg("enable-unsafe-webgpu")
should have the same effect as the switch on the command line.

Platform and versions

  • Ubuntu 24.04.4 LTS, kernel 7.0.0-31, glibc 2.39
  • Intel Core i7-7700HQ, Intel HD Graphics 630 (i915, Mesa 25.2.8). An NVIDIA GTX
    1060 Mobile is present but has no driver loaded.
  • GNOME 46 on Wayland. The runtime forces --ozone-platform=x11, so XWayland.
  • Rust 1.97.1

Additional context

On Windows (RTX 3080), the same app gets a hardware adapter with default
settings, and render, compute and present all verify. So this is Linux-only.

Related, and not a Tauri issue: on this GPU, stock Chrome's WebGPU canvas
presentation stalls under X11 while working under native Wayland, and CEF warns
'--ozone-platform=wayland' is not compatible with Vulkan. I'm noting it only
because the runtime is X11-only on Linux, so even with an adapter, presentation
may need more work there.

Activity

  1. FabianLars commented on Oct 4, 2026

    @FabianLars
    Member

    I fixed this once for bundles before the switch to gtk4, so i don't know if that's what broke it. What did you test here exactly? tauri dev or tauri build? And in case of the latter, which bundle type (deb, rpm, appimage)?

  2. aneek0 commented on Oct 4, 2026

    @aneek0

    Same failure reproduces with a shipped end-user app built on tauri-runtime-cef 3.0.0-alpha: Koharu 0.83.5 (koharu-rs/koharu, deb bundle) on Arch/CachyOS, AMD RX 7800 XT (RADV/Mesa), Wayland+Niri with XWayland, kernel 7.2.8.

    Koharu passes its switches exactly through Cef::command_line_args:

    .command_line_args([
        ("--enable-unsafe-webgpu", None),
        ("use-angle", Some("vulkan")),
        ("--ozone-platform", Some("x11")),
    ])

    Reproduced symptoms (identical to the OP's):

    • No --type=gpu-process child. Only a --type=zygote --no-zygote-sandbox fork (identifiable as the GPU process by its thread names GpuWatchdog, ANGLE-GC, VizCompositorTh) plus a network utility process. The utility process does receive --use-angle=vulkan; the zygote-forked GPU process has none of the ANGLE/WebGPU switches in its cmdline.
    • ~/.cache/<app>/cef/chrome_debug.log from the GPU process on every run:
    WARNING:gpu/vulkan/vulkan_function_pointers.cc:22] Failed to bind vulkan entrypoint: vkCreateXcbSurfaceKHR
    ERROR:gpu/ipc/service/gpu_init.cc:1434] Failed to create and initialize Vulkan implementation.
    ERROR:ui/gl/gl_surface_egl.cc:380] No suitable EGL configs found for initialization.
    ERROR:gpu/ipc/service/shared_image_stub.cc:225] SharedImageStub: Unable to create shared image (debug_label: WebGPUSwapBufferProvider_Pid:0)
    
    • Web page gets webgpu found no adapters → the app's canvas dies.

    Root-cause trail I traced in the Chromium sources (for use-angle=vulkan + VulkanFromANGLE): with kVulkanFromANGLE default, VulkanInstance::InitializeFromANGLE (gpu/vulkan/vulkan_instance.cc) takes ANGLE's VkInstance and only binds the extensions reported by QueryVkInstanceExtensionsFromANGLE (ui/gl/gl_angle_util_vulkan.cc). ANGLE's windowless/offscreen display (DisplayVkOffscreen::getWSIExtension() returns nullptr, src/libANGLE/renderer/vulkan/linux/DisplayVkOffscreen.cpp) never enables VK_KHR_xcb_surface, so BindInstanceFunctionPointers refuses to bind vkCreateXcbSurfaceKHR and the whole Vulkan implementation init fails.

    So it lines up with your "structural difference" observation: the GPU process is a zygote fork that doesn't inherit the browser-process switches, and it initializes Vulkan-from-ANGLE under an OSR display that has no WSI extension.

    Extra data point: patching the app's literal "vulkan" → "gl-egl" (use-angle=gl-egl, CEF's own OSR recommendation from chromiumembedded/cef#3953 / cf5fddc) does propagate to the child and removes the vkCreateXcbSurfaceKHR failure — but then fails earlier with eglQueryDeviceAttribEXT: Bad attribute → Failed to retrieve vkGetInstanceProcAddr → Failed to get vkGetInstanceProcAddr pointer from ANGLE (gl-egl ANGLE is not Vulkan-backed, while enable-features=Vulkan,VulkanFromANGLE still forces the Vulkan-from-ANGLE path). So both current switch combinations are broken for the zygote-forked GPU process on Linux.

    Happy to attach full logs or test a patched runtime if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions