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
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v7
with:
python-version: "3.13"
- if: runner.os == 'Linux'
run: |
sudo apt-get update
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ browser target.
- The legacy raw WebGPU C API implementation, manual resource pools, custom
half implementation, Haskell binding, obsolete build files, and abandoned
experimental targets.
- The Closure compiler and Java dependency from browser builds.
5 changes: 1 addition & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ if(EMSCRIPTEN)
endif()
target_compile_options(gpucpp INTERFACE "${EMDAWN_PORT}" "-fwasm-exceptions")
target_link_options(gpucpp INTERFACE "${EMDAWN_PORT}" "-fwasm-exceptions"
"-sJSPI=1" "--closure=1")
if(CMAKE_HOST_APPLE)
target_link_options(gpucpp INTERFACE "--closure-args=--platform=java")
endif()
"-sJSPI=1")
else()

set(GPUCPP_DAWN_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/third_party/dawn" CACHE PATH
Expand Down
9 changes: 4 additions & 5 deletions DEV.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ The same file pins the emsdk revision and Emscripten version recorded in
Dawn's DEPS. `tools/build_emdawn.sh` builds `emdawnwebgpu_pkg` from that exact
Dawn source and stages its local Emscripten port under
`third_party/emdawnwebgpu/`. The sibling `../emsdk` clone and staged package are
not committed. Closure is required by Emdawn release links and uses OpenJDK on
macOS ARM.
not committed.

The top-level build fetches the tagged pybind11 release declared in
`CMakeLists.txt`; consumers using only the C++ target do not fetch or build it.
Expand Down Expand Up @@ -69,9 +68,9 @@ local release path.
`./test --web` cross-compiles the same public core and Embind API against
Emdawnwebgpu, opens the result with `emrun`, and checks rejected invalid WGSL,
context reuse, typed-array upload, dispatch, and readback in Chrome. The
release build uses Closure, Wasm exceptions, and JSPI. CI uses
`./test --build-web` to compile the same artifacts without requiring a browser
GPU; the complete story remains the local runtime contract.
release build uses Wasm exceptions and JSPI. CI uses `./test --build-web` to
compile the same artifacts without requiring a browser GPU; the complete story
remains the local runtime contract.

## Architecture

Expand Down
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@ Dawn library, and stages its headers, library, and `spirv-as` under
and build trees under `third_party/local/dawn/` directly.

Browser builds require Chrome with WebGPU and JSPI, plus a sibling `../emsdk`
clone. On macOS they also require OpenJDK for Closure:
clone:

```bash
git clone --depth 1 https://github.com/emscripten-core/emsdk.git ../emsdk
brew install openjdk
./test --rebuild-web
```

Expand Down Expand Up @@ -132,6 +131,23 @@ target_link_libraries(my_program PRIVATE gpucpp)
See [DEV.md](DEV.md) for the dependency layout and update process, and
[CHANGELOG.md](CHANGELOG.md) for release notes.

Browser consumers can supply their own Embind surface while inheriting the
Emdawnwebgpu, Wasm-exception, and JSPI settings from `gpucpp`:

```cmake
add_subdirectory(path/to/gpu.cpp EXCLUDE_FROM_ALL)
add_executable(my_web_app app.cpp)
set_target_properties(my_web_app PROPERTIES SUFFIX ".js")
target_link_libraries(my_web_app PRIVATE gpucpp)
target_link_options(my_web_app PRIVATE "--bind" "--no-entry"
"-sMODULARIZE=1" "-sEXPORT_NAME=createModule" "-sENVIRONMENT=web"
"-sALLOW_MEMORY_GROWTH=1")
```

The Embind functions that call `createContext()`, `wait()`, or browser-facing
helpers containing them must use `emscripten::async()` so JSPI can suspend the
Wasm stack.

## Python

Install the self-contained macOS ARM64 wheel from PyPI with `pip install
Expand Down
17 changes: 7 additions & 10 deletions test
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail

external_spirv=
if [[ ${1:-} == --web || ${1:-} == --build-web || \
${1:-} == --rebuild-web ]]; then
if [[ ${1:-} == --rebuild-web || \
Expand All @@ -9,15 +10,6 @@ if [[ ${1:-} == --web || ${1:-} == --build-web || \
fi
export EMSDK_QUIET=1
source "${GPUCPP_EMSDK_ROOT:-../emsdk}/emsdk_env.sh" >/dev/null
if [[ $(uname -s) == Darwin ]]; then
if [[ -x /opt/homebrew/opt/openjdk/bin/java ]]; then
export PATH="/opt/homebrew/opt/openjdk/bin:$PATH"
fi
if ! java -version >/dev/null 2>&1; then
echo "Closure requires Java on macOS; install it with brew install openjdk" >&2
exit 1
fi
fi
emcmake cmake -S . -B build-web -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build-web --target gpu_cpp_web web_binding_test
if [[ ${1:-} != --build-web ]]; then
Expand All @@ -26,8 +18,10 @@ if [[ ${1:-} == --web || ${1:-} == --build-web || \
exit
elif [[ ${1:-} == --rebuild-dawn ]]; then
tools/build_dawn.sh
elif [[ ${1:-} == --spirv && $# -eq 2 ]]; then
external_spirv=$2
elif [[ $# -ne 0 ]]; then
echo "usage: ./test [--rebuild-dawn|--web|--build-web|--rebuild-web]" >&2
echo "usage: ./test [--rebuild-dawn|--web|--build-web|--rebuild-web|--spirv FILE]" >&2
exit 2
elif [[ ! -f third_party/dawn/include/dawn/webgpu_cpp.h &&
! -f third_party/local/dawn/build-latest/gen/include/dawn/webgpu_cpp.h ]]; then
Expand All @@ -37,3 +31,6 @@ fi
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build --output-on-failure
if [[ -n $external_spirv ]]; then
build/gpu_test build/write42.spv "$external_spirv"
fi
17 changes: 16 additions & 1 deletion tests/gpu_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ static void expect(const std::vector<T> &actual,
}

int main(int argc, char **argv) {
if (argc != 2) throw std::runtime_error("expected assembled SPIR-V path");
if (argc != 2 && argc != 3)
throw std::runtime_error("expected assembled SPIR-V path");

// Ordinary WGSL covers upload, explicit binding access, dispatch, and readback.
auto context = createContext();
Expand Down Expand Up @@ -98,5 +99,19 @@ int main(int argc, char **argv) {
wait(spirvContext, answerDownloaded);
expect(answer, std::vector<int32_t>{42}, "SPIR-V compute");

if (argc == 3) {
std::vector<int32_t> externalAnswer(1);
auto externalOutput = createTensor(spirvContext, {1}, ki32);
auto external = createKernel(
spirvContext, SPIRV{readSPIRV(argv[2]), "external SPIR-V", "main"},
Bindings{readWrite(externalOutput)});
auto externalDispatched = dispatchKernel(spirvContext, external);
wait(spirvContext, externalDispatched);
auto externalDownloaded =
toCPU(spirvContext, externalOutput, externalAnswer);
wait(spirvContext, externalDownloaded);
expect(externalAnswer, std::vector<int32_t>{42}, "external SPIR-V compute");
}

std::cout << "WGSL, f16, and SPIR-V compute stories passed\n";
}
Loading