From b2349cd9146d84cad5caef3eade4fae3cc28b293 Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Thu, 6 Aug 2026 02:59:12 -0400 Subject: [PATCH 1/3] HelloBaremetal: fix dead README_cmake.md link (drive-by) The referenced README_cmake.md doesn't exist; the cross-compiling section it pointed to lives in doc/HalideCMakePackage.md. Co-authored-by: Claude Sonnet 5 --- apps/HelloBaremetal/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/HelloBaremetal/README.md b/apps/HelloBaremetal/README.md index aa02e3fc8a4c..13168dc9c11f 100644 --- a/apps/HelloBaremetal/README.md +++ b/apps/HelloBaremetal/README.md @@ -37,15 +37,16 @@ located in `CMakeLists.txt` in the following sub directories. 3. cmake-external_project If you want to understand the detail of build steps, please read "Cross -compiling" section of [README_cmake](../../README_cmake.md#cross-compiling), and -then `build.sh` and `CMakeLists.txt` in each sub directories. +compiling" section of +[HalideCMakePackage](../../doc/HalideCMakePackage.md#cross-compiling), and then +`build.sh` and `CMakeLists.txt` in each sub directories. ### Build procedure #### Baremetal target As a prerequisite, toolchain described above needs to be installed in your host -machine. The detail of the toolchanin configuration is set in +machine. The detail of the toolchain configuration is set in [toolchain.arm-32-sample.cmake](cmake/toolchain.arm-32-sample.cmake), which you might need to modify depending on the target baremetal system. Then, just run the build script in one of the aforementioned sub directories. From 45e7b5ef62d06ad579acf50430c98e5328c619c2 Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Thu, 6 Aug 2026 04:40:08 -0400 Subject: [PATCH 2/3] add_halide_library: create OUTPUT_DIR if it does not exist Generators write their outputs there directly, so previously any OUTPUT_DIR other than an existing target directory (e.g. one already implied by another CMake target) failed with "No such file or directory". Drops the equivalent workaround from HelloWasm, which hit this since each of its wasm variants needs its own OUTPUT_DIR to avoid filename collisions. --- cmake/HalideGeneratorHelpers.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/HalideGeneratorHelpers.cmake b/cmake/HalideGeneratorHelpers.cmake index b0628d5b36e7..23712e172711 100644 --- a/cmake/HalideGeneratorHelpers.cmake +++ b/cmake/HalideGeneratorHelpers.cmake @@ -700,6 +700,7 @@ function(add_halide_library TARGET) cmake_path(SET ARG_OUTPUT_DIR "${ARG_OUTPUT_DIR}") cmake_path(ABSOLUTE_PATH ARG_OUTPUT_DIR BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" NORMALIZE) + file(MAKE_DIRECTORY "${ARG_OUTPUT_DIR}") if (ARG_NAMESPACE) set(ARG_FUNCTION_NAME "${ARG_NAMESPACE}::${ARG_FUNCTION_NAME}") From ecbf1b49781f9d8c8c56327b55097cf54969e92c Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Thu, 6 Aug 2026 00:31:50 -0400 Subject: [PATCH 3/3] Write a proper build for HelloWasm app The single-file CMakeLists.txt worked but hand-invoked emcc directly, manually reassembling include paths/flags instead of using CMake's own Emscripten toolchain integration, and never actually produced a working demo end-to-end (index.html was copied into the build dir, but main.js/ sw.js, which that copied index.html references, never were, so opening it from the build dir 404s). Replace it with a proper CMake super-build: generator/ builds the Halide generator with the host compiler; app/ is configured with Emscripten's own CMake toolchain file and builds core.cpp as an ordinary add_executable, linked against the AOT pipeline libraries via target_link_libraries -- no more hand-rolled emcc command line. index.html/main.js/sw.js get staged into the build dir alongside the compiled output, fixing the demo for real. Converting to a super-build means it can no longer be included directly from apps/CMakeLists.txt (same as HelloiOS), so it gets a dedicated build+test step in testing-macos.yml instead of riding along with the aggregate apps/ ctest run. Also moves reaction_diffusion_generator.cpp into generator/ and core.cpp into app/, matching every other multi-piece app in this repo. Fixes #8731 Co-authored-by: Claude Sonnet 5 --- .github/workflows/testing-macos.yml | 13 ++ apps/CMakeLists.txt | 1 + apps/HelloWasm/.gitignore | 3 - apps/HelloWasm/CMakeLists.txt | 150 +++++++----------- apps/HelloWasm/Makefile | 83 ---------- apps/HelloWasm/README.md | 63 ++++++-- apps/HelloWasm/app/CMakeLists.txt | 73 +++++++++ apps/HelloWasm/{ => app}/core.cpp | 0 apps/HelloWasm/generator/CMakeLists.txt | 48 ++++++ .../reaction_diffusion_generator.cpp | 0 10 files changed, 241 insertions(+), 193 deletions(-) delete mode 100644 apps/HelloWasm/.gitignore delete mode 100644 apps/HelloWasm/Makefile create mode 100644 apps/HelloWasm/app/CMakeLists.txt rename apps/HelloWasm/{ => app}/core.cpp (100%) create mode 100644 apps/HelloWasm/generator/CMakeLists.txt rename apps/HelloWasm/{ => generator}/reaction_diffusion_generator.cpp (100%) diff --git a/.github/workflows/testing-macos.yml b/.github/workflows/testing-macos.yml index f95a506db760..a67ef4dc77eb 100644 --- a/.github/workflows/testing-macos.yml +++ b/.github/workflows/testing-macos.yml @@ -232,3 +232,16 @@ jobs: run: | cmake -S . -B build -DHalide_TARGET=wasm-32-wasmrt-wasm_simd128-wasm_threads cmake --build build --target build_generator + + # HelloWasm is a super-build (not add_subdirectory()'d from apps/), so + # it needs its own configure/build/test rather than riding along with + # the "Configure/Build/Test apps" steps above. + - name: Configure HelloWasm + run: cmake -S . -B build -DCMAKE_PREFIX_PATH="${{ github.workspace }}/install" + working-directory: apps/HelloWasm + + - name: Build and test HelloWasm + run: | + cmake --build build + ctest --test-dir build --output-on-failure + working-directory: apps/HelloWasm diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index b75ba3246334..5365d7b1110a 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -60,6 +60,7 @@ add_app(harris) add_app(HelloBaremetal) # add_app(HelloiOS) # don't build HelloiOS here because it isn't universal. add_app(HelloPyTorch) +# add_app(HelloWasm) # driven by its own super-build; see apps/HelloWasm/README.md add_app(hexagon_benchmarks) add_app(hexagon_dma) add_app(hist) diff --git a/apps/HelloWasm/.gitignore b/apps/HelloWasm/.gitignore deleted file mode 100644 index 5a54ff823fc8..000000000000 --- a/apps/HelloWasm/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -js/* -build/ -bin/ diff --git a/apps/HelloWasm/CMakeLists.txt b/apps/HelloWasm/CMakeLists.txt index da9d7edff4ed..f8c0e042dddd 100644 --- a/apps/HelloWasm/CMakeLists.txt +++ b/apps/HelloWasm/CMakeLists.txt @@ -1,102 +1,62 @@ cmake_minimum_required(VERSION 3.28) -project(HelloWasm) - -enable_testing() - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED YES) -set(CMAKE_CXX_EXTENSIONS NO) - -find_package(Halide REQUIRED) -set(halide_includes "$") - -find_program(EMCC emcc REQUIRED HINTS "$ENV{EMSDK}/upstream/emscripten") - -configure_file(index.html index.html COPYONLY) - -set(EMCC_FLAGS -s WASM=1 -s USE_SDL=2 -s TOTAL_MEMORY=512MB -O3) -set(EMCC_FLAGS_threads -pthread -matomics) - -add_halide_generator(reaction_diffusion_generator SOURCES reaction_diffusion_generator.cpp) - -set(features_simd -wasm_simd128) -set(features_threads -wasm_threads) -set(params_threads threads=true) -set(params threads=false) -foreach (simd IN ITEMS "" "_simd") - foreach (threads IN ITEMS "" "_threads") - set(config "wasm${simd}${threads}") - set(js_file "js/index${simd}${threads}.js") - - set(target "wasm-32-wasmrt${features${simd}}${features${threads}}") - - foreach (gen IN ITEMS init update render) - add_halide_library( - ${config}_${gen} - FROM reaction_diffusion_generator - GENERATOR reaction_diffusion_${gen} - FUNCTION_NAME reaction_diffusion_${gen} - PARAMS ${params${threads}} - TARGETS ${target} - ) - endforeach () - - set(pipeline_includes "$") - add_custom_command( - OUTPUT ${js_file} - COMMAND - "${EMCC}" - "$" - "$" - "$" - "$" - "$" - ${EMCC_FLAGS} - ${EMCC_FLAGS${threads}} - "-I$" - "-I$" - -o "${js_file}" - VERBATIM - DEPENDS ${config}_init ${config}_update ${config}_render - COMMAND_EXPAND_LISTS - ) - - add_custom_target(${config} ALL DEPENDS ${js_file}) - endforeach () -endforeach () - -foreach (gen IN ITEMS init update render) - add_halide_library( - reaction_diffusion_${gen} - FROM reaction_diffusion_generator - REGISTRATION ${gen}_cpp - PARAMS threads=true - ) -endforeach () - -add_executable(run ${init_cpp} ${update_cpp} ${render_cpp}) -target_link_libraries( - run - PRIVATE - Halide::RunGenMain reaction_diffusion_init reaction_diffusion_update reaction_diffusion_render +project(HelloWasm-Super) + +include(ExternalProject) + +# Halide generators always run on the host, regardless of the target they +# emit code for -- only the final app (core.cpp, linked with em++) actually +# needs the Emscripten toolchain. This follows the "Use a super-build" +# pattern in doc/HalideCMakePackage.md (see also +# apps/HelloBaremetal/cmake-super_build/ for the same pattern applied to a +# baremetal cross-compile). + +# Prefer $EMSDK (set by sourcing emsdk_env.sh); PATH_SUFFIXES is what lets this +# find emcc without the PATH. +find_program(EMCC REQUIRED NAMES emcc HINTS ENV EMSDK PATH_SUFFIXES "upstream/emscripten") +get_filename_component(EMSCRIPTEN_ROOT "${EMCC}" DIRECTORY) +find_file( + EMSCRIPTEN_TOOLCHAIN_FILE REQUIRED + NAMES Emscripten.cmake + PATHS "${EMSCRIPTEN_ROOT}/cmake/Modules/Platform" + NO_DEFAULT_PATH ) -add_test( - NAME init_bench - COMMAND - run --benchmarks=all --benchmark_min_time=1 --name=reaction_diffusion_init - --output_extents=[1024,1024] --parsable_output +set(GEN_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/generator") + +# Default to Release (both sub-projects pick up -O3 this way, standard CMake +# practice, rather than hardcoding optimization flags). +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif () + +# Step 1: build the generator (and the native benchmark) with the host compiler. +ExternalProject_Add( + generator_project + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/generator + BINARY_DIR ${GEN_BINARY_DIR} + INSTALL_COMMAND "" + CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} + CONFIGURE_HANDLED_BY_BUILD TRUE ) -add_test( - NAME update_bench - COMMAND - run --benchmarks=all --benchmark_min_time=1 --name=reaction_diffusion_update - --output_extents=[1024,1024] frame=0 mouse_x=0 mouse_y=0 state=random:0:[1024,1024,3] - --parsable_output + +# Step 2: cross-compile the app with Emscripten, importing the generator +# built in step 1. +ExternalProject_Add( + app_project + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/app + BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/app + INSTALL_COMMAND "" + DEPENDS generator_project + CMAKE_ARGS + -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN_TOOLCHAIN_FILE} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} + -DHelloWasm-halide_generators_ROOT:FILEPATH=${GEN_BINARY_DIR} + CONFIGURE_HANDLED_BY_BUILD TRUE ) + +enable_testing() add_test( - NAME render_bench - COMMAND - run --benchmarks=all --benchmark_min_time=1 --name=reaction_diffusion_render - --output_extents=[1024,1024] state=random:0:[1024,1024,3] --parsable_output + NAME benchmarks + COMMAND ${CMAKE_CTEST_COMMAND} --test-dir ${GEN_BINARY_DIR} --output-on-failure ) diff --git a/apps/HelloWasm/Makefile b/apps/HelloWasm/Makefile deleted file mode 100644 index d8e89769f3bd..000000000000 --- a/apps/HelloWasm/Makefile +++ /dev/null @@ -1,83 +0,0 @@ -include ../support/Makefile.inc - -# The emscripten compiler -EMCC ?= emcc -EMCC_FLAGS ?= -std=c++17 -s WASM=1 -s USE_SDL=2 -s TOTAL_MEMORY=512MB -O3 -I $(HALIDE_DISTRIB_PATH)/include -EMCC_THREADS_FLAGS ?= $(EMCC_FLAGS) -pthread -matomics - -# the output dir for the .js products must be fixed, because that's what index.html looks for - -all: js/index.js js/index_simd.js js/index_threads.js js/index_simd_threads.js - -$(GENERATOR_BIN)/reaction_diffusion_generator: reaction_diffusion_generator.cpp $(GENERATOR_DEPS) - @mkdir -p $(@D) - $(CXX) $(CXXFLAGS) $(filter %.cpp,$^) -o $@ $(LIBHALIDE_LDFLAGS) - -$(BIN)/wasm/reaction_diffusion_%.a: $(GENERATOR_BIN)/reaction_diffusion_generator - @mkdir -p $(@D) - $< -g reaction_diffusion_$* -e $(GENERATOR_OUTPUTS) -o $(@D) target=wasm-32-wasmrt-no_runtime threads=false - -$(BIN)/wasm/runtime.a: $(GENERATOR_BIN)/reaction_diffusion_generator - @mkdir -p $(@D) - $< -r runtime -o $(@D) target=wasm-32-wasmrt - -$(BIN)/wasm_simd/reaction_diffusion_%.a: $(GENERATOR_BIN)/reaction_diffusion_generator - @mkdir -p $(@D) - $< -g reaction_diffusion_$* -e $(GENERATOR_OUTPUTS) -o $(@D) target=wasm-32-wasmrt-wasm_simd128-no_runtime threads=false - -$(BIN)/wasm_simd/runtime.a: $(GENERATOR_BIN)/reaction_diffusion_generator - @mkdir -p $(@D) - $< -r runtime -o $(@D) target=wasm-32-wasmrt-wasm_simd128 - -$(BIN)/wasm_threads/reaction_diffusion_%.a: $(GENERATOR_BIN)/reaction_diffusion_generator - @mkdir -p $(@D) - $< -g reaction_diffusion_$* -o $(@D) target=wasm-32-wasmrt-wasm_threads-no_runtime threads=true - -$(BIN)/wasm_threads/runtime.a: $(GENERATOR_BIN)/reaction_diffusion_generator - @mkdir -p $(@D) - $< -r runtime -o $(@D) target=wasm-32-wasmrt-wasm_threads - -$(BIN)/wasm_simd_threads/reaction_diffusion_%.a: $(GENERATOR_BIN)/reaction_diffusion_generator - @mkdir -p $(@D) - $< -g reaction_diffusion_$* -o $(@D) target=wasm-32-wasmrt-wasm_simd128-wasm_threads-no_runtime threads=true - -$(BIN)/wasm_simd_threads/runtime.a: $(GENERATOR_BIN)/reaction_diffusion_generator - @mkdir -p $(@D) - $< -r runtime -o $(@D) target=wasm-32-wasmrt-wasm_simd128-wasm_threads - -$(BIN)/$(HL_TARGET)/reaction_diffusion_%.a: $(GENERATOR_BIN)/reaction_diffusion_generator - @mkdir -p $(@D) - $< -g reaction_diffusion_$* -o $(@D) -e registration,static_library target=$(HL_TARGET)-no_runtime threads=true - -$(BIN)/$(HL_TARGET)/runtime.a: $(GENERATOR_BIN)/reaction_diffusion_generator - @mkdir -p $(@D) - $< -r runtime -o $(@D) target=$(HL_TARGET) - -js/index.js: core.cpp $(BIN)/wasm/reaction_diffusion_init.a $(BIN)/wasm/reaction_diffusion_update.a $(BIN)/wasm/reaction_diffusion_render.a $(BIN)/wasm/runtime.a - @mkdir -p $(@D) - $(EMCC) $^ $(EMCC_FLAGS) -I $(BIN)/wasm -o $@ - -js/index_simd.js: core.cpp $(BIN)/wasm_simd/reaction_diffusion_init.a $(BIN)/wasm_simd/reaction_diffusion_update.a $(BIN)/wasm_simd/reaction_diffusion_render.a $(BIN)/wasm_simd/runtime.a - @mkdir -p $(@D) - $(EMCC) $^ $(EMCC_FLAGS) -I $(BIN)/wasm_simd -o $@ - -js/index_threads.js: core.cpp $(BIN)/wasm_threads/reaction_diffusion_init.a $(BIN)/wasm_threads/reaction_diffusion_update.a $(BIN)/wasm_threads/reaction_diffusion_render.a $(BIN)/wasm_threads/runtime.a - @mkdir -p $(@D) - $(EMCC) $^ $(EMCC_THREADS_FLAGS) -I $(BIN)/wasm_threads -o $@ - -js/index_simd_threads.js: core.cpp $(BIN)/wasm_simd_threads/reaction_diffusion_init.a $(BIN)/wasm_simd_threads/reaction_diffusion_update.a $(BIN)/wasm_simd_threads/reaction_diffusion_render.a $(BIN)/wasm_simd_threads/runtime.a - @mkdir -p $(@D) - $(EMCC) $^ $(EMCC_THREADS_FLAGS) -I $(BIN)/wasm_simd_threads -o $@ - -clean: - rm -rf js - rm -rf $(BIN) - -$(BIN)/$(HL_TARGET)/run: $(BIN)/$(HL_TARGET)/reaction_diffusion_init.a $(BIN)/$(HL_TARGET)/reaction_diffusion_update.a $(BIN)/$(HL_TARGET)/reaction_diffusion_render.a $(BIN)/$(HL_TARGET)/runtime.a - mkdir -p $(@D) - $(CXX) $(CXXFLAGS) $(HALIDE_DISTRIB_PATH)/tools/RunGenMain.cpp $(BIN)/$(HL_TARGET)/reaction_diffusion_*.registration.cpp $^ -o $@ -I $(HALIDE_DISTRIB_PATH)/include $(IMAGE_IO_FLAGS) $(LDFLAGS) - -benchmark_native: $(BIN)/$(HL_TARGET)/run - $(BIN)/$(HL_TARGET)/run --benchmarks=all --benchmark_min_time=1 --name=reaction_diffusion_init --output_extents=[1024,1024] --parsable_output - $(BIN)/$(HL_TARGET)/run --benchmarks=all --benchmark_min_time=1 --name=reaction_diffusion_update --output_extents=[1024,1024] frame=0 mouse_x=0 mouse_y=0 state=random:0:[1024,1024,3] --parsable_output - $(BIN)/$(HL_TARGET)/run --benchmarks=all --benchmark_min_time=1 --name=reaction_diffusion_render --output_extents=[1024,1024] state=random:0:[1024,1024,3] --parsable_output diff --git a/apps/HelloWasm/README.md b/apps/HelloWasm/README.md index 810423300dfa..707a9cdcd49a 100644 --- a/apps/HelloWasm/README.md +++ b/apps/HelloWasm/README.md @@ -1,19 +1,58 @@ -This app is a demo of using the Halide webassembly backend +# HelloWasm -To try it out, +A demonstration of integrating a Halide pipeline into a WebAssembly application: +a reaction-diffusion simulation, AOT-compiled by Halide to four WASM variants +(scalar/SIMD x single/multi-threaded), linked into a browser demo with +Emscripten, and switched between at runtime based on what the browser actually +supports. -- install emscripten (see emscripten.org), and have emcc in your path. +## How the pieces fit together -- Build with `make all` +- `generator/reaction_diffusion_generator.cpp` defines the pipeline (three + Halide generators: `init`, `update`, `render`). Generators always run on the + *host*, regardless of what target they emit code for -- this is true even when + that target is `wasm-32-wasmrt`. +- `app/core.cpp` is the actual WASM application: an SDL2 program (compiled by + Emscripten) that calls into the AOT-compiled pipeline each frame and blits the + result to a ``. +- Because the generator needs a host compiler and `core.cpp` needs the + Emscripten compiler, this is a CMake ["super-build"][super-build]: the + top-level `CMakeLists.txt` builds `generator/` with the host compiler first, + then configures `app/` under Emscripten's own CMake toolchain file, importing + the host-built generator to emit the pipeline for four `wasm-32-wasmrt` + variants (`FEATURES wasm_simd128`/`wasm_threads`, or neither). +- `index.html` picks one of the four compiled variants at load time based on + `?simd`/`?threads` URL parameters, falling back to the single-threaded scalar + build by default. -- Run a local webserver using, e.g.: python3 -m http.server 8080 & +## Building -- Load Google chrome (at least version 84), go to chrome://flags, and turn on - all the experimental webassembly stuff (e.g. threads, simd). If you don't do - this, only the single-threaded scalar variant will work (at the time of - writing). +Prerequisites: a Halide install (built via CMake -- see +`doc/BuildingHalideWithCMake.md`) and the [Emscripten SDK][emsdk]. The build +finds `emcc` via `$EMSDK` if you've sourced `emsdk_env.sh`, or otherwise via +`PATH`. -- Visit http://127.0.0.1:8080/index.html +``` +cmake -S apps/HelloWasm -B build -DCMAKE_PREFIX_PATH= +cmake --build build +ctest --test-dir build --output-on-failure +``` -- Finally, run `make benchmark_native` and compare the performance you get with - native code +All build output (the four `index*.js`/`.wasm` pairs, plus copies of +`index.html`/`main.js`/`sw.js`) lands under `build/app/` -- nothing is ever +written back into the source tree. + +## Running the demo + +``` +cd build/app +python3 -m http.server 8080 +``` + +Then visit . The single-threaded scalar +variant should work in any modern browser; the SIMD and multi-threaded variants +may require enabling experimental WebAssembly features (e.g. in Chrome, +`chrome://flags`). + +[emsdk]: https://emscripten.org/docs/getting_started/downloads.html +[super-build]: ../../doc/HalideCMakePackage.md#use-a-super-build diff --git a/apps/HelloWasm/app/CMakeLists.txt b/apps/HelloWasm/app/CMakeLists.txt new file mode 100644 index 000000000000..447e8d1c74a2 --- /dev/null +++ b/apps/HelloWasm/app/CMakeLists.txt @@ -0,0 +1,73 @@ +cmake_minimum_required(VERSION 3.28) +project(HelloWasm) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS NO) + +# Halide and the generator package are both host-built config packages, not +# part of the Emscripten sysroot the toolchain file restricts find_package() +# to by default. +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) +find_package(Halide REQUIRED) +find_package(Threads REQUIRED) + +# Import the generator built by the host-side sub-project. +find_package(HelloWasm-halide_generators REQUIRED) +set(GEN_TARGET HelloWasm::halide_generators::reaction_diffusion_generator) + +# Keep the static assets next to the build outputs they reference (CMake +# practice: nothing gets written back into the source tree). +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../index.html index.html COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../main.js main.js COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../sw.js sw.js COPYONLY) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/js") + +# No explicit TARGETS on any add_halide_library call below: auto-resolved +# from the ambient CMAKE_SYSTEM_NAME=Emscripten/CMAKE_SYSTEM_PROCESSOR=x86 the +# Emscripten toolchain file sets, to wasm-32-wasmrt. +foreach (simd IN ITEMS "" "_simd") + foreach (threads IN ITEMS "" "_threads") + set(config "index${simd}${threads}") + + set(features "") + if (simd) + list(APPEND features wasm_simd128) + endif () + set(is_threads false) + if (threads) + list(APPEND features wasm_threads) + set(is_threads true) + endif () + + # FILE_BASE_NAME/OUTPUT_DIR: core.cpp #includes the fixed name + # "reaction_diffusion_.h" regardless of variant, so each variant + # needs its own output directory to avoid collisions (FILE_BASE_NAME + # would otherwise default to the per-variant TARGET name instead). + foreach (gen IN ITEMS init update render) + add_halide_library( + ${config}_${gen} + FROM ${GEN_TARGET} + GENERATOR reaction_diffusion_${gen} + FUNCTION_NAME reaction_diffusion_${gen} + FILE_BASE_NAME reaction_diffusion_${gen} + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/${config}" + FEATURES ${features} + PARAMS threads=${is_threads} + ) + endforeach () + + add_executable(${config} core.cpp) + target_link_libraries(${config} PRIVATE ${config}_init ${config}_update ${config}_render) + # -O3 here (not just relying on CMAKE_BUILD_TYPE=Release's compile + # flags) because Emscripten's link step does its own codegen/Binaryen + # optimization passes that CMake's default per-config linker flags + # don't cover. + target_link_options(${config} PRIVATE -sWASM=1 -sUSE_SDL=2 -sTOTAL_MEMORY=512MB -O3) + if (is_threads) + target_link_libraries(${config} PRIVATE Threads::Threads) + target_link_options(${config} PRIVATE -matomics) + endif () + endforeach () +endforeach () diff --git a/apps/HelloWasm/core.cpp b/apps/HelloWasm/app/core.cpp similarity index 100% rename from apps/HelloWasm/core.cpp rename to apps/HelloWasm/app/core.cpp diff --git a/apps/HelloWasm/generator/CMakeLists.txt b/apps/HelloWasm/generator/CMakeLists.txt new file mode 100644 index 000000000000..3a062c376641 --- /dev/null +++ b/apps/HelloWasm/generator/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 3.28) +project(HelloWasm) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS NO) + +find_package(Halide REQUIRED) + +add_halide_generator(reaction_diffusion_generator SOURCES reaction_diffusion_generator.cpp) + +foreach (gen IN ITEMS init update render) + add_halide_library( + reaction_diffusion_${gen} + FROM reaction_diffusion_generator + REGISTRATION ${gen}_cpp + PARAMS threads=true + ) +endforeach () + +add_executable(run ${init_cpp} ${update_cpp} ${render_cpp}) +target_link_libraries( + run + PRIVATE + Halide::RunGenMain reaction_diffusion_init reaction_diffusion_update reaction_diffusion_render +) + +enable_testing() + +add_test( + NAME init_bench + COMMAND + run --benchmarks=all --benchmark_min_time=1 --name=reaction_diffusion_init + --output_extents=[1024,1024] --parsable_output +) +add_test( + NAME update_bench + COMMAND + run --benchmarks=all --benchmark_min_time=1 --name=reaction_diffusion_update + --output_extents=[1024,1024] frame=0 mouse_x=0 mouse_y=0 state=random:0:[1024,1024,3] + --parsable_output +) +add_test( + NAME render_bench + COMMAND + run --benchmarks=all --benchmark_min_time=1 --name=reaction_diffusion_render + --output_extents=[1024,1024] state=random:0:[1024,1024,3] --parsable_output +) diff --git a/apps/HelloWasm/reaction_diffusion_generator.cpp b/apps/HelloWasm/generator/reaction_diffusion_generator.cpp similarity index 100% rename from apps/HelloWasm/reaction_diffusion_generator.cpp rename to apps/HelloWasm/generator/reaction_diffusion_generator.cpp