diff --git a/.github/workflows/build_steps.sh b/.github/workflows/build_steps.sh index 6641b9c3..7286aca6 100755 --- a/.github/workflows/build_steps.sh +++ b/.github/workflows/build_steps.sh @@ -211,8 +211,23 @@ build_test_wasm() { source emsdk/emsdk_env.sh export PATH="$(pwd)/node_modules/.bin:$PATH" - emcmake cmake -B build_wasm -DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF $WASM_CMAKE_ARGS - cmake --build build_wasm + echo "Building Multi-Threaded version..." + emcmake cmake -B build_wasm_mt \ + -DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF \ + -DMUJOCO_WASM_THREADS=ON \ + $WASM_CMAKE_ARGS + cmake --build build_wasm_mt --parallel $(nproc) + + echo "Moving Multi-Thread version under mt subfolder..." + mkdir -p wasm/dist/mt + mv wasm/dist/mujoco.* wasm/dist/mt/ + + echo "Building Single-Threaded version..." + emcmake cmake -B build_wasm_st \ + -DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF \ + -DMUJOCO_WASM_THREADS=OFF \ + $WASM_CMAKE_ARGS + cmake --build build_wasm_st --parallel $(nproc) npm run test --prefix ./wasm } diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt index b6e28b30..909401d1 100644 --- a/wasm/CMakeLists.txt +++ b/wasm/CMakeLists.txt @@ -31,11 +31,11 @@ if(NOT MUJOCO_WASM_FILES) message(FATAL_ERROR "No source files found in codegen/generated/") endif() +option(MUJOCO_WASM_THREADS "Build with multi-threading support" ON) + # Set Emscripten linker flags set(EMCC_LINKER_FLAGS "--bind" - "-pthread" - "-s PTHREAD_POOL_SIZE=navigator.hardwareConcurrency" "-s ASSERTIONS=1" "-s ALLOW_MEMORY_GROWTH=1" "-s EXPORT_ES6=1" @@ -48,6 +48,13 @@ set(EMCC_LINKER_FLAGS "-g" "--emit-tsd mujoco.d.ts" ) +if(MUJOCO_WASM_THREADS) + list(APPEND EMCC_LINKER_FLAGS + "-pthread" + "-s PTHREAD_POOL_SIZE=navigator.hardwareConcurrency" + ) + add_definitions(-DMUJOCO_WASM_THREADS) +endif() string (REPLACE ";" " " EMCC_LINKER_FLAGS_STR "${EMCC_LINKER_FLAGS}") add_executable(mujoco_wasm ${MUJOCO_WASM_FILES}) diff --git a/wasm/README.md b/wasm/README.md index 2c2595b3..1442f082 100644 --- a/wasm/README.md +++ b/wasm/README.md @@ -19,6 +19,7 @@ TypeScript. > (installation succeeded on one Windows 11 machine but failed on others)._ ## Installation + The easiest way to use the MuJoCo JavaScript bindings is to install the `@mujoco/mujoco` package from npm: @@ -30,6 +31,37 @@ This package is ESM (`type: module`) and includes the pre-compiled WebAssembly module, JavaScript bindings, and TypeScript declarations. Ensure your bundler or dev server serves the `.wasm` asset at runtime. +### Threading Models + +The `@mujoco/mujoco` package includes two distinct builds of the engine to +support different browser environments and performance needs. + +#### 1. Single-Threaded (Default) + +The standard single-threaded version is located at the root of the package. It +is compatible with all modern browsers and does not require special security +headers. +```typescript +import loadMujoco from '@mujoco/mujoco'; +``` + +#### 2. Multi-Threaded (MT) + +The multi-threaded version is located in the `/mt` subfolder. It utilizes Web +Workers and `SharedArrayBuffer` to parallelize physics computations. +```typeScript +import loadMujoco from '@mujoco/mujoco/mt'; +``` + +> [!NOTE] +> Due to the use of `SharedArrayBuffer`, browsers require Cross-Origin Isolation +> to enable multi-threading. Your web server must send the following HTTP +> headers: +> - `Cross-Origin-Opener-Policy: same-origin` +> - `Cross-Origin-Embedder-Policy: require-corp` +> +> If these headers are missing, the module will fail to initialize. + ## Build from source ### Prerequisites @@ -98,6 +130,14 @@ This command will generate the following folders under the project root: - `build`: contains MuJoCo compiled using Emscripten. - `wasm/dist`: contains the WebAssembly module, `.js` and `.d.ts` files. +The assets inside those folders are compiled and prepared to run as +single-threaded. If you need to operate with a multi-threaded version of the +module make sure to pass the `-DMUJOCO_WASM_THREADS=ON` flag like: + +```sh +emcmake cmake -B build -DMUJOCO_WASM_THREADS=ON && cmake --build build +``` + ### Example Application After generating the bindings you will be ready to write web applications using diff --git a/wasm/package.npm.json b/wasm/package.npm.json index 6301897d..68cbf0b8 100644 --- a/wasm/package.npm.json +++ b/wasm/package.npm.json @@ -24,13 +24,31 @@ "javascript", "typescript" ], - "main": "mujoco.js", - "types": "mujoco.d.ts", + "exports": { + ".": { + "types": "./mujoco.d.ts", + "import": "./mujoco.js", + "default": "./mujoco.js" + }, + "./mt": { + "types": "./mt/mujoco.d.ts", + "import": "./mt/mujoco.js", + "default": "./mt/mujoco.js" + }, + "./mujoco.wasm": "./mujoco.wasm", + "./mujoco.wasm.map": "./mujoco.wasm.map", + "./mt/mujoco.wasm": "./mt/mujoco.wasm", + "./mt/mujoco.wasm.map": "./mt/mujoco.wasm.map" + }, "files": [ "mujoco.js", "mujoco.d.ts", "mujoco.wasm", "mujoco.wasm.map", + "mt/mujoco.js", + "mt/mujoco.d.ts", + "mt/mujoco.wasm", + "mt/mujoco.wasm.map", "README.md" ], "author": "Google DeepMind",