Skip to content

web: Add experimental wasm64 (Memory64) build target#23981

Closed
falpi wants to merge 1 commit into
ruffle-rs:masterfrom
falpi:add-wasm64-target
Closed

web: Add experimental wasm64 (Memory64) build target#23981
falpi wants to merge 1 commit into
ruffle-rs:masterfrom
falpi:add-wasm64-target

Conversation

@falpi

@falpi falpi commented Jun 15, 2026

Copy link
Copy Markdown

Motivation

The wasm32 build is capped at 4 GiB of linear memory, which is hit by
XML-heavy AS3 workloads (a real Flex 4.6 dataset OOMs at ~4.1 GB on
master). The Wasm Memory64 proposal is part of Wasm 3.0 and supported in
recent V8/SpiderMonkey, so a 64-bit build is now feasible.

This PR adds an opt-in wasm64 build next to the existing wasm32
extensions/MVP variants, with runtime selection unchanged.

What this PR is

An experimental, additive BUILD_WASM64=true npm run build:wasm64
that produces a Memory64 web build. Nothing about the existing wasm32
builds changes.

Validated end-to-end:

  • cargo build on wasm64-unknown-unknown (release profile)
  • wasm-bindgen (the 0.2.120 we pin already supports the wasm64 ABI)
  • wasm-opt --enable-memory64
  • Loading and rendering a real Flex 4.6 application

Output _bg.wasm is ~14 MB, with (memory $0 i64 …) confirming
Memory64.

The pattern

Ruffle's own code is pointer-width-agnostic — the only target_arch
references in core/src/ already use target_pointer_width. The
blockers were all dependencies with cfg gates written
target_arch = "wasm32" where the intent is "any WebAssembly".
On wasm64 those gates don't fire, web code paths get excluded, and
the build fails.

The fix is mechanical and identical for every affected crate:

target_arch = "wasm32"   →   target_family = "wasm"

