Skip to content

Commit 3b001db

Browse files
committed
[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: 390566138 @exported-using-ghexport Differential Revision: [D107589774](https://our.internmc.facebook.com/intern/diff/D107589774/)
1 parent 8e2e86a commit 3b001db

8 files changed

Lines changed: 305 additions & 49 deletions

File tree

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

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,79 @@
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 upstream's published binary" pattern as setup-wgpu-native.sh.
13+
# * SwiftShader : reuse the prebuilt ALREADY on the ossci-android bucket (the one
14+
# setup-vulkan-linux-deps.sh uses). No new S3 uploads anywhere.
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_ARCHIVE="${SWIFTSHADER_ARCHIVE:-swiftshader-abe07b943-prebuilt.tar.gz}"
1628

17-
_tmp_archive="/tmp/${_swiftshader_archive}"
29+
_dawn_dir="${DAWN_PREBUILT_DIR:-/tmp/dawn-ci}"
30+
_ss_dir=/tmp/swiftshader
1831

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

22-
tar -C "${_swiftshader_dir}" -xzf "${_tmp_archive}"
51+
# The native binaries / pybind lib run INSIDE the CI conda env, whose libstdc++
52+
# predates GLIBCXX_3.4.32 (the Dawn prebuilt's floor) -- the same wall ssjia hit
53+
# for the vulkan op tests. Upgrade the conda runtime libstdc++ so the loaded
54+
# libstdc++.so.6 (conda's, not the system one) satisfies Dawn at run time.
55+
if command -v conda >/dev/null 2>&1; then
56+
conda install -y -c conda-forge "libstdcxx-ng>=14" || true
57+
fi
2358

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-
}
59+
# --- Dawn: official prebuilt from GitHub (no S3) ------------------------------
60+
mkdir -p "${_dawn_dir}"
61+
if [[ ! -d "${_dawn_dir}/lib64/cmake/Dawn" ]]; then
62+
_dawn_tar="/tmp/Dawn-${DAWN_REV}-ubuntu-latest-Release.tar.gz"
63+
curl --silent --show-error --location --fail --retry 3 --retry-all-errors \
64+
--output "${_dawn_tar}" \
65+
"https://github.com/google/dawn/releases/download/${DAWN_TAG}/Dawn-${DAWN_REV}-ubuntu-latest-Release.tar.gz"
66+
echo "${DAWN_SHA256} ${_dawn_tar}" | sha256sum -c -
67+
# archive top dir is Dawn-<rev>-ubuntu-latest-Release/{lib64,include,bin}
68+
tar -C "${_dawn_dir}" --strip-components=1 -xzf "${_dawn_tar}"
69+
fi
70+
71+
# --- SwiftShader: reuse the existing ossci prebuilt (no new upload) -----------
72+
if [[ ! -f "${_ss_dir}/swiftshader/build/Linux/vk_swiftshader_icd.json" ]]; then
73+
_ss_aws=https://ossci-android.s3.amazonaws.com
74+
mkdir -p "${_ss_dir}"
75+
curl --silent --show-error --location --fail --retry 3 --retry-all-errors \
76+
--output "/tmp/${SWIFTSHADER_ARCHIVE}" "${_ss_aws}/${SWIFTSHADER_ARCHIVE}"
77+
tar -C "${_ss_dir}" -xzf "/tmp/${SWIFTSHADER_ARCHIVE}"
78+
fi
2879

29-
install_swiftshader
30-
bash backends/webgpu/scripts/setup-wgpu-native.sh
80+
export Dawn_DIR="${_dawn_dir}/lib64/cmake/Dawn"
81+
export VK_ICD_FILENAMES="${_ss_dir}/swiftshader/build/Linux/vk_swiftshader_icd.json"
82+
export LD_LIBRARY_PATH="${_ss_dir}/swiftshader/build/Linux/:${LD_LIBRARY_PATH:-}"
83+
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: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
on:
11+
schedule:
12+
- cron: 0 2 * * *
13+
push:
14+
branches:
15+
- main
16+
- release/*
17+
tags:
18+
- ciflow/nightly/*
19+
pull_request:
20+
workflow_dispatch:
21+
22+
concurrency:
23+
group: ${{ github.workflow }}--${{ github.event.pull_request.number || github.sha }}-${{ github.event_name == 'workflow_dispatch' }}
24+
cancel-in-progress: true
25+
26+
jobs:
27+
test-webgpu-native:
28+
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
29+
with:
30+
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
31+
runner: linux.4xlarge.memory
32+
docker-image: ci-image:executorch-ubuntu-22.04-clang12
33+
submodules: recursive
34+
timeout: 120
35+
script: |
36+
set -eux
37+
38+
# The generic Linux job uses the base conda env, not the image's; activate
39+
# the image env (it has the pinned from-source torch). Mirrors
40+
# test-vulkan-operators-linux in pull.yml.
41+
CONDA_ENV=$(conda env list --json | jq -r ".envs | .[-1]")
42+
conda activate "${CONDA_ENV}"
43+
44+
# Install the python package + runtime deps (the .pte exporters).
45+
PYTHON_EXECUTABLE=python bash .ci/scripts/setup-linux.sh --build-tool cmake
46+
47+
# Vendor Dawn (Tint) + SwiftShader, then build + run the native executables.
48+
source .ci/scripts/setup-webgpu-linux-deps.sh
49+
bash backends/webgpu/scripts/test_webgpu_native_ci.sh

backends/webgpu/CMakeLists.txt

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -54,29 +54,12 @@ 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+
# dawn::webgpu_dawn's link interface references Threads::Threads.
59+
find_package(Threads REQUIRED)
60+
find_package(Dawn REQUIRED)
61+
set(WEBGPU_GPU_LIB dawn::webgpu_dawn)
62+
target_link_libraries(webgpu_backend PUBLIC dawn::webgpu_dawn)
8063

8164
if(APPLE)
8265
target_link_libraries(
@@ -106,13 +89,12 @@ if(EXECUTORCH_BUILD_WEBGPU_TEST)
10689

10790
target_include_directories(
10891
webgpu_native_test PRIVATE $<BUILD_INTERFACE:${EXECUTORCH_ROOT}/..>
109-
"${WGPU_NATIVE_DIR}/include"
11092
)
11193

11294
target_link_libraries(
11395
webgpu_native_test
11496
PRIVATE webgpu_backend
115-
wgpu_native
97+
${WEBGPU_GPU_LIB}
11698
executorch_core
11799
extension_module_static
118100
extension_data_loader
@@ -137,13 +119,12 @@ if(EXECUTORCH_BUILD_WEBGPU_TEST)
137119

138120
target_include_directories(
139121
webgpu_rms_norm_test PRIVATE $<BUILD_INTERFACE:${EXECUTORCH_ROOT}/..>
140-
"${WGPU_NATIVE_DIR}/include"
141122
)
142123

143124
target_link_libraries(
144125
webgpu_rms_norm_test
145126
PRIVATE webgpu_backend
146-
wgpu_native
127+
${WEBGPU_GPU_LIB}
147128
executorch_core
148129
extension_module_static
149130
extension_data_loader
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
#if defined(__EMSCRIPTEN__)
14+
#include <emscripten/emscripten.h>
15+
#else
16+
#include <chrono>
17+
#include <thread>
18+
#endif
19+
20+
namespace executorch::backends::webgpu {
21+
22+
// Drive pending callbacks; callers loop until their done flag is set. The
23+
// native (Dawn) build pumps the instance event queue then briefly yields the
24+
// CPU so the caller's wait loop does not busy-spin a core at 100%
25+
// (wgpuInstanceProcessEvents is non-blocking); the browser yields to the JS
26+
// event loop.
27+
inline void webgpu_poll(WGPUInstance instance, WGPUDevice device) {
28+
#if defined(__EMSCRIPTEN__)
29+
(void)instance;
30+
(void)device;
31+
emscripten_sleep(0);
32+
#else
33+
(void)device;
34+
wgpuInstanceProcessEvents(instance);
35+
std::this_thread::sleep_for(std::chrono::microseconds(50));
36+
#endif
37+
}
38+
39+
} // namespace executorch::backends::webgpu

backends/webgpu/runtime/WebGPUDevice.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,11 @@ WebGPUContext create_webgpu_context() {
101101
adapter_cb.callback = on_adapter_request;
102102
adapter_cb.userdata1 = &adapter_result;
103103

104-
wgpuInstanceRequestAdapter(ctx.instance, nullptr, adapter_cb);
104+
// No bundled fallback adapter in the release; SwiftShader via the Vulkan ICD.
105+
WGPURequestAdapterOptions adapter_opts = {};
106+
adapter_opts.backendType = WGPUBackendType_Vulkan;
107+
adapter_opts.forceFallbackAdapter = false;
108+
wgpuInstanceRequestAdapter(ctx.instance, &adapter_opts, adapter_cb);
105109
while (!adapter_result.done) {
106110
wgpuInstanceProcessEvents(ctx.instance);
107111
}

backends/webgpu/runtime/WebGPUGraph.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
#include <executorch/backends/vulkan/serialization/schema_generated.h>
1313
#include <executorch/runtime/core/named_data_map.h>
1414

15+
#include <executorch/backends/webgpu/runtime/WebGPUCompat.h>
1516
#include <executorch/backends/webgpu/runtime/WebGPUDevice.h>
16-
#include <webgpu/wgpu.h>
1717

1818
#include <cstring>
1919
#include <stdexcept>
@@ -510,7 +510,17 @@ void WebGPUGraph::copy_outputs(std::vector<std::pair<void*, size_t>>& outputs) {
510510
cb_info);
511511
}
512512

513-
wgpuDevicePoll(device_, true, nullptr);
513+
bool all_mapped = false;
514+
while (!all_mapped) {
515+
webgpu_poll(instance_, device_);
516+
all_mapped = true;
517+
for (size_t i = 0; i < count; i++) {
518+
if (outputs[i].second != 0 && !cb_data[i].done) {
519+
all_mapped = false;
520+
break;
521+
}
522+
}
523+
}
514524

515525
for (size_t i = 0; i < count; i++) {
516526
if (outputs[i].second == 0) {

0 commit comments

Comments
 (0)