Skip to content

feat(webgpu): WebGPU backend via Dawn, rebased onto v1.75.0 - #242

Open
nmfisher wants to merge 23 commits into
developfrom
asb/webgpu
Open

feat(webgpu): WebGPU backend via Dawn, rebased onto v1.75.0#242
nmfisher wants to merge 23 commits into
developfrom
asb/webgpu

Conversation

@nmfisher

Copy link
Copy Markdown
Owner

Rebases the WebGPU/Dawn backend work onto filament-v1.75.0 (22 commits).

Highlights:

  • Opt-in WebGPU/Dawn backend scaffolding (native + FFI: BACKEND_WEBGPU, Thermion_createWebGPUPlatform)
  • 4-way material variant build system (_native/_webgpu splits, WGSL support) — feat(materials) series
  • WebGPU readPixels + FLOAT-to-UBYTE workaround for the blitter blending bug
  • WebGPU asset test suite (thermion_dart/test/_webgpu_asset_tests.dart)
  • FFI bindings regenerated after the rebase (v1.75.0 symbols + WebGPU symbols)

Verification: flutter analyze — 0 errors in thermion_dart, thermion_flutter, and the quickstart example.

Caveat: the branch's material .filamat blobs were built against an older Filament — regenerate materials (make materials / CI regen workflow) before any golden-image comparison.

Base automatically changed from filament-v1.75.0 to develop August 15, 2026 08:33
nmfisher and others added 19 commits August 15, 2026 08:53
Materials are now built as two separate variants:
- _native: OpenGL + Metal + Vulkan shaders (smaller for standard builds)
- _webgpu: WGSL shaders only (smaller for WebGPU builds)

Both variants use identical C symbol names (IMAGE_PACKAGE, etc.) so
runtime code doesn't change. Only one .c is compiled by the build hook
based on the webgpu user-define. Forwarding headers dispatch via

Build hook has a fallback to unsuffixed .c files so existing materials
continue to work until make materials is re-run with matc.

Changes:
- materials/build.sh: runs matc twice per material (native + webgpu),
  generates suffixed .c/.h files and forwarding headers
- hook/build.dart: selects _native or _webgpu suffix with fallback
- Removed unused .S/.apple.S/.bin files (build hook uses .c files)

NOTE: make materials requires FILAMENT_PATH with matc built with
FILAMENT_SUPPORTS_WEBGPU=ON. Until then, existing unsuffixed materials
are used via the build hook fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rebuilt all materials using matc with FILAMENT_SUPPORTS_WEBGPU=ON.
Each material now has two variants:
- _native: OpenGL + Metal + Vulkan (68KB avg per material)
- _webgpu: WGSL only (19KB avg per material)

Fixed build.sh to use same .filamat filename for both variants so
resgen produces identical C symbol names (IMAGE_IMAGE_DATA etc.)
in both headers.

WebGPU test results with new WGSL materials:
- quad_tests: PASS (was crashing: Image material not built for WGSL)
- overlay_tests: 9/9 PASS (was crashing: Grid material not built for WGSL)
- wireframe: PASS (was crashing: Wireframe material not built for WGSL)
- depth_tests: still crashes (LinearDepth uses gl_FragCoord.z,
  a GLSL-specific built-in not supported in WGSL transpilation)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce compile-time backend selection with four material variants:

  _native       — OpenGL + Metal + Vulkan (GLSL, SPIR-V, MSL)
  _webgpu       — WebGPU only (WGSL)
  _web_webgl    — WebGL2 only (GLSL), smallest web variant
  _web_combined — WebGL2 + WebGPU (GLSL + WGSL), runtime selection

Downstream developers select via pubspec.yaml:
  hooks.user_defines.thermion_dart.backend: native|webgpu|webgl2|hybrid

Changes:
- build.sh: produces 4 variants per material, refactored with build_variant()
- build.dart: 'backend' user define replaces legacy 'webgpu: true/false'
  (legacy still works for backward compat). Maps to material suffix and
  THERMION_MATERIAL_* define.
- Web CMakeLists: THERMION_MATERIAL_VARIANT cmake variable selects variant.
  Explicit file list replaces broken *.c glob that caused duplicate symbols.
