Skip to content

web: Build two WASM modules, with/without extensions, load the appropriate one #5834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 12, 2022
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
2 changes: 0 additions & 2 deletions .cargo/config.toml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/release_nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ jobs:
shell: bash -l {0}
run: |
npm ci
npm run build
npm run build:dual-wasm
npm run docs

- name: Sign Firefox extension
Expand Down
2 changes: 2 additions & 0 deletions web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ In this project, you may run the following commands to build all packages:
- Output will be available in the `dist/` of each package (for example, `./packages/selfhosted/dist`),
save for the extension which is directory `build/`.
- You may also use `npm run build:debug` to disable Webpack optimizations and activate the (extremely verbose) ActionScript debugging output.
- There is `npm run build:dual-wasm` as well, to build a second WebAssembly module that makes use of some WebAssembly extensions,
potentially resulting in better performance in browsers that support them, at the expense of longer build time.

From here, you may follow the instructions to [use Ruffle on your website](packages/selfhosted/README.md),
or run a demo locally with `npm run demo`.
Expand Down
1 change: 1 addition & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"scripts": {
"build": "npm run build --workspaces",
"build:debug": "cross-env NODE_ENV=development CARGO_FEATURES=avm_debug npm run build",
"build:dual-wasm": "cross-env ENABLE_WASM_EXTENSIONS=true npm run build",
"demo": "npm run serve --workspace ruffle-demo",
"test": "npm run test --workspaces --if-present",
"docs": "npm run docs --workspaces --if-present",
Expand Down
27 changes: 24 additions & 3 deletions web/packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,35 @@
"pkg/"
],
"scripts": {
"build": "npm run build:cargo && npm run build:wasm-bindgen && npm run build:wasm-opt && npm run build:ts",
"build": "npm run build:ruffle_web && npm run build:ruffle_web-wasm_extensions && npm run build:ts",

"//1": "# Setting ENABLE_WASM_EXTENSIONS=true causes a second module to be built as well, that utilizes WebAssembly extensions, instead of it just being a 'fake' - a copy of the 'vanilla' one.",
"//2": "# Unfortunately, we have to set $RUSTFLAGS here, instead of in .cargo/config.toml (for example), because it's not possible to specify them per-profile; see cargo issue #7878.",
"//3": "# Enabling 'build-std' would also be great, but it's not stable yet.",

"build:ruffle_web": "cross-env OUT_NAME=ruffle_web RUSTFLAGS=\"--cfg=web_sys_unstable_apis\" npm run build:cargo_bindgen_opt",

"//4": "# Dispatches to either building the real, or copying the fake (stand-in), 'with-extensions' module.",
"build:ruffle_web-wasm_extensions": "node -e \"process.exit(process.env.ENABLE_WASM_EXTENSIONS == 'true' ? 0 : 1)\" && npm run build:ruffle_web-wasm_extensions-real || npm run build:ruffle_web-wasm_extensions-fake",
"build:ruffle_web-wasm_extensions-real": "echo \"Building module with WebAssembly extensions\" && cross-env OUT_NAME=ruffle_web-wasm_extensions RUSTFLAGS=\"--cfg=web_sys_unstable_apis -C target-feature=+bulk-memory,+simd128,+nontrapping-fptoint,+sign-ext\" npm run build:cargo_bindgen_opt",
"build:ruffle_web-wasm_extensions-fake": "echo \"Copying the vanilla module as stand-in\" && shx cp ./pkg/ruffle_web_bg.wasm ./pkg/ruffle_web-wasm_extensions_bg.wasm && shx cp ./pkg/ruffle_web_bg.wasm.d.ts ./pkg/ruffle_web-wasm_extensions_bg.wasm.d.ts && shx cp ./pkg/ruffle_web.js ./pkg/ruffle_web-wasm_extensions.js && shx cp ./pkg/ruffle_web.d.ts ./pkg/ruffle_web-wasm_extensions.d.ts",

"//5": "# This just chains together the three commands after it.",
"build:cargo_bindgen_opt": "npm run build:cargo && npm run build:wasm-bindgen && npm run build:wasm-opt",

"build:cargo": "cross-env-shell \"cargo build --release --target wasm32-unknown-unknown --features \\\"$CARGO_FEATURES\\\"\"",
"build:wasm-bindgen": "wasm-bindgen ../../../target/wasm32-unknown-unknown/release/ruffle_web.wasm --target web --out-dir ./pkg --out-name ruffle_web",
"build:wasm-opt": "wasm-opt -o ./pkg/ruffle_web_bg.wasm -O -g ./pkg/ruffle_web_bg.wasm || npm run build:wasm-opt-failed",
"build:wasm-bindgen": "cross-env-shell wasm-bindgen \"../../../target/wasm32-unknown-unknown/release/ruffle_web.wasm\" --target web --out-dir ./pkg --out-name \"$OUT_NAME\"",
"build:wasm-opt": "cross-env-shell wasm-opt -o \"./pkg/${OUT_NAME}_bg.wasm\" -O -g \"./pkg/${OUT_NAME}_bg.wasm\" || npm run build:wasm-opt-failed",

"build:wasm-opt-failed": "echo 'NOTE: Since wasm-opt could not be found (or it failed), the resulting module might not perform that well, but it should still work.' && echo ; [ \"$CI\" != true ] # > nul",

"build:ts": "tsc -d && node tools/set_version.js",
"docs": "typedoc",
"test": "cross-env TS_NODE_COMPILER_OPTIONS={\\\"module\\\":\\\"commonjs\\\"} mocha"
},
"dependencies": {
"wasm-feature-detect": "^1.2.11"
},
"devDependencies": {
"@types/mocha": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^5.8.1",
Expand All @@ -26,6 +46,7 @@
"eslint-plugin-jsdoc": "^37.1.0",
"mocha": "^9.1.3",
"replace-in-file": "^6.3.2",
"shx": "^0.3.3",
"ts-node": "^10.4.0",
"typedoc": "^0.22.10",
"typescript": "^4.5.2"
Expand Down
38 changes: 35 additions & 3 deletions web/packages/core/src/load-ruffle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
* Conditional ruffle loader
*/

import init, { Ruffle } from "../pkg/ruffle_web";
import {
bulkMemory,
simd,
saturatedFloatToInt,
signExtensions,
} from "wasm-feature-detect";
import { setPolyfillsOnLoad } from "./js-polyfills";
import { publicPath } from "./public-path";
import { Config } from "./config";
Expand All @@ -27,13 +32,40 @@ async function fetchRuffle(config: Config): Promise<typeof Ruffle> {
// libraries, if needed.
setPolyfillsOnLoad();

// NOTE: Keep this list in sync with $RUSTFLAGS in the CI build config!
const extensionsSupported: boolean = (
await Promise.all([
bulkMemory(),
simd(),
saturatedFloatToInt(),
signExtensions(),
])
).every(Boolean);

if (!extensionsSupported) {
console.log(
"Some WebAssembly extensions are NOT available, falling back to the vanilla WebAssembly module"
);
}

__webpack_public_path__ = publicPath(config);

// Note: The argument passed to import() has to be a simple string literal,
// otherwise some bundler will get confused and won't include the module?
const { default: init, Ruffle } = await (extensionsSupported
? import("../pkg/ruffle_web-wasm_extensions")
: import("../pkg/ruffle_web"));

await init();

return Ruffle;
}

let lastLoaded: Promise<typeof Ruffle> | null = null;
type Ruffle =
| typeof import("../pkg/ruffle_web")["Ruffle"]
| typeof import("../pkg/ruffle_web-wasm_extensions")["Ruffle"];

let lastLoaded: Promise<Ruffle> | null = null;

/**
* Obtain an instance of `Ruffle`.
Expand All @@ -44,7 +76,7 @@ let lastLoaded: Promise<typeof Ruffle> | null = null;
* @returns A ruffle constructor that may be used to create new Ruffle
* instances.
*/
export function loadRuffle(config: Config): Promise<typeof Ruffle> {
export function loadRuffle(config: Config): Promise<Ruffle> {
if (lastLoaded == null) {
lastLoaded = fetchRuffle(config);
}
Expand Down
6 changes: 5 additions & 1 deletion web/packages/core/src/ruffle-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,11 @@ export class RufflePlayer extends HTMLElement {
this,
config
);
console.log("New Ruffle instance created.");
console.log(
"New Ruffle instance created (WebAssembly extensions: " +
(ruffleConstructor.is_wasm_simd_used() ? "ON" : "OFF") +
")"
);

// In Firefox, AudioContext.state is always "suspended" when the object has just been created.
// It may change by itself to "running" some milliseconds later. So we need to wait a little
Expand Down
2 changes: 1 addition & 1 deletion web/packages/core/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"extends": "@tsconfig/recommended/tsconfig.json",
"compilerOptions": {
"target": "es2017",
"module": "es2015",
"module": "es2020",
"moduleResolution": "node",
"strict": true,
"outDir": "pkg",
Expand Down
2 changes: 1 addition & 1 deletion web/packages/extension/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"extends": "@tsconfig/recommended/tsconfig.json",
"compilerOptions": {
"target": "es2017",
"module": "es2015",
"module": "es2020",
"moduleResolution": "node",
"strict": true,
},
Expand Down
9 changes: 9 additions & 0 deletions web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,15 @@ impl Ruffle {
})
.unwrap_or_default()
}

/// Returns whether the `simd128` target feature was enabled at build time.
/// This is intended to discriminate between the two WebAssembly module
/// versions, one of which uses WebAssembly extensions, and the other one
/// being "vanilla". `simd128` is used as proxy for most extensions, since
/// no other WebAssembly target feature is exposed to `cfg!`.
pub fn is_wasm_simd_used() -> bool {
cfg!(target_feature = "simd128")
}
}

impl Ruffle {
Expand Down