Skip to content

Commit ff03c50

Browse files
committed
[ExecuTorch][WebGPU] Select emdawnwebgpu for Emscripten builds
Pull Request resolved: #21133 Building the WebGPU backend for the browser previously required a manual source-tree patch to pick the right WebGPU implementation. This makes the CMake configuration select the emdawnwebgpu port for Emscripten builds while native builds keep Dawn and their platform libraries, so the production kernel stack builds for the browser unpatched. Build-system integration only; no model or kernel behavior changes. Key changes: - CMakeLists.txt: choose emdawnwebgpu under Emscripten, Dawn otherwise. - test/test_cmake_configuration.py: contract test asserting the selection. ghstack-source-id: 411961450 @exported-using-ghexport Differential Revision: [D113171750](https://our.internmc.facebook.com/intern/diff/D113171750/)
1 parent c4b7230 commit ff03c50

2 files changed

Lines changed: 107 additions & 15 deletions

File tree

backends/webgpu/CMakeLists.txt

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,22 +64,27 @@ target_include_directories(
6464

6565
target_link_libraries(webgpu_backend PRIVATE vulkan_schema executorch_core)
6666

67-
# Native WebGPU backend: Dawn (Tint) + SwiftShader; deps script sets Dawn_DIR.
68-
# Native-only: browser/Emscripten builds use the system webgpu.h and never reach
69-
# this find_package (root CMake gates it via EXECUTORCH_BUILD_WEBGPU).
70-
# dawn::webgpu_dawn's link interface references Threads::Threads.
71-
find_package(Threads REQUIRED)
72-
find_package(Dawn REQUIRED)
73-
set(WEBGPU_GPU_LIB dawn::webgpu_dawn)
74-
target_link_libraries(webgpu_backend PUBLIC ${WEBGPU_GPU_LIB})
75-
76-
if(APPLE)
77-
target_link_libraries(
78-
webgpu_backend PRIVATE "-framework Metal" "-framework QuartzCore"
79-
"-framework CoreGraphics" "-framework Foundation"
80-
)
67+
# WASM gets its WebGPU implementation from emdawnwebgpu at executable link time.
68+
# Native builds link Dawn (Tint) and the platform GPU libraries.
69+
if(EMSCRIPTEN)
70+
target_compile_options(webgpu_backend PUBLIC "--use-port=emdawnwebgpu")
71+
# --use-port is also required at link time: the link step pulls in the port's
72+
# headers, JS glue, and libraries for consumers that link webgpu_backend.
73+
target_link_options(webgpu_backend PUBLIC "--use-port=emdawnwebgpu")
8174
else()
82-
target_link_libraries(webgpu_backend PRIVATE dl m pthread)
75+
find_package(Threads REQUIRED)
76+
find_package(Dawn REQUIRED)
77+
set(WEBGPU_GPU_LIB dawn::webgpu_dawn)
78+
target_link_libraries(webgpu_backend PUBLIC ${WEBGPU_GPU_LIB})
79+
80+
if(APPLE)
81+
target_link_libraries(
82+
webgpu_backend PRIVATE "-framework Metal" "-framework QuartzCore"
83+
"-framework CoreGraphics" "-framework Foundation"
84+
)
85+
else()
86+
target_link_libraries(webgpu_backend PRIVATE dl m pthread)
87+
endif()
8388
endif()
8489

8590
target_compile_options(webgpu_backend PRIVATE -fexceptions)
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
import pathlib
8+
import re
9+
import unittest
10+
11+
12+
def _conditional_branches(source: str, condition: str) -> tuple[str, str]:
13+
lines = source.splitlines()
14+
condition_pattern = re.compile(
15+
rf"^\s*if\s*\(\s*{re.escape(condition)}\s*\)\s*$",
16+
re.IGNORECASE,
17+
)
18+
command_pattern = re.compile(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(")
19+
start = next(
20+
(index for index, line in enumerate(lines) if condition_pattern.match(line)),
21+
None,
22+
)
23+
if start is None:
24+
raise AssertionError(f"if({condition}) branch not found")
25+
26+
depth = 1
27+
else_index = None
28+
for index in range(start + 1, len(lines)):
29+
match = command_pattern.match(lines[index])
30+
if match is None:
31+
continue
32+
command = match.group(1).lower()
33+
if command == "if":
34+
depth += 1
35+
elif command == "endif":
36+
depth -= 1
37+
if depth == 0:
38+
if else_index is None:
39+
raise AssertionError(f"if({condition}) has no else() branch")
40+
return (
41+
"\n".join(lines[start + 1 : else_index]),
42+
"\n".join(lines[else_index + 1 : index]),
43+
)
44+
elif command == "else" and depth == 1:
45+
if else_index is not None:
46+
raise AssertionError(f"if({condition}) has multiple else() branches")
47+
else_index = index
48+
raise AssertionError(f"if({condition}) has no matching endif()")
49+
50+
51+
class TestCMakeConfiguration(unittest.TestCase):
52+
def test_branch_parser_keeps_nested_conditionals_in_native_branch(self) -> None:
53+
source = """
54+
if ( EMSCRIPTEN )
55+
wasm_command()
56+
else()
57+
if(APPLE)
58+
apple_command()
59+
else()
60+
linux_command()
61+
endif()
62+
endif()
63+
"""
64+
wasm_branch, native_branch = _conditional_branches(source, "EMSCRIPTEN")
65+
66+
self.assertIn("wasm_command()", wasm_branch)
67+
self.assertIn("apple_command()", native_branch)
68+
self.assertIn("linux_command()", native_branch)
69+
70+
def test_emscripten_uses_port_instead_of_native_dawn(self) -> None:
71+
cmake = pathlib.Path(__file__).parents[1] / "CMakeLists.txt"
72+
wasm_branch, native_branch = _conditional_branches(
73+
cmake.read_text(), "EMSCRIPTEN"
74+
)
75+
port_flag = r'"--use-port=emdawnwebgpu"'
76+
77+
self.assertRegex(
78+
wasm_branch,
79+
rf"target_compile_options\s*\(\s*webgpu_backend\s+PUBLIC\s+{port_flag}\s*\)",
80+
)
81+
self.assertRegex(
82+
wasm_branch,
83+
rf"target_link_options\s*\(\s*webgpu_backend\s+PUBLIC\s+{port_flag}\s*\)",
84+
)
85+
self.assertNotIn("find_package(Dawn", wasm_branch)
86+
self.assertNotIn("--use-port=emdawnwebgpu", native_branch)
87+
self.assertRegex(native_branch, r"find_package\s*\(\s*Dawn\s+REQUIRED\s*\)")

0 commit comments

Comments
 (0)