- macOS test CMakeLists: updated to use _native suffixed files.
- All forwarding headers: 4-way #ifdef dispatch with #error fallback.
- capture_uv: split into all 4 variants (was previously unsplit).
- Removed dead #include "material/outline.h" from TMaterialInstance.cpp.
- Removed stale .S/.bin assembly files and outline.c/outline.h.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Users can now pick Backend.WEBGPU on web; WebGL2 remains the default.
Selection on Flutter is via WebOptions(backend: ...); a
WebGpu.isSupported() helper exposes navigator.gpu detection so callers
can soft-fall-back without round-tripping into wasm.

Native:
- Insert BACKEND_WEBGPU=4 in TBackend, bump NOOP to 5 to align with
  filament::backend::Backend (which added WEBGPU=4 upstream — Thermion's
  NOOP was silently mapping to Filament's WEBGPU).
- ThermionWebGPUPlatform subclasses Filament's abstract WebGPUPlatform,
  binding wgpu::Surface to #thermion_canvas. (Filament doesn't ship an
  Emscripten concrete subclass — only Linux/Apple/Windows/Android.)
- Engine_create branches on backend: WEBGPU -> WebGPU platform; else
  existing WebGL2 path.
- CMakeLists adds -sUSE_WEBGPU=1.

Materials: matc now invoked with -a webgpu in addition to opengl/metal/
vulkan, so .filamat blobs carry WebGPU shader variants.

Examples: js_wasm and Flutter quickstart accept ?backend=webgpu URL
toggle, with auto-fallback to WebGL2 if navigator.gpu is missing.

Requires a Filament prebuilt built with FILAMENT_SUPPORTS_WEBGPU=ON;
the current pinned v1.69.1 web zip will need rebuilding.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the C++/Dart plumbing for Backend.WEBGPU on desktop & Android, gated
behind a userDefine so default builds remain unaffected. Intended for
local validation of the WebGPU path on a platform where the developer
controls the Filament build; production native builds should stay on
Metal/Vulkan/OpenGL.

- TEngine.cpp: when THERMION_SUPPORTS_WEBGPU is defined and the caller
  passes BACKEND_WEBGPU with a null platform, auto-construct the
  platform-specific WebGPUPlatform subclass (Apple/Linux/Windows/Android).
  Headers gated on the same define so default builds don't pull in
  webgpu_cpp.h transitively.
- hook/build.dart: new `webgpu` userDefine gates THERMION_SUPPORTS_WEBGPU
  and reserves a slot for the Dawn lib list (left as a TODO until the
  exact lib names can be enumerated from a built Filament).
- native plugin: Backend.WEBGPU is now an accepted value in the
  supported-backends switch; no platform restriction since Dawn
  translates to the native graphics API on every supported OS.

Consumer opts in via pubspec:
  hooks:
    user_defines:
      thermion_dart:
        webgpu: true

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Extends scripts/build_macos.sh with three orthogonal flags so the same
script can fetch + build + ship a Filament artifact with the Dawn-backed
WebGPU backend.

- FILAMENT_VERSION=latest: resolves the newest v*.* tag from
  github.com/google/filament via `git ls-remote --sort=-version:refname`.
- --webgpu: injects -DFILAMENT_SUPPORTS_WEBGPU=ON into the cmake commands
  alongside the existing -DFILAMENT_SKIP_SAMPLES=ON patch, and after the
  build copies all Dawn static libs (recursively, since Dawn produces
  many granular targets) plus the generated webgpu/webgpu_cpp.h header
  into the target dir.
- --upload: pipes the resulting zip(s) through scripts/upload_r2.sh
  after the build completes.

Existing invocations (no new flags) are unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lets the manual workflow_dispatch trigger a WebGPU-enabled macOS build
against any branch (incl. claude/webgpu) without merging first.

- New `webgpu` boolean input (default false) appends `--webgpu` to the
  build_macos.sh invocation in the build-macos job.
