Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion .github/workflows/windows_webgpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ jobs:
--cmake_generator "Visual Studio 17 2022" `
--enable_onnx_tests `
--use_webgpu shared_lib `
--wgsl_template static `
--use_vcpkg --use_vcpkg_ms_internal_asset_cache `
--cmake_extra_defines onnxruntime_BUILD_UNIT_TESTS=ON onnxruntime_ENABLE_DAWN_BACKEND_D3D12=1 onnxruntime_ENABLE_DAWN_BACKEND_VULKAN=1 `
--disable_rtti `
Expand Down
2 changes: 2 additions & 0 deletions .lintrunner.toml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ exclude_patterns = [
'winml/lib/Api.Image/shaders/**', # Contains data chunks
'onnxruntime/contrib_ops/cuda/bert/flash_attention/flash_fwd_launch_template.h', # Bool Switches hang Clang
'onnxruntime/core/providers/coreml/mlprogram_test_scripts/**', # test scripts only
'tools/python/wgsl_template/test/testcases/**', # Generated golden fixtures; must match tool output byte-for-byte
'tools/python/wgsl_template/test/in_tree_golden/**', # Generated golden snapshots; must match tool output byte-for-byte
]
command = [
'python',
Expand Down
1 change: 0 additions & 1 deletion cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ option(onnxruntime_USE_XNNPACK "Build with XNNPACK support. Provides an alternat
option(onnxruntime_USE_WEBNN "Build with WebNN support. Enable hardware acceleration in web browsers." OFF)
option(onnxruntime_USE_WEBGPU "Build with WebGPU support. Enable WebGPU via C/C++ interface." OFF)
option(onnxruntime_USE_EP_API_ADAPTERS "Build EP (e.g. WebGPU) as a plugin EP shared library using EP API adapters" OFF)
option(onnxruntime_WGSL_TEMPLATE "Specify the code generator for WGSL template. Default is static." "static")
option(onnxruntime_USE_EXTERNAL_DAWN "Build with treating Dawn as external dependency. Will not link Dawn at build time." OFF)
option(onnxruntime_CUSTOM_DAWN_SRC_PATH "Path to custom Dawn src dir.")
option(onnxruntime_BUILD_DAWN_SHARED_LIBRARY "Build Dawn as a shared library" OFF)
Expand Down
1 change: 1 addition & 0 deletions cmake/deps.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,6 @@ kleidiai;https://github.com/ARM-software/kleidiai/archive/refs/tags/v1.20.0.tar.
# kleidiai-qmx is pinned to a specific commit as there are no tagged releases. When an appropriate tagged release becomes available,
# this entry will be updated to use refs/tags/<version> instead of the raw commit hash.
kleidiai-qmx;https://github.com/qualcomm/kleidiai/archive/2f10c9a8d32f81ffeeb6d4885a29cc35d2b0da87.zip;5e855730a2d69057a569f43dd7532db3b2d2a05c
# TODO(danielsongmicrosoft): Remove duktape once the dynamic WGSL generator is deleted; it is no longer used by any active build path.
duktape;https://github.com/svaarala/duktape/releases/download/v2.7.0/duktape-2.7.0.tar.xz;8200c8e417dbab7adcc12c4dbdef7651cfc55794
vulkan_headers;https://codeload.github.com/KhronosGroup/Vulkan-Headers/tar.gz/refs/tags/v1.4.344;57bc528ef7c4a3f7bfbb59e64a187e3734bd29d8
3 changes: 3 additions & 0 deletions cmake/external/onnxruntime_external_deps.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,9 @@ if (onnxruntime_USE_WEBGPU)
list(APPEND onnxruntime_EXTERNAL_LIBRARIES webgpu_glfw glfw)
endif()

# TODO: Remove duktape once the dynamic WGSL generator is deleted. It is only
# needed by that generator, which is no longer wired up, so
# onnxruntime_WGSL_TEMPLATE is unset and this block never runs.
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten" AND onnxruntime_WGSL_TEMPLATE STREQUAL "dynamic")
if(onnxruntime_USE_VCPKG)
find_package(unofficial-duktape CONFIG REQUIRED)
Expand Down
97 changes: 33 additions & 64 deletions cmake/onnxruntime_providers_webgpu.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,6 @@
if (onnxruntime_ENABLE_WEBASSEMBLY_THREADS)
add_definitions(-DENABLE_WEBASSEMBLY_THREADS=1)
endif()
if (onnxruntime_WGSL_TEMPLATE STREQUAL "dynamic")
if (onnxruntime_DISABLE_EXCEPTIONS)
message(FATAL_ERROR "Dynamic WGSL template generation requires exception handling to be enabled.")
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
message(FATAL_ERROR "Dynamic WGSL template generation is not supported when targeting WebAssembly.")
endif()
add_definitions(-DORT_WGSL_TEMPLATE_DYNAMIC=1)
elseif (NOT onnxruntime_WGSL_TEMPLATE STREQUAL "static")
message(FATAL_ERROR "Unsupported value for onnxruntime_WGSL_TEMPLATE: ${onnxruntime_WGSL_TEMPLATE}. Supported values are 'static' or 'dynamic'.")
endif()

file(GLOB_RECURSE onnxruntime_providers_webgpu_cc_srcs CONFIGURE_DEPENDS
"${ONNXRUNTIME_ROOT}/core/providers/webgpu/*.h"
Expand Down Expand Up @@ -271,36 +260,21 @@

add_dependencies(onnxruntime_providers_webgpu onnx ${onnxruntime_EXTERNAL_DEPENDENCIES})

if (onnxruntime_WGSL_TEMPLATE)
# Define the WGSL templates directory and output directory
set(WGSL_TEMPLATES_DIR "${ONNXRUNTIME_ROOT}/core/providers/webgpu/wgsl_templates")
if (onnxruntime_USE_WEBGPU)
# The Python wgsl-gen tool lives at the repo level under tools/python.
set(WGSL_GEN_PYTHON_DIR "${REPO_ROOT}/tools/python")
set(WGSL_GENERATED_ROOT "${CMAKE_CURRENT_BINARY_DIR}/wgsl_generated")

# Include the Node.js helper for finding and validating Node.js and NPM
include(node_helper.cmake)
# The top-level find_package(Python ...) in cmake/CMakeLists.txt is gated
# on BUILD_SHARED_LIB OR ENABLE_PYTHON, so Python_EXECUTABLE is not always
# set in WebGPU-enabled builds (e.g. the WASM lane). Find Python ourselves
# so this branch works in every config.
find_package(Python 3.10 COMPONENTS Interpreter REQUIRED)

# Install npm dependencies
add_custom_command(
OUTPUT "${WGSL_TEMPLATES_DIR}/node_modules/.install_complete"
COMMAND ${NPM_CLI} ci
COMMAND ${CMAKE_COMMAND} -E touch "${WGSL_TEMPLATES_DIR}/node_modules/.install_complete"
DEPENDS "${WGSL_TEMPLATES_DIR}/package.json" "${WGSL_TEMPLATES_DIR}/package-lock.json"
WORKING_DIRECTORY ${WGSL_TEMPLATES_DIR}
COMMENT "Installing npm dependencies for WGSL template generation"
VERBATIM
)

if (onnxruntime_WGSL_TEMPLATE STREQUAL "static")
set(WGSL_GENERATED_DIR "${WGSL_GENERATED_ROOT}/wgsl_template_gen")
# set(WGSL_GEN_OUTPUTS "${WGSL_GENERATED_DIR}/index.h" "${WGSL_GENERATED_DIR}/index_impl.h")
# Define the output files that will be generated
set(WGSL_GENERATED_INDEX_H "${WGSL_GENERATED_DIR}/index.h")
set(WGSL_GENERATED_INDEX_IMPL_H "${WGSL_GENERATED_DIR}/index_impl.h")
elseif(onnxruntime_WGSL_TEMPLATE STREQUAL "dynamic")
set(WGSL_GENERATED_DIR "${WGSL_GENERATED_ROOT}/dynamic")
# set(WGSL_GEN_OUTPUTS "${WGSL_GENERATED_DIR}/templates.js")
set(WGSL_GENERATED_TEMPLATES_JS "${WGSL_GENERATED_DIR}/templates.js")
endif()
set(WGSL_GENERATED_DIR "${WGSL_GENERATED_ROOT}/wgsl_template_gen")
# Define the output files that will be generated
set(WGSL_GENERATED_INDEX_H "${WGSL_GENERATED_DIR}/index.h")
set(WGSL_GENERATED_INDEX_IMPL_H "${WGSL_GENERATED_DIR}/index_impl.h")

# Ensure the output directory exists
file(MAKE_DIRECTORY ${WGSL_GENERATED_DIR})
Expand All @@ -324,47 +298,42 @@
list(APPEND WGSL_GEN_OPTIONS "-i" "${ONNXRUNTIME_ROOT}/contrib_ops/webgpu")
endif()

if (onnxruntime_WGSL_TEMPLATE STREQUAL "static")
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
list(APPEND WGSL_GEN_OPTIONS "--generator" "static-cpp-literal")
else()
list(APPEND WGSL_GEN_OPTIONS "--generator" "static-cpp")
endif()
elseif(onnxruntime_WGSL_TEMPLATE STREQUAL "dynamic")
list(APPEND WGSL_GEN_OPTIONS "--generator" "dynamic")
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
list(APPEND WGSL_GEN_OPTIONS "--generator" "static-cpp-literal")
else()
list(APPEND WGSL_GEN_OPTIONS "--generator" "static-cpp")
endif()

# Generate WGSL templates
add_custom_command(
OUTPUT ${WGSL_GENERATED_INDEX_H} ${WGSL_GENERATED_INDEX_IMPL_H} ${WGSL_GENERATED_TEMPLATES_JS}
COMMAND ${NPM_CLI} run gen -- ${WGSL_GEN_OPTIONS}
DEPENDS "${WGSL_TEMPLATES_DIR}/node_modules/.install_complete" ${WGSL_TEMPLATE_FILES}
WORKING_DIRECTORY ${WGSL_TEMPLATES_DIR}
COMMENT "Generating WGSL templates from *.wgsl.template files"
OUTPUT ${WGSL_GENERATED_INDEX_H} ${WGSL_GENERATED_INDEX_IMPL_H}
COMMAND ${Python_EXECUTABLE} "${WGSL_GEN_PYTHON_DIR}/wgsl_gen.py" ${WGSL_GEN_OPTIONS}
DEPENDS ${WGSL_TEMPLATE_FILES}
WORKING_DIRECTORY ${WGSL_GEN_PYTHON_DIR}
COMMENT "Generating WGSL templates from *.wgsl.template files (Python)"
COMMAND_EXPAND_LISTS
VERBATIM
)

# Create a target to represent the generation step
add_custom_target(onnxruntime_webgpu_wgsl_generation
DEPENDS ${WGSL_GENERATED_INDEX_H} ${WGSL_GENERATED_INDEX_IMPL_H} ${WGSL_GENERATED_TEMPLATES_JS}
DEPENDS ${WGSL_GENERATED_INDEX_H} ${WGSL_GENERATED_INDEX_IMPL_H}
SOURCES ${WGSL_TEMPLATE_FILES}
)

if (onnxruntime_WGSL_TEMPLATE STREQUAL "static")
# Add the generated directory to include paths
target_include_directories(onnxruntime_providers_webgpu PRIVATE ${WGSL_GENERATED_ROOT})
elseif(onnxruntime_WGSL_TEMPLATE STREQUAL "dynamic")
target_link_libraries(onnxruntime_providers_webgpu PRIVATE duktape_static)
onnxruntime_add_include_to_target(onnxruntime_providers_webgpu duktape_static)

# Define the path to the generated templates.js file
target_compile_definitions(onnxruntime_providers_webgpu PRIVATE
"ORT_WGSL_TEMPLATES_JS_PATH=\"${WGSL_GENERATED_TEMPLATES_JS}\"")
endif()
# Add the generated directory to include paths
target_include_directories(onnxruntime_providers_webgpu PRIVATE ${WGSL_GENERATED_ROOT})

# Make sure generation happens before building the provider
add_dependencies(onnxruntime_providers_webgpu onnxruntime_webgpu_wgsl_generation)

# Wire the Python wgsl_template test suite into ctest.
if (BUILD_TESTING)
add_test(
NAME wgsl_template_python_tests
COMMAND ${Python_EXECUTABLE} "${WGSL_GEN_PYTHON_DIR}/wgsl_template/test/run_tests.py"
WORKING_DIRECTORY ${WGSL_GEN_PYTHON_DIR}
)
endif()
endif()

if (NOT onnxruntime_BUILD_SHARED_LIB)
Expand Down

This file was deleted.

125 changes: 60 additions & 65 deletions onnxruntime/core/providers/webgpu/wgsl_templates/README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
# WebGPU WGSL Templates

This directory contains the infrastructure, scripts, and documentation for the WGSL template system used by the ONNX Runtime WebGPU Execution Provider (EP). The template system enables the generation of optimized WGSL shaders at build time or runtime, with parameterization and reusability across different operators.
This directory contains the infrastructure and documentation for the WGSL
template system used by the ONNX Runtime WebGPU Execution Provider (EP). The
template system generates optimized WGSL shaders at build time, with
parameterization and reusability across different operators.

For detailed information about the underlying template engine, see [wgsl-template](https://github.com/fs-eire/wgsl-template).
The template engine is implemented in Python and lives at
[`tools/python/wgsl_gen.py`](../../../../../tools/python/wgsl_gen.py) (with the
supporting package at
[`tools/python/wgsl_template/`](../../../../../tools/python/wgsl_template/)).
It requires only a Python 3.10+ interpreter.

## Overview

The WGSL template system provides a flexible framework for generating WebGPU shaders for ONNX Runtime operators. Instead of writing static shader code, developers can create parameterized templates that adapt to different input configurations, data types, and optimization requirements.
The WGSL template system provides a flexible framework for generating WebGPU
shaders for ONNX Runtime operators. Instead of writing static shader code,
developers can create parameterized templates that adapt to different input
configurations, data types, and optimization requirements.

**Key Benefits:**

- **Code Reusability**: Share common shader patterns across multiple operators
- **Type Safety**: Template parameters are validated at build time (static mode)
- **Type Safety**: Template parameters are validated at build time
- **Performance**: Generated shaders are optimized for specific use cases
- **Maintainability**: Centralized shader logic with clear parameterization

## Terms

- **WGSL**: The **W**eb**G**PU **S**hader **L**anguage - the shading language for WebGPU
- **WGSL Template File**: A file with `.wgsl.template` extension containing WGSL shader code with template syntax and utilities
- **WGSL-Template**: A JavaScript library developed specifically for generating WGSL code from template files
- **WGSL Template File**: A file with the `.wgsl.template` extension containing WGSL shader code with template syntax and utilities
- **Template Parameters**: Configuration objects that control shader generation (data types, dimensions, etc.)

## How It Works

The system supports two generation modes to balance performance and flexibility:

### Static Generation Mode

Templates are processed at **build time** to generate C++ header files embedded in the WebGPU EP binary.
Templates are processed at **build time** to generate C++ header files embedded
in the WebGPU EP binary.

**Advantages:**

Expand All @@ -37,31 +43,16 @@ Templates are processed at **build time** to generate C++ header files embedded
- Type-safe template parameters validated at compile time
- Optimal for production deployments

**Use Cases:**

- Production builds

### Dynamic Generation Mode

Templates are processed at **runtime** using an embedded JavaScript engine to generate shaders on-demand.

**Advantages:**

- Faster development iteration (no rebuild required)
- Support for runtime-dependent parameters
- Easier debugging and experimentation
- Flexible for research and development

**Use Cases:**

- Development and debugging workflows
- Research environments with frequently changing parameters
CMake invokes the Python tool, which walks the `.wgsl.template` files and emits
`index.h` / `index_impl.h` (plus per-template headers) into the build directory.
The EP includes these generated headers via [`wgsl_gen.h`](wgsl_gen.h) /
[`wgsl_gen.cc`](wgsl_gen.cc).

## Development

This section includes instructions for how to use the template system in the development.
This section describes how to use the template system during development.

1. Create WGSL template files in `.wgsl.template` extension.
1. Create WGSL template files with the `.wgsl.template` extension.

- [Reference: Template Syntax](https://github.com/fs-eire/wgsl-template?tab=readme-ov-file#template-syntax)
- [Reference: Built-in Utilities](https://github.com/fs-eire/wgsl-template?tab=readme-ov-file#Utilities)
Expand All @@ -73,48 +64,52 @@ This section includes instructions for how to use the template system in the dev

3. Build.

- Using static code generator

Static code generator is enabled by default:

```sh
./build.sh --use_webgpu
```

Rebuild is needed when any C/C++ source file or WGSL template file is updated.

- Using dynamic code generator

Append `--wgsl_template dynamic` to the ORT build script:
The static code generator is always enabled when WebGPU is built:

```sh
./build.sh --use_webgpu --wgsl_template dynamic
```
```sh
./build.sh --use_webgpu
```

Rebuild is not needed when only WGSL template files are updated. In this case, you just need to compile the template files. See the next section below.
A rebuild is needed when any C/C++ source file or WGSL template file is updated.

4. Compile the template files. (for dynamic code generator only)
## Python tool reference

When you are using dynamic code generator, you don't need to rebuild ONNX Runtime when you made changes to the WGSL template files.
The build invokes [`tools/python/wgsl_gen.py`](../../../../../tools/python/wgsl_gen.py)
directly from CMake; you should not normally need to run it by hand. The CLI
surface is:

> Suppose `<BUILD_DIR>` is the build directory. It's usually something like `<ORT_REPO_ROOT>/build/Linux/Debug` if not explicitly specified.
```
python tools/python/wgsl_gen.py \
-i <source-dir> [-i <source-dir> ...] \
--output <out-dir> \
--generator {static-cpp|static-cpp-literal} \
[-I <include-prefix>] \
[--ext .wgsl.template] \
[--preserve-code-ref] \
[--clean] \
[--verbose]
```

There are 2 ways to compile the template files:
* `static-cpp` (Release): emits short `__str_N` identifiers backed by a `string_table.h` for shader-string deduplication.
* `static-cpp-literal` (Debug): inlines string literals; easier to read while debugging.

1. Use a NPM script in the current folder:
### Running the test suite

```sh
npm run gen -- -i ../ --preserve-code-ref --verbose --generator dynamic --output <BUILD_DIR>/wgsl_generated/dynamic
```
The Python tool ships with a unit + fixture test suite. CMake adds
`wgsl_template_python_tests` to `ctest`, so any standard ORT build with WGSL
templates enabled will run them:

This script will generate the file `<BUILD_DIR>/wgsl_generated/dynamic/templates.js` and exit.
```
ctest -R wgsl_template_python_tests
```

2. Use the same script but in watch mode (recommended):
You can also run the suite directly from the source tree:

Use the same script as above but append `--watch` flag. This will launch a service monitoring the file system change and automatically compile the templates if any change occurred.
```
python tools/python/wgsl_template/test/run_tests.py
```

The typical development workflow is:
1. Build ORT once with dynamic template mode
2. Launch wgsl-gen in watch mode
3. Run ORT to debug/validate the shader
4. Make changes to the template files, and repeat step (c)
Tests cover the loader, parser, generator, build orchestrator, and a smoke test
against the in-tree templates (Pad, Transpose, im2col-matmul). The fixtures live
under
[`tools/python/wgsl_template/test/testcases/`](../../../../../tools/python/wgsl_template/test/testcases).
37 changes: 0 additions & 37 deletions onnxruntime/core/providers/webgpu/wgsl_templates/package-lock.json

This file was deleted.

Loading
Loading