What's included

  • web/packages/core/tools/build_wasm.ts: a buildWasm64 branch that
    invokes cargo build --target wasm64-unknown-unknown -Z build-std
    with RUSTC_BOOTSTRAP=1 (stable + bootstrap, no nightly toolchain
    required), then wasm-bindgen and wasm-opt --enable-memory64.

  • web/packages/core/tools/vendor_wasm64.ts: a small shim that fetches
    the affected crate sources (cargo cache offline-first, fallback to
    crates.io), widens their wasm32 cfg gates to target_family = "wasm",
    and regenerates a marker-fenced [patch.crates-io] block. Idempotent,
    fails loudly if a crate stops needing the patch (so it can be removed
    from the table). Per-crate skipPaths allow narrowing the widening so
    wasm32-specific intrinsic modules (e.g. jpeg-decoder's SIMD path, which
    uses std::arch::wasm32::* and has no wasm64 equivalent) stay intact.
    The vendor/ directory is gitignored — sources are re-derived from
    Cargo.lock at build time; Cargo.lock is realigned automatically
    when a new entry lands in the table. Currently patched:
    wgpu, wgpu-types, wgpu-core, wgpu-hal, wgpu-core-deps-wasm,
    glow, rfd, chrono (wasmbind gate), jpeg-decoder (multi-threaded
    worker not(wasm32)-gated).

  • web/src/getrandom_custom.rs: a getrandom custom backend routed
    to crypto.getRandomValues. Upstream's wasm64 fix
    (Add wasm64-unknown-unknown support for wasm_js backend rust-random/getrandom#848) only exists in 0.4.x, while rand 0.9
    pins 0.3.x — until a 0.3.x release ships it, we provide the symbol
    ourselves.

How it's designed to be removed

Each entry in VENDORED corresponds to one upstream cfg gate. When a
crate ships native wasm64 support, drop its row from the table — the
[patch.crates-io] block shrinks automatically. When the table is
empty, delete vendor_wasm64.ts, the call site in build_wasm.ts,
and the marker block in Cargo.toml. The shim is self-policing: if a
crate has 0 matches for the wasm32 gate, the script fails with a clear
message ("review and remove its VENDORED entry").

Caveats / known limitations

  • Experimental, opt-in. npm run build is unchanged.
  • Renderer validated with wgpu-webgl. The WebGPU backend should work
    the same way (same wgpu cfg gates are widened), but I haven't validated it.
  • The getrandom shim is sound (Web Crypto, same source as the
    wasm_js backend) but ideally lives upstream once 0.3.x ships Implement Avm1String #848.
  • Memory64 pointers are 2× wider; expect some overhead. Numbers TBD on
    representative workloads.
  • Browser support: Memory64 is in V8 and SpiderMonkey, not yet in
    Safari. The existing wasm32 builds remain the default fallback for everyone else.

Toolchain

Stable Rust (1.95+) with RUSTC_BOOTSTRAP=1 (the existing wasm web
build already uses this pattern). Requires rust-src for build-std.
Java in PATH for the playerglobal build (same as today).

Try it

rustup component add rust-src
cd web
npm run build:wasm64

Open questions for reviewers

  • Appetite for the vendor_wasm64.ts approach as an interim, vs.
    waiting for the upstreams to land their fixes?
  • Anything you'd like measured before this can move from "experimental"
    to "supported"?

Checklist

  • I, a human, have self-reviewed this PR and fully understand the changes within.
  • I have made or updated tests where possible.
  • All of my commits are properly scoped, compile successfully, and pass all tests.
  • This PR does not make sense to split up into smaller PRs.
  • An LLM was involved in the authoring of this code.

@falpi
falpi force-pushed the add-wasm64-target branch from 5108cb7 to fa97571 Compare June 15, 2026 08:09
@falpi
falpi marked this pull request as ready for review June 15, 2026 11:27
@falpi
falpi force-pushed the add-wasm64-target branch from fa97571 to 4b328cc Compare June 15, 2026 11:57

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is something we'll accept. The crate ecosystem should catch up instead.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The branch for my use case (a Flex Enterprise application with a lot of XML) is invaluable because it allows me to overcome the 4GB memory limit.

But obviously this PR is only to stimulate discussion about enabling the WASM64 target, which has now been officially announced. The Ruffle codebase is practically ready for the 64-bit build. The only issues concern external crates, which we hope will be fixed with the small adjustments needed to make them 64-bit compliant.

@falpi
falpi force-pushed the add-wasm64-target branch 4 times, most recently from 9b4592a to 6c005e9 Compare June 17, 2026 00:09
Build the web player for wasm64-unknown-unknown to lift the 4 GiB
linear-memory ceiling of wasm32, which OOMs on XML-heavy content.

Ruffle's own code is already pointer-width-agnostic; the blockers were
dependencies whose cfg gates are written `target_arch = "wasm32"` instead
of `target_family = "wasm"`, which excludes wasm64. This adds:

- A `build:wasm64` npm script and a `buildWasm64` path in build_wasm.ts
  (target wasm64-unknown-unknown, -Z build-std, wasm-opt --enable-memory64).
- tools/vendor_wasm64.ts: fetches and patches the affected crates (wgpu,
  wgpu-types, wgpu-core, wgpu-hal, wgpu-core-deps-wasm, glow, rfd, chrono,
  jpeg-decoder), widening their wasm32 gates and regenerating
  [patch.crates-io]. Vendored sources are gitignored and regenerated at
  build time; Cargo.lock is realigned automatically when a new entry lands
  in the table. Per-crate `skipPaths` allow narrowing the widening so
  wasm32-specific intrinsic modules (e.g. jpeg-decoder's SIMD path, which
  uses `std::arch::wasm32::*` and has no wasm64 equivalent) stay intact.
- A getrandom custom backend (web/src/getrandom_custom.rs) backed by
  crypto.getRandomValues, since getrandom's wasm64 fix only exists in 0.4.x
  while rand 0.9 pins 0.3.x.

All vendoring/patching is isolated and removable: as upstreams ship wasm64
support, drop entries from the VENDORED table until the shim can be deleted.

Experimental. Renderer: webgpu + webgl (ANGLE).
@falpi
falpi force-pushed the add-wasm64-target branch from 6c005e9 to af70b08 Compare June 19, 2026 21:34
@falpi

falpi commented Jun 21, 2026

Copy link
Copy Markdown
Author

I realize this PR is too specific to hope for acceptance. Similarly, other branches I'm developing, which implement heavy optimizations appropriate for enterprise Flex applications, will no longer be submitted as PRs but will remain available on my fork.

@falpi falpi closed this Jun 21, 2026
@SuchAFuriousDeath

SuchAFuriousDeath commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

I'm gonna try to slowly push this through, but it's gonna take a long time to bubble up into releases.

So far, I filed:
PolyMeilex/rfd#325
chronotope/chrono#1803

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants