Skip to content

Commit

Permalink
Split WebGPU runtime into two variants (halide#7248 workaround) (hali…
Browse files Browse the repository at this point in the history
…de#7419)

* Split WebGPU runtime into two variants (halide#7248 workaround)

Halide promises that you can crosscompile to *any* supported target from a 'stock' build of libHalide. Unfortunately, the initial landing of WebGPU support breaks that promise: we compile the webgpu runtime support (webgpu.cpp) with code that is predicated on `WITH_DAWN_NATIVE` (for Dawn vs Emscripten, respectively).

This means that if you build Halide with `WITH_DAWN_NATIVE` defined, you can *only* target Dawn with that build of Halide; similarly, if you build with `WITH_DAWN_NATIVE` not-defined, you can only target Emscripten. (Trying to use the 'wrong' version will produce link-time errors.)

For people who build everything from source, this isn't a big deal, but for people who just pull binary builds, this is a big problem.

This PR proposes a temporary workaround until the API discrepancies are resolved:
- Compile the existing webgpu.cpp runtime *both* ways
- in LLVM_Runtime_Linker.cpp, select the correct variant based on whether the Target is targeting wasm or not
- Profit!

This is a rather ugly hack, but it should hopefully be (relatively) temporary.

* A few more fixes

* Update HalideGeneratorHelpers.cmake

* Update interpreter.cpp

* Update interpreter.cpp
  • Loading branch information
steven-johnson authored and ardier committed Mar 3, 2024
1 parent 14c012e commit 911e769
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 27 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,8 @@ RUNTIME_CPP_COMPONENTS = \
trace_helper \
tracing \
wasm_cpu_features \
webgpu \
webgpu_dawn \
webgpu_emscripten \
windows_clock \
windows_cuda \
windows_d3d12compute_arm \
Expand Down
8 changes: 4 additions & 4 deletions README_webgpu.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ device codegen may be required before it becomes profitable to use.
Halide can generate WebGPU code that can be integrated with WASM code using
Emscripten.
Halide must currently be built *without* the `WEBGPU_NATIVE_LIB` flag when
targeting Emscripten.

When invoking `emcc` to link Halide-generated objects, include these flags:
`-s USE_WEBGPU=1 -s ASYNCIFY`.
Expand All @@ -53,12 +51,14 @@ For testing purposes, Halide can also target native WebGPU libraries, such as
This is currently the only path that can run the JIT correctness tests.
See [below](#setting-up-dawn) for instructions on building Dawn.

Due to differences between the APIs implemented by native WebGPU libraries and
Emscripten, this currently requires a separate build of Halide.
Pass `-DWEBGPU_NATIVE_LIB=/path/to/native/library.{so,dylib.dll}` to CMake when
configuring Halide to enable this path, which will automatically use this
library for the AOT and JIT tests.

Note that it is explicitly legal to specify both WEBGPU_NATIVE_LIB and
WEBGPU_NODE_BINDINGS for the same build; the correct executable environment
will be selected based on the Halide target specified.

## Setting up Dawn

Building Dawn's Node.js bindings currently requires using CMake.
Expand Down
2 changes: 1 addition & 1 deletion cmake/HalideGeneratorHelpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ function(_Halide_target_link_gpu_libs TARGET VISIBILITY)

if ("${ARGN}" MATCHES "webgpu")
if (WEBGPU_NATIVE_LIB)
target_link_libraries(${TARGET} PRIVATE ${WEBGPU_NATIVE_LIB})
target_link_libraries(${TARGET} INTERFACE ${WEBGPU_NATIVE_LIB})
endif ()
endif ()
endfunction()
Expand Down
6 changes: 0 additions & 6 deletions dependencies/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,6 @@ function(add_wasm_executable TARGET)
)
endif ()

# TODO: Remove this when Emscripten and Dawn implement the same APIs.
# See https://github.com/halide/Halide/issues/7248
if (WEBGPU_NATIVE_LIB)
message(FATAL_ERROR "Cannot generate WASM executables when targeting native WebGPU implementations.")
endif ()

set(SRCS)
foreach (S IN LISTS args_SRCS)
list(APPEND SRCS "${CMAKE_CURRENT_SOURCE_DIR}/${S}")
Expand Down
13 changes: 11 additions & 2 deletions src/LLVM_Runtime_Linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,10 @@ DECLARE_CPP_INITMOD(timer_profiler)
DECLARE_CPP_INITMOD(to_string)
DECLARE_CPP_INITMOD(trace_helper)
DECLARE_CPP_INITMOD(tracing)
DECLARE_CPP_INITMOD(webgpu)
// TODO(https://github.com/halide/Halide/issues/7248)
// DECLARE_CPP_INITMOD(webgpu)
DECLARE_CPP_INITMOD(webgpu_dawn)
DECLARE_CPP_INITMOD(webgpu_emscripten)
DECLARE_CPP_INITMOD(windows_clock)
DECLARE_CPP_INITMOD(windows_cuda)
DECLARE_CPP_INITMOD(windows_get_symbol)
Expand Down Expand Up @@ -1194,7 +1197,13 @@ std::unique_ptr<llvm::Module> get_initial_module_for_target(Target t, llvm::LLVM
// See https://github.com/halide/Halide/issues/7249
user_error << "WebGPU runtime not yet supported on Windows.\n";
} else {
modules.push_back(get_initmod_webgpu(c, bits_64, debug));
// Select the right WebGPU runtime variant based on the Target's OS:
// if we are targeting wasm, use the Emscripten variant; in all other cases, use Dawn variant.
if (t.os == Target::WebAssemblyRuntime) {
modules.push_back(get_initmod_webgpu_emscripten(c, bits_64, debug));
} else {
modules.push_back(get_initmod_webgpu_dawn(c, bits_64, debug));
}
}
}
if (t.arch != Target::Hexagon && t.has_feature(Target::HVX)) {
Expand Down
9 changes: 4 additions & 5 deletions src/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ set(RUNTIME_CPP
trace_helper
tracing
wasm_cpu_features
webgpu
# TODO(https://github.com/halide/Halide/issues/7248)
# webgpu
webgpu_dawn
webgpu_emscripten
windows_clock
windows_cuda
windows_d3d12compute_arm
Expand Down Expand Up @@ -227,10 +230,6 @@ foreach (i IN LISTS RUNTIME_CPP)
set(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${i}.cpp")

set(RUNTIME_DEFINES -DCOMPILING_HALIDE_RUNTIME -DBITS_${j})
if (WEBGPU_NATIVE_LIB)
# TODO: Remove this once Emscripten and Dawn agree with each other.
set(RUNTIME_DEFINES -DWITH_DAWN_NATIVE ${RUNTIME_DEFINES})
endif ()
set(RUNTIME_DEFINES_debug -g -DDEBUG_RUNTIME ${RUNTIME_DEFINES})

foreach (SUFFIX IN ITEMS "" "_debug")
Expand Down
18 changes: 11 additions & 7 deletions src/runtime/webgpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

#include "mini_webgpu.h"

#ifndef HALIDE_RUNTIME_WEBGPU_NATIVE_API
#error "HALIDE_RUNTIME_WEBGPU_NATIVE_API must be defined"
#endif

namespace Halide {
namespace Runtime {
namespace Internal {
Expand Down Expand Up @@ -40,7 +44,7 @@ using namespace Halide::Runtime::Internal::WebGPU;
extern "C" {
// TODO: Remove all of this when wgpuInstanceProcessEvents() is supported.
// See https://github.com/halide/Halide/issues/7248
#ifdef WITH_DAWN_NATIVE
#if HALIDE_RUNTIME_WEBGPU_NATIVE_API
// Defined by Dawn, and used to yield execution to asynchronous commands.
void wgpuDeviceTick(WGPUDevice);
// From <unistd.h>, used to spin-lock while waiting for device initialization.
Expand Down Expand Up @@ -274,7 +278,7 @@ void request_adapter_callback(WGPURequestAdapterStatus status,

// TODO: Enable for Emscripten when wgpuAdapterGetLimits is supported.
// See https://github.com/halide/Halide/issues/7248
#ifdef WITH_DAWN_NATIVE
#if HALIDE_RUNTIME_WEBGPU_NATIVE_API
WGPUSupportedLimits supportedLimits{};
supportedLimits.nextInChain = nullptr;
if (!wgpuAdapterGetLimits(adapter, &supportedLimits)) {
Expand Down Expand Up @@ -310,7 +314,7 @@ size_t round_up_to_multiple_of_4(size_t x) {
WEAK int create_webgpu_context(void *user_context) {
// TODO: Unify this when Emscripten implements wgpuCreateInstance().
// See https://github.com/halide/Halide/issues/7248
#ifdef WITH_DAWN_NATIVE
#if HALIDE_RUNTIME_WEBGPU_NATIVE_API
WGPUInstanceDescriptor desc{};
desc.nextInChain = nullptr;
global_instance = wgpuCreateInstance(&desc);
Expand All @@ -325,10 +329,10 @@ WEAK int create_webgpu_context(void *user_context) {
while (!global_device && init_error_code == halide_error_code_success) {
// TODO: Use wgpuInstanceProcessEvents() when it is supported.
// See https://github.com/halide/Halide/issues/7248
#ifndef WITH_DAWN_NATIVE
emscripten_sleep(10);
#else
#if HALIDE_RUNTIME_WEBGPU_NATIVE_API
usleep(1000);
#else
emscripten_sleep(10);
#endif
}
if (init_error_code != halide_error_code_success) {
Expand Down Expand Up @@ -514,7 +518,7 @@ WEAK int halide_webgpu_device_release(void *user_context) {

// TODO: Unify this when Emscripten supports wgpuInstanceRelease().
// See https://github.com/halide/Halide/issues/7248
#ifdef WITH_DAWN_NATIVE
#if HALIDE_RUNTIME_WEBGPU_NATIVE_API
wgpuInstanceRelease(instance);
global_instance = nullptr;
#endif
Expand Down
12 changes: 12 additions & 0 deletions src/runtime/webgpu_dawn.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// TODO(https://github.com/halide/Halide/issues/7248):
//
// For now, we must build the webgpu runtime two ways:
// - once for the native API (Dawn)
// - once for the Emscripten API (Chrome)
//
// Once the API discrepancies are resolved we can
// go back to building this in a sane manner, but for now,
// we use this sad-but-effective approach.

#define HALIDE_RUNTIME_WEBGPU_NATIVE_API 1
#include "webgpu.cpp"
12 changes: 12 additions & 0 deletions src/runtime/webgpu_emscripten.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// TODO(https://github.com/halide/Halide/issues/7248):
//
// For now, we must build the webgpu runtime two ways:
// - once for the native API (Dawn)
// - once for the Emscripten API (Chrome)
//
// Once the API discrepancies are resolved we can
// go back to building this in a sane manner, but for now,
// we use this sad-but-effective approach.

#define HALIDE_RUNTIME_WEBGPU_NATIVE_API 0
#include "webgpu.cpp"
6 changes: 6 additions & 0 deletions test/correctness/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ int main(int argc, char **argv) {
return 0;
}

// Workaround for https://github.com/halide/Halide/issues/7420
if (target.has_feature(Target::WebGPU)) {
printf("[SKIP] workaround for issue #7420\n");
return 0;
}

// This test demonstrates a trick for writing interpreters in
// Halide, and as a side-effect tests our ability to correctly
// emit switch statements.
Expand Down
4 changes: 3 additions & 1 deletion test/generator/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ function(_add_one_aot_test TARGET)
if ("${Halide_TARGET}" MATCHES "webgpu")
target_compile_definitions("${TARGET}" PRIVATE TEST_WEBGPU)
target_include_directories("${TARGET}" PRIVATE ${args_INCLUDES} "${Halide_SOURCE_DIR}/src/runtime")
if (WEBGPU_NATIVE_LIB)
if ("${Halide_TARGET}" MATCHES "wasmrt")
# nothing
else ()
# TODO: Remove this once Emscripten and Dawn agree with each other.
target_compile_definitions("${TARGET}" PRIVATE WITH_DAWN_NATIVE)
endif ()
Expand Down

0 comments on commit 911e769

Please sign in to comment.