- The setup job's Read Filament version step now resolves
  FILAMENT_VERSION=latest to a concrete v*.* tag via
  `git ls-remote --sort=-version:refname` before the per-platform jobs
  do their depth-1 clone (which can't handle the literal "latest").

R2 upload continues to use the existing `aws s3 cp` step gated on
`upload_to_r2`; the build script's own --upload flag is ignored here.

Other platforms (ios/android/linux/windows/web) are unchanged — their
build_*.sh scripts don't currently honour --webgpu.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…l R2 keys

A --webgpu run was about to upload to the exact same S3 key as the
canonical Filament build (e.g. filament-v1.69.1-macos-release.zip),
silently swapping in Dawn-enabled libs for every downstream consumer
fetching via hook/build.dart.

Now the build_macos.sh script and the build-macos workflow job both
suffix the output zip with "-webgpu" whenever the flag is on:
  filament-<version>-macos-release-webgpu.zip
The canonical key is untouched and the WebGPU artifact sits beside it.

The workflow computes ZIP_SUFFIX once via GITHUB_ENV so the artifact
path, artifact name, and S3 key all stay in sync.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mirrors the macOS WebGPU plumbing for the Linux build so the Filament
artifact for Linux can also be produced with Dawn/WebGPU linked in.
Useful for headless testing inside a Docker container.

- build_linux.sh: --webgpu injects -DFILAMENT_SUPPORTS_WEBGPU=ON into
  Filament's build.sh, recurses Dawn's build tree to collect static libs
  and generated webgpu headers, and suffixes the resulting zip with
  -webgpu so it never overwrites the canonical Linux artifact on R2.
- build_linux.sh: --upload pipes the resulting zip(s) through
  upload_r2.sh after a successful build.
- build_linux.sh: FILAMENT_VERSION=latest now resolves to the newest
  v*.* tag from github.com/google/filament.
- build-filament.yml: the existing webgpu workflow_dispatch input now
  also drives the Linux job — compute ZIP_SUFFIX, pass --webgpu through,
  and suffix artifact paths, artifact names, and S3 destinations.
Dawn's X11 backend (DAWN_USE_X11) includes <X11/Xlib-xcb.h> via
dawn/common/xlib_with_undefs.h, which lives in libx11-xcb-dev, not the
plain libx11-dev we were installing. Add libx11-xcb-dev plus libxcb1-dev
to the Linux build job's apt-get list so the Dawn-linked Filament build
gets past the dawn_native compile.

Fixes the failure on the first Build Filament(linux, webgpu) run:
  third_party/dawn/src/dawn/common/xlib_with_undefs.h:44:10:
  fatal error: 'X11/Xlib-xcb.h' file not found
Dawn's X11/XCB backend pulls in headers from a sprawl of xcb-* extension
packages (dri3, present, randr, sync, shape, xfixes, image, render,
keysyms, util, xkb, shm-fence, xkbcommon). Install them all up front
rather than discovering each missing header on a fresh CI run.
- TestHelper: add optional backend parameter so tests can select
  Backend.WEBGPU instead of the platform default (OpenGL on Linux)
- TEngine: store WebGPUPlatform* global when WebGPU backend is selected,
  for future use by Dawn event processing
- TRenderer: add explanatory comments about the async readback challenge
- capture_tests_webgpu.dart: placeholder test documenting that Filament
  v1.71.5's WebGPU driver does NOT implement readPixels (confirmed via
  symbol analysis — WebGPUDriver has readTextureToBuffer but not
  readPixels; the base class no-op is used instead)

The WebGPU smoke test (engine creation + teardown) continues to pass.
Capture/readback tests will be re-enabled when Filament adds readPixels
support for the WebGPU backend.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Filament v1.71.5 did not implement readPixels for the WebGPU backend.
The latest main branch includes a full readPixels implementation with
format conversion, staging buffers, and async buffer mapping.

- Update filament.version to point at main branch
- Sync all public headers from latest Filament source
- New: filament/FrameHistoryStream.h
- Updated: 38 header files across backend/, filament/, and libs
- Preserved manually-added dirs: bluevk/, vulkan/, webgpu/, dawn/,
  utils/android/, utils/linux/, etc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ing bug

WebGPU readPixels now works with Filament built from main (post-v1.71.5).
The ConcreteDispatcher<WebGPUDriver>::readPixels symbol is compiled in
(the method is inlined via UTILS_ALWAYS_INLINE).

Key findings:
- The headless swapchain uses RGBA8Unorm format
- Requesting FLOAT readback triggers a format-conversion blit in
  Filament's WebGPUBlitter, which fails because it enables blending on
  RGBA32Float (a non-blendable format in WebGPU/Dawn)
- UBYTE readback matches the swapchain format and avoids the blit entirely

