[WebGPU] allow build WebGPU EP for WebAssembly (#23364)

### Description

This PR allows WebGPU EP to be built with Emscripten for WebAssembly,
Including:


- cmake build files update to support correct setup for Emscripten.
- code changes to fix build breaks for wasm
- change in Web CI pipeline to add a build-only target for wasm with
`--use_webgpu`.
This commit is contained in:
Yulong Wang 2025-01-16 10:52:17 -08:00 committed by carzh
parent 8d0ee4f1a6
commit 542ce0d600
24 changed files with 338 additions and 146 deletions

View file

@ -631,78 +631,99 @@ if (onnxruntime_USE_WEBGPU)
URL_HASH SHA1=${DEP_SHA1_dawn}
# All previous patches are merged into the upstream dawn project. We don't need to apply any patches right now.
# if we need to apply patches in the future, we can uncomment the following line.
# PATCH_COMMAND ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/dawn/dawn.patch
PATCH_COMMAND ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/dawn/dawn.patch
)
endif()
if (onnxruntime_BUILD_DAWN_MONOLITHIC_LIBRARY)
set(DAWN_BUILD_MONOLITHIC_LIBRARY ON CACHE BOOL "" FORCE)
set(DAWN_ENABLE_INSTALL ON CACHE BOOL "" FORCE)
if (onnxruntime_USE_EXTERNAL_DAWN)
message(FATAL_ERROR "onnxruntime_USE_EXTERNAL_DAWN and onnxruntime_BUILD_DAWN_MONOLITHIC_LIBRARY cannot be enabled at the same time.")
endif()
else()
# use dawn::dawn_native and dawn::dawn_proc instead of the monolithic dawn::webgpu_dawn to minimize binary size
set(DAWN_BUILD_MONOLITHIC_LIBRARY OFF CACHE BOOL "" FORCE)
set(DAWN_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
endif()
set(DAWN_BUILD_SAMPLES OFF CACHE BOOL "" FORCE)
set(DAWN_ENABLE_NULL OFF CACHE BOOL "" FORCE)
set(DAWN_FETCH_DEPENDENCIES ON CACHE BOOL "" FORCE)
# disable things we don't use
set(DAWN_DXC_ENABLE_ASSERTS_IN_NDEBUG OFF)
set(DAWN_ENABLE_DESKTOP_GL OFF CACHE BOOL "" FORCE)
set(DAWN_ENABLE_OPENGLES OFF CACHE BOOL "" FORCE)
set(DAWN_SUPPORTS_GLFW_FOR_WINDOWING OFF CACHE BOOL "" FORCE)
set(DAWN_USE_GLFW OFF CACHE BOOL "" FORCE)
set(DAWN_USE_WINDOWS_UI OFF CACHE BOOL "" FORCE)
set(DAWN_USE_X11 OFF CACHE BOOL "" FORCE)
if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
set(DAWN_EMSCRIPTEN_TOOLCHAIN "${REPO_ROOT}/cmake/external/emsdk/upstream/emscripten" CACHE STRING "" FORCE)
set(TINT_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_CMD_TOOLS OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_GLSL_WRITER OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_GLSL_VALIDATOR OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_IR_BINARY OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_SPV_READER OFF CACHE BOOL "" FORCE) # don't need. disabling is a large binary size saving
set(TINT_BUILD_WGSL_WRITER ON CACHE BOOL "" FORCE) # needed to create cache key. runtime error if not enabled.
# Add the missing files from the emsdk installation
#
# For a "standard" emscripten build, the folder "${DAWN_EMSCRIPTEN_TOOLCHAIN}/tools/maint/" is not used. This is the
# reason why EMSDK installation does not include it.
# However, currently the WebGPU support in Emscripten is still being developed and the Dawn project is maintaining
# a fork of the Emscripten toolchain. As an extra build step, Dawn needs to generate some files using the file
# "${DAWN_EMSCRIPTEN_TOOLCHAIN}/tools/maint/gen_struct_info.py" from emscripten, which is missing in the emscripten
# installed by emsdk.
#
# We keep a copy of the missing file(s) in ${PROJECT_SOURCE_DIR}/patches/emscripten/, and now we extract them to the
# emscripten toolchain folder.
execute_process(COMMAND ${CMAKE_COMMAND} -E tar x "${PROJECT_SOURCE_DIR}/patches/emscripten/patch_3.1.74.tgz"
WORKING_DIRECTORY ${DAWN_EMSCRIPTEN_TOOLCHAIN})
else()
if (onnxruntime_BUILD_DAWN_MONOLITHIC_LIBRARY)
set(DAWN_BUILD_MONOLITHIC_LIBRARY ON CACHE BOOL "" FORCE)
set(DAWN_ENABLE_INSTALL ON CACHE BOOL "" FORCE)
# SPIR-V validation shouldn't be required given we're using Tint to create the SPIR-V.
set(DAWN_ENABLE_SPIRV_VALIDATION OFF CACHE BOOL "" FORCE)
if (WIN32)
# building this requires the HLSL writer to be enabled in Tint. TBD if that we need either of these to be ON.
set(DAWN_USE_BUILT_DXC ON CACHE BOOL "" FORCE)
set(TINT_BUILD_HLSL_WRITER ON CACHE BOOL "" FORCE)
if ((NOT onnxruntime_ENABLE_DAWN_BACKEND_VULKAN) AND (NOT onnxruntime_ENABLE_DAWN_BACKEND_D3D12))
message(FATAL_ERROR "At least one of onnxruntime_ENABLE_DAWN_BACKEND_VULKAN or onnxruntime_ENABLE_DAWN_BACKEND_D3D12 must be enabled when using Dawn on Windows.")
endif()
if (onnxruntime_ENABLE_DAWN_BACKEND_VULKAN)
set(DAWN_ENABLE_VULKAN ON CACHE BOOL "" FORCE)
set(TINT_BUILD_SPV_WRITER ON CACHE BOOL "" FORCE)
if (onnxruntime_USE_EXTERNAL_DAWN)
message(FATAL_ERROR "onnxruntime_USE_EXTERNAL_DAWN and onnxruntime_BUILD_DAWN_MONOLITHIC_LIBRARY cannot be enabled at the same time.")
endif()
else()
set(DAWN_ENABLE_VULKAN OFF CACHE BOOL "" FORCE)
# use dawn::dawn_native and dawn::dawn_proc instead of the monolithic dawn::webgpu_dawn to minimize binary size
set(DAWN_BUILD_MONOLITHIC_LIBRARY OFF CACHE BOOL "" FORCE)
set(DAWN_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
endif()
if (onnxruntime_ENABLE_DAWN_BACKEND_D3D12)
set(DAWN_ENABLE_D3D12 ON CACHE BOOL "" FORCE)
else()
set(DAWN_ENABLE_D3D12 OFF CACHE BOOL "" FORCE)
# disable things we don't use
set(DAWN_DXC_ENABLE_ASSERTS_IN_NDEBUG OFF)
set(DAWN_ENABLE_DESKTOP_GL OFF CACHE BOOL "" FORCE)
set(DAWN_ENABLE_OPENGLES OFF CACHE BOOL "" FORCE)
set(DAWN_SUPPORTS_GLFW_FOR_WINDOWING OFF CACHE BOOL "" FORCE)
set(DAWN_USE_GLFW OFF CACHE BOOL "" FORCE)
set(DAWN_USE_WINDOWS_UI OFF CACHE BOOL "" FORCE)
set(DAWN_USE_X11 OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_CMD_TOOLS OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_GLSL_WRITER OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_GLSL_VALIDATOR OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_IR_BINARY OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_SPV_READER OFF CACHE BOOL "" FORCE) # don't need. disabling is a large binary size saving
set(TINT_BUILD_WGSL_WRITER ON CACHE BOOL "" FORCE) # needed to create cache key. runtime error if not enabled.
# SPIR-V validation shouldn't be required given we're using Tint to create the SPIR-V.
set(DAWN_ENABLE_SPIRV_VALIDATION OFF CACHE BOOL "" FORCE)
if (WIN32)
# building this requires the HLSL writer to be enabled in Tint. TBD if that we need either of these to be ON.
set(DAWN_USE_BUILT_DXC ON CACHE BOOL "" FORCE)
set(TINT_BUILD_HLSL_WRITER ON CACHE BOOL "" FORCE)
if ((NOT onnxruntime_ENABLE_DAWN_BACKEND_VULKAN) AND (NOT onnxruntime_ENABLE_DAWN_BACKEND_D3D12))
message(FATAL_ERROR "At least one of onnxruntime_ENABLE_DAWN_BACKEND_VULKAN or onnxruntime_ENABLE_DAWN_BACKEND_D3D12 must be enabled when using Dawn on Windows.")
endif()
if (onnxruntime_ENABLE_DAWN_BACKEND_VULKAN)
set(DAWN_ENABLE_VULKAN ON CACHE BOOL "" FORCE)
set(TINT_BUILD_SPV_WRITER ON CACHE BOOL "" FORCE)
else()
set(DAWN_ENABLE_VULKAN OFF CACHE BOOL "" FORCE)
endif()
if (onnxruntime_ENABLE_DAWN_BACKEND_D3D12)
set(DAWN_ENABLE_D3D12 ON CACHE BOOL "" FORCE)
else()
set(DAWN_ENABLE_D3D12 OFF CACHE BOOL "" FORCE)
endif()
# We are currently always using the D3D12 backend.
set(DAWN_ENABLE_D3D11 OFF CACHE BOOL "" FORCE)
endif()
# We are currently always using the D3D12 backend.
set(DAWN_ENABLE_D3D11 OFF CACHE BOOL "" FORCE)
endif()
onnxruntime_fetchcontent_makeavailable(dawn)
if (onnxruntime_BUILD_DAWN_MONOLITHIC_LIBRARY)
list(APPEND onnxruntime_EXTERNAL_LIBRARIES dawn::webgpu_dawn)
else()
if (NOT onnxruntime_USE_EXTERNAL_DAWN)
list(APPEND onnxruntime_EXTERNAL_LIBRARIES dawn::dawn_native)
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
if (onnxruntime_BUILD_DAWN_MONOLITHIC_LIBRARY)
list(APPEND onnxruntime_EXTERNAL_LIBRARIES dawn::webgpu_dawn)
else()
if (NOT onnxruntime_USE_EXTERNAL_DAWN)
list(APPEND onnxruntime_EXTERNAL_LIBRARIES dawn::dawn_native)
endif()
list(APPEND onnxruntime_EXTERNAL_LIBRARIES dawn::dawn_proc)
endif()
list(APPEND onnxruntime_EXTERNAL_LIBRARIES dawn::dawn_proc)
endif()
endif()

View file

@ -21,44 +21,71 @@
source_group(TREE ${REPO_ROOT} FILES ${onnxruntime_providers_webgpu_cc_srcs})
onnxruntime_add_static_library(onnxruntime_providers_webgpu ${onnxruntime_providers_webgpu_cc_srcs})
onnxruntime_add_include_to_target(onnxruntime_providers_webgpu
onnxruntime_common dawn::dawncpp_headers dawn::dawn_headers onnx onnx_proto flatbuffers::flatbuffers Boost::mp11 safeint_interface)
onnxruntime_common onnx onnx_proto flatbuffers::flatbuffers Boost::mp11 safeint_interface)
set(onnxruntime_providers_webgpu_dll_deps)
if (onnxruntime_BUILD_DAWN_MONOLITHIC_LIBRARY)
target_link_libraries(onnxruntime_providers_webgpu dawn::webgpu_dawn)
if (WIN32)
if (onnxruntime_ENABLE_DELAY_LOADING_WIN_DLLS)
list(APPEND onnxruntime_DELAYLOAD_FLAGS "/DELAYLOAD:webgpu_dawn.dll")
endif()
list(APPEND onnxruntime_providers_webgpu_dll_deps "$<TARGET_FILE:dawn::webgpu_dawn>")
if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
# target "emdawnwebgpu_c" is created by Dawn, including "-fno-exceptions" in its compile options by default.
#
# in ONNX Runtime build, "-s DISABLE_EXCEPTION_CATCHING=0" is appended to CMAKE_CXX_FLAGS by default unless build flag
# "--disable_wasm_exception_catching" is specified. It is not compatible with "-fno-exceptions".
#
# if "-s DISABLE_EXCEPTION_CATCHING=0" is set, we need to remove "-fno-exceptions" from emdawnwebgpu_c
if (CMAKE_CXX_FLAGS MATCHES "DISABLE_EXCEPTION_CATCHING=0")
get_property(EM_DAWN_WEBGPU_C_COMPILE_OPTIONS TARGET emdawnwebgpu_c PROPERTY COMPILE_OPTIONS)
list(REMOVE_ITEM EM_DAWN_WEBGPU_C_COMPILE_OPTIONS "-fno-exceptions")
set_property(TARGET emdawnwebgpu_c PROPERTY COMPILE_OPTIONS ${EM_DAWN_WEBGPU_C_COMPILE_OPTIONS})
endif()
# target "emdawnwebgpu_cpp" is created by Dawn. When it is linked to onnxruntime_providers_webgpu as "PUBLIC"
# dependency, a few build/link flags will be set automatically to make sure emscripten can generate correct
# WebAssembly/JavaScript code for WebGPU support.
target_link_libraries(onnxruntime_providers_webgpu PUBLIC emdawnwebgpu_cpp)
# ASYNCIFY is required for WGPUFuture support (ie. async functions in WebGPU API)
target_link_options(onnxruntime_providers_webgpu PUBLIC
"SHELL:-s ASYNCIFY=1"
"SHELL:-s ASYNCIFY_STACK_SIZE=65536"
)
else()
if (NOT onnxruntime_USE_EXTERNAL_DAWN)
target_link_libraries(onnxruntime_providers_webgpu dawn::dawn_native)
onnxruntime_add_include_to_target(onnxruntime_providers_webgpu dawn::dawncpp_headers dawn::dawn_headers)
set(onnxruntime_providers_webgpu_dll_deps)
if (onnxruntime_BUILD_DAWN_MONOLITHIC_LIBRARY)
target_link_libraries(onnxruntime_providers_webgpu dawn::webgpu_dawn)
if (WIN32)
if (onnxruntime_ENABLE_DELAY_LOADING_WIN_DLLS)
list(APPEND onnxruntime_DELAYLOAD_FLAGS "/DELAYLOAD:webgpu_dawn.dll")
endif()
list(APPEND onnxruntime_providers_webgpu_dll_deps "$<TARGET_FILE:dawn::webgpu_dawn>")
endif()
else()
if (NOT onnxruntime_USE_EXTERNAL_DAWN)
target_link_libraries(onnxruntime_providers_webgpu dawn::dawn_native)
endif()
target_link_libraries(onnxruntime_providers_webgpu dawn::dawn_proc)
endif()
target_link_libraries(onnxruntime_providers_webgpu dawn::dawn_proc)
endif()
if (WIN32 AND onnxruntime_ENABLE_DAWN_BACKEND_D3D12)
# Ensure dxil.dll and dxcompiler.dll exist in the output directory $<TARGET_FILE_DIR:dxcompiler>
add_dependencies(onnxruntime_providers_webgpu copy_dxil_dll)
add_dependencies(onnxruntime_providers_webgpu dxcompiler)
if (WIN32 AND onnxruntime_ENABLE_DAWN_BACKEND_D3D12)
# Ensure dxil.dll and dxcompiler.dll exist in the output directory $<TARGET_FILE_DIR:dxcompiler>
add_dependencies(onnxruntime_providers_webgpu copy_dxil_dll)
add_dependencies(onnxruntime_providers_webgpu dxcompiler)
list(APPEND onnxruntime_providers_webgpu_dll_deps "$<TARGET_FILE_DIR:dxcompiler>/dxil.dll")
list(APPEND onnxruntime_providers_webgpu_dll_deps "$<TARGET_FILE_DIR:dxcompiler>/dxcompiler.dll")
endif()
list(APPEND onnxruntime_providers_webgpu_dll_deps "$<TARGET_FILE_DIR:dxcompiler>/dxil.dll")
list(APPEND onnxruntime_providers_webgpu_dll_deps "$<TARGET_FILE_DIR:dxcompiler>/dxcompiler.dll")
endif()
if (onnxruntime_providers_webgpu_dll_deps)
# Copy dependency DLLs to the output directory
add_custom_command(
TARGET onnxruntime_providers_webgpu
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${onnxruntime_providers_webgpu_dll_deps}" "$<TARGET_FILE_DIR:onnxruntime_providers_webgpu>"
COMMAND_EXPAND_LISTS
VERBATIM )
if (onnxruntime_providers_webgpu_dll_deps)
# Copy dependency DLLs to the output directory
add_custom_command(
TARGET onnxruntime_providers_webgpu
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${onnxruntime_providers_webgpu_dll_deps}" "$<TARGET_FILE_DIR:onnxruntime_providers_webgpu>"
COMMAND_EXPAND_LISTS
VERBATIM )
endif()
endif()
add_dependencies(onnxruntime_providers_webgpu ${onnxruntime_EXTERNAL_DEPENDENCIES})

View file

@ -111,6 +111,7 @@ if (onnxruntime_BUILD_WEBASSEMBLY_STATIC_LIB)
${PROVIDERS_JS}
${PROVIDERS_XNNPACK}
${PROVIDERS_WEBNN}
${PROVIDERS_WEBGPU}
onnxruntime_session
onnxruntime_util
re2::re2
@ -188,6 +189,7 @@ else()
${PROVIDERS_JS}
${PROVIDERS_XNNPACK}
${PROVIDERS_WEBNN}
${PROVIDERS_WEBGPU}
onnxruntime_session
onnxruntime_util
re2::re2
@ -391,6 +393,10 @@ jsepDownload:_pp_")
endif()
endif()
if (onnxruntime_USE_WEBGPU)
target_compile_definitions(onnxruntime_webassembly PRIVATE USE_WEBGPU=1)
endif()
if (onnxruntime_EMSCRIPTEN_SETTINGS)
foreach(setting IN LISTS onnxruntime_EMSCRIPTEN_SETTINGS)
target_link_options(onnxruntime_webassembly PRIVATE "SHELL:-s ${setting}")

View file

@ -0,0 +1,118 @@
diff --git a/src/emdawnwebgpu/CMakeLists.txt b/src/emdawnwebgpu/CMakeLists.txt
index de673537d3..c98dc46de7 100644
--- a/src/emdawnwebgpu/CMakeLists.txt
+++ b/src/emdawnwebgpu/CMakeLists.txt
@@ -78,6 +78,7 @@ if (${DAWN_ENABLE_EMSCRIPTEN})
endif()
set(ARGS
+ ${Python3_EXECUTABLE}
"${DAWN_EMSCRIPTEN_TOOLCHAIN}/tools/maint/gen_struct_info.py"
-q
"${EM_BUILD_GEN_DIR}/struct_info_webgpu.json"
diff --git a/third_party/emdawnwebgpu/library_webgpu.js b/third_party/emdawnwebgpu/library_webgpu.js
index d1835cb090..df03ea2f94 100644
--- a/third_party/emdawnwebgpu/library_webgpu.js
+++ b/third_party/emdawnwebgpu/library_webgpu.js
@@ -16,10 +16,19 @@
throw new Error("To use Dawn's library_webgpu.js, disable -sUSE_WEBGPU and first include Dawn's library_webgpu_struct_info.js and library_webgpu_enum_tables.js (before library_webgpu.js)");
}
+ if (MEMORY64) {
+ throw new Error("The current implementation of Dawn's library_webgpu.js does not support MEMORY64 yet");
+ }
+
// Helper functions for code generation
globalThis.gpu = {
- convertSentinelToUndefined: function(name) {
- return `if (${name} == -1) ${name} = undefined;`;
+ convertSentinelToUndefined: function(name, isPtr = false) {
+ // When `CAN_ADDRESS_2GB` is true, value `-1` is normalized to `0xFFFFFFFF` for pointer.
+ if (CAN_ADDRESS_2GB && isPtr) {
+ return `if (${name} == 0xFFFFFFFF) ${name} = undefined;`;
+ } else {
+ return `if (${name} == -1) ${name} = undefined;`;
+ }
},
makeGetBool: function(struct, offset) {
@@ -700,6 +709,7 @@ var LibraryWebGPU = {
{{{ makeSetValue('info', C_STRUCTS.WGPUAdapterInfo.adapterType, 'adapterType', 'i32') }}};
{{{ makeSetValue('info', C_STRUCTS.WGPUAdapterInfo.vendorID, '0', 'i32') }}};
{{{ makeSetValue('info', C_STRUCTS.WGPUAdapterInfo.deviceID, '0', 'i32') }}};
+ return 1;
},
wgpuAdapterGetLimits: (adapterPtr, limitsOutPtr) => {
@@ -882,7 +892,7 @@ var LibraryWebGPU = {
if (size === 0) warnOnce('getMappedRange size=0 no longer means WGPU_WHOLE_MAP_SIZE');
- {{{ gpu.convertSentinelToUndefined('size') }}}
+ {{{ gpu.convertSentinelToUndefined('size', true) }}}
var mapped;
try {
@@ -909,7 +919,7 @@ var LibraryWebGPU = {
if (size === 0) warnOnce('getMappedRange size=0 no longer means WGPU_WHOLE_MAP_SIZE');
- {{{ gpu.convertSentinelToUndefined('size') }}}
+ {{{ gpu.convertSentinelToUndefined('size', true) }}}
var mapped;
try {
@@ -950,7 +960,7 @@ var LibraryWebGPU = {
var buffer = WebGPU.getJsObject(bufferPtr);
WebGPU.Internals.bufferOnUnmaps[bufferPtr] = [];
- {{{ gpu.convertSentinelToUndefined('size') }}}
+ {{{ gpu.convertSentinelToUndefined('size', true) }}}
{{{ runtimeKeepalivePush() }}}
WebGPU.Internals.futureInsert(futureId, buffer.mapAsync(mode, offset, size).then(() => {
@@ -1145,7 +1155,7 @@ var LibraryWebGPU = {
wgpuCommandEncoderClearBuffer: (encoderPtr, bufferPtr, offset, size) => {
var commandEncoder = WebGPU.getJsObject(encoderPtr);
- {{{ gpu.convertSentinelToUndefined('size') }}}
+ {{{ gpu.convertSentinelToUndefined('size', true) }}}
var buffer = WebGPU.getJsObject(bufferPtr);
commandEncoder.clearBuffer(buffer, offset, size);
@@ -2103,7 +2113,7 @@ var LibraryWebGPU = {
wgpuRenderBundleEncoderSetIndexBuffer: (passPtr, bufferPtr, format, offset, size) => {
var pass = WebGPU.getJsObject(passPtr);
var buffer = WebGPU.getJsObject(bufferPtr);
- {{{ gpu.convertSentinelToUndefined('size') }}}
+ {{{ gpu.convertSentinelToUndefined('size', true) }}}
pass.setIndexBuffer(buffer, WebGPU.IndexFormat[format], offset, size);
},
@@ -2116,7 +2126,7 @@ var LibraryWebGPU = {
wgpuRenderBundleEncoderSetVertexBuffer: (passPtr, slot, bufferPtr, offset, size) => {
var pass = WebGPU.getJsObject(passPtr);
var buffer = WebGPU.getJsObject(bufferPtr);
- {{{ gpu.convertSentinelToUndefined('size') }}}
+ {{{ gpu.convertSentinelToUndefined('size', true) }}}
pass.setVertexBuffer(slot, buffer, offset, size);
},
@@ -2211,7 +2221,7 @@ var LibraryWebGPU = {
wgpuRenderPassEncoderSetIndexBuffer: (passPtr, bufferPtr, format, offset, size) => {
var pass = WebGPU.getJsObject(passPtr);
var buffer = WebGPU.getJsObject(bufferPtr);
- {{{ gpu.convertSentinelToUndefined('size') }}}
+ {{{ gpu.convertSentinelToUndefined('size', true) }}}
pass.setIndexBuffer(buffer, WebGPU.IndexFormat[format], offset, size);
},
@@ -2234,7 +2244,7 @@ var LibraryWebGPU = {
wgpuRenderPassEncoderSetVertexBuffer: (passPtr, slot, bufferPtr, offset, size) => {
var pass = WebGPU.getJsObject(passPtr);
var buffer = WebGPU.getJsObject(bufferPtr);
- {{{ gpu.convertSentinelToUndefined('size') }}}
+ {{{ gpu.convertSentinelToUndefined('size', true) }}}
pass.setVertexBuffer(slot, buffer, offset, size);
},

Binary file not shown.

View file

@ -116,7 +116,7 @@ Status SkipLayerNorm<simplified>::ComputeInternal(onnxruntime::webgpu::ComputeCo
auto* output = context.Output(0, x_shape);
auto* input_skip_bias_sum = context.Output(3, x_shape);
size_t data_size = x_shape.Size();
int64_t data_size = x_shape.Size();
if (data_size == 0) {
return Status::OK();
}

View file

@ -1,10 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#include "core/framework/session_state.h"
#include "core/providers/webgpu/allocator.h"
#include "core/providers/webgpu/webgpu_context.h"

View file

@ -85,7 +85,7 @@ class SimpleCacheManager : public IBufferCacheManager {
void OnRefresh() override {
for (auto& buffer : pending_buffers_) {
buffers_[wgpuBufferGetSize(buffer)].push_back(buffer);
buffers_[static_cast<size_t>(wgpuBufferGetSize(buffer))].push_back(buffer);
}
pending_buffers_.clear();
}
@ -167,7 +167,7 @@ class BucketCacheManager : public IBufferCacheManager {
// TODO: consider graph capture. currently not supported
for (auto& buffer : pending_buffers_) {
auto buffer_size = wgpuBufferGetSize(buffer);
auto buffer_size = static_cast<size_t>(wgpuBufferGetSize(buffer));
auto it = buckets_.find(buffer_size);
if (it != buckets_.end() && it->second.size() < buckets_limit_[buffer_size]) {

View file

@ -5,10 +5,6 @@
#include <iosfwd>
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
#include <webgpu/webgpu_cpp.h>
#include "core/framework/execution_provider.h"

View file

@ -3,10 +3,6 @@
#pragma once
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
#include <webgpu/webgpu_cpp.h>
#include <utility>

View file

@ -1,10 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#include "core/providers/webgpu/data_transfer.h"
#include "core/providers/webgpu/webgpu_context.h"

View file

@ -123,10 +123,10 @@ Status BinaryElementwise::ComputeInternal(ComputeContext& context) const {
if (a_last_dim_divisible_by_4 || b_last_dim_divisible_by_4) {
vectorize = true;
} else {
size_t shared_dimension = 1;
int64_t shared_dimension = 1;
for (size_t i = 1; i < output_shape.NumDimensions(); i++) {
size_t dimA = lhs_shape.NumDimensions() >= i ? lhs_shape[lhs_shape.NumDimensions() - i] : 1;
size_t dimB = rhs_shape.NumDimensions() >= i ? rhs_shape[rhs_shape.NumDimensions() - i] : 1;
int64_t dimA = lhs_shape.NumDimensions() >= i ? lhs_shape[lhs_shape.NumDimensions() - i] : 1;
int64_t dimB = rhs_shape.NumDimensions() >= i ? rhs_shape[rhs_shape.NumDimensions() - i] : 1;
if (dimA == dimB) {
shared_dimension *= dimA;
num_shared_dimension++;

View file

@ -85,8 +85,7 @@ Status LayerNorm<simplified>::ComputeInternal(onnxruntime::webgpu::ComputeContex
auto* output = context.Output(0, x_shape);
size_t data_size = x_shape.Size();
if (data_size == 0) {
if (x_shape.Size() == 0) {
return Status::OK();
}

View file

@ -3,10 +3,6 @@
#pragma once
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
#include <string>
#include <unordered_map>

View file

@ -5,10 +5,6 @@
#include <sstream>
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
#include <webgpu/webgpu_cpp.h>
#include "core/framework/tensor_shape.h"

View file

@ -106,7 +106,8 @@ Status Concat::ComputeInternal(ComputeContext& context) const {
uint32_t output_size = gsl::narrow_cast<int32_t>(prepare.output_tensor->Shape().Size());
ConcatProgram program{prepare.axis};
size_t axis = static_cast<size_t>(prepare.axis);
ConcatProgram program{axis};
std::vector<uint32_t> sizes_in_concat_axis;
sizes_in_concat_axis.reserve(input_count);
@ -118,7 +119,7 @@ Status Concat::ComputeInternal(ComputeContext& context) const {
}
program.AddInput({input.tensor, ProgramTensorMetadataDependency::TypeAndRank});
auto axis_size = input.tensor->Shape()[prepare.axis];
auto axis_size = input.tensor->Shape()[axis];
sum += static_cast<uint32_t>(axis_size);
sizes_in_concat_axis.push_back(sum);
}

View file

@ -31,15 +31,8 @@ class Flatten final : public OpKernel {
return Status(common::ONNXRUNTIME, common::FAIL, "Invalid value for axis, must be less than or equal to input_rank");
}
int64_t first_dim = 1;
for (int64_t i = 0; i < axis; i++) {
first_dim *= input_shape[i];
}
int64_t second_dim = 1;
for (int64_t i = axis; i < input_rank; i++) {
second_dim *= input_shape[i];
}
int64_t first_dim = input_shape.SizeToDimension(static_cast<size_t>(axis));
int64_t second_dim = input_shape.SizeFromDimension(static_cast<size_t>(axis));
TensorShape output_shape({first_dim, second_dim});
Tensor* output_tensor = context->Output(0, output_shape);
@ -59,4 +52,4 @@ class Flatten final : public OpKernel {
};
} // namespace webgpu
} // namespace onnxruntime
} // namespace onnxruntime

View file

@ -59,7 +59,7 @@ Status GatherElements::ComputeInternal(ComputeContext& context) const {
axis += input_rank;
}
auto axis_dim_limit = input_shape[axis];
auto axis_dim_limit = input_shape[static_cast<size_t>(axis)];
auto output_dims = indices_shape.AsShapeVector();
TensorShape output_shape(output_dims);
@ -83,4 +83,4 @@ Status GatherElements::ComputeInternal(ComputeContext& context) const {
}
} // namespace webgpu
} // namespace onnxruntime
} // namespace onnxruntime

View file

@ -4,10 +4,12 @@
#include <memory>
#include <cmath>
#if !defined(__wasm__)
#include "dawn/dawn_proc.h"
#if !defined(USE_EXTERNAL_DAWN)
#include "dawn/native/DawnNative.h"
#endif
#endif
#include "core/common/common.h"
#include "core/common/path_string.h"
@ -29,7 +31,7 @@ void WebGpuContext::Initialize(const WebGpuBufferCacheConfig& buffer_cache_confi
std::call_once(init_flag_, [this, &buffer_cache_config, backend_type]() {
// Create wgpu::Adapter
if (adapter_ == nullptr) {
#if !defined(__EMSCRIPTEN__) && defined(_MSC_VER) && defined(DAWN_ENABLE_D3D12) && !defined(USE_EXTERNAL_DAWN)
#if !defined(__wasm__) && defined(_MSC_VER) && defined(DAWN_ENABLE_D3D12) && !defined(USE_EXTERNAL_DAWN)
// If we are using the D3D12 backend on Windows and the build does not use external Dawn, dxil.dll and dxcompiler.dll are required.
//
// Dawn will try to load them later, but if they are in the different directory to the executable, it may fail to find them.
@ -54,15 +56,19 @@ void WebGpuContext::Initialize(const WebGpuBufferCacheConfig& buffer_cache_confi
#endif
wgpu::RequestAdapterOptions req_adapter_options = {};
wgpu::DawnTogglesDescriptor adapter_toggles_desc = {};
req_adapter_options.nextInChain = &adapter_toggles_desc;
req_adapter_options.backendType = static_cast<wgpu::BackendType>(backend_type);
req_adapter_options.powerPreference = wgpu::PowerPreference::HighPerformance;
#if !defined(__wasm__)
auto enabled_adapter_toggles = GetEnabledAdapterToggles();
wgpu::DawnTogglesDescriptor adapter_toggles_desc = {};
adapter_toggles_desc.enabledToggleCount = enabled_adapter_toggles.size();
adapter_toggles_desc.enabledToggles = enabled_adapter_toggles.data();
req_adapter_options.nextInChain = &adapter_toggles_desc;
#endif
ORT_ENFORCE(wgpu::WaitStatus::Success == instance_.WaitAny(instance_.RequestAdapter(
&req_adapter_options,
wgpu::CallbackMode::WaitAnyOnly,
@ -78,6 +84,8 @@ void WebGpuContext::Initialize(const WebGpuBufferCacheConfig& buffer_cache_confi
// Create wgpu::Device
if (device_ == nullptr) {
wgpu::DeviceDescriptor device_desc = {};
#if !defined(__wasm__)
wgpu::DawnTogglesDescriptor device_toggles_desc = {};
device_desc.nextInChain = &device_toggles_desc;
@ -88,6 +96,7 @@ void WebGpuContext::Initialize(const WebGpuBufferCacheConfig& buffer_cache_confi
auto disabled_device_toggles = GetDisabledDeviceToggles();
device_toggles_desc.disabledToggleCount = disabled_device_toggles.size();
device_toggles_desc.disabledToggles = disabled_device_toggles.data();
#endif
std::vector<wgpu::FeatureName> required_features = GetAvailableRequiredFeatures(adapter_);
if (required_features.size() > 0) {
@ -135,9 +144,12 @@ void WebGpuContext::Initialize(const WebGpuBufferCacheConfig& buffer_cache_confi
program_mgr_ = std::make_unique<ProgramManager>(Device(), DeviceLimits());
// set query type
#if !defined(__wasm__)
if (device_.HasFeature(wgpu::FeatureName::ChromiumExperimentalTimestampQueryInsidePasses)) {
query_type_ = TimestampQueryType::InsidePasses;
} else if (device_.HasFeature(wgpu::FeatureName::TimestampQuery)) {
} else
#endif
if (device_.HasFeature(wgpu::FeatureName::TimestampQuery)) {
query_type_ = TimestampQueryType::AtPasses;
} else {
query_type_ = TimestampQueryType::None;
@ -455,7 +467,9 @@ std::vector<const char*> WebGpuContext::GetDisabledDeviceToggles() const {
std::vector<wgpu::FeatureName> WebGpuContext::GetAvailableRequiredFeatures(const wgpu::Adapter& adapter) const {
std::vector<wgpu::FeatureName> required_features;
constexpr wgpu::FeatureName features[]{
#if !defined(__wasm__)
wgpu::FeatureName::ChromiumExperimentalTimestampQueryInsidePasses,
#endif
wgpu::FeatureName::TimestampQuery,
wgpu::FeatureName::ShaderF16,
wgpu::FeatureName::Subgroups,
@ -530,7 +544,7 @@ void WebGpuContext::CollectProfilingData(profiling::Events& events) {
ORT_ENFORCE(Wait(query_read_buffer.MapAsync(wgpu::MapMode::Read,
0,
query_read_buffer.GetSize(),
static_cast<size_t>(query_read_buffer.GetSize()),
wgpu::CallbackMode::WaitAnyOnly,
[](wgpu::MapAsyncStatus status, wgpu::StringView message) {
ORT_ENFORCE(status == wgpu::MapAsyncStatus::Success, "Failed to download data from buffer: ", std::string_view{message});
@ -657,8 +671,14 @@ WebGpuContext& WebGpuContextFactory::CreateContext(const WebGpuContextConfig& co
ORT_ENFORCE(instance == nullptr && adapter == nullptr && device == nullptr,
"WebGPU EP default context (contextId=0) must not have custom WebGPU instance, adapter or device.");
std::call_once(init_default_flag_, [dawn_proc_table = config.dawn_proc_table]() {
// Step.1 - setup dawn proc table
std::call_once(init_default_flag_, [
#if !defined(__wasm__)
dawn_proc_table = config.dawn_proc_table
#endif
]() {
// Step.1 - setup dawn proc table (only for non-WASM build)
#if !defined(__wasm__)
const DawnProcTable* dawn_procs = reinterpret_cast<const DawnProcTable*>(dawn_proc_table);
#if defined(BUILD_DAWN_MONOLITHIC_LIBRARY)
ORT_ENFORCE(dawn_procs == nullptr, "setting DawnProcTable is not allowed when dynamically linked to webgpu_dawn.");
@ -671,12 +691,17 @@ WebGpuContext& WebGpuContextFactory::CreateContext(const WebGpuContextConfig& co
ORT_ENFORCE(dawn_procs != nullptr, "DawnProcTable must be provided.");
#endif
dawnProcSetProcs(dawn_procs);
#endif
#endif
// Step.2 - Create wgpu::Instance
#if !defined(__wasm__)
wgpu::InstanceDescriptor instance_desc{};
instance_desc.features.timedWaitAnyEnable = true;
default_instance_ = wgpu::CreateInstance(&instance_desc);
#else
default_instance_ = wgpu::CreateInstance(nullptr);
#endif
ORT_ENFORCE(default_instance_ != nullptr, "Failed to create wgpu::Instance.");
});

View file

@ -3,10 +3,6 @@
#pragma once
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
#include <memory>
#include <mutex>

View file

@ -3,9 +3,6 @@
#include "core/providers/webgpu/webgpu_execution_provider.h"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#include <string_view>
#include <unordered_map>
#include <unordered_set>

View file

@ -38,6 +38,10 @@ const gtestReporter = {'reporter:gtest': ['type', function() {
};
}]};
// In Node.js v16 and below, 'localhost' is using IPv4, so need to listen to '0.0.0.0'
// In Node.js v17+, 'localhost' is using IPv6, so need to listen to '::'
const listenAddress = Number.parseInt(process.versions.node.split('.')[0]) >= 17 ? '::' : '0.0.0.0';
module.exports = function(config) {
config.set({
basePath: '.',
@ -60,6 +64,7 @@ module.exports = function(config) {
browserDisconnectTimeout: 600000,
// allow running tests for 30 minutes
browserNoActivityTimeout: 30 * 60 * 1000,
listenAddress,
customLaunchers: {
ChromeTest: {
base: 'ChromeCanary',

View file

@ -26,6 +26,10 @@ parameters:
type: boolean
default: false
- name: BuildWebGPU
type: boolean
default: false
# In fact, it's only used on Linux for compiler cache.
- name: BuildStaticLib
type: boolean
@ -122,6 +126,24 @@ jobs:
DisplayName: 'Build (simd + threads + JSEP)'
WithCache: ${{ parameters.WithCache }}
- ${{ if eq(parameters.BuildWebGPU, true) }}:
# This step only verifies whether the build is successful.
# currently, we uses EMSDK 3.1.59, which is not compatible with Dawn's changes in its Emscripten fork. Closure compiler will not work for WebGPU build.
# Only enables in DEBUG build.
#
# TODO: when upgrading to a newer Emscripten version, we should fix this step.
- template: build-linux-wasm-step.yml
parameters:
Today: $(Today)
${{ if eq(parameters.BuildStaticLib, true)}}:
AdditionalKey: wasm_inferencing_webgpu_exp | ${{ parameters.BuildConfig }} | static
${{ else }}:
AdditionalKey: wasm_inferencing_webgpu_exp | ${{ parameters.BuildConfig }}
CacheDir: $(ORT_CACHE_DIR)/wasm_inferencing_webgpu
Arguments: '$(CommonBuildArgs) --build_dir $(Build.BinariesDirectory)/wasm_inferencing_webgpu --use_webgpu --target onnxruntime_webassembly --skip_tests'
DisplayName: 'Build (simd + threads + WebGPU experimental)'
WithCache: ${{ parameters.WithCache }}
- ${{ if eq(parameters.SkipPublish, false) }}:
- script: |
cp $(Build.BinariesDirectory)/wasm_inferencing/${{ parameters.BuildConfig }}/ort-wasm-simd-threaded.wasm $(Build.ArtifactStagingDirectory)

View file

@ -23,6 +23,10 @@ parameters:
displayName: 'Build JSEP'
type: boolean
default: true
- name: BuildWebGPU
displayName: 'Build WebGPU (EP)'
type: boolean
default: true
- name: WASMTemplate
type: string
@ -98,6 +102,7 @@ stages:
ExtraBuildArgs: '--enable_wasm_profiling ${{ parameters.ExtraBuildArgs }}'
PoolName: ${{ parameters.PoolName }}
BuildJsep: ${{ parameters.BuildJsep }}
BuildWebGPU: ${{ parameters.BuildWebGPU }}
WithCache: ${{ parameters.WithCache }}
- stage: Build_web_Debug
@ -128,6 +133,7 @@ stages:
ExtraBuildArgs: '--target onnxruntime_webassembly --skip_tests --enable_wasm_api_exception_catching --disable_rtti ${{ parameters.ExtraBuildArgs }}'
PoolName: ${{ parameters.PoolName }}
BuildJsep: ${{ parameters.BuildJsep }}
BuildWebGPU: false
WithCache: ${{ parameters.WithCache }}
- ${{ if eq(parameters.BuildStaticLib, 'true') }}: