From 6cb64ab6c2f9455ae3c88d8eea394b1e6a2196fe Mon Sep 17 00:00:00 2001 From: Jeremy Howard Date: Fri, 21 Aug 2026 17:18:56 +1000 Subject: [PATCH 1/2] Simplify browser builds and SPIR-V testing --- CHANGELOG.md | 1 + CMakeLists.txt | 5 +---- DEV.md | 9 ++++----- README.md | 20 ++++++++++++++++++-- test | 17 +++++++---------- tests/gpu_test.cpp | 17 ++++++++++++++++- 6 files changed, 47 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afd1f69..0839174 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/CMakeLists.txt b/CMakeLists.txt index 6363cc5..a1d4d65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/DEV.md b/DEV.md index d3d4d1e..fc0b476 100644 --- a/DEV.md +++ b/DEV.md @@ -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. @@ -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 diff --git a/README.md b/README.md index d211ff1..588a9db 100644 --- a/README.md +++ b/README.md @@ -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 ``` @@ -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 diff --git a/test b/test index e704673..9b9db17 100755 --- a/test +++ b/test @@ -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 || \ @@ -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 @@ -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 @@ -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 diff --git a/tests/gpu_test.cpp b/tests/gpu_test.cpp index 3b0dc68..bec98cb 100644 --- a/tests/gpu_test.cpp +++ b/tests/gpu_test.cpp @@ -51,7 +51,8 @@ static void expect(const std::vector &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(); @@ -98,5 +99,19 @@ int main(int argc, char **argv) { wait(spirvContext, answerDownloaded); expect(answer, std::vector{42}, "SPIR-V compute"); + if (argc == 3) { + std::vector 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{42}, "external SPIR-V compute"); + } + std::cout << "WGSL, f16, and SPIR-V compute stories passed\n"; } From 0246970fd2bf34d6b27434039ad26ffee91718fb Mon Sep 17 00:00:00 2001 From: Jeremy Howard Date: Fri, 21 Aug 2026 17:25:57 +1000 Subject: [PATCH 2/2] Use managed Python in CI --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f4f2995..269b7f2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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