Skip to content

Commit f8bf776

Browse files
[ExecuTorch][WebGPU] Switch native backend from wgpu-native to Dawn (Tint) + SwiftShader
Pull Request resolved: #20079 Make Dawn (Chrome's WebGPU implementation, whose WGSL compiler Tint is the spec reference) running on SwiftShader the sole native WebGPU backend, replacing wgpu-native (naga), so the op tests run on a spec-faithful, headless, deterministic CLI backend. The `WEBGPU_IMPL` cache variable, the wgpu-native CMake branch, and the `WEBGPU_IMPL_DAWN` compile define are removed -- CMake now unconditionally `find_package(Dawn REQUIRED)` and links `dawn::webgpu_dawn`. `WebGPUCompat.h` drives pending callbacks via Dawn's `wgpuInstanceProcessEvents` on native and yields to the JS event loop under Emscripten. Dawn is vendored with NO new S3 artifact: `oss/.ci/scripts/setup-webgpu-linux-deps.sh` downloads Google's official `ubuntu-latest-Release` prebuilt directly from github.com/google/dawn/releases (pinned tag + sha256, the same pattern as `setup-wgpu-native.sh`), and reuses the SwiftShader prebuilt already on the ossci bucket. The release exports `dawn::webgpu_dawn` (a static lib) which drops into the existing `find_package(Dawn)`. It has no bundled SwiftShader, so `WebGPUDevice.cpp` requests a normal Vulkan adapter (`forceFallbackAdapter=false`) and `VK_ICD_FILENAMES` makes SwiftShader the only device. The release is built with a recent GCC, so the deps script also pulls a current libstdc++ from the `ubuntu-toolchain-r` PPA (its lib references `_M_replace_cold`, a GCC 13+ symbol) plus `libvulkan1` (Dawn dlopens the Vulkan loader) -- all scoped to the WebGPU CI job, backward-compatible, no repo-wide impact. Authored with assistance from Claude. ghstack-source-id: 391968389 @exported-using-ghexport Differential Revision: [D107589774](https://our.internmc.facebook.com/intern/diff/D107589774/)
1 parent c26c649 commit f8bf776

10 files changed

Lines changed: 352 additions & 182 deletions

File tree

.ci/scripts/setup-webgpu-linux-deps.sh

Lines changed: 83 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,93 @@
55
# This source code is licensed under the BSD-style license found in the
66
# LICENSE file in the root directory of this source tree.
77

8+
# Vendor Dawn (Tint) + SwiftShader for the WebGPU backend CI WITHOUT hosting a
9+
# private prebuilt:
10+
# * Dawn : Google's official nightly prebuilt, downloaded directly from
11+
# github.com/google/dawn/releases (pinned tag+rev+sha256) -- the same
12+
# "fetch a pinned upstream prebuilt" pattern used for other CI deps.
13+
# * SwiftShader : built from source at a pinned rev compatible with the Dawn
14+
# above (the ossci prebuilt is from 2020, too old for current Dawn). No S3.
15+
# Dawn (Chrome's WebGPU impl; its WGSL compiler Tint is the spec reference) on
16+
# SwiftShader gives a headless, deterministic, spec-faithful CLI backend.
17+
#
18+
# Exports Dawn_DIR / VK_ICD_FILENAMES / LD_LIBRARY_PATH for the cmake build+run.
19+
# Local/rig override: set DAWN_PREBUILT_DIR=<dir containing lib64/cmake/Dawn> to
20+
# skip the Dawn download.
821
set -ex
922

10-
# SwiftShader: software Vulkan adapter for GPU-less CI (LunarG SDK not needed).
11-
install_swiftshader() {
12-
_https_amazon_aws=https://ossci-android.s3.amazonaws.com
13-
_swiftshader_archive=swiftshader-abe07b943-prebuilt.tar.gz
14-
_swiftshader_dir=/tmp/swiftshader
15-
mkdir -p $_swiftshader_dir
23+
# --- pinned versions (bump rev+sha together when upgrading Dawn) --------------
24+
DAWN_TAG="${DAWN_TAG:-v20260423.175430}"
25+
DAWN_REV="${DAWN_REV:-31e25af254ab572c77054edec4946d2244e184dd}"
26+
DAWN_SHA256="${DAWN_SHA256:-ac76fac090162dc1ecea5ed0f28a557bb8f49efc47faab01886105ace82b7b64}"
27+
# SwiftShader rev verified compatible with DAWN_REV (the old ossci prebuilt is
28+
# from 2020 and is incompatible with current Dawn -> no adapter / zero compute).
29+
SWIFTSHADER_REV="${SWIFTSHADER_REV:-9898204d91d6a60b6a08ad74fe4ac52a6913111b}"
1630

17-
_tmp_archive="/tmp/${_swiftshader_archive}"
31+
_dawn_dir="${DAWN_PREBUILT_DIR:-/tmp/dawn-ci}"
32+
_ss_dir=/tmp/swiftshader
1833

19-
curl --silent --show-error --location --fail --retry 3 --retry-all-errors \
20-
--output "${_tmp_archive}" "$_https_amazon_aws/${_swiftshader_archive}"
34+
# --- toolchain prereqs --------------------------------------------------------
35+
# Dawn dlopens the system Vulkan loader at runtime (libvulkan1). And the
36+
# ubuntu-latest prebuilt is built with a bleeding-edge GCC: it references
37+
# libstdc++ symbols newer than ubuntu-22.04's default (e.g. _M_replace_cold,
38+
# GCC 13+), so the static .a won't link against the stock runtime. Pull a current
39+
# libstdc++ from the ubuntu-toolchain-r PPA when the symbol floor isn't met. All
40+
# of this is scoped to the WebGPU CI job; newer libstdc++ is backward-compatible.
41+
if command -v apt-get >/dev/null 2>&1; then
42+
_SUDO=""; command -v sudo >/dev/null 2>&1 && _SUDO="sudo"
43+
${_SUDO} apt-get update -y || true
44+
${_SUDO} apt-get install -y libvulkan1 software-properties-common || true
45+
if ! strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 2>/dev/null \
46+
| grep -q "GLIBCXX_3.4.32"; then
47+
${_SUDO} add-apt-repository -y ppa:ubuntu-toolchain-r/test || true
48+
${_SUDO} apt-get update -y || true
49+
${_SUDO} apt-get install -y libstdc++6 || true # newest GCC runtime
50+
fi
51+
fi
2152

22-
tar -C "${_swiftshader_dir}" -xzf "${_tmp_archive}"
53+
# The native binaries / pybind lib run INSIDE the CI conda env, whose libstdc++
54+
# predates GLIBCXX_3.4.32 (the Dawn prebuilt's floor) -- the same wall ssjia hit
55+
# for the vulkan op tests. Upgrade the conda runtime libstdc++ so the loaded
56+
# libstdc++.so.6 (conda's, not the system one) satisfies Dawn at run time.
57+
if command -v conda >/dev/null 2>&1; then
58+
conda install -y -c conda-forge "libstdcxx-ng>=14" || true
59+
fi
60+
61+
# --- Dawn: official prebuilt from GitHub (no S3) ------------------------------
62+
mkdir -p "${_dawn_dir}"
63+
if [[ ! -d "${_dawn_dir}/lib64/cmake/Dawn" ]]; then
64+
_dawn_tar="/tmp/Dawn-${DAWN_REV}-ubuntu-latest-Release.tar.gz"
65+
curl --silent --show-error --location --fail --retry 3 --retry-all-errors \
66+
--output "${_dawn_tar}" \
67+
"https://github.com/google/dawn/releases/download/${DAWN_TAG}/Dawn-${DAWN_REV}-ubuntu-latest-Release.tar.gz"
68+
echo "${DAWN_SHA256} ${_dawn_tar}" | sha256sum -c -
69+
# archive top dir is Dawn-<rev>-ubuntu-latest-Release/{lib64,include,bin}
70+
tar -C "${_dawn_dir}" --strip-components=1 -xzf "${_dawn_tar}"
71+
fi
2372

24-
export VK_ICD_FILENAMES="${_swiftshader_dir}/swiftshader/build/Linux/vk_swiftshader_icd.json"
25-
export LD_LIBRARY_PATH="${_swiftshader_dir}/swiftshader/build/Linux/:${LD_LIBRARY_PATH}"
26-
export ETVK_USING_SWIFTSHADER=1
27-
}
73+
# --- SwiftShader: build from source at a pinned rev (no S3) -------------------
74+
# The old ossci prebuilt (swiftshader-abe07b943, 2020) is incompatible with the
75+
# current Dawn; build a matching modern SwiftShader instead. Self-contained
76+
# cmake build (vendored LLVM); the ICD lands under build/<OS>/.
77+
if [[ ! -d "${_ss_dir}/build" ]]; then
78+
if [[ ! -d "${_ss_dir}/.git" ]]; then
79+
git clone https://github.com/google/swiftshader "${_ss_dir}"
80+
fi
81+
git -C "${_ss_dir}" checkout "${SWIFTSHADER_REV}"
82+
# vk_swiftshader's deps are vendored in-tree; tolerate unreachable
83+
# disabled-feature submodules (angle, test-only) failing to fetch.
84+
git -C "${_ss_dir}" submodule update --init --recursive || true
85+
cmake -S "${_ss_dir}" -B "${_ss_dir}/build" -DCMAKE_BUILD_TYPE=Release \
86+
-DSWIFTSHADER_BUILD_TESTS=OFF -DSWIFTSHADER_BUILD_PVR=OFF \
87+
-DSWIFTSHADER_BUILD_BENCHMARKS=OFF
88+
cmake --build "${_ss_dir}/build" --parallel "$(nproc)" --target vk_swiftshader
89+
fi
90+
_ss_icd="$(find "${_ss_dir}/build" -name vk_swiftshader_icd.json 2>/dev/null | head -1)"
91+
[[ -n "${_ss_icd}" ]] || { echo "ERROR: SwiftShader ICD not found after build" >&2; exit 1; }
2892

