From 16a1139eab3e822dfc8ad2efba4dbc0a64bac7e9 Mon Sep 17 00:00:00 2001 From: funman300 Date: Mon, 6 Jul 2026 15:44:16 -0700 Subject: [PATCH] perf(web): size-focused wasm-release profile for the canvas build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit canvas_bg.wasm shipped at 36.2 MB — plain release (opt-level 3, thin LTO) piped through wasm-opt -O2. Download size, not throughput, is the binding constraint for the browser canvas, so solitaire_web now builds with a dedicated wasm-release profile: opt-level "s", fat LTO, one codegen unit. Local result: 23.2 MB after the unchanged wasm-opt -O2 pass — 35.9% smaller. The binaryen pass stays at -O2 (the -Oz grey-screen miscompile note in build_wasm.sh still applies). profile.strip is deliberately NOT set: on wasm it also removes the target_features custom section, which makes wasm-opt reject the module ('all used features should be allowed' on trunc_sat). Verified with the new artifact: all 5 play_canvas e2e specs pass (debug bridge, draw3 param, apply/undo, replay diagnostics, 40-seed autoplay invariants). Headless pixel verification is inconclusive in this environment — wgpu cannot create a usable adapter locally and panics identically on the OLD artifact too — so visual parity should be confirmed against production after the next deploy. pkg/ artifacts are intentionally not committed: the web-wasm-rebuild workflow is the single source of truth and will regenerate them with this profile on merge. Co-Authored-By: Claude Fable 5 --- Cargo.toml | 16 ++++++++++++++++ build_wasm.sh | 6 ++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3ddcf9e..2a7f490 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -156,3 +156,19 @@ opt-level = 3 [profile.release] opt-level = 3 lto = "thin" + +# Size-focused profile for the browser canvas build (solitaire_web → +# canvas_bg.wasm). Download size is the constraint on the web, not peak +# throughput: fat LTO + one codegen unit + opt-level "s" cut the Bevy wasm +# bundle substantially versus plain release. This is rustc-side sizing only — +# the binaryen pass in build_wasm.sh stays at wasm-opt -O2 because -Oz has +# miscompiled Bevy's render pipeline before (grey screen on first load). +[profile.wasm-release] +inherits = "release" +opt-level = "s" +lto = "fat" +codegen-units = 1 +# No `strip`: on wasm it also removes the target_features custom section, +# which makes wasm-opt reject the module ("all used features should be +# allowed" on trunc_sat). wasm-opt drops the name section in its output +# anyway, so strip buys nothing here. diff --git a/build_wasm.sh b/build_wasm.sh index 995d17d..cff37ab 100755 --- a/build_wasm.sh +++ b/build_wasm.sh @@ -67,7 +67,9 @@ if ! command -v wasm-bindgen &> /dev/null; then fi echo "Building solitaire_web (Bevy WASM app)..." -cargo build --release --target wasm32-unknown-unknown -p solitaire_web +# wasm-release is the size-focused profile (fat LTO, CGU=1, opt-level "s") — +# see [profile.wasm-release] in Cargo.toml. Download size is the constraint. +cargo build --profile wasm-release --target wasm32-unknown-unknown -p solitaire_web echo "Running wasm-bindgen for solitaire_web..." wasm-bindgen \ @@ -75,7 +77,7 @@ wasm-bindgen \ --out-name canvas \ --target web \ --no-typescript \ - "$REPO_ROOT/target/wasm32-unknown-unknown/release/solitaire_web.wasm" + "$REPO_ROOT/target/wasm32-unknown-unknown/wasm-release/solitaire_web.wasm" # Optional size optimisation — Bevy bundles are large (~5-15 MB uncompressed). # wasm-opt passes are skipped silently when the tool is not installed.