Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1f620f9
[ExecuTorch][WebGPU] Shared pipeline + dispatch-grid + vec4-align hel…
JCNTH Jul 23, 2026
a95b422
[ExecuTorch][WebGPU] Add optimized gelu op
JCNTH Jul 23, 2026
a90388e
[ExecuTorch][WebGPU] gelu op tests
JCNTH Jul 23, 2026
e1de13c
[ExecuTorch][WebGPU] Add optimized layer_norm op
JCNTH Jul 23, 2026
908e52c
[ExecuTorch][WebGPU] layer_norm op tests
JCNTH Jul 23, 2026
7ebf5fd
[ExecuTorch][WebGPU] linear_fp32 op tests
JCNTH Jul 23, 2026
97d4c91
[ExecuTorch][WebGPU] Add optimized conv2d op
JCNTH Jul 23, 2026
805a52c
[ExecuTorch][WebGPU] conv2d op tests
JCNTH Jul 23, 2026
82b20ff
[ExecuTorch][WebGPU] Add optimized SDPA op
JCNTH Jul 23, 2026
5a984c6
[ExecuTorch][WebGPU] SDPA op tests
JCNTH Jul 23, 2026
1fe89ed
[ExecuTorch][WebGPU] embedding op tests
JCNTH Jul 23, 2026
2535a2f
[ExecuTorch][WebGPU] Add optimized addmm op
JCNTH Jul 23, 2026
9bfc36b
[ExecuTorch][WebGPU] addmm op tests
JCNTH Jul 23, 2026
dbca29f
[ExecuTorch][WebGPU] Add constant_pad_nd op
JCNTH Jul 23, 2026
61edf42
[ExecuTorch][WebGPU] constant_pad_nd op tests
JCNTH Jul 23, 2026
5f45764
[ExecuTorch][WebGPU] Add upsample_nearest2d op
JCNTH Jul 23, 2026
0cd69f3
[ExecuTorch][WebGPU] upsample_nearest2d op tests
JCNTH Jul 23, 2026
e15feb7
[ExecuTorch][WebGPU] Add max_pool2d op
JCNTH Jul 23, 2026
e498661
[ExecuTorch][WebGPU] max_pool2d op tests
JCNTH Jul 23, 2026
2986c67
[ExecuTorch][WebGPU] Add relu op (shared unary handler; sigmoid adopt…
JCNTH Jul 23, 2026
ab6eec5
[ExecuTorch][WebGPU] relu op tests
JCNTH Jul 23, 2026
164f549
[ExecuTorch][WebGPU] slice_copy Double-arg scalar handling + view_cop…
JCNTH Jul 23, 2026
ad334e2
[ExecuTorch][WebGPU] slice_copy native golden test
JCNTH Jul 23, 2026
2bfd028
[ExecuTorch][WebGPU] et_vk.sdpa: shape-route QK to a per-entry kernel…
JCNTH Jul 23, 2026
a57413b
[ExecuTorch][WebGPU] et_vk.sdpa channel-attention tests
JCNTH Jul 23, 2026
0a41420
[ExecuTorch][WebGPU] et_vk conv2d: route standard (groups==1) conv th…
JCNTH Jul 23, 2026
a506d03
[ExecuTorch][WebGPU] et_vk conv2d im2col-GEMM tests
JCNTH Jul 23, 2026
5ff79aa
[ExecuTorch][WebGPU] et_vk.apply_rotary_emb_hf: HuggingFace rotate-ha…
JCNTH Jul 23, 2026
a14237b
[ExecuTorch][WebGPU] apply_rotary_emb_hf op tests
JCNTH Jul 23, 2026
4d5e1d9
[ExecuTorch][WebGPU] Tests for aten.sub.Tensor broadcast
JCNTH Jul 23, 2026
c740ba7
[ExecuTorch][WebGPU] Add aten._to_copy.default with int<->float numer…
JCNTH Jul 23, 2026
35e1e4c
[ExecuTorch][WebGPU] Tests for aten._to_copy.default int<->float convert
JCNTH Jul 23, 2026
51f3064
[ExecuTorch][WebGPU] Add leaky_relu op
JCNTH Jul 23, 2026
3696742
[ExecuTorch][WebGPU] leaky_relu op tests
JCNTH Jul 23, 2026
4c7e000
[ExecuTorch][WebGPU] Add upsample_bilinear2d op
JCNTH Jul 23, 2026
7d54ee1
[ExecuTorch][WebGPU] upsample_bilinear2d op tests
JCNTH Jul 23, 2026
30abd20
[ExecuTorch][WebGPU] Add batch_norm op
JCNTH Jul 23, 2026
6c89f5f
[ExecuTorch][WebGPU] batch_norm op tests
JCNTH Jul 23, 2026
707d037
[ExecuTorch][WebGPU] Add split_with_sizes_copy op
JCNTH Jul 23, 2026
b1911f9
[ExecuTorch][WebGPU] split_with_sizes_copy op tests
JCNTH Jul 23, 2026
044f3ab
[ExecuTorch][WebGPU] Migrate 14 single-pipeline ops to make_compute_p…
JCNTH Jul 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions backends/webgpu/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ function(add_webgpu_native_test test_name test_src)
target_link_libraries(
${test_name}
PRIVATE webgpu_backend
vulkan_schema
${WEBGPU_GPU_LIB}
executorch_core
extension_module_static
Expand Down Expand Up @@ -153,10 +154,11 @@ if(EXECUTORCH_BUILD_WEBGPU_TEST)
)

# Device-free util unit test: no backend/Dawn link (pure manifest/tolerance
# helpers), so it does NOT use the native-test helper.
# + dispatch-grid-math helpers), so it does NOT use the native-test helper.
add_executable(
webgpu_op_test_util_test test/op_tests/test_driver_util.cpp
test/op_tests/driver_util.cpp
webgpu_op_test_util_test
test/op_tests/test_driver_util.cpp test/op_tests/driver_util.cpp
test/native/test_webgpu_utils.cpp
)
target_include_directories(
webgpu_op_test_util_test
Expand Down
108 changes: 108 additions & 0 deletions backends/webgpu/runtime/WebGPUDispatchMath.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

#pragma once

// Pure dispatch-grid math with zero WebGPU/Dawn dependency, so it is
// unit-testable without a WGPUDevice (split out of WebGPUUtils.h, which
// requires <webgpu/webgpu.h> for its device-facing functions).

#include <cmath>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <vector>

namespace executorch::backends::webgpu::utils {

// Ceiling division for non-negative integers (mirrors Vulkan's utils::div_up).
template <typename T>
inline T div_up(T a, T b) {
return (a + b - 1) / b;
}

// Product of a tensor's dims; the same accumulation was duplicated per-op.
inline uint64_t numel(const std::vector<int64_t>& dims) {
uint64_t n = 1;
for (int64_t d : dims) {
if (d < 0) {
throw std::runtime_error("numel: negative dimension");
}
uint64_t ud = static_cast<uint64_t>(d);
if (ud != 0 && n > UINT64_MAX / ud) {
throw std::runtime_error("numel: element count overflow");
}
n *= ud;
}
return n;
}

// Broadcasts a 1- or 2-element int list to (h, w); PyTorch's convention for
// kernel_size/stride/padding/dilation args. Was duplicated as a local `hw`
// lambda in conv2d/conv_transpose2d/max_pool2d.
inline void parse_hw(
const std::vector<int64_t>& v,
uint32_t& h,
uint32_t& w,
const char* op_name,
const char* arg_name) {
if (v.size() == 1) {
h = w = static_cast<uint32_t>(v[0]);
} else if (v.size() == 2) {
h = static_cast<uint32_t>(v[0]);
w = static_cast<uint32_t>(v[1]);
} else {
throw std::runtime_error(
std::string("WebGPU ") + op_name + ": " + arg_name +
" must be 1 or 2 elements");
}
}

// Adaptive 1D->2D dispatch grid. `count_x`/`count_y` are the dispatch dims;
// `stride_x` (= count_x * wg_size) lets the shader decode a flat index as
// `gid.y * stride_x + gid.x`. Used by ops whose element count can exceed the
// 65535 per-dimension ceiling that compute_1d_workgroup_count throws on.
struct DispatchGrid {
uint32_t wg_size;
uint32_t count_x;
uint32_t count_y;
uint32_t stride_x;
};

// Given the workgroup count needed (1D) and the device's per-dimension
// dispatch-count ceiling, compute a near-square 2D grid rather than
// {max_dim, div_up(total, max_dim)} — maxing one dim pads the other with
// mostly-idle workgroups (up to ~2x the needed launch) when total isn't a
// clean multiple of max_dim.
inline DispatchGrid compute_dispatch_grid_from_limits(
uint32_t total, // workgroups needed (1D)
uint32_t wg_size,
uint32_t max_dim,
const char* op_name) {
DispatchGrid g;
g.wg_size = wg_size;
if (total <= max_dim) {
g.count_x = total;
g.count_y = 1;
} else {
uint32_t sq =
static_cast<uint32_t>(std::ceil(std::sqrt(static_cast<double>(total))));
g.count_x = sq < max_dim ? sq : max_dim;
g.count_y = div_up(total, g.count_x);
if (g.count_y >
max_dim) { // > max_dim^2 * wg threads — astronomically large
throw std::runtime_error(
std::string("WebGPU ") + op_name +
": dispatch exceeds 2D grid capacity");
}
}
g.stride_x = g.count_x * g.wg_size;
return g;
}

} // namespace executorch::backends::webgpu::utils
9 changes: 9 additions & 0 deletions backends/webgpu/runtime/WebGPUGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ void WebGPUGraph::build(
value_lists_.resize(num_vals);
doubles_.resize(num_vals, 0.0);
bools_.resize(num_vals, false);
strings_.resize(num_vals);

// Pre-scan the op chain: a constant may be DEFERRED (no eager GPU buffer; the
// prepack node materializes it once) only if it is a prepack source AND never
Expand Down Expand Up @@ -625,6 +626,14 @@ void WebGPUGraph::build(
bools_[i] = val->value_as_Bool()->bool_val();
break;
}
case vkgraph::GraphTypes::String: {
value_types_[i] = ValueType::String;
const auto* sv = val->value_as_String()->string_val();
if (sv) {
strings_[i] = sv->str();
}
break;
}
case vkgraph::GraphTypes::SymInt: {
// Live scalar: small Uniform buffer the CPU rewrites per execute.
value_types_[i] = ValueType::SymInt;
Expand Down
19 changes: 19 additions & 0 deletions backends/webgpu/runtime/WebGPUGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ class WebGPUGraph {
bool get_bool(int id) const {
return bools_[id];
}
// String value (e.g. gelu's `approximate` kwarg).
const std::string& get_string(int id) const {
return strings_[id];
}

// Live-scalar (SymInt) API; mirrors the Vulkan SymInt/ParamsBuffer UBO.
// set_symint writes the buffer + marks dirty only if the value changed.
Expand Down Expand Up @@ -240,6 +244,20 @@ class WebGPUGraph {
return dispatches_.size() - 1;
}

// 2D sibling of add_dispatch (sets workgroup_count_y); returns the index.
size_t add_dispatch_2d(
WGPUComputePipeline pipeline,
WGPUBindGroup bind_group,
uint32_t count_x,
uint32_t count_y) {
WebGPUDispatch d;
d.pipeline = pipeline;
d.bind_group = bind_group;
d.workgroup_count_x = count_x;
d.workgroup_count_y = count_y;
return add_dispatch(d);
}

// In-graph buffer-to-buffer DMA (e.g. flat copy); returns the dispatch index.
size_t add_buffer_copy(WGPUBuffer src, WGPUBuffer dst, size_t nbytes) {
WebGPUDispatch d;
Expand Down Expand Up @@ -376,6 +394,7 @@ class WebGPUGraph {
std::vector<std::vector<int>> value_lists_;
std::vector<double> doubles_;
std::vector<bool> bools_;
std::vector<std::string> strings_;

// SymInt (live scalar): id -> {live Uniform buffer, current value}, sparse.
struct SymIntSlot {
Expand Down
Loading
Loading