29-
install_swiftshader
30-
bash backends/webgpu/scripts/setup-wgpu-native.sh
93+
_ss_libdir="$(dirname "${_ss_icd}")"
94+
export Dawn_DIR="${_dawn_dir}/lib64/cmake/Dawn"
95+
export VK_ICD_FILENAMES="${_ss_icd}"
96+
export LD_LIBRARY_PATH="${_ss_libdir}:${LD_LIBRARY_PATH:-}"
97+
export WEBGPU_USING_SWIFTSHADER=1

.ci/scripts/test_backend.sh

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,10 @@ if [[ "$FLOW" == *vulkan* ]]; then
5858
fi
5959

6060
if [[ "$FLOW" == *webgpu* ]]; then
61-
# Setup swiftshader (software Vulkan adapter for GPU-less runners) and wgpu-native,
62-
# which are required to build and run the WebGPU delegate.
61+
# Dawn (Tint) + SwiftShader, the spec-faithful headless WebGPU backend.
6362
source .ci/scripts/setup-webgpu-linux-deps.sh
6463

65-
EXTRA_BUILD_ARGS+=" -DEXECUTORCH_BUILD_WEBGPU=ON"
64+
EXTRA_BUILD_ARGS+=" -DEXECUTORCH_BUILD_WEBGPU=ON -DDawn_DIR=$Dawn_DIR"
6665
fi
6766

6867
if [[ "$FLOW" == *arm* ]]; then
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Test WebGPU Native (Dawn)
2+
3+
# The substantive WebGPU op-coverage gate. The shared operators suite only
4+
# delegates add.Tensor to WebGPU (everything else is CPU fallback), so the real
5+
# Dawn coverage comes from the native test executables (rms_norm, multi-dispatch
6+
# ordering, scratch). This runs them on Dawn (Tint) + SwiftShader, headless, on a
7+
# CPU runner -- separate from _test_backend.yml so that reusable template stays
8+
# untouched.
9+
10+
# Nightly-only for now: this job builds SwiftShader from source (vendored LLVM),
11+
# which is too expensive to run on every PR while the workflow's reliability is
12+
# still being established. Once it has proven stable, re-enable a scoped PR
13+
# trigger with a paths: filter (backends/webgpu/**, the webgpu CI scripts, and
14+
# this file). The pull_request-aware ref/concurrency expressions below are kept
15+
# intentionally so that re-enable is a one-line change.
16+
on:
17+
schedule:
18+
- cron: 0 2 * * *
19+
push:
20+
branches:
21+
- main
22+
- release/*
23+
tags:
24+
- ciflow/nightly/*
25+
workflow_dispatch:
26+
27+
concurrency:
28+
group: ${{ github.workflow }}--${{ github.event.pull_request.number || github.sha }}-${{ github.event_name == 'workflow_dispatch' }}
29+
cancel-in-progress: true
30+
31+
jobs:
32+
test-webgpu-native:
33+
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
34+
with:
35+
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
36+
runner: linux.4xlarge.memory
37+
docker-image: ci-image:executorch-ubuntu-22.04-clang12
38+
submodules: recursive
39+
timeout: 120
40+
script: |
41+
set -eux
42+
43+
# The generic Linux job uses the base conda env, not the image's; activate
44+
# the image env (it has the pinned from-source torch). Mirrors
45+
# test-vulkan-operators-linux in pull.yml.
46+
CONDA_ENV=$(conda env list --json | jq -r ".envs | .[-1]")
47+
conda activate "${CONDA_ENV}"
48+
49+
# Install the python package + runtime deps (the .pte exporters).
50+
PYTHON_EXECUTABLE=python bash .ci/scripts/setup-linux.sh --build-tool cmake
51+
52+
# Vendor Dawn (Tint) + SwiftShader, then build + run the native executables.
53+
source .ci/scripts/setup-webgpu-linux-deps.sh
54+
bash backends/webgpu/scripts/test_webgpu_native_ci.sh

backends/webgpu/CMakeLists.txt

Lines changed: 24 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -54,29 +54,14 @@ target_include_directories(
5454

5555
target_link_libraries(webgpu_backend PRIVATE vulkan_schema executorch_core)
5656

57-
# Native build: link against wgpu-native
58-
set(WGPU_NATIVE_DIR
59-
"${CMAKE_CURRENT_SOURCE_DIR}/third-party/wgpu-native"
60-
CACHE PATH "Path to wgpu-native installation"
61-
)
62-
63-
# Link the shared lib; the static .a carries LLVM bitcode that breaks LTO.
64-
# Suffix resolves per platform: .so on Linux, .dylib on macOS.
65-
set(WGPU_LIB_NAME "libwgpu_native${CMAKE_SHARED_LIBRARY_SUFFIX}")
66-
set(WGPU_LIB "${WGPU_NATIVE_DIR}/lib/${WGPU_LIB_NAME}")
67-
if(NOT EXISTS "${WGPU_LIB}")
68-
message(FATAL_ERROR "wgpu-native not found at ${WGPU_NATIVE_DIR}. "
69-
"Run: bash backends/webgpu/scripts/setup-wgpu-native.sh"
70-
)
71-
endif()
72-
73-
add_library(wgpu_native SHARED IMPORTED)
74-
set_target_properties(wgpu_native PROPERTIES IMPORTED_LOCATION "${WGPU_LIB}")
75-
76-
target_include_directories(
77-
webgpu_backend PUBLIC $<BUILD_INTERFACE:${WGPU_NATIVE_DIR}/include>
78-
)
79-
target_link_libraries(webgpu_backend PRIVATE wgpu_native)
57+
# Native WebGPU backend: Dawn (Tint) + SwiftShader; deps script sets Dawn_DIR.
58+
# Native-only: browser/Emscripten builds use the system webgpu.h and never reach
59+
# this find_package (root CMake gates it via EXECUTORCH_BUILD_WEBGPU).
60+
# dawn::webgpu_dawn's link interface references Threads::Threads.
61+
find_package(Threads REQUIRED)
62+
find_package(Dawn REQUIRED)
63+
set(WEBGPU_GPU_LIB dawn::webgpu_dawn)
64+
target_link_libraries(webgpu_backend PUBLIC ${WEBGPU_GPU_LIB})
8065

8166
if(APPLE)
8267
target_link_libraries(
@@ -100,67 +85,37 @@ install(
10085
DESTINATION ${CMAKE_INSTALL_LIBDIR}
10186
)
10287

103-
# Native test target
104-
if(EXECUTORCH_BUILD_WEBGPU_TEST)
105-
add_executable(webgpu_native_test test/test_webgpu_native.cpp)
106-
107-
target_include_directories(
108-
webgpu_native_test PRIVATE $<BUILD_INTERFACE:${EXECUTORCH_ROOT}/..>
109-
"${WGPU_NATIVE_DIR}/include"
110-
)
111-
112-
target_link_libraries(
113-
webgpu_native_test
114-
PRIVATE webgpu_backend
115-
wgpu_native
116-
executorch_core
117-
extension_module_static
118-
extension_data_loader
119-
extension_tensor
120-
portable_kernels
121-
portable_ops_lib
122-
)
123-
124-
if(APPLE)
125-
target_link_libraries(
126-
webgpu_native_test PRIVATE "-framework Metal" "-framework QuartzCore"
127-
"-framework CoreGraphics"
128-
)
129-
else()
130-
target_link_libraries(webgpu_native_test PRIVATE dl m pthread)
131-
endif()
132-
133-
target_compile_options(webgpu_native_test PRIVATE -fexceptions)
134-
set_property(TARGET webgpu_native_test PROPERTY CXX_STANDARD 17)
135-
136-
add_executable(webgpu_rms_norm_test test/native/test_rms_norm.cpp)
137-
88+
# Native test targets. Helper mirrors backends/vulkan's vulkan_op_test: every
89+
# test executable links the same backend + runtime libs.
90+
function(add_webgpu_native_test test_name test_src)
91+
add_executable(${test_name} ${test_src})
13892
target_include_directories(
139-
webgpu_rms_norm_test PRIVATE $<BUILD_INTERFACE:${EXECUTORCH_ROOT}/..>
140-
"${WGPU_NATIVE_DIR}/include"
93+
${test_name} PRIVATE $<BUILD_INTERFACE:${EXECUTORCH_ROOT}/..>
14194
)
142-
14395
target_link_libraries(
144-
webgpu_rms_norm_test
96+
${test_name}
14597
PRIVATE webgpu_backend
146-
wgpu_native
98+
${WEBGPU_GPU_LIB}
14799
executorch_core
148100
extension_module_static
149101
extension_data_loader
150102
extension_tensor
151103
portable_kernels
152104
portable_ops_lib
153105
)
154-
155106
if(APPLE)
156107
target_link_libraries(
157-
webgpu_rms_norm_test PRIVATE "-framework Metal" "-framework QuartzCore"
158-
"-framework CoreGraphics"
108+
${test_name} PRIVATE "-framework Metal" "-framework QuartzCore"
109+
"-framework CoreGraphics"
159110
)
160111
else()
161-
target_link_libraries(webgpu_rms_norm_test PRIVATE dl m pthread)
112+
target_link_libraries(${test_name} PRIVATE dl m pthread)
162113
endif()
114+
target_compile_options(${test_name} PRIVATE -fexceptions)
115+
set_property(TARGET ${test_name} PROPERTY CXX_STANDARD 17)
116+
endfunction()
163117

164-
target_compile_options(webgpu_rms_norm_test PRIVATE -fexceptions)
165-
set_property(TARGET webgpu_rms_norm_test PROPERTY CXX_STANDARD 17)
118+
if(EXECUTORCH_BUILD_WEBGPU_TEST)
119+
add_webgpu_native_test(webgpu_native_test test/test_webgpu_native.cpp)
120+
add_webgpu_native_test(webgpu_rms_norm_test test/native/test_rms_norm.cpp)
166121
endif()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#pragma once
10+
11+
#include <webgpu/webgpu.h>
12+
13+
#include <cstdint>
14+
15+
namespace executorch::backends::webgpu {
16+
17+
// Caller's instance must enable TimedWaitAny; returns the WaitAny status.
18+
inline WGPUWaitStatus webgpu_wait(WGPUInstance instance, WGPUFuture future) {
19+
WGPUFutureWaitInfo info = {};
20+
info.future = future;
21+
return wgpuInstanceWaitAny(instance, 1, &info, UINT64_MAX);
22+
}
23+
24+
} // namespace executorch::backends::webgpu

0 commit comments

Comments
 (0)