From cc631623d0bbc8a24f21859d583558f6b47efa15 Mon Sep 17 00:00:00 2001 From: mes123456 Date: Fri, 4 Sep 2026 12:12:53 +0800 Subject: [PATCH] feat(web): generate split wasm debug secondary modules --- my-tools/authorize_next_wbv2_task.py | 15 ++++- tools/web/split-wbv2-wasm.mjs | 56 ++++++++++++++++++ .../modules/pthread/web_engine.secondary.wasm | Bin 0 -> 8 bytes .../modules/single/web_engine.secondary.wasm | Bin 0 -> 80 bytes web/app/public/wasm-module-manifest.json | 41 +++++++++++-- 5 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 tools/web/split-wbv2-wasm.mjs create mode 100644 web/app/public/vendor/blender/modules/pthread/web_engine.secondary.wasm create mode 100644 web/app/public/vendor/blender/modules/single/web_engine.secondary.wasm diff --git a/my-tools/authorize_next_wbv2_task.py b/my-tools/authorize_next_wbv2_task.py index 71200a08d..02bc86eb7 100644 --- a/my-tools/authorize_next_wbv2_task.py +++ b/my-tools/authorize_next_wbv2_task.py @@ -575,7 +575,7 @@ def recover_blocked_native_batch(current: str, index: dict, plan: dict, inventor "positive": [f"{name} type is read from Blender Main" for name in batch["identifiers"]] + ["shared RNA metadata is preserved", "save/reopen preserves the batch metadata"], "commands": [ - "source tools/web/emscripten-env.sh && cmake --build build_web_blender6 --target web_engine -- -j8 && cp build_web_blender6/bin/web_engine.wasm web/app/src/vendor/blender/web_engine.wasm && cp build_web_blender6/bin/web_engine.wasm web/app/public/vendor/blender/web_engine.wasm", + "source tools/web/emscripten-env.sh && cmake --build build_web_blender6 --target web_engine -- -j8 && cp build_web_blender6/bin/web_engine.wasm web/app/src/vendor/blender/web_engine.wasm && cp build_web_blender6/bin/web_engine.wasm web/app/public/vendor/blender/web_engine.wasm && node tools/web/split-wbv2-wasm.mjs --input web/app/public/vendor/blender/web_engine.wasm --output-dir web/app/public/vendor/blender/modules/single --split-funcs web_engine_get_live_handles && node tools/web/split-wbv2-wasm.mjs --input web/app/public/vendor/blender/pthread/web_engine.wasm --output-dir web/app/public/vendor/blender/modules/pthread --split-funcs web_engine_get_live_handles", "npm --prefix web run typecheck", "npm --prefix web run build", command, @@ -957,7 +957,16 @@ def main() -> int: source_paths += [dependency["productionEvidence"] for dependency in batch["dependencyReuse"]] source_paths += [batch["test"]] else: - source_paths += ["blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", batch["test"], *fixture_paths, "web/app/src/vendor/blender/web_engine.wasm"] + source_paths += [ + "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", + batch["test"], + *fixture_paths, + "web/app/src/vendor/blender/web_engine.wasm", + "web/app/public/vendor/blender/web_engine.wasm", + "web/app/public/wasm-module-manifest.json", + "web/app/public/vendor/blender/modules/single/web_engine.secondary.wasm", + "web/app/public/vendor/blender/modules/pthread/web_engine.secondary.wasm", + ] batch_types = json.dumps(batch["types"], separators=(',', ':')) batch_units = json.dumps([unit["id"] for unit in batch["units"]], separators=(',', ':')) if batch.get("dependencyReuse"): @@ -971,7 +980,7 @@ def main() -> int: commands = [f"node tools/web/reuse-wbv2-source-dependencies.mjs --task {module_task} --write"] else: commands = [ - "source tools/web/emscripten-env.sh && cmake --build build_web_blender6 --target web_engine -- -j8 && cp build_web_blender6/bin/web_engine.wasm web/app/src/vendor/blender/web_engine.wasm && cp build_web_blender6/bin/web_engine.wasm web/app/public/vendor/blender/web_engine.wasm", + "source tools/web/emscripten-env.sh && cmake --build build_web_blender6 --target web_engine -- -j8 && cp build_web_blender6/bin/web_engine.wasm web/app/src/vendor/blender/web_engine.wasm && cp build_web_blender6/bin/web_engine.wasm web/app/public/vendor/blender/web_engine.wasm && node tools/web/split-wbv2-wasm.mjs --input web/app/public/vendor/blender/web_engine.wasm --output-dir web/app/public/vendor/blender/modules/single --split-funcs web_engine_get_live_handles && node tools/web/split-wbv2-wasm.mjs --input web/app/public/vendor/blender/pthread/web_engine.wasm --output-dir web/app/public/vendor/blender/modules/pthread --split-funcs web_engine_get_live_handles", "npm --prefix web run typecheck", "npm --prefix web run build", f"WBV2_BATCH_TASK={module_task} {production_env} {fixture_env} WBV2_BATCH_TYPES='{batch_types}' WBV2_BATCH_UNITS='{batch_units}' WEB_TEST_PORT=5371 npm --prefix web run test:e2e -- --workers=1 tests/e2e/{Path(batch['test']).name}", diff --git a/tools/web/split-wbv2-wasm.mjs b/tools/web/split-wbv2-wasm.mjs new file mode 100644 index 000000000..4ce7b6ba3 --- /dev/null +++ b/tools/web/split-wbv2-wasm.mjs @@ -0,0 +1,56 @@ +import crypto from "node:crypto"; +import fs from "node:fs"; +import path from "node:path"; +import { spawnSync } from "node:child_process"; + +const root = path.resolve(import.meta.dirname, "../.."); +const arg = (name, fallback = null) => { + const index = process.argv.indexOf(name); + return index >= 0 ? process.argv[index + 1] : fallback; +}; +const wasmSplit = path.join(root, ".emsdk/upstream/bin/wasm-split"); +const input = arg("--input", "web/app/public/vendor/blender/web_engine.wasm"); +const outputDir = arg("--output-dir", "web/app/public/vendor/blender/modules"); +const manifestPath = arg("--manifest", "web/app/public/wasm-module-manifest.json"); +const splitFunctions = arg("--split-funcs", "web_engine_get_live_handles"); +if (!fs.existsSync(path.join(root, input))) throw new Error(`WASM input is missing: ${input}`); +if (!fs.existsSync(wasmSplit)) throw new Error(`wasm-split is missing: ${wasmSplit}`); +const destination = path.join(root, outputDir); +fs.mkdirSync(destination, { recursive: true }); +const stem = path.basename(input, ".wasm"); +const primary = path.join(destination, `${stem}.primary.wasm`); +const secondary = path.join(destination, `${stem}.secondary.wasm`); +const result = spawnSync(wasmSplit, [ + "--split", "--all-features", "--split-funcs", splitFunctions, + "--primary-output", primary, + "--secondary-output", secondary, + path.join(root, input), +], { cwd: root, encoding: "utf8" }); +if (result.status !== 0 || !fs.existsSync(primary) || !fs.existsSync(secondary)) { + throw new Error(`wasm-split failed (${result.status}):\n${result.stdout ?? ""}${result.stderr ?? ""}`); +} +const digest = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); +const variant = input.includes("/pthread/") ? "pthread" : "single"; +if (fs.existsSync(path.join(root, manifestPath))) { + const manifestFile = path.join(root, manifestPath); + const manifest = JSON.parse(fs.readFileSync(manifestFile, "utf8")); + const debug = manifest.modules?.find((entry) => entry.id === "debug-incremental"); + const secondaryEntry = manifest.modules?.find((entry) => entry.id === "debug-incremental-secondary"); + if (debug?.resources?.[variant]) { + debug.resources[variant].sha256 = digest(path.join(root, input)); + debug.resources[variant].url = `/${input.replace(/^web\/app\/public\//u, "")}`; + } + if (secondaryEntry?.resources?.[variant]) { + secondaryEntry.resources[variant].fileName = path.basename(secondary); + secondaryEntry.resources[variant].url = `/${path.relative(path.join(root, "web/app/public"), secondary).replaceAll(path.sep, "/")}`; + secondaryEntry.resources[variant].sha256 = digest(secondary); + } + fs.writeFileSync(manifestFile, `${JSON.stringify(manifest, null, 2)}\n`); +} +console.log(JSON.stringify({ + status: "PASS", + input, + splitFunctions: splitFunctions.split(",").filter(Boolean), + primary: { path: path.relative(root, primary), bytes: fs.statSync(primary).size, sha256: digest(primary) }, + secondary: { path: path.relative(root, secondary), bytes: fs.statSync(secondary).size, sha256: digest(secondary) }, +}, null, 2)); diff --git a/web/app/public/vendor/blender/modules/pthread/web_engine.secondary.wasm b/web/app/public/vendor/blender/modules/pthread/web_engine.secondary.wasm new file mode 100644 index 0000000000000000000000000000000000000000..d8fc92d022fbf4d1072da17bc8e0840054b51ddc GIT binary patch literal 8 PcmZQbEY4+QU|;|M2ZjMd literal 0 HcmV?d00001 diff --git a/web/app/public/vendor/blender/modules/single/web_engine.secondary.wasm b/web/app/public/vendor/blender/modules/single/web_engine.secondary.wasm new file mode 100644 index 0000000000000000000000000000000000000000..5985ecd404d73d2a5b921db834cf63a5fc9c60a6 GIT binary patch literal 80 zcmZQbEY4+QU|?WmWlUgTtY=bXVlODl%uOt+WXny>%`d8CVr<}OXn>0{CNdT<{9 literal 0 HcmV?d00001 diff --git a/web/app/public/wasm-module-manifest.json b/web/app/public/wasm-module-manifest.json index 5c6a81bb6..1ebf9cdaa 100644 --- a/web/app/public/wasm-module-manifest.json +++ b/web/app/public/wasm-module-manifest.json @@ -24,8 +24,16 @@ "lifecycle": "complete", "preload": true, "resources": { - "single": { "fileName": "web_engine.wasm", "url": "/vendor/blender/single/web_engine.wasm", "sha256": "7f3a50f1d41b2ac0e6366e84caaabefc3cdfc47c055d5ea459abec654da6fcc0" }, - "pthread": { "fileName": "web_engine.wasm", "url": "/vendor/blender/pthread/web_engine.wasm", "sha256": "f2a26a9221b5d4d5e710463a89e2d93faf9b2d8a9076a455c5ce1a0e52d88b5f" } + "single": { + "fileName": "web_engine.wasm", + "url": "/vendor/blender/single/web_engine.wasm", + "sha256": "7f3a50f1d41b2ac0e6366e84caaabefc3cdfc47c055d5ea459abec654da6fcc0" + }, + "pthread": { + "fileName": "web_engine.wasm", + "url": "/vendor/blender/pthread/web_engine.wasm", + "sha256": "f2a26a9221b5d4d5e710463a89e2d93faf9b2d8a9076a455c5ce1a0e52d88b5f" + } } }, { @@ -33,8 +41,16 @@ "lifecycle": "complete", "preload": true, "resources": { - "single": { "fileName": "web_engine.wasm", "url": "/vendor/blender/single/web_engine.wasm", "sha256": "7f3a50f1d41b2ac0e6366e84caaabefc3cdfc47c055d5ea459abec654da6fcc0" }, - "pthread": { "fileName": "web_engine.wasm", "url": "/vendor/blender/pthread/web_engine.wasm", "sha256": "f2a26a9221b5d4d5e710463a89e2d93faf9b2d8a9076a455c5ce1a0e52d88b5f" } + "single": { + "fileName": "web_engine.wasm", + "url": "/vendor/blender/single/web_engine.wasm", + "sha256": "7f3a50f1d41b2ac0e6366e84caaabefc3cdfc47c055d5ea459abec654da6fcc0" + }, + "pthread": { + "fileName": "web_engine.wasm", + "url": "/vendor/blender/pthread/web_engine.wasm", + "sha256": "f2a26a9221b5d4d5e710463a89e2d93faf9b2d8a9076a455c5ce1a0e52d88b5f" + } } }, { @@ -53,6 +69,23 @@ "sha256": "f2a26a9221b5d4d5e710463a89e2d93faf9b2d8a9076a455c5ce1a0e52d88b5f" } } + }, + { + "id": "debug-incremental-secondary", + "lifecycle": "debug", + "preload": false, + "resources": { + "single": { + "fileName": "web_engine.secondary.wasm", + "url": "/vendor/blender/modules/single/web_engine.secondary.wasm", + "sha256": "b862e9be41ce56d59c08e3be053498abf7ef8ba5b62b8e844f28c4030ed12390" + }, + "pthread": { + "fileName": "web_engine.secondary.wasm", + "url": "/vendor/blender/modules/pthread/web_engine.secondary.wasm", + "sha256": "93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476" + } + } } ] }