Changes:
- Add WebGPU readPixels capture tests (UBYTE, both direct and via
  FLOAT-to-UBYTE workaround)
- Add backend getter to FilamentApp/FFIFilamentApp for backend-aware
  workarounds
- Add WebGPU workaround in capture(): auto-downgrade FLOAT to UBYTE
  when reading from swapchain (no render target)
- Sync Filament headers from main (bluevk, utils) for UTILS_GUARDED_BY
  and other new macros needed by Platform.h
- Document upstream Filament blitter issue in docs/upstream.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The test helper's capture() method now mirrors the WebGPU workaround
in FFIFilamentApp.capture() — when the backend is WebGPU and FLOAT
readback is requested from the swapchain, it uses UBYTE for the PNG
writer's isFloat check so the buffer is interpreted correctly.

This fixes all asset_tests running against the WebGPU backend (5/5 pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Runs asset add/remove/destroy/bounding-box tests against the WebGPU
backend (Dawn + lavapipe).

Co-Authored-By: Claude <noreply@anthropic.com>
Nothing references this tree. Build scripts no longer copy Filament
headers into a committed directory (see comments in scripts/build_*.sh);
consumers source them from the version-matched R2 artifact at build time
via thermion_dart/hook/build.dart, which adds only native/include and
the artifact include root to the include path.

Co-Authored-By: Claude <noreply@anthropic.com>
build_linux.sh: add -stdlib=libc++ to CMAKE_CXX_FLAGS for imageio and
tinyexr so their static archives use the same C++ stdlib as the rest
of the .so. Removes the root cause of the libstdc++/libc++ ABI clash
that forced us to skip both libs on Linux.

hook/build.dart: move webgpu detection before getLibDir so the build
hook can download the -webgpu suffixed zip from R2 (e.g.
filament-v1.71.5-linux-release-webgpu.zip) instead of the canonical
artifact. Previously the webgpu build had to be manually staged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nmfisher and others added 4 commits August 15, 2026 10:46
…PLES injection

The webgpu change (e9605e8) rewrote the build.sh patch sed with double
quotes, collapsing the escaped backslashes so sed failed with "unterminated
`s' command". The failure was unchecked, so -DFILAMENT_SKIP_SAMPLES=ON was
never injected, Filament built the samples, and the debug link failed with
multiple-definition errors for stb_image_write symbols (seen on the Linux
arm64 CI run 31877482056).

Restore the single-quoted form from before the regression, splicing in
$CMAKE_INJECT between quoted segments, and fail fast if the patch does not
match.

Co-Authored-By: Claude <noreply@anthropic.com>
Mirrors the merged PR #243 change from develop. workflow_dispatch runs
read .github/workflows/build-filament.yml from the dispatched ref, so the
macos-15 bump must be present on asb/webgpu for the re-dispatched macOS
Dawn build to pick it up. macos-14's Xcode 15.4 / Clang 15 lacks C++20
parenthesized aggregate initialization (P0960), which Tint requires.

Co-Authored-By: Claude <noreply@anthropic.com>
The WebGPU macOS build (run 31880432397) compiled everything but
failed zipping the debug artifact: 'zip I/O error - No space left on
device'. Dawn debug artifacts are huge and the stock macos-15 image
leaves too little free disk.

jlumbroso/free-disk-space is Ubuntu-only (apt-based), so remove the
large preinstalled toolchains by hand before the build: extra Xcode
versions (keeping the selected one), Android SDK/NDK, CoreSimulator
runtimes, dotnet, ghcup and boost. Typically frees 30+ GB.

Co-Authored-By: Claude <noreply@anthropic.com>
…terials regen

build_linux.sh and build_macos.sh now copy the built matc and resgen
into the -webgpu zips (bin/matc, bin/resgen) so the R2 artifacts carry
a WebGPU-capable toolchain. The official Filament release matc is built
without FILAMENT_SUPPORTS_WEBGPU and cannot compile -a webgpu (WGSL)
material variants.

regenerate-materials.sh downloads bin/matc + bin/resgen from the
webgpu-suffixed R2 zip (filament-<tag>-linux-release-webgpu.zip)
instead of the release tarball, caches them per version under a
distinct key, and smoke-tests that the cached matc accepts -a webgpu
before regenerating.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant