From 0fe8d2bb5634a85b7dcbfb75dac7fb0908766ed2 Mon Sep 17 00:00:00 2001 From: mes123456 Date: Mon, 17 Aug 2026 04:37:07 -0400 Subject: [PATCH] Advance M8-M11 parity workflows --- .gitignore | 2 + WEB_BLENDER_MIGRATION_EXECUTION_PLAN.md | 2 +- .../blender/modifiers/intern/MOD_nodes_web.cc | 837 ++++++- .../blender/web_engine/web_engine_api.cpp | 35 +- .../web_engine/web_engine_blend_reader.cpp | 28 +- .../web_engine/web_engine_depsgraph.cpp | 42 +- .../web_engine/web_engine_main_state.cpp | 632 ++++- .../web_engine/web_engine_main_state.h | 19 + docs/CURRENT_EXECUTION_PLAN.md | 193 +- docs/PROJECT_STATUS_AND_NEXT_WORK.md | 55 +- .../UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md | 24 +- .../VDB_NANOVDB_WEBGPU_IMPLEMENTATION_PLAN.md | 11 +- docs/status/M10-01.md | 51 + docs/status/M10-02.md | 56 + docs/status/M10-03.md | 73 + docs/status/M10-04.md | 75 + docs/status/M10-05.md | 68 + docs/status/M10-06.md | 84 + docs/status/M10-07.md | 59 + docs/status/M10-08.md | 59 + docs/status/M10-09.md | 40 + docs/status/M10-10.md | 39 + docs/status/M10-11.md | 44 + docs/status/M10-12.md | 45 + docs/status/M10-13.md | 48 + docs/status/M10-14.md | 47 + docs/status/M10-15.md | 51 + docs/status/M11-01.md | 42 + docs/status/M11-02.md | 48 + docs/status/M11-03.md | 55 + docs/status/M11-04.md | 56 + docs/status/M11-05.md | 47 + docs/status/M11-06.md | 48 + docs/status/M11-07.md | 36 + docs/status/M11-08.md | 37 + docs/status/M11-09.md | 43 + docs/status/M11-10.md | 57 + docs/status/M11-11.md | 52 + docs/status/M11-12.md | 58 + docs/status/M11-13.md | 58 + docs/status/M9-14.md | 57 + docs/status/N-012.md | 50 +- docs/status/N-013.md | 19 +- docs/status/N-014.md | 17 +- docs/status/N-015.md | 38 +- docs/status/N-016.md | 32 +- docs/status/N-017.md | 53 +- docs/status/N-018.md | 18 +- docs/status/N-019.md | 42 +- docs/status/N-020.md | 19 +- docs/status/N-021.md | 54 +- docs/status/parity-ledger.json | 14 +- docs/status/release-evidence.json | 32 +- .../geometry_node_allowlist_evaluator.blend | Bin 0 -> 100021 bytes .../files/web/m11_compositor_allowlist.blend | Bin 0 -> 90542 bytes tests/files/web/m11_render_reference.blend | Bin 0 -> 89535 bytes tests/files/web/manifest.json | 28 + tests/files/web/media/sequencer-probe.mp4 | Bin 0 -> 1484 bytes tests/files/web/nla_time_mapping_scene.blend | Bin 0 -> 87005 bytes .../M10-01/geometry-node-main-reader.json | 46 + .../M10-02/geometry-node-allowlist.json | 32 + .../M10-03/geometry-node-evaluator.json | 1187 +++++++++ tests/golden/M10-07/shader-compile.json | 34 + tests/golden/M10-08/shader-compile-key.json | 10 + tests/golden/M10-09/shader-pipeline.json | 8 + .../M10-10/shader-capability-block.json | 10 + tests/golden/M10-11/nla-evaluation.json | 308 +++ tests/golden/M10-13/physics-solver-probe.json | 24 + tests/golden/M10-14/physics-cache-family.json | 26 + tests/golden/M10-15/domain-browser-gates.json | 33 + .../golden/M11-01/lighting-field-parity.json | 128 + .../M11-02/lighting-field-roundtrip.json | 46 + .../golden/M11-03/render-resource-budget.json | 32 + .../golden/M11-04/blender-eevee-reference.png | Bin 0 -> 3941 bytes tests/golden/M11-04/manifest.json | 30 + tests/golden/M11-05/render-routing.json | 48 + tests/golden/M11-06/server-render-job.json | 18 + .../golden/M11-07/compositor-node-golden.json | 46 + .../M11-08/compositor-unsupported-gate.json | 19 + .../golden/M11-09/sequencer-codec-probe.json | 41 + .../golden/M11-10/sequencer-media-cache.json | 14 + .../M11-11/sequencer-media-revision.json | 14 + .../golden/M11-12/sequencer-final-export.json | 27 + .../M11-13/sequencer-audio-recovery.json | 16 + tests/golden/M8-19/generated-smoke-x.rgba | Bin 0 -> 16384 bytes tests/golden/M8-19/generated-smoke-y.rgba | Bin 0 -> 16384 bytes tests/golden/M8-19/generated-smoke-z.rgba | Bin 0 -> 16384 bytes tests/golden/M8-19/manifest.json | 75 + tests/golden/M9-05/curve-toggle-cyclic.json | 29 + tests/golden/M9-08/grease-pencil-reorder.json | 59 + tests/golden/M9-12/weight-paint.json | 193 ++ tests/golden/M9-14/manifest.json | 31 + tools/vdb/CMakeLists.txt | 2 +- tools/vdb/generate-volume-golden.mjs | 86 + tools/vdb/vdb_volume_golden.cc | 179 ++ tools/web/check-compositor-node-golden.mjs | 81 + tools/web/check-editing-soak-report.mjs | 32 + .../check-geometry-node-evaluator-golden.mjs | 140 ++ tools/web/check-geometry-node-main-reader.mjs | 111 + tools/web/check-grease-pencil-roundtrip.mjs | 35 +- tools/web/check-lighting-field-parity.mjs | 78 + tools/web/check-lighting-roundtrip.mjs | 10 +- tools/web/check-nla-evaluation-golden.mjs | 119 + tools/web/check-paint-roundtrip.mjs | 27 +- tools/web/check-render-reference.mjs | 50 + tools/web/check-server-render-job.mjs | 167 ++ tools/web/check-weight-paint-golden.mjs | 119 + tools/web/generate-curve-toggle-golden.py | 51 + ...generate-geometry-node-evaluator-golden.py | 369 +++ .../generate-grease-pencil-reorder-golden.py | 65 + .../web/generate-m11-compositor-allowlist.py | 54 + tools/web/generate-m11-render-reference.py | 99 + tools/web/generate-nla-evaluation-fixture.py | 94 + tools/web/generate-nla-evaluation-golden.py | 77 + tools/web/generate-sequencer-codec-fixture.sh | 19 + tools/web/generate-weight-paint-golden.py | 110 + tools/web/render-server-job.py | 45 + web/app/public/engine-manifest.json | 10 +- .../vendor/blender/pthread/web_engine.js | 2 +- .../vendor/blender/pthread/web_engine.wasm | Bin 369042 -> 371657 bytes .../vendor/blender/single/web_engine.js | 2 +- .../vendor/blender/single/web_engine.wasm | Bin 366066 -> 368764 bytes web/app/public/vendor/blender/web_engine.js | 2 +- web/app/public/vendor/blender/web_engine.wasm | Bin 15048040 -> 15250022 bytes web/app/src/app/App.tsx | 968 +++++++- web/app/src/app/app-shell.css | 21 +- web/app/src/compositor/CompositorExecutor.ts | 4 + web/app/src/compositor/CompositorWebGPU.ts | 83 + web/app/src/engine-client/WebEngineClient.ts | 115 +- web/app/src/fonts/external-vfont-import.ts | 130 + web/app/src/render/nanovdb-volume-renderer.ts | 281 ++- .../src/sequencer/SequencerAudioSession.ts | 232 ++ web/app/src/sequencer/SequencerCodecProbe.ts | 145 ++ web/app/src/sequencer/SequencerFinalExport.ts | 18 + .../src/sequencer/SequencerMediaProxyCache.ts | 201 ++ .../sequencer/SequencerMediaRevisionGate.ts | 76 + web/app/src/sequencer/SequencerTimeline.ts | 47 + web/app/src/storage/StorageClient.ts | 178 +- web/app/src/storage/migrations.ts | 8 +- web/app/src/storage/opfs-files.ts | 151 ++ .../src/testing/editing-domain-recovery.ts | 275 +++ web/app/src/testing/oom-recovery-scenarios.ts | 118 +- web/app/src/three-adapter/grease-pencil.ts | 71 +- .../offscreen-viewport-protocol.ts | 22 +- .../src/three-adapter/offscreen-viewport.ts | 123 +- .../three-adapter/paint-depth-visibility.ts | 171 ++ web/app/src/three-adapter/pbr.ts | 98 +- .../three-adapter/render-image-comparison.ts | 9 + web/app/src/three-adapter/render-routing.ts | 10 + web/app/src/three-adapter/texture-assets.ts | 31 +- web/app/src/three-adapter/viewport.ts | 140 +- web/app/src/vendor/blender/web_engine.js | 2 +- web/app/src/vendor/blender/web_engine.wasm | Bin 15048040 -> 15250022 bytes web/app/src/volume/nanovdb-float32.ts | 28 +- web/app/src/volume/nanovdb-viewport.ts | 181 ++ .../curve-topology-contract-test.worker.ts | 43 + .../src/workers/external-vfont-test.worker.ts | 45 + .../grease-pencil-schema-test.worker.ts | 20 +- .../grease-pencil-viewport-test.worker.ts | 8 +- .../workers/m10-domain-gate-test.worker.ts | 285 +++ ...vdb-offscreen-page-feedback-test.worker.ts | 83 + .../nanovdb-opfs-restart-test.worker.ts | 126 + .../nanovdb-page-feedback-test.worker.ts | 450 ++++ .../nanovdb-page-resume-test.worker.ts | 79 + .../nanovdb-render-golden-test.worker.ts | 55 + .../nanovdb-sparse-performance-test.worker.ts | 170 ++ .../physics-cache-family-test.worker.ts | 87 + .../workers/physics-simulation-test.worker.ts | 9 +- .../physics-solver-probe-test.worker.ts | 48 + web/app/src/workers/sequencer-test.worker.ts | 15 +- web/app/src/workers/storage.worker.ts | 821 ++++++- web/app/src/workers/vdb-fault-test.worker.ts | 46 +- web/app/src/workers/viewport-render.worker.ts | 100 +- web/app/src/workers/web-engine.worker.ts | 229 +- web/app/vite.config.ts | 128 + web/engine/web_engine_native_reader_stub.cpp | 21 +- web/package-lock.json | 22 + web/package.json | 55 +- web/protocol/capability-gates.ts | 2 +- web/protocol/compositor.ts | 83 +- web/protocol/curve-topology-editor.ts | 315 +++ web/protocol/depsgraph.ts | 134 +- web/protocol/diagnostic-report.ts | 160 ++ web/protocol/editing-domain-recovery.ts | 197 ++ web/protocol/error.ts | 49 + web/protocol/external-vfont.ts | 210 ++ web/protocol/geometry-nodes.ts | 327 ++- web/protocol/grease-pencil-marquee.ts | 225 ++ web/protocol/grease-pencil-reorder.ts | 122 + web/protocol/grease-pencil-selection.ts | 212 ++ web/protocol/grease-pencil.ts | 20 +- web/protocol/nanovdb-device-recovery.ts | 36 + web/protocol/nanovdb-page-feedback.ts | 234 ++ web/protocol/nanovdb-progressive-redraw.ts | 37 + web/protocol/nanovdb-render-golden.ts | 90 + web/protocol/nla.ts | 104 +- web/protocol/oom-recovery.ts | 2 + web/protocol/paint-depth-visibility.ts | 157 ++ web/protocol/paint-pbvh-capability.ts | 150 ++ web/protocol/paint-stroke-session.ts | 282 +++ web/protocol/paint.ts | 19 + web/protocol/physics-simulation.ts | 272 ++- web/protocol/recent-projects.ts | 134 + web/protocol/render-assets.ts | 2 +- web/protocol/render-budget.ts | 245 ++ web/protocol/render-capabilities.ts | 4 +- web/protocol/render-image-comparison.ts | 218 ++ web/protocol/render-routing.ts | 105 + web/protocol/scene-ir.ts | 26 + web/protocol/sequencer-audio-session.ts | 86 + web/protocol/sequencer-export.ts | 184 ++ web/protocol/sequencer-media-cache.ts | 270 +++ web/protocol/sequencer-media-revision.ts | 162 ++ web/protocol/sequencer.ts | 118 +- web/protocol/server-render-job.ts | 313 +++ web/protocol/shader-compiler.ts | 502 ++++ web/protocol/simulation-cache.ts | 199 +- web/protocol/storage-budget.ts | 39 + web/protocol/storage.ts | 66 +- web/protocol/texture-paint-asset.ts | 177 ++ web/protocol/ui-schema.ts | 7 +- web/protocol/viewport-camera.ts | 58 + web/protocol/web-engine.ts | 28 +- web/protocol/weight-paint.ts | 195 ++ web/protocol/worker-fault.ts | 21 + web/tests/e2e/compositor-node-golden.spec.ts | 81 + .../e2e/compositor-unsupported-gate.spec.ts | 63 + web/tests/e2e/curve-topology-contract.spec.ts | 23 + web/tests/e2e/curve-topology-operator.spec.ts | 59 + web/tests/e2e/diagnostic-report.spec.ts | 87 + web/tests/e2e/editing-domain-recovery.spec.ts | 34 + web/tests/e2e/editing-soak.spec.ts | 239 ++ web/tests/e2e/external-vfont.spec.ts | 218 ++ web/tests/e2e/file-import-progress.spec.ts | 3 +- web/tests/e2e/geometry-node-allowlist.spec.ts | 67 + .../geometry-node-evaluator-golden.spec.ts | 164 ++ .../e2e/geometry-node-field-budget.spec.ts | 108 + .../e2e/geometry-node-main-reader.spec.ts | 57 + web/tests/e2e/grease-pencil-marquee.spec.ts | 37 + web/tests/e2e/grease-pencil-reorder.spec.ts | 65 + web/tests/e2e/grease-pencil-selection.spec.ts | 50 + web/tests/e2e/keyboard-accessibility.spec.ts | 100 + .../e2e/lighting-field-roundtrip.spec.ts | 143 ++ web/tests/e2e/long-media-performance.spec.ts | 15 +- .../e2e/m10-domain-browser-gates.spec.ts | 47 + web/tests/e2e/nanovdb-main-thread.spec.ts | 101 + .../nanovdb-offscreen-page-feedback.spec.ts | 25 + web/tests/e2e/nanovdb-opfs-restart.spec.ts | 25 + web/tests/e2e/nanovdb-page-feedback.spec.ts | 197 ++ web/tests/e2e/nanovdb-page-resume.spec.ts | 20 + web/tests/e2e/nanovdb-render-golden.spec.ts | 88 + .../e2e/nanovdb-sparse-performance.spec.ts | 53 + .../e2e/nanovdb-volume-roundtrip.spec.ts | 156 ++ web/tests/e2e/nla-evaluation-golden.spec.ts | 42 + web/tests/e2e/nla-operator.spec.ts | 100 + web/tests/e2e/oom-recovery.spec.ts | 14 +- web/tests/e2e/paint-depth-visibility.spec.ts | 116 + web/tests/e2e/paint-pbvh-capability.spec.ts | 96 + web/tests/e2e/paint-stroke-session.spec.ts | 76 + web/tests/e2e/physics-cache-family.spec.ts | 30 + web/tests/e2e/physics-solver-probe.spec.ts | 34 + .../e2e/recent-projects-recovery.spec.ts | 198 ++ web/tests/e2e/render-reference.spec.ts | 103 + web/tests/e2e/render-resource-budget.spec.ts | 187 ++ web/tests/e2e/render-routing.spec.ts | 43 + web/tests/e2e/responsive-layout.spec.ts | 50 + .../e2e/sequencer-audio-recovery.spec.ts | 72 + web/tests/e2e/sequencer-codec-probe.spec.ts | 84 + web/tests/e2e/sequencer-final-export.spec.ts | 94 + web/tests/e2e/sequencer-media-cache.spec.ts | 132 + .../e2e/sequencer-media-revision.spec.ts | 94 + web/tests/e2e/shader-capability-block.spec.ts | 30 + web/tests/e2e/shader-compile-key.spec.ts | 48 + web/tests/e2e/shader-compile.spec.ts | 157 ++ web/tests/e2e/shader-pipeline.spec.ts | 56 + .../e2e/simulation-cache-identity.spec.ts | 115 + .../e2e/simulation-cache-lifecycle.spec.ts | 211 ++ .../e2e/simulation-cache-performance.spec.ts | 21 +- web/tests/e2e/smoke.spec.ts | 99 +- web/tests/e2e/storage-budget.spec.ts | 42 + web/tests/e2e/storage-cleanup.spec.ts | 55 + web/tests/e2e/texture-paint-asset.spec.ts | 146 ++ web/tests/e2e/ui-context.spec.ts | 47 + web/tests/e2e/viewport-consistency.spec.ts | 61 + web/tests/e2e/worker-crash-recovery.spec.ts | 41 + .../unit/compositor-unsupported-gate.test.mjs | 76 + web/tests/unit/compositor-webgpu.test.mjs | 89 + web/tests/unit/curve-topology-editor.test.mjs | 142 ++ web/tests/unit/diagnostic-report.test.mjs | 66 + .../unit/editing-domain-recovery.test.mjs | 62 + web/tests/unit/external-vfont.test.mjs | 165 ++ web/tests/unit/geometry-nodes.test.mjs | 258 ++ web/tests/unit/grease-pencil-marquee.test.mjs | 81 + web/tests/unit/grease-pencil-reorder.test.mjs | 83 + .../unit/grease-pencil-selection.test.mjs | 78 + .../unit/nanovdb-device-recovery.test.mjs | 41 + web/tests/unit/nanovdb-page-feedback.test.mjs | 182 ++ .../unit/nanovdb-progressive-redraw.test.mjs | 47 + web/tests/unit/nanovdb-render-golden.test.mjs | 52 + web/tests/unit/nla.test.mjs | 91 + .../unit/paint-depth-visibility.test.mjs | 79 + web/tests/unit/paint-pbvh-capability.test.mjs | 92 + web/tests/unit/paint-stroke-session.test.mjs | 94 + web/tests/unit/physics-cache-family.test.mjs | 121 + web/tests/unit/physics-solver-probe.test.mjs | 98 + web/tests/unit/recent-projects.test.mjs | 80 + web/tests/unit/render-budget.test.mjs | 77 + .../unit/render-image-comparison.test.mjs | 65 + web/tests/unit/render-routing.test.mjs | 53 + .../unit/sequencer-audio-recovery.test.mjs | 164 ++ web/tests/unit/sequencer-codec-probe.test.mjs | 68 + .../unit/sequencer-final-export.test.mjs | 132 + web/tests/unit/sequencer-media-cache.test.mjs | 120 + .../unit/sequencer-media-revision.test.mjs | 87 + web/tests/unit/server-render-job.test.mjs | 66 + web/tests/unit/shader-compiler.test.mjs | 179 ++ web/tests/unit/simulation-cache.test.mjs | 123 + web/tests/unit/storage-budget.test.mjs | 29 + web/tests/unit/texture-paint-asset.test.mjs | 71 + web/tests/unit/ui-schema.test.mjs | 35 + web/tests/unit/viewport-camera.test.mjs | 31 + web/tests/unit/weight-paint.test.mjs | 71 + web/tests/unit/worker-fault.test.mjs | 39 + 后续工作.txt | 2149 ++++++++++++++++- 324 files changed, 31920 insertions(+), 863 deletions(-) create mode 100644 docs/status/M10-01.md create mode 100644 docs/status/M10-02.md create mode 100644 docs/status/M10-03.md create mode 100644 docs/status/M10-04.md create mode 100644 docs/status/M10-05.md create mode 100644 docs/status/M10-06.md create mode 100644 docs/status/M10-07.md create mode 100644 docs/status/M10-08.md create mode 100644 docs/status/M10-09.md create mode 100644 docs/status/M10-10.md create mode 100644 docs/status/M10-11.md create mode 100644 docs/status/M10-12.md create mode 100644 docs/status/M10-13.md create mode 100644 docs/status/M10-14.md create mode 100644 docs/status/M10-15.md create mode 100644 docs/status/M11-01.md create mode 100644 docs/status/M11-02.md create mode 100644 docs/status/M11-03.md create mode 100644 docs/status/M11-04.md create mode 100644 docs/status/M11-05.md create mode 100644 docs/status/M11-06.md create mode 100644 docs/status/M11-07.md create mode 100644 docs/status/M11-08.md create mode 100644 docs/status/M11-09.md create mode 100644 docs/status/M11-10.md create mode 100644 docs/status/M11-11.md create mode 100644 docs/status/M11-12.md create mode 100644 docs/status/M11-13.md create mode 100644 docs/status/M9-14.md create mode 100644 tests/files/web/geometry_node_allowlist_evaluator.blend create mode 100644 tests/files/web/m11_compositor_allowlist.blend create mode 100644 tests/files/web/m11_render_reference.blend create mode 100644 tests/files/web/media/sequencer-probe.mp4 create mode 100644 tests/files/web/nla_time_mapping_scene.blend create mode 100644 tests/golden/M10-01/geometry-node-main-reader.json create mode 100644 tests/golden/M10-02/geometry-node-allowlist.json create mode 100644 tests/golden/M10-03/geometry-node-evaluator.json create mode 100644 tests/golden/M10-07/shader-compile.json create mode 100644 tests/golden/M10-08/shader-compile-key.json create mode 100644 tests/golden/M10-09/shader-pipeline.json create mode 100644 tests/golden/M10-10/shader-capability-block.json create mode 100644 tests/golden/M10-11/nla-evaluation.json create mode 100644 tests/golden/M10-13/physics-solver-probe.json create mode 100644 tests/golden/M10-14/physics-cache-family.json create mode 100644 tests/golden/M10-15/domain-browser-gates.json create mode 100644 tests/golden/M11-01/lighting-field-parity.json create mode 100644 tests/golden/M11-02/lighting-field-roundtrip.json create mode 100644 tests/golden/M11-03/render-resource-budget.json create mode 100644 tests/golden/M11-04/blender-eevee-reference.png create mode 100644 tests/golden/M11-04/manifest.json create mode 100644 tests/golden/M11-05/render-routing.json create mode 100644 tests/golden/M11-06/server-render-job.json create mode 100644 tests/golden/M11-07/compositor-node-golden.json create mode 100644 tests/golden/M11-08/compositor-unsupported-gate.json create mode 100644 tests/golden/M11-09/sequencer-codec-probe.json create mode 100644 tests/golden/M11-10/sequencer-media-cache.json create mode 100644 tests/golden/M11-11/sequencer-media-revision.json create mode 100644 tests/golden/M11-12/sequencer-final-export.json create mode 100644 tests/golden/M11-13/sequencer-audio-recovery.json create mode 100644 tests/golden/M8-19/generated-smoke-x.rgba create mode 100644 tests/golden/M8-19/generated-smoke-y.rgba create mode 100644 tests/golden/M8-19/generated-smoke-z.rgba create mode 100644 tests/golden/M8-19/manifest.json create mode 100644 tests/golden/M9-05/curve-toggle-cyclic.json create mode 100644 tests/golden/M9-08/grease-pencil-reorder.json create mode 100644 tests/golden/M9-12/weight-paint.json create mode 100644 tests/golden/M9-14/manifest.json create mode 100644 tools/vdb/generate-volume-golden.mjs create mode 100644 tools/vdb/vdb_volume_golden.cc create mode 100644 tools/web/check-compositor-node-golden.mjs create mode 100644 tools/web/check-editing-soak-report.mjs create mode 100644 tools/web/check-geometry-node-evaluator-golden.mjs create mode 100644 tools/web/check-geometry-node-main-reader.mjs create mode 100644 tools/web/check-lighting-field-parity.mjs create mode 100644 tools/web/check-nla-evaluation-golden.mjs create mode 100644 tools/web/check-render-reference.mjs create mode 100644 tools/web/check-server-render-job.mjs create mode 100644 tools/web/check-weight-paint-golden.mjs create mode 100644 tools/web/generate-curve-toggle-golden.py create mode 100644 tools/web/generate-geometry-node-evaluator-golden.py create mode 100644 tools/web/generate-grease-pencil-reorder-golden.py create mode 100644 tools/web/generate-m11-compositor-allowlist.py create mode 100644 tools/web/generate-m11-render-reference.py create mode 100644 tools/web/generate-nla-evaluation-fixture.py create mode 100644 tools/web/generate-nla-evaluation-golden.py create mode 100755 tools/web/generate-sequencer-codec-fixture.sh create mode 100644 tools/web/generate-weight-paint-golden.py create mode 100644 tools/web/render-server-job.py mode change 100755 => 100644 web/app/public/vendor/blender/web_engine.wasm create mode 100644 web/app/src/compositor/CompositorWebGPU.ts create mode 100644 web/app/src/fonts/external-vfont-import.ts create mode 100644 web/app/src/sequencer/SequencerAudioSession.ts create mode 100644 web/app/src/sequencer/SequencerCodecProbe.ts create mode 100644 web/app/src/sequencer/SequencerFinalExport.ts create mode 100644 web/app/src/sequencer/SequencerMediaProxyCache.ts create mode 100644 web/app/src/sequencer/SequencerMediaRevisionGate.ts create mode 100644 web/app/src/testing/editing-domain-recovery.ts create mode 100644 web/app/src/three-adapter/paint-depth-visibility.ts create mode 100644 web/app/src/three-adapter/render-image-comparison.ts create mode 100644 web/app/src/three-adapter/render-routing.ts mode change 100755 => 100644 web/app/src/vendor/blender/web_engine.wasm create mode 100644 web/app/src/workers/curve-topology-contract-test.worker.ts create mode 100644 web/app/src/workers/external-vfont-test.worker.ts create mode 100644 web/app/src/workers/m10-domain-gate-test.worker.ts create mode 100644 web/app/src/workers/nanovdb-offscreen-page-feedback-test.worker.ts create mode 100644 web/app/src/workers/nanovdb-opfs-restart-test.worker.ts create mode 100644 web/app/src/workers/nanovdb-page-feedback-test.worker.ts create mode 100644 web/app/src/workers/nanovdb-page-resume-test.worker.ts create mode 100644 web/app/src/workers/nanovdb-render-golden-test.worker.ts create mode 100644 web/app/src/workers/nanovdb-sparse-performance-test.worker.ts create mode 100644 web/app/src/workers/physics-cache-family-test.worker.ts create mode 100644 web/app/src/workers/physics-solver-probe-test.worker.ts create mode 100644 web/protocol/curve-topology-editor.ts create mode 100644 web/protocol/diagnostic-report.ts create mode 100644 web/protocol/editing-domain-recovery.ts create mode 100644 web/protocol/external-vfont.ts create mode 100644 web/protocol/grease-pencil-marquee.ts create mode 100644 web/protocol/grease-pencil-reorder.ts create mode 100644 web/protocol/grease-pencil-selection.ts create mode 100644 web/protocol/nanovdb-device-recovery.ts create mode 100644 web/protocol/nanovdb-page-feedback.ts create mode 100644 web/protocol/nanovdb-progressive-redraw.ts create mode 100644 web/protocol/nanovdb-render-golden.ts create mode 100644 web/protocol/paint-depth-visibility.ts create mode 100644 web/protocol/paint-pbvh-capability.ts create mode 100644 web/protocol/paint-stroke-session.ts create mode 100644 web/protocol/recent-projects.ts create mode 100644 web/protocol/render-budget.ts create mode 100644 web/protocol/render-image-comparison.ts create mode 100644 web/protocol/render-routing.ts create mode 100644 web/protocol/sequencer-audio-session.ts create mode 100644 web/protocol/sequencer-export.ts create mode 100644 web/protocol/sequencer-media-cache.ts create mode 100644 web/protocol/sequencer-media-revision.ts create mode 100644 web/protocol/server-render-job.ts create mode 100644 web/protocol/shader-compiler.ts create mode 100644 web/protocol/storage-budget.ts create mode 100644 web/protocol/texture-paint-asset.ts create mode 100644 web/protocol/viewport-camera.ts create mode 100644 web/protocol/weight-paint.ts create mode 100644 web/protocol/worker-fault.ts create mode 100644 web/tests/e2e/compositor-node-golden.spec.ts create mode 100644 web/tests/e2e/compositor-unsupported-gate.spec.ts create mode 100644 web/tests/e2e/curve-topology-contract.spec.ts create mode 100644 web/tests/e2e/curve-topology-operator.spec.ts create mode 100644 web/tests/e2e/diagnostic-report.spec.ts create mode 100644 web/tests/e2e/editing-domain-recovery.spec.ts create mode 100644 web/tests/e2e/editing-soak.spec.ts create mode 100644 web/tests/e2e/external-vfont.spec.ts create mode 100644 web/tests/e2e/geometry-node-allowlist.spec.ts create mode 100644 web/tests/e2e/geometry-node-evaluator-golden.spec.ts create mode 100644 web/tests/e2e/geometry-node-field-budget.spec.ts create mode 100644 web/tests/e2e/geometry-node-main-reader.spec.ts create mode 100644 web/tests/e2e/grease-pencil-marquee.spec.ts create mode 100644 web/tests/e2e/grease-pencil-reorder.spec.ts create mode 100644 web/tests/e2e/grease-pencil-selection.spec.ts create mode 100644 web/tests/e2e/keyboard-accessibility.spec.ts create mode 100644 web/tests/e2e/lighting-field-roundtrip.spec.ts create mode 100644 web/tests/e2e/m10-domain-browser-gates.spec.ts create mode 100644 web/tests/e2e/nanovdb-main-thread.spec.ts create mode 100644 web/tests/e2e/nanovdb-offscreen-page-feedback.spec.ts create mode 100644 web/tests/e2e/nanovdb-opfs-restart.spec.ts create mode 100644 web/tests/e2e/nanovdb-page-feedback.spec.ts create mode 100644 web/tests/e2e/nanovdb-page-resume.spec.ts create mode 100644 web/tests/e2e/nanovdb-render-golden.spec.ts create mode 100644 web/tests/e2e/nanovdb-sparse-performance.spec.ts create mode 100644 web/tests/e2e/nanovdb-volume-roundtrip.spec.ts create mode 100644 web/tests/e2e/nla-evaluation-golden.spec.ts create mode 100644 web/tests/e2e/nla-operator.spec.ts create mode 100644 web/tests/e2e/paint-depth-visibility.spec.ts create mode 100644 web/tests/e2e/paint-pbvh-capability.spec.ts create mode 100644 web/tests/e2e/paint-stroke-session.spec.ts create mode 100644 web/tests/e2e/physics-cache-family.spec.ts create mode 100644 web/tests/e2e/physics-solver-probe.spec.ts create mode 100644 web/tests/e2e/recent-projects-recovery.spec.ts create mode 100644 web/tests/e2e/render-reference.spec.ts create mode 100644 web/tests/e2e/render-resource-budget.spec.ts create mode 100644 web/tests/e2e/render-routing.spec.ts create mode 100644 web/tests/e2e/responsive-layout.spec.ts create mode 100644 web/tests/e2e/sequencer-audio-recovery.spec.ts create mode 100644 web/tests/e2e/sequencer-codec-probe.spec.ts create mode 100644 web/tests/e2e/sequencer-final-export.spec.ts create mode 100644 web/tests/e2e/sequencer-media-cache.spec.ts create mode 100644 web/tests/e2e/sequencer-media-revision.spec.ts create mode 100644 web/tests/e2e/shader-capability-block.spec.ts create mode 100644 web/tests/e2e/shader-compile-key.spec.ts create mode 100644 web/tests/e2e/shader-compile.spec.ts create mode 100644 web/tests/e2e/shader-pipeline.spec.ts create mode 100644 web/tests/e2e/simulation-cache-identity.spec.ts create mode 100644 web/tests/e2e/simulation-cache-lifecycle.spec.ts create mode 100644 web/tests/e2e/storage-budget.spec.ts create mode 100644 web/tests/e2e/storage-cleanup.spec.ts create mode 100644 web/tests/e2e/texture-paint-asset.spec.ts create mode 100644 web/tests/e2e/ui-context.spec.ts create mode 100644 web/tests/e2e/viewport-consistency.spec.ts create mode 100644 web/tests/e2e/worker-crash-recovery.spec.ts create mode 100644 web/tests/unit/compositor-unsupported-gate.test.mjs create mode 100644 web/tests/unit/compositor-webgpu.test.mjs create mode 100644 web/tests/unit/curve-topology-editor.test.mjs create mode 100644 web/tests/unit/diagnostic-report.test.mjs create mode 100644 web/tests/unit/editing-domain-recovery.test.mjs create mode 100644 web/tests/unit/external-vfont.test.mjs create mode 100644 web/tests/unit/geometry-nodes.test.mjs create mode 100644 web/tests/unit/grease-pencil-marquee.test.mjs create mode 100644 web/tests/unit/grease-pencil-reorder.test.mjs create mode 100644 web/tests/unit/grease-pencil-selection.test.mjs create mode 100644 web/tests/unit/nanovdb-device-recovery.test.mjs create mode 100644 web/tests/unit/nanovdb-page-feedback.test.mjs create mode 100644 web/tests/unit/nanovdb-progressive-redraw.test.mjs create mode 100644 web/tests/unit/nanovdb-render-golden.test.mjs create mode 100644 web/tests/unit/nla.test.mjs create mode 100644 web/tests/unit/paint-depth-visibility.test.mjs create mode 100644 web/tests/unit/paint-pbvh-capability.test.mjs create mode 100644 web/tests/unit/paint-stroke-session.test.mjs create mode 100644 web/tests/unit/physics-cache-family.test.mjs create mode 100644 web/tests/unit/physics-solver-probe.test.mjs create mode 100644 web/tests/unit/recent-projects.test.mjs create mode 100644 web/tests/unit/render-budget.test.mjs create mode 100644 web/tests/unit/render-image-comparison.test.mjs create mode 100644 web/tests/unit/render-routing.test.mjs create mode 100644 web/tests/unit/sequencer-audio-recovery.test.mjs create mode 100644 web/tests/unit/sequencer-codec-probe.test.mjs create mode 100644 web/tests/unit/sequencer-final-export.test.mjs create mode 100644 web/tests/unit/sequencer-media-cache.test.mjs create mode 100644 web/tests/unit/sequencer-media-revision.test.mjs create mode 100644 web/tests/unit/server-render-job.test.mjs create mode 100644 web/tests/unit/shader-compiler.test.mjs create mode 100644 web/tests/unit/simulation-cache.test.mjs create mode 100644 web/tests/unit/storage-budget.test.mjs create mode 100644 web/tests/unit/texture-paint-asset.test.mjs create mode 100644 web/tests/unit/ui-schema.test.mjs create mode 100644 web/tests/unit/viewport-camera.test.mjs create mode 100644 web/tests/unit/weight-paint.test.mjs create mode 100644 web/tests/unit/worker-fault.test.mjs diff --git a/.gitignore b/.gitignore index fc078711..e83dc9f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # Local toolchains and caches .emcache/ .emscripten-web +__pycache__/ +*.py[cod] *.log mylog.txt diff --git a/WEB_BLENDER_MIGRATION_EXECUTION_PLAN.md b/WEB_BLENDER_MIGRATION_EXECUTION_PLAN.md index 99adceac..fe6b842d 100644 --- a/WEB_BLENDER_MIGRATION_EXECUTION_PLAN.md +++ b/WEB_BLENDER_MIGRATION_EXECUTION_PLAN.md @@ -521,7 +521,7 @@ tests/files/web/large_scene.blend | W-038 | `in_progress` | SceneDelta diff/apply、revision 冲突校验、原生 C ABI `get_scene_delta` 以及 `setFrame`/`setObjectVisibility` 最小命令路径已闭环;mesh-buffer/material range、拓扑 delta 和完整 command registry 仍待完成 | | W-039 | `in_progress` | basic_scene 已闭环;等待 W-033~W-038 的完整验收 | | W-040 | `in_progress` | OPFS 项目路径校验、projects/ 场景/资产/缩略图/tmp/cache/lod 目录和临时文件写入辅助已实现;跨浏览器 rename/crash recovery 仍待完成 | -| W-041 | `in_progress` | IndexedDB schema v6 已包含 project/asset/snapshot/log/quarantine/LOD/Simulation manifest stores 并有逐版升级 E2E;旧版本回滚测试仍待完成 | +| W-041 | `in_progress` | IndexedDB schema v7 已包含 project/asset/snapshot/log/quarantine/LOD/Simulation manifest/quarantine stores,并有 v6→v7 migration 与逐版升级 E2E;旧版本回滚测试仍待完成 | | W-042 | `in_progress` | StorageWorker transferable save 已实现 OPFS tmp->replace 与 IndexedDB project revision/size metadata,并有 E2E 证据;hash/crash recovery/snapshot manifest 仍待完成 | | W-043 | `in_progress` | 1.5 秒可取消 autosave 已接入 React dirty 状态并经 E2E 验证;命令数阈值、快照保留和大文件背压仍待完成 | | W-044 | `in_progress` | operation_log schema、payload/inversePayload 写入和 StorageClient 接口已实现并经 E2E 验证;replay、损坏隔离和 native command recovery 仍待完成 | diff --git a/blender-5.2.0/source/blender/modifiers/intern/MOD_nodes_web.cc b/blender-5.2.0/source/blender/modifiers/intern/MOD_nodes_web.cc index 25cf7e89..54bf0185 100644 --- a/blender-5.2.0/source/blender/modifiers/intern/MOD_nodes_web.cc +++ b/blender-5.2.0/source/blender/modifiers/intern/MOD_nodes_web.cc @@ -5,12 +5,17 @@ /** \file * \ingroup modifiers * - * Native Web evaluator for the first bounded Geometry Nodes closure. It - * accepts a single Group Input -> Transform Geometry -> Group Output graph. - * Unsupported graphs remain visible and report an explicit modifier error. + * Native Web evaluator for the bounded Geometry Nodes allowlist. Unsupported + * graphs remain visible and report an explicit modifier error. */ +#include +#include +#include #include +#include +#include +#include #include "MEM_guardedalloc.h" @@ -18,19 +23,32 @@ #include "BLI_math_rotation.hh" #include "BLI_listbase.h" #include "BLI_string.h" +#include "BLI_vector.hh" +#include "BKE_attribute.hh" +#include "BKE_geometry_set.hh" +#include "BKE_instances.hh" #include "BKE_lib_query.hh" #include "BKE_mesh.hh" #include "BKE_modifier.hh" #include "BKE_node.hh" #include "BKE_node_legacy_types.hh" +#include "BKE_object.hh" #include "BLO_read_write.hh" #include "DEG_depsgraph_build.hh" +#include "DEG_depsgraph_query.hh" +#include "DNA_collection_types.h" +#include "DNA_image_types.h" #include "DNA_modifier_types.h" #include "DNA_node_types.h" +#include "DNA_object_types.h" + +#include "GEO_join_geometries.hh" +#include "GEO_realize_instances.hh" +#include "GEO_transform.hh" #include "RNA_prototypes.hh" #include "UI_resources.hh" @@ -89,8 +107,39 @@ static void foreach_ID_link(ModifierData *md, Object *object, IDWalkFunc walk, v static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { NodesModifierData *nmd = reinterpret_cast(md); - if (nmd->node_group != nullptr) { - DEG_add_node_tree_output_relation(ctx->node, nmd->node_group, "Web Geometry Nodes Modifier"); + if (nmd->node_group == nullptr) { + return; + } + DEG_add_node_tree_output_relation(ctx->node, nmd->node_group, "Web Geometry Nodes Modifier"); + for (const bNode &node : nmd->node_group->nodes) { + for (const bNodeSocket &socket : node.inputs) { + if (socket.default_value == nullptr) { + continue; + } + if (socket.type == SOCK_OBJECT) { + Object *object = static_cast(socket.default_value)->value; + if (object != nullptr && object != ctx->object) { + DEG_add_object_relation( + ctx->node, object, DEG_OB_COMP_TRANSFORM, "Web Geometry Nodes Object Info"); + DEG_add_object_relation( + ctx->node, object, DEG_OB_COMP_GEOMETRY, "Web Geometry Nodes Object Info"); + } + } + else if (socket.type == SOCK_COLLECTION) { + Collection *collection = + static_cast(socket.default_value)->value; + if (collection != nullptr) { + DEG_add_collection_geometry_relation( + ctx->node, collection, "Web Geometry Nodes Collection Info"); + } + } + else if (socket.type == SOCK_IMAGE) { + Image *image = static_cast(socket.default_value)->value; + if (image != nullptr) { + DEG_add_generic_id_relation(ctx->node, &image->id, "Web Geometry Nodes Image Info"); + } + } + } } } @@ -102,7 +151,7 @@ static bool is_disabled(const Scene *, ModifierData *md, bool) static const bNodeSocket *find_input(const bNode &node, const char *identifier) { for (const bNodeSocket &socket : node.inputs) { - if (STREQ(socket.identifier, identifier)) { + if (STREQ(socket.identifier, identifier) || STREQ(socket.name, identifier)) { return &socket; } } @@ -119,24 +168,12 @@ static const bNodeSocket *find_output(const bNode &node, const char *identifier) return nullptr; } -static bool has_link(const bNodeTree &tree, - const bNode &from_node, - const char *from_socket, - const bNode &to_node, - const char *to_socket) +static bool node_is(const bNode &node, const char *idname) { - const auto matches_socket = [](const bNodeSocket &socket, const char *name) { - return STREQ(socket.identifier, name) || STREQ(socket.name, name); - }; - for (const bNodeLink &link : tree.links) { - if (link.fromnode == &from_node && link.tonode == &to_node && link.fromsock != nullptr && - link.tosock != nullptr && matches_socket(*link.fromsock, from_socket) && - matches_socket(*link.tosock, to_socket)) - { - return true; - } + if (STREQ(node.idname, idname)) { + return true; } - return false; + return std::string(node.idname) == "Undefined[" + std::string(idname) + "]"; } static bool has_input_link(const bNodeTree &tree, const bNode &node, const char *socket_name) @@ -151,6 +188,659 @@ static bool has_input_link(const bNodeTree &tree, const bNode &node, const char return false; } +using WebNodeValue = std::variant; + +class WebGeometryNodeEvaluator { + public: + static constexpr int max_evaluations = 4096; + static constexpr int max_point_elements = 1'000'000; + static constexpr int max_edge_elements = 2'000'000; + static constexpr int max_face_elements = 2'000'000; + static constexpr int max_corner_elements = 4'000'000; + static constexpr int max_instance_elements = 100'000; + static constexpr int64_t max_field_bytes = 64 * 1024 * 1024; + + private: + const bNodeTree &tree_; + const ModifierEvalContext &ctx_; + const Mesh &input_mesh_; + std::string error_; + int evaluation_count_ = 0; + + bool mesh_within_domain_budget(const Mesh &mesh) + { + if (mesh.verts_num > max_point_elements || mesh.edges_num > max_edge_elements || + mesh.faces_num > max_face_elements || mesh.corners_num > max_corner_elements) + { + error_ = "field/domain element budget exceeded"; + return false; + } + return true; + } + + bool geometry_within_domain_budget(const bke::GeometrySet &geometry) + { + if (const Mesh *mesh = geometry.get_mesh()) { + if (!mesh_within_domain_budget(*mesh)) { + return false; + } + } + if (const bke::Instances *instances = geometry.get_instances()) { + if (instances->instances_num() > max_instance_elements) { + error_ = "instance domain element budget exceeded"; + return false; + } + } + return true; + } + + WebNodeValue bounded_geometry(bke::GeometrySet geometry) + { + if (!geometry_within_domain_budget(geometry)) { + return {}; + } + return std::move(geometry); + } + + const bNode *owner_of(const bNodeSocket &socket) const + { + for (const bNode &node : tree_.nodes) { + for (const bNodeSocket &candidate : node.inputs) { + if (&candidate == &socket) { + return &node; + } + } + for (const bNodeSocket &candidate : node.outputs) { + if (&candidate == &socket) { + return &node; + } + } + } + return nullptr; + } + + Vector input_links(const bNodeSocket &socket) const + { + Vector links; + for (const bNodeLink &link : tree_.links) { + if (link.tosock == &socket && link.fromnode != nullptr && link.fromsock != nullptr) { + links.append(&link); + } + } + std::ranges::sort(links, [](const bNodeLink *a, const bNodeLink *b) { + return a->multi_input_sort_id > b->multi_input_sort_id; + }); + return links; + } + + WebNodeValue default_value(const bNodeSocket &socket) + { + if (socket.default_value == nullptr) { + return {}; + } + switch (socket.type) { + case SOCK_BOOLEAN: + return static_cast(socket.default_value)->value != 0; + case SOCK_INT: + return static_cast(socket.default_value)->value; + case SOCK_FLOAT: + return static_cast(socket.default_value)->value; + case SOCK_VECTOR: + return float3(static_cast(socket.default_value)->value); + case SOCK_STRING: + return std::string( + static_cast(socket.default_value)->value); + case SOCK_OBJECT: + return static_cast(socket.default_value)->value; + case SOCK_COLLECTION: + return static_cast(socket.default_value)->value; + case SOCK_IMAGE: + return static_cast(socket.default_value)->value; + default: + return {}; + } + } + + std::optional number(const WebNodeValue &value) const + { + if (const int *integer = std::get_if(&value)) { + return float(*integer); + } + if (const float *scalar = std::get_if(&value)) { + return *scalar; + } + return std::nullopt; + } + + bke::GeometrySet copy_geometry(const bke::GeometrySet &geometry) const + { + if (const Mesh *mesh = geometry.get_mesh()) { + return bke::GeometrySet::from_mesh(BKE_mesh_copy_for_eval(*mesh)); + } + bke::GeometrySet copy = geometry; + copy.ensure_owns_all_data(); + return copy; + } + + WebNodeValue evaluate_input(const bNodeSocket &socket) + { + const Vector links = input_links(socket); + if (links.size() == 1) { + return evaluate_output(*links.first()->fromsock); + } + if (links.size() > 1) { + error_ = "multiple links on a non-multi-input socket"; + return {}; + } + return default_value(socket); + } + + std::optional geometry_input(const bNode &node, const char *name) + { + const bNodeSocket *socket = find_input(node, name); + if (socket == nullptr) { + error_ = std::string("missing geometry input: ") + name; + return std::nullopt; + } + WebNodeValue value = evaluate_input(*socket); + if (bke::GeometrySet *geometry = std::get_if(&value)) { + return std::move(*geometry); + } + error_ = std::string("geometry input did not evaluate to geometry: ") + name; + return std::nullopt; + } + + WebNodeValue evaluate_compare(const bNode &node) + { + const NodeFunctionCompare *storage = static_cast(node.storage); + const bNodeSocket *a_socket = find_input(node, "A"); + const bNodeSocket *b_socket = find_input(node, "B"); + if (storage == nullptr || a_socket == nullptr || b_socket == nullptr || + !ELEM(storage->data_type, SOCK_INT, SOCK_FLOAT)) + { + error_ = "Compare supports bounded Int/Float scalar inputs only"; + return {}; + } + const std::optional a = number(evaluate_input(*a_socket)); + const std::optional b = number(evaluate_input(*b_socket)); + if (!a || !b) { + error_ = "Compare scalar input is unavailable"; + return {}; + } + switch (storage->operation) { + case NODE_COMPARE_LESS_THAN: + return *a < *b; + case NODE_COMPARE_LESS_EQUAL: + return *a <= *b; + case NODE_COMPARE_GREATER_THAN: + return *a > *b; + case NODE_COMPARE_GREATER_EQUAL: + return *a >= *b; + case NODE_COMPARE_EQUAL: + return *a == *b; + case NODE_COMPARE_NOT_EQUAL: + return *a != *b; + default: + error_ = "Compare operation is outside the bounded evaluator"; + return {}; + } + } + + WebNodeValue evaluate_math(const bNode &node) + { + const bNodeSocket *a_socket = find_input(node, "Value"); + const bNodeSocket *b_socket = nullptr; + for (const bNodeSocket &socket : node.inputs) { + if (STREQ(socket.identifier, "Value_001")) { + b_socket = &socket; + } + } + if (a_socket == nullptr || b_socket == nullptr) { + error_ = "Math sockets are incomplete"; + return {}; + } + const std::optional a = number(evaluate_input(*a_socket)); + const std::optional b = number(evaluate_input(*b_socket)); + if (!a || !b) { + error_ = "Math scalar input is unavailable"; + return {}; + } + switch (node.custom1) { + case NODE_MATH_ADD: + return *a + *b; + case NODE_MATH_SUBTRACT: + return *a - *b; + case NODE_MATH_MULTIPLY: + return *a * *b; + case NODE_MATH_DIVIDE: + return *b == 0.0f ? 0.0f : *a / *b; + case NODE_MATH_MINIMUM: + return std::min(*a, *b); + case NODE_MATH_MAXIMUM: + return std::max(*a, *b); + default: + error_ = "Math operation is outside the bounded evaluator"; + return {}; + } + } + + WebNodeValue evaluate_collection_info(const bNode &node) + { + const bNodeSocket *collection_socket = find_input(node, "Collection"); + const bNodeSocket *separate_socket = find_input(node, "Separate Children"); + const bNodeSocket *reset_socket = find_input(node, "Reset Children"); + if (collection_socket == nullptr || separate_socket == nullptr || reset_socket == nullptr) { + error_ = "Collection Info sockets are incomplete"; + return {}; + } + const WebNodeValue collection_value = evaluate_input(*collection_socket); + const WebNodeValue separate_value = evaluate_input(*separate_socket); + const WebNodeValue reset_value = evaluate_input(*reset_socket); + Collection *const *collection = std::get_if(&collection_value); + const bool *separate_children = std::get_if(&separate_value); + const bool *reset_children = std::get_if(&reset_value); + if (collection == nullptr || *collection == nullptr || separate_children == nullptr || + reset_children == nullptr || !*separate_children) + { + error_ = "Collection Info requires a concrete collection with Separate Children enabled"; + return {}; + } + + Vector objects; + for (const CollectionObject &entry : (*collection)->gobject) { + if (entry.ob != nullptr && entry.ob != ctx_.object) { + objects.append(entry.ob); + } + } + if (objects.size() > max_instance_elements) { + error_ = "Collection Info instance domain budget exceeded"; + return {}; + } + auto instances = std::make_unique(); + instances->resize(objects.size()); + MutableSpan handles = instances->reference_handles_for_write(); + MutableSpan transforms = instances->transforms_for_write(); + for (const int index : objects.index_range()) { + Object *evaluated = reinterpret_cast( + DEG_get_evaluated_id(ctx_.depsgraph, &objects[index]->id)); + if (evaluated == nullptr) { + error_ = "Collection Info object is not available in the evaluated dependency graph"; + return {}; + } + handles[index] = instances->add_reference(*evaluated); + transforms[index] = *reset_children ? float4x4::identity() : evaluated->object_to_world(); + } + instances->tag_reference_handles_changed(); + return bounded_geometry(bke::GeometrySet::from_instances(std::move(instances))); + } + + WebNodeValue evaluate_output(const bNodeSocket &socket) + { + if (++evaluation_count_ > max_evaluations) { + error_ = "evaluation budget exceeded"; + return {}; + } + const bNode *node = owner_of(socket); + if (node == nullptr) { + error_ = "output socket owner is missing"; + return {}; + } + if (node_is(*node, "NodeGroupInput")) { + if (socket.type != SOCK_GEOMETRY) { + error_ = "only the Geometry group input is supported"; + return {}; + } + if (!mesh_within_domain_budget(input_mesh_)) { + return {}; + } + return bounded_geometry( + bke::GeometrySet::from_mesh(BKE_mesh_copy_for_eval(input_mesh_))); + } + if (node_is(*node, "FunctionNodeInputInt")) { + const NodeInputInt *storage = static_cast(node->storage); + return storage == nullptr ? WebNodeValue{} : WebNodeValue{storage->integer}; + } + if (node_is(*node, "FunctionNodeInputVector")) { + const NodeInputVector *storage = static_cast(node->storage); + if (storage == nullptr || storage->dimensions != 3) { + error_ = "Vector input requires exactly three dimensions"; + return {}; + } + return float3(storage->vector); + } + if (node_is(*node, "ShaderNodeValue")) { + return default_value(socket); + } + if (node_is(*node, "ShaderNodeMath")) { + return evaluate_math(*node); + } + if (node_is(*node, "FunctionNodeCompare")) { + return evaluate_compare(*node); + } + if (node_is(*node, "GeometryNodeImageInfo")) { + const bNodeSocket *image_socket = find_input(*node, "Image"); + if (image_socket == nullptr) { + error_ = "Image Info image input is missing"; + return {}; + } + const WebNodeValue image_value = evaluate_input(*image_socket); + Image *const *image = std::get_if(&image_value); + if (image == nullptr || *image == nullptr) { + error_ = "Image Info image is unavailable"; + return {}; + } + if (STREQ(socket.identifier, "Width")) { + return (*image)->gen_x; + } + if (STREQ(socket.identifier, "Height")) { + return (*image)->gen_y; + } + if (STREQ(socket.identifier, "Has Alpha")) { + return true; + } + if (STREQ(socket.identifier, "Frame Count")) { + return 1; + } + if (STREQ(socket.identifier, "FPS")) { + return 0.0f; + } + error_ = "Image Info output is unsupported"; + return {}; + } + if (node_is(*node, "GeometryNodeObjectInfo")) { + const bNodeSocket *object_socket = find_input(*node, "Object"); + if (object_socket == nullptr) { + error_ = "Object Info object input is missing"; + return {}; + } + const WebNodeValue object_value = evaluate_input(*object_socket); + Object *const *object = std::get_if(&object_value); + if (object == nullptr || *object == nullptr || *object == ctx_.object) { + error_ = "Object Info target is unavailable or recursive"; + return {}; + } + Object *evaluated = reinterpret_cast( + DEG_get_evaluated_id(ctx_.depsgraph, &(*object)->id)); + if (evaluated == nullptr) { + error_ = "Object Info target is not evaluated"; + return {}; + } + if (STREQ(socket.identifier, "Location")) { + return float3(evaluated->object_to_world().location()); + } + if (STREQ(socket.identifier, "Scale")) { + return float3(evaluated->scale); + } + if (STREQ(socket.identifier, "Geometry")) { + Mesh *target_mesh = BKE_object_get_evaluated_mesh(evaluated); + if (target_mesh == nullptr) { + error_ = "Object Info target has no evaluated mesh"; + return {}; + } + if (!mesh_within_domain_budget(*target_mesh)) { + return {}; + } + return bounded_geometry( + bke::GeometrySet::from_mesh(BKE_mesh_copy_for_eval(*target_mesh))); + } + error_ = "Object Info output is outside the bounded evaluator"; + return {}; + } + if (node_is(*node, "GeometryNodeCollectionInfo")) { + return evaluate_collection_info(*node); + } + if (node_is(*node, "GeometryNodeJoinGeometry")) { + const bNodeSocket *input = find_input(*node, "Geometry"); + if (input == nullptr) { + error_ = "Join Geometry input is missing"; + return {}; + } + Vector geometries; + for (const bNodeLink *link : input_links(*input)) { + WebNodeValue value = evaluate_output(*link->fromsock); + bke::GeometrySet *geometry = std::get_if(&value); + if (geometry == nullptr) { + error_ = "Join Geometry received a non-geometry input"; + return {}; + } + geometries.append(std::move(*geometry)); + } + if (geometries.is_empty()) { + return bke::GeometrySet(); + } + int64_t points = 0; + int64_t edges = 0; + int64_t faces = 0; + int64_t corners = 0; + int64_t instances = 0; + for (const bke::GeometrySet &geometry : geometries) { + if (const Mesh *mesh = geometry.get_mesh()) { + points += mesh->verts_num; + edges += mesh->edges_num; + faces += mesh->faces_num; + corners += mesh->corners_num; + } + if (const bke::Instances *geometry_instances = geometry.get_instances()) { + instances += geometry_instances->instances_num(); + } + } + if (points > max_point_elements || edges > max_edge_elements || + faces > max_face_elements || corners > max_corner_elements || + instances > max_instance_elements) + { + error_ = "Join Geometry field/domain budget exceeded"; + return {}; + } + return bounded_geometry(geometry::join_geometries( + geometries.as_span(), bke::AttributeFilter::default_filter())); + } + if (node_is(*node, "GeometryNodeSeparateGeometry")) { + std::optional geometry = geometry_input(*node, "Geometry"); + const bNodeSocket *selection_socket = find_input(*node, "Selection"); + if (!geometry || selection_socket == nullptr) { + return {}; + } + const WebNodeValue selection_value = evaluate_input(*selection_socket); + const bool *selection = std::get_if(&selection_value); + if (selection == nullptr) { + error_ = "Separate Geometry requires a bounded constant selection"; + return {}; + } + const bool selected_output = STREQ(socket.identifier, "Selection"); + const bool inverted_output = STREQ(socket.identifier, "Inverted"); + if (!selected_output && !inverted_output) { + error_ = "Separate Geometry output is invalid"; + return {}; + } + return bounded_geometry((*selection == selected_output) ? std::move(*geometry) : + bke::GeometrySet()); + } + if (node_is(*node, "GeometryNodeRealizeInstances")) { + std::optional geometry = geometry_input(*node, "Geometry"); + if (!geometry) { + return {}; + } + geometry::RealizeInstancesOptions options; + options.keep_original_ids = false; + options.realize_instance_attributes = true; + options.realize_to_point_domain = true; + geometry::RealizeInstancesResult result = geometry::realize_instances(std::move(*geometry), + options); + if (!result.errors.is_empty()) { + error_ = result.errors.first(); + return {}; + } + return bounded_geometry(std::move(result.geometry)); + } + if (node_is(*node, "GeometryNodeStoreNamedAttribute")) { + std::optional geometry = geometry_input(*node, "Geometry"); + const bNodeSocket *selection_socket = find_input(*node, "Selection"); + const bNodeSocket *name_socket = find_input(*node, "Name"); + const bNodeSocket *value_socket = find_input(*node, "Value"); + const NodeGeometryStoreNamedAttribute *storage = + static_cast(node->storage); + if (!geometry || selection_socket == nullptr || name_socket == nullptr || + value_socket == nullptr || storage == nullptr || storage->data_type != CD_PROP_FLOAT || + storage->domain != int8_t(bke::AttrDomain::Point)) + { + error_ = "Store Named Attribute supports Float point attributes only"; + return {}; + } + const WebNodeValue selection_value = evaluate_input(*selection_socket); + const WebNodeValue name_value = evaluate_input(*name_socket); + const std::optional attribute_value = number(evaluate_input(*value_socket)); + const bool *selection = std::get_if(&selection_value); + const std::string *name = std::get_if(&name_value); + if (selection == nullptr || name == nullptr || name->empty() || name->size() > 64 || + !attribute_value) + { + error_ = "Store Named Attribute inputs are invalid"; + return {}; + } + bke::GeometrySet result = copy_geometry(*geometry); + if (*selection) { + Mesh *result_mesh = result.get_mesh_for_write(); + if (result_mesh == nullptr) { + error_ = "Store Named Attribute requires mesh geometry"; + return {}; + } + if (!mesh_within_domain_budget(*result_mesh) || + int64_t(result_mesh->verts_num) * int64_t(sizeof(float)) > max_field_bytes) + { + error_ = "Store Named Attribute field budget exceeded"; + return {}; + } + bke::SpanAttributeWriter writer = + result_mesh->attributes_for_write().lookup_or_add_for_write_span( + *name, bke::AttrDomain::Point); + if (!writer) { + error_ = "Store Named Attribute could not allocate the point attribute"; + return {}; + } + writer.span.fill(*attribute_value); + writer.finish(); + } + return bounded_geometry(std::move(result)); + } + if (node_is(*node, "GeometryNodeTransform")) { + std::optional geometry = geometry_input(*node, "Geometry"); + const bNodeSocket *translation_socket = find_input(*node, "Translation"); + const bNodeSocket *rotation_socket = find_input(*node, "Rotation"); + const bNodeSocket *scale_socket = find_input(*node, "Scale"); + if (!geometry || translation_socket == nullptr || rotation_socket == nullptr || + scale_socket == nullptr || translation_socket->default_value == nullptr || + rotation_socket->default_value == nullptr || scale_socket->default_value == nullptr || + has_input_link(tree_, *node, "Rotation") || has_input_link(tree_, *node, "Scale")) + { + error_ = "Transform Geometry inputs are outside the bounded evaluator"; + return {}; + } + WebNodeValue translation_value = evaluate_input(*translation_socket); + const float3 *translation = std::get_if(&translation_value); + if (translation == nullptr) { + error_ = "Transform Geometry translation is unavailable"; + return {}; + } + const bNodeSocketValueRotation &rotation_value = + *static_cast(rotation_socket->default_value); + const math::Quaternion rotation = math::to_quaternion( + math::EulerXYZ(float3(rotation_value.value_euler))); + const float3 scale( + static_cast(scale_socket->default_value)->value); + bke::GeometrySet result = copy_geometry(*geometry); + geometry::transform_geometry( + result, math::from_loc_rot_scale(*translation, rotation, scale)); + return bounded_geometry(std::move(result)); + } + if (node_is(*node, "GeometryNodeSetPosition")) { + std::optional geometry = geometry_input(*node, "Geometry"); + const bNodeSocket *selection_socket = find_input(*node, "Selection"); + const bNodeSocket *offset_socket = find_input(*node, "Offset"); + if (!geometry || selection_socket == nullptr || offset_socket == nullptr || + has_input_link(tree_, *node, "Position")) + { + error_ = "Set Position inputs are outside the bounded evaluator"; + return {}; + } + const WebNodeValue selection_value = evaluate_input(*selection_socket); + const WebNodeValue offset_value = evaluate_input(*offset_socket); + const bool *selection = std::get_if(&selection_value); + const float3 *offset = std::get_if(&offset_value); + if (selection == nullptr || offset == nullptr) { + error_ = "Set Position requires a constant selection and vector offset"; + return {}; + } + bke::GeometrySet result = copy_geometry(*geometry); + if (*selection) { + geometry::translate_geometry(result, *offset); + } + return bounded_geometry(std::move(result)); + } + + error_ = std::string("unsupported node type: ") + node->idname; + return {}; + } + + public: + WebGeometryNodeEvaluator(const bNodeTree &tree, + const ModifierEvalContext &ctx, + const Mesh &input_mesh) + : tree_(tree), ctx_(ctx), input_mesh_(input_mesh) + { + } + + Mesh *evaluate() + { + const bNode *group_output = nullptr; + for (const bNode &node : tree_.nodes) { + if (node_is(node, "NodeGroupOutput")) { + if (group_output != nullptr) { + error_ = "multiple Group Output nodes are unsupported"; + return nullptr; + } + group_output = &node; + } + } + const bNodeSocket *geometry_socket = group_output == nullptr ? nullptr : + find_input(*group_output, + "Geometry"); + if (geometry_socket == nullptr) { + error_ = "Geometry Group Output is missing"; + return nullptr; + } + WebNodeValue value = evaluate_input(*geometry_socket); + bke::GeometrySet *geometry = std::get_if(&value); + if (geometry == nullptr || !error_.empty()) { + if (error_.empty()) { + error_ = "Geometry Group Output did not evaluate to geometry"; + } + return nullptr; + } + if (!geometry_within_domain_budget(*geometry)) { + return nullptr; + } + bke::MeshComponent &mesh_component = + geometry->get_component_for_write(); + mesh_component.ensure_owns_direct_data(); + Mesh *result = mesh_component.release(); + return result == nullptr ? BKE_mesh_new_nomain(0, 0, 0, 0) : result; + } + + const std::string &error() const + { + return error_; + } +}; + static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { const NodesModifierData *nmd = reinterpret_cast(md); @@ -168,13 +858,8 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh return mesh; } - const bNode *group_input = nullptr; - const bNode *group_output = nullptr; - const bNode *transform = nullptr; - const bNode *set_position = nullptr; bool has_simulation_node = false; int node_count = 0; - int link_count = 0; for (const bNode &node : tree->nodes) { node_count++; if (ELEM(node.type_legacy, @@ -188,25 +873,6 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh { has_simulation_node = true; } - if (find_input(node, "Translation") != nullptr && find_input(node, "Rotation") != nullptr && - find_input(node, "Scale") != nullptr) - { - transform = &node; - } - else if (find_input(node, "Offset") != nullptr && find_input(node, "Position") != nullptr && - find_input(node, "Selection") != nullptr) - { - set_position = &node; - } - else if (node.inputs.first == nullptr && node.outputs.first != nullptr) { - group_input = &node; - } - else if (node.inputs.first != nullptr && node.outputs.first == nullptr) { - group_output = &node; - } - } - for ([[maybe_unused]] const bNodeLink &link : tree->links) { - link_count++; } if (has_simulation_node) { @@ -217,85 +883,20 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh return mesh; } - const bNode *geometry_node = transform != nullptr ? transform : set_position; - const bool input_link_valid = group_input != nullptr && geometry_node != nullptr && - has_link(*tree, *group_input, "Geometry", *geometry_node, "Geometry"); - const bool output_link_valid = geometry_node != nullptr && group_output != nullptr && - has_link(*tree, *geometry_node, "Geometry", *group_output, "Geometry"); - if (node_count != 3 || link_count != 2 || group_input == nullptr || - group_output == nullptr || geometry_node == nullptr || (transform != nullptr && set_position != nullptr) || - !input_link_valid || !output_link_valid) - { - BKE_modifier_set_error( - ctx->object, - md, - "Web Geometry Nodes supports only a single constant Transform Geometry or Set Position node"); - return mesh; - } - - - if (set_position != nullptr) { - const bNodeSocket *selection_socket = find_input(*set_position, "Selection"); - const bNodeSocket *offset_socket = find_input(*set_position, "Offset"); - if (selection_socket == nullptr || offset_socket == nullptr || - selection_socket->default_value == nullptr || offset_socket->default_value == nullptr || - has_input_link(*tree, *set_position, "Selection") || - has_input_link(*tree, *set_position, "Position") || - has_input_link(*tree, *set_position, "Offset")) - { - BKE_modifier_set_error( - ctx->object, md, "Web Geometry Nodes Set Position supports only unlinked constant inputs"); - return mesh; - } - Mesh *result = BKE_mesh_copy_for_eval(*mesh); - const bool selected = static_cast( - selection_socket->default_value) - ->value; - if (selected) { - const float3 offset( - static_cast(offset_socket->default_value)->value); - for (float3 &position : result->vert_positions_for_write()) { - position += offset; - } - result->tag_positions_changed(); - } - return result; - } - - const bNodeSocket *mode_socket = find_input(*transform, "Mode"); - const int mode = mode_socket && mode_socket->default_value ? - static_cast(mode_socket->default_value)->value : - GEO_NODE_TRANSFORM_MODE_COMPONENTS; - if (mode != GEO_NODE_TRANSFORM_MODE_COMPONENTS) { + if (node_count > WebGeometryNodeEvaluator::max_evaluations) { BKE_modifier_set_error(ctx->object, md, - "Web Geometry Nodes Transform supports Components mode only"); + "WEB_GEOMETRY_NODES_EVALUATOR_UNSUPPORTED: node budget exceeded"); return mesh; } - const bNodeSocket *translation_socket = find_input(*transform, "Translation"); - const bNodeSocket *rotation_socket = find_input(*transform, "Rotation"); - const bNodeSocket *scale_socket = find_input(*transform, "Scale"); - if (translation_socket == nullptr || rotation_socket == nullptr || scale_socket == nullptr || - translation_socket->default_value == nullptr || rotation_socket->default_value == nullptr || - scale_socket->default_value == nullptr) - { - BKE_modifier_set_error(ctx->object, md, "Web Geometry Nodes Transform sockets are incomplete"); + WebGeometryNodeEvaluator evaluator(*tree, *ctx, *mesh); + Mesh *result = evaluator.evaluate(); + if (result == nullptr) { + const std::string message = "WEB_GEOMETRY_NODES_EVALUATOR_UNSUPPORTED: " + evaluator.error(); + BKE_modifier_set_error(ctx->object, md, "%s", message.c_str()); return mesh; } - - const float3 translation( - static_cast(translation_socket->default_value)->value); - const bNodeSocketValueRotation &rotation_value = - *static_cast(rotation_socket->default_value); - const math::Quaternion rotation = math::to_quaternion( - math::EulerXYZ(float3(rotation_value.value_euler))); - const float3 scale( - static_cast(scale_socket->default_value)->value); - - Mesh *result = BKE_mesh_copy_for_eval(*mesh); - bke::mesh_transform( - *result, math::from_loc_rot_scale(translation, rotation, scale), false); return result; } diff --git a/blender-5.2.0/source/blender/web_engine/web_engine_api.cpp b/blender-5.2.0/source/blender/web_engine/web_engine_api.cpp index 39bcd9e6..c0427d59 100644 --- a/blender-5.2.0/source/blender/web_engine/web_engine_api.cpp +++ b/blender-5.2.0/source/blender/web_engine/web_engine_api.cpp @@ -829,6 +829,13 @@ bool refresh_scene_from_main(EngineState &engine, return false; } snapshot["greasePencils"] = json::parse(grease_pencils_json); + std::string geometry_node_graphs_json; + if (!web_engine_blend_main_geometry_node_graphs_json( + engine.authoritative_main, geometry_node_graphs_json, error)) + { + return false; + } + snapshot["geometryNodeGraphs"] = json::parse(geometry_node_graphs_json); std::string physics_json; if (!web_engine_blend_main_physics_simulation_json( engine.authoritative_main, physics_json, error)) @@ -1132,6 +1139,15 @@ EMSCRIPTEN_KEEPALIVE int web_engine_open_blend(const int handle, return last_error_code; } opened_snapshot["greasePencils"] = json::parse(grease_pencils_json); + std::string geometry_node_graphs_json; + if (!web_engine_blend_main_geometry_node_graphs_json( + authoritative_main, geometry_node_graphs_json, main_error)) + { + web_engine_blend_main_free(authoritative_main); + set_error(WEB_ENGINE_BLEND_READ_FAILED, main_error.c_str()); + return last_error_code; + } + opened_snapshot["geometryNodeGraphs"] = json::parse(geometry_node_graphs_json); std::string physics_json; if (!web_engine_blend_main_physics_simulation_json( authoritative_main, physics_json, main_error)) @@ -1543,9 +1559,10 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle, type == "moveObjectToCollection" || type == "renameId" || type == "joinObjects" || type == "separateMeshFaces" || type == "applyObjectTransform" || type == "setObjectOrigin" || type == "setCurveControlPoints" || type == "setCurveHandle" || type == "setCurveTopology" || type == "setCurveSplines" || type == "setSurfaceTopology" || type == "setFontBody" || - type == "setFontProperties" || type == "setFontAdvanced" || type == "setFontLinks" || type == "setVolumeProperties" || type == "deleteNonMeshData" || + type == "setFontProperties" || type == "setFontAdvanced" || type == "importVFont" || type == "setFontLinks" || type == "setVolumeProperties" || type == "deleteNonMeshData" || type == "setMetaballElements" || type == "createGreasePencilLayer" || type == "removeGreasePencilLayer" || type == "moveGreasePencilLayer" || + type == "moveGreasePencilFrame" || type == "insertGreasePencilFrame" || type == "removeGreasePencilFrame" || type == "setGreasePencilStrokes" || type == "setVertexColors" || type == "setVertexWeights" || type == "setCameraProperties" || type == "setLightProperties" || @@ -1919,6 +1936,13 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle, engine->authoritative_main, command.value("dataId", "").c_str(), characters, text_boxes, command.value("activeTextBox", -1), main_error); } + else if (type == "importVFont") { + std::string font_id; + applied = web_engine_blend_main_import_vfont( + engine->authoritative_main, command.value("name", "").c_str(), + command.value("sourcePath", "").c_str(), command.value("base64", "").c_str(), + font_id, main_error); + } else if (type == "setFontLinks") { const json links = command.value("links", json::object()); const std::array fields = {"regular", "bold", "italic", "boldItalic"}; @@ -1984,6 +2008,12 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle, engine->authoritative_main, command.value("dataId", "").c_str(), command.value("layerId", "").c_str(), command.value("direction", "UP").c_str(), main_error); } + else if (type == "moveGreasePencilFrame") { + applied = web_engine_blend_main_move_grease_pencil_frame( + engine->authoritative_main, command.value("dataId", "").c_str(), + command.value("layerId", "").c_str(), command.value("frame", -1), + command.value("targetFrame", -1), command.value("drawingId", "").c_str(), main_error); + } else if (type == "insertGreasePencilFrame") { applied = web_engine_blend_main_insert_grease_pencil_frame( engine->authoritative_main, command.value("dataId", "").c_str(), @@ -2066,7 +2096,8 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle, command.value("vertexGroup", "").c_str(), command.value("indices", std::vector()), command.value("values", std::vector()), - command.value("normalize", false), command.value("mirror", false), main_error); + command.value("normalize", false), command.value("limit", 0u), command.value("mirror", false), + command.value("mirrorAxis", 0), command.value("mirrorTolerance", 1e-4f), main_error); } else if (type == "setCameraProperties" || type == "setLightProperties" || type == "setWorldProperties") { diff --git a/blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp b/blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp index cdd33a89..b9faa0dd 100644 --- a/blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp +++ b/blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp @@ -3513,7 +3513,16 @@ json scene_ir_from_blend(const ParsedBlend &blend, {"emissionStrength", std::clamp(emission_strength, 0.0f, 1000000.0f)} }; if (!normal_image_id.empty()) material["normalImageId"] = normal_image_id; if (!image_ids.empty()) material["imageIds"] = image_ids; - if (!material_nodes.empty()) material["nodes"] = std::move(material_nodes); + if (!material_nodes.empty()) { + /* The browser compiler binds its report to this exact reader graph. */ + material["shaderGraphHash"] = sha256_hex(json({ + {"schemaVersion", 1}, + {"materialId", record.id}, + {"nodes", material_nodes}, + {"links", material_links}, + }).dump()); + material["nodes"] = std::move(material_nodes); + } if (!material_links.empty()) material["links"] = std::move(material_links); if (!warnings.empty()) material["warnings"] = warnings; materials.push_back(std::move(material)); @@ -3636,9 +3645,20 @@ json scene_ir_from_blend(const ParsedBlend &blend, } else if (record.type_name == "VFont") { const std::string filepath = read_string(*blend.sdna, element, "filepath"); - vfonts.push_back({{"id", record.id}, {"name", record.name}, {"sourcePath", filepath}, - {"builtin", filepath == ""}, - {"packed", read_pointer(*blend.sdna, element, "packedfile").value_or(0) != 0}}); + const std::optional packed_file = read_pointer(*blend.sdna, element, "packedfile"); + const std::vector packed_bytes = packed_file ? packed_file_bytes(blend, *packed_file) : + std::vector(); + json resource = {{"id", record.id}, + {"name", record.name}, + {"sourcePath", filepath}, + {"builtin", filepath == ""}, + {"packed", !packed_bytes.empty()}}; + if (!packed_bytes.empty()) { + resource["packedByteLength"] = packed_bytes.size(); + resource["sha256"] = sha256_hex(std::string( + reinterpret_cast(packed_bytes.data()), packed_bytes.size())); + } + vfonts.push_back(std::move(resource)); } else if (record.type_name == "Camera") { const int64_t camera_type = read_integer(*blend.sdna, element, "type").value_or(0); diff --git a/blender-5.2.0/source/blender/web_engine/web_engine_depsgraph.cpp b/blender-5.2.0/source/blender/web_engine/web_engine_depsgraph.cpp index 159e24ad..723d405d 100644 --- a/blender-5.2.0/source/blender/web_engine/web_engine_depsgraph.cpp +++ b/blender-5.2.0/source/blender/web_engine/web_engine_depsgraph.cpp @@ -80,6 +80,7 @@ using json = nlohmann::json; using namespace blender; std::once_flag blender_runtime_once; +constexpr int64_t geometry_node_max_json_scalar_values = 65'536; void initialize_blender_runtime() { @@ -341,6 +342,11 @@ json evaluated_modifier_report(const Object &object, const Object *object_eval) entry["suggestion"] = "Bake a deterministic cache in desktop Blender or disable the simulation zone"; } + else if (error.starts_with("WEB_GEOMETRY_NODES_EVALUATOR_UNSUPPORTED:")) { + entry["errorCode"] = "GEOMETRY_NODES_EVALUATOR_UNSUPPORTED"; + entry["suggestion"] = + "Use only the versioned bounded Geometry Nodes evaluator closure"; + } else if (error.starts_with("WEB_DISPLACE_CONFIGURATION_UNAVAILABLE:")) { entry["errorCode"] = "MODIFIER_CONFIGURATION_UNSUPPORTED"; entry["suggestion"] = @@ -399,7 +405,15 @@ json evaluated_mesh_report(const Depsgraph *graph, Object *object) {"modifiers", evaluated_modifier_report(*object, object_eval)}, {"worldMatrix", json::array()}, {"positions", json::array()}, - {"indices", json::array()}}; + {"indices", json::array()}, + {"domainCardinality", + {{"POINT", mesh->verts_num}, + {"EDGE", mesh->edges_num}, + {"FACE", mesh->faces_num}, + {"CORNER", mesh->corners_num}, + {"CURVE", 0}, + {"INSTANCE", 0}, + {"LAYER", 0}}}}; for (int row = 0; row < 4; row++) { for (int column = 0; column < 4; column++) { /* The Web scene IR uses the same column-major flattening as Blender's Python API and glTF. */ @@ -411,6 +425,32 @@ json evaluated_mesh_report(const Depsgraph *graph, Object *object) report["positions"].push_back(position.y); report["positions"].push_back(position.z); } + const bke::AttributeReader m10_value = mesh->attributes().lookup( + "m10_value", bke::AttrDomain::Point); + if (m10_value) { + json materialization = {{"schemaVersion", 1}, + {"fieldId", "attribute:m10_value"}, + {"domain", "POINT"}, + {"dataType", "FLOAT"}, + {"elementCount", positions.size()}, + {"scalarValueCount", positions.size()}, + {"materializedByteLength", positions.size() * sizeof(float)}}; + if (positions.size() <= geometry_node_max_json_scalar_values) { + materialization["transport"] = "JSON"; + report["attributes"] = {{"m10_value", + {{"domain", "POINT"}, + {"dataType", "FLOAT"}, + {"values", json::array()}}}}; + for (const int index : positions.index_range()) { + report["attributes"]["m10_value"]["values"].push_back(m10_value.varray[index]); + } + } + else { + materialization["transport"] = "BINARY_REQUIRED"; + materialization["errorCode"] = "GN_FIELD_JSON_BUDGET_EXCEEDED"; + } + report["fieldMaterializations"] = json::array({std::move(materialization)}); + } for (const int3 &triangle : corner_tris) { for (int index = 0; index < 3; index++) { report["indices"].push_back(corner_verts[triangle[index]]); diff --git a/blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp b/blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp index a98aed07..4d6c0f75 100644 --- a/blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp +++ b/blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include @@ -36,7 +38,9 @@ #include "BKE_nla.hh" #include "BKE_object.hh" #include "BKE_object_deform.h" +#include "BKE_packedFile.hh" #include "BKE_scene.hh" +#include "BKE_vfont.hh" #include "BKE_volume.hh" #include "BKE_fcurve.hh" #include "BKE_image.hh" @@ -615,6 +619,217 @@ bool append_write_bytes(const void *data, const size_t size, void *user_data) } } +const char *geometry_socket_data_type(const eNodeSocketDatatype type) +{ + switch (type) { + case SOCK_FLOAT: return "FLOAT"; + case SOCK_VECTOR: return "VECTOR"; + case SOCK_RGBA: return "COLOR"; + case SOCK_SHADER: return "SHADER"; + case SOCK_BOOLEAN: return "BOOLEAN"; + case SOCK_INT: return "INT"; + case SOCK_STRING: return "STRING"; + case SOCK_OBJECT: return "OBJECT"; + case SOCK_IMAGE: return "IMAGE"; + case SOCK_GEOMETRY: return "GEOMETRY"; + case SOCK_COLLECTION: return "COLLECTION"; + case SOCK_TEXTURE: return "TEXTURE"; + case SOCK_MATERIAL: return "MATERIAL"; + case SOCK_ROTATION: return "ROTATION"; + case SOCK_MENU: return "MENU"; + case SOCK_MATRIX: return "MATRIX"; + case SOCK_BUNDLE: return "BUNDLE"; + case SOCK_CLOSURE: return "CLOSURE"; + case SOCK_FONT: return "FONT"; + case SOCK_SCENE: return "SCENE"; + case SOCK_TEXT_ID: return "TEXT"; + case SOCK_MASK: return "MASK"; + case SOCK_SOUND: return "SOUND"; + case SOCK_INT_VECTOR: return "INT_VECTOR"; + case SOCK_CUSTOM: return "CUSTOM"; + } + return "CUSTOM"; +} + +eNodeSocketDatatype geometry_interface_socket_type(const char *socket_type) +{ + if (socket_type == nullptr) return SOCK_CUSTOM; + const std::string type(socket_type); + if (type.starts_with("NodeSocketIntVector")) return SOCK_INT_VECTOR; + if (type.starts_with("NodeSocketFloat")) return SOCK_FLOAT; + if (type.starts_with("NodeSocketVector")) return SOCK_VECTOR; + if (type == "NodeSocketColor") return SOCK_RGBA; + if (type == "NodeSocketShader") return SOCK_SHADER; + if (type == "NodeSocketBool") return SOCK_BOOLEAN; + if (type.starts_with("NodeSocketInt")) return SOCK_INT; + if (type.starts_with("NodeSocketString")) return SOCK_STRING; + if (type == "NodeSocketObject") return SOCK_OBJECT; + if (type == "NodeSocketImage") return SOCK_IMAGE; + if (type == "NodeSocketGeometry") return SOCK_GEOMETRY; + if (type == "NodeSocketCollection") return SOCK_COLLECTION; + if (type == "NodeSocketTexture") return SOCK_TEXTURE; + if (type == "NodeSocketMaterial") return SOCK_MATERIAL; + if (type == "NodeSocketRotation") return SOCK_ROTATION; + if (type == "NodeSocketMenu") return SOCK_MENU; + if (type == "NodeSocketMatrix") return SOCK_MATRIX; + if (type == "NodeSocketBundle") return SOCK_BUNDLE; + if (type == "NodeSocketClosure") return SOCK_CLOSURE; + if (type == "NodeSocketFont") return SOCK_FONT; + if (type == "NodeSocketScene") return SOCK_SCENE; + if (type == "NodeSocketText") return SOCK_TEXT_ID; + if (type == "NodeSocketMask") return SOCK_MASK; + if (type == "NodeSocketSound") return SOCK_SOUND; + return SOCK_CUSTOM; +} + +std::string geometry_id_reference(const ID *id) +{ + if (id == nullptr) return {}; + const std::string code(id->name, 2); + const char *prefix = code == "OB" ? "object:" : code == "IM" ? "image:" : + code == "GR" ? "collection:" : code == "TE" ? "texture:" : + code == "MA" ? "material:" : code == "VF" ? "vfont:" : + code == "SC" ? "scene:" : code == "TX" ? "text:" : + code == "MS" ? "mask:" : code == "SO" ? "sound:" : + code == "NT" ? "node-group:" : "id:"; + return std::string(prefix) + id_name(*id); +} + +std::optional geometry_socket_default(const eNodeSocketDatatype type, + const void *default_value) +{ + if (default_value == nullptr) return std::nullopt; + switch (type) { + case SOCK_FLOAT: { + const float value = static_cast(default_value)->value; + return std::isfinite(value) ? std::optional(value) : std::nullopt; + } + case SOCK_INT: + return static_cast(default_value)->value; + case SOCK_BOOLEAN: + return static_cast(default_value)->value != 0; + case SOCK_VECTOR: { + const bNodeSocketValueVector &value = *static_cast(default_value); + const int dimensions = std::clamp(value.dimensions, 2, 4); + json result = json::array(); + for (int index = 0; index < dimensions; index++) { + if (!std::isfinite(value.value[index])) return std::nullopt; + result.push_back(value.value[index]); + } + return result; + } + case SOCK_INT_VECTOR: { + const bNodeSocketValueIntVector &value = *static_cast(default_value); + const int dimensions = std::clamp(value.dimensions, 2, 3); + json result = json::array(); + for (int index = 0; index < dimensions; index++) result.push_back(value.value[index]); + return result; + } + case SOCK_ROTATION: { + const bNodeSocketValueRotation &value = *static_cast(default_value); + if (!std::all_of(std::begin(value.value_euler), std::end(value.value_euler), + [](const float component) { return std::isfinite(component); })) + { + return std::nullopt; + } + return json::array({value.value_euler[0], value.value_euler[1], value.value_euler[2]}); + } + case SOCK_RGBA: { + const bNodeSocketValueRGBA &value = *static_cast(default_value); + if (!std::all_of(std::begin(value.value), std::end(value.value), + [](const float component) { return std::isfinite(component); })) + { + return std::nullopt; + } + return json::array({value.value[0], value.value[1], value.value[2], value.value[3]}); + } + case SOCK_STRING: { + const bNodeSocketValueString &value = *static_cast(default_value); + return std::string(value.value, strnlen(value.value, sizeof(value.value))); + } + case SOCK_MENU: + return static_cast(default_value)->value; + case SOCK_OBJECT: + return geometry_id_reference(reinterpret_cast( + static_cast(default_value)->value)); + case SOCK_IMAGE: + return geometry_id_reference(reinterpret_cast( + static_cast(default_value)->value)); + case SOCK_COLLECTION: + return geometry_id_reference(reinterpret_cast( + static_cast(default_value)->value)); + case SOCK_TEXTURE: + return geometry_id_reference(reinterpret_cast( + static_cast(default_value)->value)); + case SOCK_MATERIAL: + return geometry_id_reference(reinterpret_cast( + static_cast(default_value)->value)); + case SOCK_FONT: + return geometry_id_reference(reinterpret_cast( + static_cast(default_value)->value)); + case SOCK_SCENE: + return geometry_id_reference(reinterpret_cast( + static_cast(default_value)->value)); + case SOCK_TEXT_ID: + return geometry_id_reference(reinterpret_cast( + static_cast(default_value)->value)); + case SOCK_MASK: + return geometry_id_reference(reinterpret_cast( + static_cast(default_value)->value)); + case SOCK_SOUND: + return geometry_id_reference(reinterpret_cast( + static_cast(default_value)->value)); + case SOCK_CUSTOM: + case SOCK_SHADER: + case SOCK_GEOMETRY: + case SOCK_MATRIX: + case SOCK_BUNDLE: + case SOCK_CLOSURE: + return std::nullopt; + } + return std::nullopt; +} + +std::string geometry_node_id(const bNode &node) +{ + return "geometry-node:" + std::to_string(node.identifier); +} + +std::string geometry_node_type(const bNode &node) +{ + std::string stored(node.idname); + constexpr std::string_view undefined_prefix = "Undefined["; + while (stored.starts_with(undefined_prefix) && stored.ends_with("]") && + stored.size() > undefined_prefix.size() + 1) + { + stored = stored.substr(undefined_prefix.size(), stored.size() - undefined_prefix.size() - 1); + } + return stored; +} + +std::string geometry_socket_id(const bNodeSocket &socket, const eNodeSocketInOut direction) +{ + return std::string("geometry-socket:") + (direction == SOCK_IN ? "input:" : "output:") + + socket.identifier; +} + +json geometry_socket_json(const bNodeSocket &socket, const eNodeSocketInOut direction) +{ + json result = {{"id", geometry_socket_id(socket, direction)}, + {"name", socket.name}, + {"direction", direction == SOCK_IN ? "INPUT" : "OUTPUT"}, + {"dataType", geometry_socket_data_type(socket.type)}}; + if (const std::optional value = geometry_socket_default(socket.type, socket.default_value)) { + if (!(value->is_string() && value->get_ref().empty() && + ELEM(socket.type, SOCK_OBJECT, SOCK_IMAGE, SOCK_COLLECTION, SOCK_TEXTURE, SOCK_MATERIAL, + SOCK_FONT, SOCK_SCENE, SOCK_TEXT_ID, SOCK_MASK, SOCK_SOUND))) + { + result["defaultValue"] = *value; + } + } + return result; +} + } // namespace WebBlendMainState *web_engine_blend_main_open(const uint8_t *data, @@ -3451,6 +3666,58 @@ bool web_engine_blend_main_set_font_advanced( return true; } +bool web_engine_blend_main_import_vfont(WebBlendMainState *state, + const char *name, + const char *source_path, + const char *base64, + std::string &font_id, + std::string &error) +{ + if (state == nullptr || state->main == nullptr || name == nullptr || source_path == nullptr || + base64 == nullptr || name[0] == '\0' || strlen(name) > 63 || strlen(source_path) >= FILE_MAX || + strncmp(source_path, "//fonts/", 8) != 0 || strstr(source_path, "\\") != nullptr || + strstr(source_path, "/../") != nullptr || strstr(source_path, "/./") != nullptr) + { + error = "NON_MESH_RESOURCE_OUTSIDE_PROJECT: VFont source must be a bounded //fonts project path"; + return false; + } + for (const VFont &existing : state->main->fonts) { + if (STREQ(existing.filepath, source_path)) { + error = "NON_MESH_PROPERTY_INVALID: A VFont with the same project source path already exists"; + return false; + } + } + const std::vector decoded = decode_base64(base64); + if (decoded.empty() || decoded.size() > 32 * 1024 * 1024) { + error = "NON_MESH_DATA_BUDGET_EXCEEDED: VFont payload is empty or exceeds 32 MiB"; + return false; + } + uint8_t *owned = MEM_new_array_uninitialized(decoded.size(), "WebEngine packed VFont"); + if (owned == nullptr) { + error = "NON_MESH_DATA_BUDGET_EXCEEDED: VFont packed allocation failed"; + return false; + } + memcpy(owned, decoded.data(), decoded.size()); + PackedFile *packed = BKE_packedfile_new_from_memory(owned, int(decoded.size())); + VFont *vfont = static_cast(BKE_libblock_alloc(state->main, ID_VF, name, 0)); + if (packed == nullptr || vfont == nullptr) { + if (packed != nullptr) BKE_packedfile_free(packed); + else MEM_delete(owned); + error = "NON_MESH_BINARY_INVALID: Blender could not allocate the packed VFont"; + return false; + } + STRNCPY(vfont->filepath, source_path); + vfont->packedfile = packed; + BKE_vfont_data_ensure(vfont); + if (vfont->data == nullptr) { + BKE_id_delete(state->main, vfont); + error = "NON_MESH_BINARY_INVALID: Blender rejected the external VFont bytes"; + return false; + } + font_id = "vfont:" + id_name(vfont->id); + return true; +} + bool web_engine_blend_main_set_font_links(WebBlendMainState *state, const char *data_id, const std::array &font_ids, @@ -3630,6 +3897,19 @@ bool web_engine_blend_main_grease_pencils_json(WebBlendMainState *state, {{"name", "vertex_color"}, {"domain", "POINT"}, {"dataType", "FLOAT_COLOR"}, {"components", 4}}, {{"name", "cyclic"}, {"domain", "STROKE"}, {"dataType", "BOOL"}, {"components", 1}}, {{"name", "material_index"}, {"domain", "STROKE"}, {"dataType", "INT"}, {"components", 1}}})}}; + const bke::greasepencil::Layer *active_layer = grease_pencil.get_active_layer(); + if (active_layer == nullptr) { + for (const bke::greasepencil::Layer *layer : layers) { + if (layer != nullptr) { + active_layer = layer; + break; + } + } + } + if (active_layer != nullptr) { + data["activeLayerId"] = "grease-pencil-layer:" + id_name(grease_pencil.id) + ":" + + std::string(active_layer->name()); + } if (budget_blocked) { data["errorCode"] = "GREASE_PENCIL_BUDGET_EXCEEDED"; grease_pencils.push_back(std::move(data)); @@ -3644,6 +3924,9 @@ bool web_engine_blend_main_grease_pencils_json(WebBlendMainState *state, if (frame.is_end()) continue; json strokes = json::array(); uint64_t drawing_point_count = 0; + const std::string drawing_suffix = id_name(grease_pencil.id) + ":" + + std::to_string(frame.drawing_index); + const std::string drawing_id = "grease-pencil-drawing:" + drawing_suffix; const bke::greasepencil::Drawing *drawing = grease_pencil.get_drawing_at(*layer, frame_number); if (drawing != nullptr) { @@ -3665,13 +3948,15 @@ bool web_engine_blend_main_grease_pencils_json(WebBlendMainState *state, for (const int point_index : point_range) { const float3 &position = positions[point_index]; const ColorGeometry4f color = vertex_colors[point_index]; - points.push_back({{"position", json::array({position.x, position.y, position.z})}, + points.push_back({{"id", "grease-pencil-point:" + drawing_suffix + ":" + + std::to_string(curve_index) + ":" + + std::to_string(point_index - point_range.start())}, + {"position", json::array({position.x, position.y, position.z})}, {"radius", radii[point_index]}, {"opacity", opacities[point_index]}, {"vertexColor", json::array({color.r, color.g, color.b, color.a})}}); } - strokes.push_back({{"id", "grease-pencil-stroke:" + id_name(grease_pencil.id) + ":" + - std::to_string(frame.drawing_index) + ":" + + strokes.push_back({{"id", "grease-pencil-stroke:" + drawing_suffix + ":" + std::to_string(curve_index)}, {"cyclic", cyclic[curve_index]}, {"pointCount", point_range.size()}, @@ -3682,8 +3967,7 @@ bool web_engine_blend_main_grease_pencils_json(WebBlendMainState *state, json frame_entry = { {"frame", frame_number}, {"drawing", - {{"id", "grease-pencil-drawing:" + id_name(grease_pencil.id) + ":" + - std::to_string(frame.drawing_index)}, + {{"id", drawing_id}, {"strokeCount", strokes.size()}, {"pointCount", drawing_point_count}, {"strokes", std::move(strokes)}}}}; @@ -3707,6 +3991,183 @@ bool web_engine_blend_main_grease_pencils_json(WebBlendMainState *state, return true; } +bool web_engine_blend_main_geometry_node_graphs_json(WebBlendMainState *state, + std::string &geometry_node_graphs_json, + std::string &error) +{ + if (state == nullptr || state->main == nullptr) { + error = "GN_INVALID_GRAPH: authoritative Main is not open"; + return false; + } + constexpr size_t max_graphs = 4096; + constexpr size_t max_nodes_per_graph = 4096; + constexpr size_t max_links_per_graph = 16384; + constexpr size_t max_sockets_per_graph = 65536; + constexpr size_t max_interface_sockets_per_graph = 4096; + json graphs = json::array(); + try { + for (const bNodeTree &tree : state->main->nodetrees) { + if (strcmp(tree.idname, "GeometryNodeTree") != 0) continue; + if (graphs.size() >= max_graphs) { + error = "GN_GRAPH_BUDGET_EXCEEDED: Geometry Node graph count exceeds 4096"; + return false; + } + const size_t node_count = size_t(BLI_listbase_count(&tree.nodes)); + const size_t link_count = size_t(BLI_listbase_count(&tree.links)); + if (node_count > max_nodes_per_graph || link_count > max_links_per_graph || + tree.interface_inputs().size() > max_interface_sockets_per_graph || + tree.interface_outputs().size() > max_interface_sockets_per_graph) + { + error = "GN_GRAPH_BUDGET_EXCEEDED: Geometry Node graph topology exceeds its bounded reader limits"; + return false; + } + + const std::string graph_id = "node-group:" + id_name(tree.id); + json interface_inputs = json::array(); + json interface_outputs = json::array(); + auto append_interface_socket = [&](const bNodeTreeInterfaceSocket &socket, + const char *direction, + json &destination) -> bool { + if (socket.identifier == nullptr || socket.identifier[0] == '\0' || + socket.name == nullptr || socket.socket_type == nullptr) + { + error = "GN_INVALID_GRAPH: Geometry Node interface socket has no stable identifier, name, or type"; + return false; + } + const eNodeSocketDatatype type = geometry_interface_socket_type(socket.socket_type); + json socket_json = { + {"id", std::string("geometry-interface:") + + (strcmp(direction, "INPUT") == 0 ? "input:" : "output:") + + socket.identifier}, + {"name", socket.name}, + {"direction", direction}, + {"dataType", geometry_socket_data_type(type)}}; + if (const std::optional value = geometry_socket_default(type, socket.socket_data)) { + if (!(value->is_string() && value->get_ref().empty())) { + socket_json["defaultValue"] = *value; + } + } + destination.push_back(std::move(socket_json)); + return true; + }; + for (const bNodeTreeInterfaceSocket *socket : tree.interface_inputs()) { + if (socket != nullptr && !append_interface_socket(*socket, "INPUT", interface_inputs)) { + return false; + } + } + for (const bNodeTreeInterfaceSocket *socket : tree.interface_outputs()) { + if (socket != nullptr && !append_interface_socket(*socket, "OUTPUT", interface_outputs)) { + return false; + } + } + + std::vector source_nodes; + source_nodes.reserve(node_count); + for (const bNode &node : tree.nodes) source_nodes.push_back(&node); + std::sort(source_nodes.begin(), source_nodes.end(), [](const bNode *a, const bNode *b) { + return a->identifier < b->identifier; + }); + json nodes = json::array(); + std::unordered_set node_identifiers; + std::unordered_set group_references; + size_t socket_count = 0; + for (const bNode *node : source_nodes) { + if (node == nullptr || node->identifier <= 0 || + !node_identifiers.insert(node->identifier).second || node->idname[0] == '\0') + { + error = "GN_INVALID_GRAPH: Geometry Node has a missing or duplicate stable identifier"; + return false; + } + json sockets = json::array(); + std::unordered_set socket_ids; + auto append_node_sockets = [&](const ListBaseT &source, + const eNodeSocketInOut direction) -> bool { + for (const bNodeSocket &socket : source) { + if (socket.identifier[0] == '\0' || strcmp(socket.identifier, "__extend__") == 0) { + continue; + } + const std::string socket_id = geometry_socket_id(socket, direction); + if (!socket_ids.insert(socket_id).second) { + error = "GN_INVALID_GRAPH: Geometry Node contains duplicate stable socket identifiers"; + return false; + } + socket_count++; + if (socket_count > max_sockets_per_graph) { + error = "GN_GRAPH_BUDGET_EXCEEDED: Geometry Node socket count exceeds 65536"; + return false; + } + sockets.push_back(geometry_socket_json(socket, direction)); + } + return true; + }; + if (!append_node_sockets(node->inputs, SOCK_IN) || + !append_node_sockets(node->outputs, SOCK_OUT)) + { + return false; + } + const std::string node_type = geometry_node_type(*node); + json node_json = {{"id", geometry_node_id(*node)}, + {"type", node_type}, + {"name", node->name[0] != '\0' ? node->name : node_type}, + {"sockets", std::move(sockets)}}; + if (node->id != nullptr && std::string(node->id->name, 2) == "NT") { + const std::string reference = geometry_id_reference(node->id); + node_json["groupTreeId"] = reference; + group_references.insert(reference); + } + nodes.push_back(std::move(node_json)); + } + + std::vector source_links; + source_links.reserve(link_count); + for (const bNodeLink &link : tree.links) { + if (link.fromnode == nullptr || link.tonode == nullptr || link.fromsock == nullptr || + link.tosock == nullptr || strcmp(link.fromsock->identifier, "__extend__") == 0 || + strcmp(link.tosock->identifier, "__extend__") == 0) + { + continue; + } + if (!node_identifiers.contains(link.fromnode->identifier) || + !node_identifiers.contains(link.tonode->identifier)) + { + error = "GN_INVALID_GRAPH: Geometry Node link references a node outside its graph"; + return false; + } + source_links.push_back({{"fromNodeId", geometry_node_id(*link.fromnode)}, + {"fromSocketId", geometry_socket_id(*link.fromsock, SOCK_OUT)}, + {"toNodeId", geometry_node_id(*link.tonode)}, + {"toSocketId", geometry_socket_id(*link.tosock, SOCK_IN)}}); + } + std::sort(source_links.begin(), source_links.end(), [](const json &a, const json &b) { + return std::tie(a.at("fromNodeId"), a.at("fromSocketId"), a.at("toNodeId"), + a.at("toSocketId")) < + std::tie(b.at("fromNodeId"), b.at("fromSocketId"), b.at("toNodeId"), + b.at("toSocketId")); + }); + json links = json::array(); + for (json &link : source_links) links.push_back(std::move(link)); + std::vector references(group_references.begin(), group_references.end()); + std::sort(references.begin(), references.end()); + json graph = {{"schemaVersion", 1}, + {"id", graph_id}, + {"name", id_name(tree.id)}, + {"interfaceInputs", std::move(interface_inputs)}, + {"interfaceOutputs", std::move(interface_outputs)}, + {"nodes", std::move(nodes)}, + {"links", std::move(links)}, + {"groupReferences", std::move(references)}}; + graph["graphHash"] = web_engine_sha256_hex(graph.dump()); + graphs.push_back(std::move(graph)); + } + geometry_node_graphs_json = graphs.dump(); + return true; + } + catch (const std::exception &exception) { + error = std::string("GN_INVALID_GRAPH: Geometry Node Main reader failed: ") + exception.what(); + return false; + } +} + bool web_engine_blend_main_physics_simulation_json(WebBlendMainState *state, std::string &physics_json, std::string &error) @@ -3884,6 +4345,50 @@ bool web_engine_blend_main_move_grease_pencil_layer(WebBlendMainState *state, return true; } +bool web_engine_blend_main_move_grease_pencil_frame(WebBlendMainState *state, + const char *data_id, + const char *layer_id, + const int frame, + const int target_frame, + const char *drawing_id, + std::string &error) +{ + GreasePencil *grease_pencil = find_grease_pencil(state != nullptr ? state->main : nullptr, + data_id); + bke::greasepencil::Layer *layer = find_grease_pencil_layer(grease_pencil, layer_id); + if (grease_pencil == nullptr || layer == nullptr || drawing_id == nullptr || + !ensure_single_user_data(state != nullptr ? state->main : nullptr, + grease_pencil != nullptr ? &grease_pencil->id : nullptr, + error)) + { + if (error.empty()) error = "GREASE_PENCIL_SCHEMA_INVALID: Grease Pencil frame target was not found"; + return false; + } + if (frame < -1000000 || frame > 1000000 || target_frame < -1000000 || + target_frame > 1000000 || frame == target_frame || layer->frames().contains(target_frame)) + { + error = "GREASE_PENCIL_SCHEMA_INVALID: frame move is outside the bounded range or target frame already exists"; + return false; + } + const GreasePencilFrame *source = layer->frames().lookup_ptr(frame); + if (source == nullptr || source->is_end()) { + error = "GREASE_PENCIL_SCHEMA_INVALID: source Grease Pencil frame was not found"; + return false; + } + const std::string expected_drawing_id = "grease-pencil-drawing:" + + id_name(grease_pencil->id) + ":" + + std::to_string(source->drawing_index); + if (expected_drawing_id != drawing_id) { + error = "GREASE_PENCIL_SCHEMA_INVALID: source drawing identity does not match the frame"; + return false; + } + blender::Map destinations; + destinations.add(frame, target_frame); + grease_pencil->move_frames(*layer, destinations); + grease_pencil->id.recalc |= ID_RECALC_GEOMETRY; + return true; +} + bool web_engine_blend_main_insert_grease_pencil_frame(WebBlendMainState *state, const char *data_id, const char *layer_id, @@ -4039,7 +4544,10 @@ bool web_engine_blend_main_set_vertex_weights(WebBlendMainState *state, const std::vector &indices, const std::vector &values, const bool normalize, + const uint32_t limit, const bool mirror, + const int mirror_axis, + const float mirror_tolerance, std::string &error) { Object *object = find_object(state != nullptr ? state->main : nullptr, object_id); @@ -4050,10 +4558,6 @@ bool web_engine_blend_main_set_vertex_weights(WebBlendMainState *state, if (error.empty()) error = "PAINT_SCHEMA_INVALID: Mesh object was not found"; return false; } - if (mirror) { - error = "CAPABILITY_MISSING: topology mirror requires a verified mesh symmetry map"; - return false; - } if (strlen(vertex_group) == 0 || strlen(vertex_group) > 63 || indices.empty() || indices.size() > 1000000 || indices.size() != values.size() || std::any_of(indices.begin(), indices.end(), [&](const uint32_t index) { return index >= uint32_t(mesh->verts_num); }) || @@ -4061,6 +4565,88 @@ bool web_engine_blend_main_set_vertex_weights(WebBlendMainState *state, error = "PAINT_SCHEMA_INVALID: vertex weight patch is outside the bounded domain"; return false; } + if (limit > 32 || (mirror && (mirror_axis < 0 || mirror_axis > 2 || !std::isfinite(mirror_tolerance) || + mirror_tolerance <= 0.0f || mirror_tolerance > 1.0f))) { + error = "PAINT_SCHEMA_INVALID: vertex weight limit or mirror options are outside the bounded domain"; + return false; + } + std::unordered_set seen_indices; + for (const uint32_t index : indices) { + if (!seen_indices.insert(index).second) { + error = "PAINT_SCHEMA_INVALID: vertex weight patch contains duplicate vertex indices"; + return false; + } + } + std::unordered_map updates; + updates.reserve(indices.size() * (mirror ? 2 : 1)); + for (size_t index = 0; index < indices.size(); index++) updates.emplace(indices[index], values[index]); + if (mirror) { + /* Build a reciprocal local-coordinate symmetry map before mutating Main. */ + const Span positions = mesh->vert_positions(); + if (positions.size() > 100000) { + error = "PAINT_BUDGET_EXCEEDED: verified weight mirror is limited to 100000 vertices"; + return false; + } + auto cell_key = [](const float3 &position, const float cell_size, const int dx = 0, + const int dy = 0, const int dz = 0) { + const int64_t x = int64_t(std::llround(position.x / cell_size)) + dx; + const int64_t y = int64_t(std::llround(position.y / cell_size)) + dy; + const int64_t z = int64_t(std::llround(position.z / cell_size)) + dz; + return std::to_string(x) + ":" + std::to_string(y) + ":" + std::to_string(z); + }; + std::unordered_map> buckets; + buckets.reserve(positions.size()); + for (int64_t index = 0; index < int64_t(positions.size()); index++) { + buckets[cell_key(positions[index], mirror_tolerance)].push_back(int32_t(index)); + } + std::vector mirror_vertices(positions.size(), -1); + for (int64_t source = 0; source < int64_t(positions.size()); source++) { + const float3 reflected = [&]() { + float3 value = positions[source]; + value[mirror_axis] = -value[mirror_axis]; + return value; + }(); + float best_distance = std::numeric_limits::max(); + int32_t best = -1; + for (int dx = -1; dx <= 1; dx++) { + for (int dy = -1; dy <= 1; dy++) { + for (int dz = -1; dz <= 1; dz++) { + const auto bucket = buckets.find(cell_key(reflected, mirror_tolerance, dx, dy, dz)); + if (bucket == buckets.end()) continue; + for (const int32_t candidate : bucket->second) { + const float3 delta = positions[candidate] - reflected; + const float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y + delta.z * delta.z); + if (distance < best_distance || (distance == best_distance && candidate < best)) { + best_distance = distance; + best = candidate; + } + } + } + } + } + if (best < 0 || best_distance > mirror_tolerance) { + error = "CAPABILITY_MISSING: WEIGHT_MIRROR_SYMMETRY_UNVERIFIED"; + return false; + } + mirror_vertices[source] = best; + } + for (size_t source = 0; source < mirror_vertices.size(); source++) { + const int32_t target = mirror_vertices[source]; + if (target < 0 || mirror_vertices[size_t(target)] != int32_t(source)) { + error = "CAPABILITY_MISSING: WEIGHT_MIRROR_SYMMETRY_UNVERIFIED"; + return false; + } + } + for (const auto &[source, value] : std::vector>(updates.begin(), updates.end())) { + const uint32_t target = uint32_t(mirror_vertices[source]); + const auto existing = updates.find(target); + if (existing != updates.end() && std::abs(existing->second - value) > 1e-6f) { + error = "PAINT_SCHEMA_INVALID: mirrored weight patch contains conflicting values"; + return false; + } + updates[target] = value; + } + } int group_index = BKE_object_defgroup_name_index(object, vertex_group); if (group_index < 0) { if (BKE_object_defgroup_add_name(object, vertex_group) == nullptr) { @@ -4070,15 +4656,35 @@ bool web_engine_blend_main_set_vertex_weights(WebBlendMainState *state, group_index = BKE_object_defgroup_name_index(object, vertex_group); } MutableSpan deform_verts = mesh->deform_verts_for_write(); - for (size_t index = 0; index < indices.size(); index++) { - MDeformVert &deform_vert = deform_verts[indices[index]]; - if (values[index] == 0.0f) { + std::vector touched_vertices; + touched_vertices.reserve(updates.size()); + for (const auto &[vertex_index, value] : updates) { + MDeformVert &deform_vert = deform_verts[vertex_index]; + if (value == 0.0f) { if (MDeformWeight *weight = BKE_defvert_find_index(&deform_vert, group_index)) { BKE_defvert_remove_group(&deform_vert, weight); } } else { - BKE_defvert_ensure_index(&deform_vert, group_index)->weight = values[index]; + BKE_defvert_ensure_index(&deform_vert, group_index)->weight = value; + } + touched_vertices.push_back(vertex_index); + } + for (const uint32_t vertex_index : touched_vertices) { + MDeformVert &deform_vert = deform_verts[vertex_index]; + if (limit > 0) { + while (deform_vert.totweight > int(limit)) { + int remove_index = 0; + for (int index = 1; index < deform_vert.totweight; index++) { + const MDeformWeight &candidate = deform_vert.dw[index]; + const MDeformWeight ¤t = deform_vert.dw[remove_index]; + if (candidate.weight < current.weight || + (candidate.weight == current.weight && candidate.def_nr > current.def_nr)) { + remove_index = index; + } + } + BKE_defvert_remove_group(&deform_vert, &deform_vert.dw[remove_index]); + } } if (normalize) BKE_defvert_normalize(deform_vert); } diff --git a/blender-5.2.0/source/blender/web_engine/web_engine_main_state.h b/blender-5.2.0/source/blender/web_engine/web_engine_main_state.h index 91135984..a428e62e 100644 --- a/blender-5.2.0/source/blender/web_engine/web_engine_main_state.h +++ b/blender-5.2.0/source/blender/web_engine/web_engine_main_state.h @@ -346,6 +346,12 @@ bool web_engine_blend_main_set_font_advanced(WebBlendMainState *state, const std::vector &text_boxes, int active_text_box, std::string &error); +bool web_engine_blend_main_import_vfont(WebBlendMainState *state, + const char *name, + const char *source_path, + const char *base64, + std::string &font_id, + std::string &error); bool web_engine_blend_main_set_font_links(WebBlendMainState *state, const char *data_id, const std::array &font_ids, @@ -361,6 +367,9 @@ bool web_engine_blend_main_set_metaball_elements(WebBlendMainState *state, bool web_engine_blend_main_grease_pencils_json(WebBlendMainState *state, std::string &grease_pencils_json, std::string &error); +bool web_engine_blend_main_geometry_node_graphs_json(WebBlendMainState *state, + std::string &geometry_node_graphs_json, + std::string &error); bool web_engine_blend_main_physics_simulation_json(WebBlendMainState *state, std::string &physics_json, std::string &error); @@ -377,6 +386,13 @@ bool web_engine_blend_main_move_grease_pencil_layer(WebBlendMainState *state, const char *layer_id, const char *direction, std::string &error); +bool web_engine_blend_main_move_grease_pencil_frame(WebBlendMainState *state, + const char *data_id, + const char *layer_id, + int frame, + int target_frame, + const char *drawing_id, + std::string &error); bool web_engine_blend_main_insert_grease_pencil_frame(WebBlendMainState *state, const char *data_id, const char *layer_id, @@ -407,7 +423,10 @@ bool web_engine_blend_main_set_vertex_weights(WebBlendMainState *state, const std::vector &indices, const std::vector &values, bool normalize, + uint32_t limit, bool mirror, + int mirror_axis, + float mirror_tolerance, std::string &error); bool web_engine_blend_main_set_render_properties(WebBlendMainState *state, const char *target_id, diff --git a/docs/CURRENT_EXECUTION_PLAN.md b/docs/CURRENT_EXECUTION_PLAN.md index 7946fb1e..4b7b6523 100644 --- a/docs/CURRENT_EXECUTION_PLAN.md +++ b/docs/CURRENT_EXECUTION_PLAN.md @@ -1,6 +1,6 @@ # Web Blender V1 当前执行计划 -更新时间:2026-08-15 +更新时间:2026-08-17 ## 1. 交付目标 @@ -124,12 +124,13 @@ Linux archive SHA-256 为 `96f6c181a30f4950607839dc84d42a354b250d8a0231b098b59b7 ### 4.4 当前阻断与决策顺序 -1. `P0`:V1 可部署 RC 已完成 M6 71/71;M7 已完成 7/18,下一领取点为 `M7-08` Worker 崩溃恢复。 +1. `P0`:V1 可部署 RC 已完成 M6 71/71;M7 核心体验硬化已完成 18/18,后续领取点以 + `后续工作.txt` 的机器队列记录为准。 2. `P1`:binary/source archive 已在独立目录复验,quick/Chromium/release 三条 CI lane 与 RC manifest 已联合校验;发布/限制/恢复和运维合同均已进入离线包。 3. `P1`:pthread/single-thread、真实 HTTP、缓存升级、归档离线、OPFS 断网和空目录 - deploy/upgrade/rollback 均已有可复验证据;M7 开始核心项目体验硬化。 -4. `P2`:完整 Blender 功能域继续受 VDB demand paging、PBVH、Geometry Nodes evaluator、 + deploy/upgrade/rollback 均已有可复验证据;M7 项目体验硬化已完成并进入持续回归。 +4. `P2`:完整 Blender 功能域继续受 VDB depth composition/完整 Volume IO、PBVH、Geometry Nodes evaluator、 通用 Shader、完整编辑器/渲染/媒体和安全脚本边界阻断。 ### 4.5 真实发布缺口 @@ -151,18 +152,19 @@ loss、恶意 blend、zip bomb、离线包和 V1 用户闭环均已在最终 M5 | M4 OOM | 四类确定性内存故障与恢复证据 | 已完成,持续回归 | `faults.oom=true` | | M5 V1 RC | 可复现离线候选包、SBOM、校验和 | 已完成,持续回归 | release gate `READY`、49/49 acceptance | | M6 可部署 RC | 双 WASM 运行时选择、真实 HTTP、离线包、CI、运维合同 | 已完成,71/71 | single/pthread 可解释,真实部署与归档 P0 通过 | -| M7 核心体验硬化 | 项目状态机、恢复、存储、输入和可访问性 | 进行中,7/18 | 30 分钟编辑/保存/重开 soak 通过 | -| M8 VDB 自动分页 | GPU 缺页反馈、range/OPFS、LRU、双视口恢复 | 未开始 | 64 MiB sparse bundle 与三视角 golden 通过 | -| M9 非 Mesh/GP/Paint | 字体、Curve、Grease Pencil、Paint 增量闭环 | 未开始 | 每个新增 writer 独立通过 Main/undo/save/golden | -| M10 GN/Shader/NLA/Simulation | 白名单求值、cache、编译与阻断 | 未开始 | 四域分别通过 desktop/WASM/fault 门 | -| M11 Render/Compositor/Media | 灯光、渲染、合成、媒体执行边界 | 未开始 | 本地白名单与 server 边界均可审计 | +| M7 核心体验硬化 | 项目状态机、恢复、存储、输入和可访问性 | 已完成,18/18 | 30 分钟编辑/保存/重开 soak 通过 | +| M8 VDB 自动分页 | GPU 缺页反馈、range/OPFS、LRU、双视口恢复 | 已完成,20/20 | 联合重开与 desktop/main/Offscreen 三轴 golden 通过 | +| M9 非 Mesh/GP/Paint | 字体、Curve、Grease Pencil、Paint 增量闭环 | 已完成,14/14 | 每个新增 writer 独立通过 Main/undo/save/golden;三域故障恢复闭环通过 | +| M10 GN/Shader/NLA/Simulation | 白名单求值、cache、编译与阻断 | 已完成,15/15 | 四域分别通过 desktop/WASM/fault 门 | +| M11 Render/Compositor/Media | 灯光、渲染、合成、媒体执行边界 | 进行中,13/14 | 本地白名单与 server 边界均可审计 | | M12 Asset/IO/Editors | 资产、格式、编辑器和上下文工作流 | 未开始 | 每个格式/editor 有独立 round-trip 或稳定阻断 | | M13 Scripting/Security | 脚本默认拒绝、服务端隔离、CSP、供应链 | 未开始 | 恶意输入矩阵和 release 安全门通过 | | M14 跨浏览器/设备 | Firefox、WebKit、触控、笔、HiDPI、IME | 未开始 | 新浏览器进入 quick/P0/full CI 后才声明支持 | | M15 全域审计 | Blender 5.2 全域差距和下一发布 | 未开始 | 逐 family 审计,不由聚合 release gate 反推完成 | -M0 至 M5 已完成并转入持续回归;当前严格领取 M6。里程碑内部允许先写纯校验任务,但不得在 -前一小里程碑失败时把后一小里程碑标为完成,M7 至 M15 不得绕过 M6 发布合同扩功能。 +M0 至 M7 已完成并转入持续回归;后续严格读取机器队列最新 `nextTask`。里程碑内部允许先写 +纯校验任务,但不得在前一小里程碑失败时把后一小里程碑标为完成,M8 至 M15 不得绕过 M6/M7 +发布与体验合同扩功能。 ## 6. M0 范围、状态和 P0 闭环 @@ -555,7 +557,8 @@ SBOM、状态和 evidence SHA-256 相互一致。 M5 最终 archive SHA-256:binary `f2903a75026a0a28615aeb5f4834a3859af83022c82bc50e28437dcbed68e549`;source -`9a9c1b505c68a618de842be7c248f628b104b11b80e8a40d111f62540fcba327`。当前下一任务:`M7-08`。 +`9a9c1b505c68a618de842be7c248f628b104b11b80e8a40d111f62540fcba327`。后续领取点只读取机器队列 +最新 `nextTask`,本段不缓存任务名称。 ## 12. V1 后原子里程碑 @@ -716,91 +719,107 @@ CI 可从 lockfile 和对应源码重现当前 binary/source hash。 - [x] `M7-05` save 中断时旧 revision 和旧 hash 保持不变。 - [x] `M7-06` dirty state 只由成功 Main transaction 改变。 - [x] `M7-07` undo 回到保存 revision 时 dirty state 清除。 -- [ ] `M7-08` Worker 崩溃时展示可恢复错误,不清空当前项目列表。 -- [ ] `M7-09` 重启 Worker 后恢复 selection、frame、workspace 和项目 revision。 -- [ ] `M7-10` recent projects 对缺失/损坏 OPFS 条目执行隔离和修复提示。 -- [ ] `M7-11` 存储预算面板报告项目、快照、LOD、媒体、VDB 分项字节。 -- [ ] `M7-12` 清理单个项目时只删除该项目 content-addressed 引用的孤儿资源。 -- [ ] `M7-13` 键盘焦点、菜单、modal、Escape 取消形成一致上下文规则。 -- [ ] `M7-14` 1440x900、1280x720、移动窄屏下无控制重叠和文本溢出。 -- [ ] `M7-15` 主线程与 Offscreen 视口的选择、相机和 gizmo 行为一致。 -- [ ] `M7-16` P0 用户流程增加无鼠标键盘路径和基础可访问性检查。 -- [ ] `M7-17` 用户可见错误只显示稳定短消息,详细诊断进入可导出报告。 -- [ ] `M7-18` 完成 30 分钟持续编辑、自动保存、重开 soak 测试。 +- [x] `M7-08` Worker 崩溃时展示可恢复错误,不清空当前项目列表。 +- [x] `M7-09` 重启 Worker 后恢复 selection、frame、workspace 和项目 revision。 +- [x] `M7-10` recent projects 对缺失/损坏 OPFS 条目执行隔离和修复提示。 +- [x] `M7-11` 存储预算面板报告项目、快照、LOD、媒体、VDB 分项字节。 +- [x] `M7-12` 清理单个项目时只删除该项目 content-addressed 引用的孤儿资源。 +- [x] `M7-13` 键盘焦点、菜单、modal、Escape 取消形成一致上下文规则。 +- [x] `M7-14` 1440x900、1280x720、移动窄屏下无控制重叠和文本溢出。 +- [x] `M7-15` 主线程与 Offscreen 视口的选择、相机和 gizmo 行为一致。 +- [x] `M7-16` P0 用户流程增加无鼠标键盘路径和基础可访问性检查。 +- [x] `M7-17` 用户可见错误只显示稳定短消息,详细诊断进入可导出报告。 +- [x] `M7-18` 完成 30 分钟持续编辑、自动保存、重开 soak 测试。 ### M8 VDB 自动分页最小闭环 -- [ ] `M8-01` 定义 GPU page-fault feedback buffer schema、容量和 overflow code。 -- [ ] `M8-02` shader 对未驻留 leaf 记录唯一 page ID,不越界写 feedback。 -- [ ] `M8-03` CPU 读取 feedback 后排序、去重并绑定 render revision。 -- [ ] `M8-04` stale frame feedback 不触发 I/O。 -- [ ] `M8-05` page request 使用现有 manifest range/hash,不建立第二套地址模型。 -- [ ] `M8-06` 同页并发请求合并,取消最后一个订阅者时中止 range。 -- [ ] `M8-07` page hash 错误不写 resident cache,并返回稳定错误。 -- [ ] `M8-08` LRU 淘汰跳过当前 frame pin 的 page。 -- [ ] `M8-09` page upload 成功后只安排一次渐进重绘。 -- [ ] `M8-10` 重绘上限防止坏数据造成无限 render loop。 -- [ ] `M8-11` 主线程 WebGPU 验证缺页、加载、重绘到确定性像素。 -- [ ] `M8-12` Offscreen Worker 验证相同页序列和最终像素。 -- [ ] `M8-13` page fetch 中 network interruption 可从精确 byte offset 续传。 -- [ ] `M8-14` Worker restart 从 OPFS 恢复 manifest,但不信任未复验 resident 状态。 -- [ ] `M8-15` device loss 重建 page table,并按可见集合有界回放。 -- [ ] `M8-16` OOM 释放 page table/resident/feedback buffer 各一次。 -- [ ] `M8-17` 64 MiB sparse bundle 性能与取消门。 -- [ ] `M8-18` Volume Main、asset binding、双视口联合保存重开。 -- [ ] `M8-19` desktop/主线程/Offscreen 三视角 golden 和误差阈值。 -- [ ] `M8-20` 只有 M8 全部通过后更新 N-015 对应 slice,N-015 全域仍不自动 COMPLETE。 +- [x] `M8-01` 定义 GPU page-fault feedback buffer schema、容量和 overflow code。 +- [x] `M8-02` shader 对未驻留 leaf 记录唯一 page ID,不越界写 feedback。 +- [x] `M8-03` CPU 读取 feedback 后排序、去重并绑定 render revision。 +- [x] `M8-04` stale frame feedback 不触发 I/O。 +- [x] `M8-05` page request 使用现有 manifest range/hash,不建立第二套地址模型。 +- [x] `M8-06` 同页并发请求合并,取消最后一个订阅者时中止 range。 +- [x] `M8-07` page hash 错误不写 resident cache,并返回稳定错误。 +- [x] `M8-08` LRU 淘汰跳过当前 frame pin 的 page。 +- [x] `M8-09` page upload 成功后只安排一次渐进重绘。 +- [x] `M8-10` 重绘上限防止坏数据造成无限 render loop。 +- [x] `M8-11` 主线程 WebGPU 验证缺页、加载、重绘到确定性像素。 +- [x] `M8-12` Offscreen Worker 验证相同页序列和最终像素。 +- [x] `M8-13` page fetch 中 network interruption 可从精确 byte offset 续传。 +- [x] `M8-14` Worker restart 从 OPFS 恢复 manifest,但不信任未复验 resident 状态。 +- [x] `M8-15` device loss 重建 page table,并按可见集合有界回放。 +- [x] `M8-16` OOM 释放 page table/resident/feedback buffer 各一次。 +- [x] `M8-17` 64 MiB sparse bundle 性能与取消门。 +- [x] `M8-18` Volume Main、asset binding、双视口联合保存重开。 +- [x] `M8-19` desktop/主线程/Offscreen 三视角 golden 和误差阈值。 +- [x] `M8-20` 只有 M8 全部通过后更新 N-015 对应 slice,N-015 全域仍不自动 COMPLETE。 ### M9 非 Mesh、Grease Pencil 与 Paint 增量 -- [ ] `M9-01` 外部字体导入先做路径、类型、大小和 hash 校验。 -- [ ] `M9-02` 字体进入 OPFS content-addressed asset 后才写 Main VFont。 -- [ ] `M9-03` 字体替换/撤销/保存/重开/缺失资产形成闭环。 -- [ ] `M9-04` Curve 完整 topology editor 先冻结 operator 白名单和预算。 -- [ ] `M9-05` 每个新增 Curve operator 单独完成 Main/undo/save/golden 后再开放 UI。 -- [ ] `M9-06` Grease Pencil marquee 只操作当前 drawing 的稳定 point/stroke ID。 -- [ ] `M9-07` Grease Pencil 2D canvas 与 3D viewport 使用同一 selection revision。 -- [ ] `M9-08` Grease Pencil layer/frame reorder 完成 undo/save/reopen。 -- [ ] `M9-09` Paint 建立真实深度可见性采样,不用 CPU proxy 冒充。 -- [ ] `M9-10` Paint stroke 分块提交且一次 pointer session 只产生一个 undo step。 -- [ ] `M9-11` Texture paint dirty tile 原子写入 packed/UDIM 资产。 -- [ ] `M9-12` Weight paint normalize/limit/mirror 与 Blender desktop golden 对比。 -- [ ] `M9-13` PBVH 若不进入 WASM,则相关笔刷保持明确 capability block。 -- [ ] `M9-14` 三个编辑域分别通过 Worker restart、OOM、GPU release 和小场景恢复。 +- [x] `M9-01` 外部字体导入先做路径、类型、大小和 hash 校验。 +- [x] `M9-02` 字体进入 OPFS content-addressed asset 后才写 Main VFont。 +- [x] `M9-03` 字体替换/撤销/保存/重开/缺失资产形成闭环。 +- [x] `M9-04` Curve 完整 topology editor 先冻结 operator 白名单和预算。 +- [x] `M9-05` 每个新增 Curve operator 单独完成 Main/undo/save/golden 后再开放 UI。 +- [x] `M9-06` Grease Pencil marquee 只操作当前 drawing 的稳定 point/stroke ID。 +- [x] `M9-07` Grease Pencil 2D canvas 与 3D viewport 使用同一 selection revision。 +- [x] `M9-08` Grease Pencil layer/frame reorder 完成 undo/save/reopen。 +- [x] `M9-09` Paint 建立真实深度可见性采样,不用 CPU proxy 冒充。 +- [x] `M9-10` Paint stroke 分块提交且一次 pointer session 只产生一个 undo step。 +- [x] `M9-11` Texture paint dirty tile 原子写入 packed/UDIM 资产。 +- [x] `M9-12` Weight paint normalize/limit/mirror 与 Blender 5.2 desktop golden 对比;镜像仅对 + 复核后的局部坐标对称映射放行,限权后按 Blender 语义归一化。 +- [x] `M9-13` PBVH 若不进入 WASM,则相关笔刷保持明确 capability block。 +- [x] `M9-14` 三个编辑域分别通过 Worker restart、OOM、GPU release 和小场景恢复。 -### M10 Geometry Nodes、Shader、NLA 与 Simulation +### M10 Geometry Nodes、Shader、NLA 与 Simulation(15/15) -- [ ] `M10-01` 从 Blender Main 读取图拓扑、socket 默认值、link 和稳定 node ID。 -- [ ] `M10-02` 为 Geometry Nodes 建立 allowlist,不支持节点保留原数据并阻断求值。 -- [ ] `M10-03` 每个 allowlist 节点增加 desktop fixture、WASM 输出和误差阈值。 -- [ ] `M10-04` field/domain 转换显式预算,禁止按无限域展开 JSON。 -- [ ] `M10-05` Simulation Zone cache 绑定 graph/source/revision hash。 -- [ ] `M10-06` cache 取消、LRU、重启和损坏隔离通过后才允许播放。 -- [ ] `M10-07` Shader 图只编译声明的 Principled/Image/Normal/Math 子集。 -- [ ] `M10-08` shader compile key 包含 graph、texture、color space 和 renderer backend。 -- [ ] `M10-09` compile failure 不替换上一份可用 material pipeline。 -- [ ] `M10-10` 任意未支持 Shader 节点返回 stable capability block,不静默降级。 -- [ ] `M10-11` NLA track/strip/action/time mapping 先完成只读精确求值。 -- [ ] `M10-12` NLA 单个编辑 operator 分别完成 Main transaction、undo 和保存重开。 -- [ ] `M10-13` Physics 本地 solver 按 family 探测;不支持项只消费 desktop/server bake。 -- [ ] `M10-14` 每类 cache 验证 source hash、frame range、字节预算和版本。 -- [ ] `M10-15` GN/Shader/NLA/Simulation 分别建立浏览器性能、OOM 和恶意图输入门。 +- [x] `M10-01` 从 Blender Main 读取图拓扑、socket 默认值、link 和稳定 node ID。 +- [x] `M10-02` 为 Geometry Nodes 建立 allowlist,不支持节点保留原数据并阻断求值。 +- [x] `M10-03` 每个 allowlist 节点增加 desktop fixture、WASM 输出和误差阈值。 +- [x] `M10-04` field/domain 转换显式预算,禁止按无限域展开 JSON。 +- [x] `M10-05` Simulation Zone cache 绑定 graph/source/revision hash。 +- [x] `M10-06` cache 取消、LRU、重启和损坏隔离通过后才允许播放。 +- [x] `M10-07` Shader 图只编译声明的 Principled/Image/Normal/Math 子集。 +- [x] `M10-08` shader compile key 包含 graph、texture、color space 和 renderer backend。 +- [x] `M10-09` compile failure 不替换上一份可用 material pipeline。 +- [x] `M10-10` 任意未支持 Shader 节点返回 stable capability block,不静默降级。 +- [x] `M10-11` NLA track/strip/action/time mapping 先完成只读精确求值。 +- [x] `M10-12` NLA 单个编辑 operator 分别完成 Main transaction、undo 和保存重开。 +- [x] `M10-13` Physics 本地 solver 按 family 探测;不支持项只消费 desktop/server bake。 +- [x] `M10-14` 每类 cache 验证 source hash、frame range、字节预算和版本。 +- [x] `M10-15` GN/Shader/NLA/Simulation 分别建立浏览器性能、OOM 和恶意图输入门。 -### M11 Lighting、Render、Compositor 与 Sequencer +### M11 Lighting、Render、Compositor 与 Sequencer(13/14) -- [ ] `M11-01` Camera/Light/World/Scene color management 建立字段级 parity 表。 -- [ ] `M11-02` 每个支持字段通过 Main edit、undo、save/reopen 和 viewport 映射。 -- [ ] `M11-03` Three/WebGPU 灯光数量、shadow map 和纹理预算显式化。 -- [ ] `M11-04` Web 实时渲染结果与 Blender reference 采用可解释图像误差指标。 -- [ ] `M11-05` Cycles/复杂 Eevee/硬件后端固定为 server job,不在浏览器伪造等价。 -- [ ] `M11-06` server render job 绑定 source hash、Blender build、设置和输出 hash。 -- [ ] `M11-07` Compositor allowlist 每新增一个 node 单独增加 CPU/WebGPU golden。 -- [ ] `M11-08` unsupported compositor graph 保留原图并阻断执行。 -- [ ] `M11-09` Sequencer IMAGE/SOUND/MOVIE codec 由运行时 probe 决定,不按扩展名猜测。 -- [ ] `M11-10` long media proxy/cache 绑定源 hash 和 decode capability。 -- [ ] `M11-11` seek/scrub/decode 迟到结果全部受 revision gate 控制。 -- [ ] `M11-12` 最终视频编码固定 server/export capability,不误报本地完成。 -- [ ] `M11-13` audio context suspend/resume、设备缺失和静音恢复有专项测试。 +- [x] `M11-01` Camera/Light/World/Scene color management 建立字段级 parity 表。 +- [x] `M11-02` 每个支持字段通过 Main edit、undo、save/reopen 和 viewport 映射。 +- [x] `M11-03` Three/WebGPU 灯光数量、shadow map 和纹理预算显式化。 +- [x] `M11-04` Web 实时渲染结果与 Blender reference 采用可解释图像误差指标;固定 SRGB8/STRAIGHT RGBA、MAE/RMS/P95/坏像素/前景 IoU/alpha 覆盖率,主线程与 Offscreen 共享同一 256×256 Blender 5.2 Eevee fixture golden。 +- [x] `M11-05` Cycles/复杂 Eevee/硬件后端固定为 server job,不在浏览器伪造等价;schema 1 将 bounded Eevee/WebGL2 与 server target 分离,endpoint 未配置时稳定 fail-closed。 +- [x] `M11-06` server render job 绑定 source `.blend` hash、Blender 5.2 build hash、规范化设置 hash 和实际输出 hash;本地 loopback server 由 Blender 5.2 headless 执行 fixture render,source/settings/build/output 篡改均 fail-closed。 +- [x] `M11-07` Compositor allowlist 每新增一个 node 单独增加 CPU/WebGPU golden;当前仅冻结 + Constant Color、Exposure、Invert、Composite 的线性 sRGB Float32 有界链,真实 Blender 5.2 + Main fixture 在 CPU 与 Chromium WebGPU compute 上逐节点 hash 等价,其余节点仍阻断。 +- [x] `M11-08` unsupported compositor graph 保留原图并阻断执行;CPU 与 cached 入口都在 + 求值/缓存命中前扫描完整图,未连接到输出的 Unsupported 节点也稳定阻断,真实 Main graph、 + Blender type metadata 和 revision 保持不变。 +- [x] `M11-09` Sequencer IMAGE/SOUND/MOVIE codec 由运行时 probe 决定,不按扩展名猜测; + receipt 绑定 strip family、MIME、字节长度和 source SHA-256,Chromium 分别以 ImageBitmap、 + 固定采样率 Web Audio 和 HTMLMedia 实际解码 PNG/WAV/H.264 MP4。 +- [x] `M11-10` long media proxy/cache 绑定源 hash 和 decode capability;schema 1 将 source + family/MIME/bytes/SHA-256、M11-09 READY receipt、RGBA8 profile 和 source frame 共同绑定为 + cache identity,payload 独立验 hash,Chromium 实际生成首帧 proxy 并通过 LRU/Storage Worker + 重开门。 +- [x] `M11-11` seek/scrub/decode 迟到结果全部受 revision gate 控制;三类请求共享单调 + request revision,结果必须回显 timeline/request identity,timeline 换代、旧请求和伪造结果 + 均在 publish/cache callback 前返回 `STALE/REVISION_CONFLICT`。 +- [x] `M11-12` 最终视频编码固定 server/export capability,不误报本地完成;schema 1 绑定 + timeline/source revision、`.blend` SHA-256、帧率/范围、分辨率和 codec settings hash, + endpoint 缺失时 fail-closed;即使检测到 `VideoEncoder` 也保持 `localEncoding=BLOCKED`。 +- [x] `M11-13` audio context suspend/resume、设备缺失和静音恢复有专项测试;schema 1 + 区分 context/output 状态,真实 Chromium `AudioContext` 通过用户手势、挂起、静音恢复、 + 二次挂起/恢复和 close,缺设备与 resume failure 保持结构化静音阻断。 - [ ] `M11-14` render/compositor/media 全部覆盖取消、重启、预算释放和恢复。 ### M12 Asset、IO、Editor 与工作流 diff --git a/docs/PROJECT_STATUS_AND_NEXT_WORK.md b/docs/PROJECT_STATUS_AND_NEXT_WORK.md index 71620773..5b82481c 100644 --- a/docs/PROJECT_STATUS_AND_NEXT_WORK.md +++ b/docs/PROJECT_STATUS_AND_NEXT_WORK.md @@ -1,6 +1,6 @@ # Web Blender 项目现状与后续连续任务 -更新时间:2026-08-15 +更新时间:2026-08-17 当前短周期任务、领取顺序和阶段退出条件统一维护在 `docs/CURRENT_EXECUTION_PLAN.md`。本文件保留实现事实、长期能力台账和完整验收命令,不再作为 @@ -29,14 +29,19 @@ SQLite WASM 和 Bitbybit/OCCT 均不在当前依赖范围内。Three.js、WASM | --- | --- | --- | --- | | V1 release family | 12/12 `READY` | 0 | V1 已声明功能闭环完成 | | Blender 5.2 全域 parity | 0/12 complete | 12/12 `BLOCKED` | 不是完整 Blender Web 移植 | -| slice 台账 | 177 completed | 60 blocked | blocked 项均留在 M8-M15 长期路线 | +| slice 台账 | 191 completed | 58 blocked | blocked 项均留在 M9-M15 长期路线 | | acceptance | 50 declarations / 49 unique passed | 0 failed | 本地 V1 RC 证据完整 | | M6 可部署 RC | 71/71 原子任务 | 0 | 可部署 RC 已冻结 | -| M7 核心体验硬化 | 7/18 原子任务 | 11 | 当前严格领取 M7 项目体验工作 | +| M7 核心体验硬化 | 18/18 原子任务 | 0 | 已完成并进入持续回归 | +| M8 VDB 自动分页 | 20/20 原子任务 | 0 | 联合重开与 desktop/main/Offscreen 三轴 golden 通过 | +| M9 非 Mesh/GP/Paint | 14/14 原子任务 | 0 | `TOGGLE_CYCLIC` 已通过 Main/undo/save/reopen 与 Blender 5.2 golden;GP current-drawing marquee、2D/3D 共享 selection revision 及 layer/frame reorder 已通过 Main/undo/save/reopen 门;Paint 主线程/Offscreen 真实 GPU depth、单 undo 分块 pointer session、normalize/limit/mirror 权重与 Blender 5.2 golden、packed/UDIM dirty tile 原子资产绑定,以及 46 项 PBVH brush 的 WASM 入口显式阻断已通过;Curve、Grease Pencil、Paint 三域的 Worker restart、OOM、GPU release、小场景恢复已通过;PBVH/桌面 brush 求值仍 BLOCKED | +| M10 GN/Shader/NLA/Simulation | 15/15 原子任务 | 0 | GN/Simulation cache、Shader、NLA 和 Physics 有界闭环全部完成;M10-15 以四个隔离 Chromium Worker 分别通过性能、超预算/OOM-prevention、恶意输入和同会话小输入恢复门 | +| M11 Lighting/Render/Compositor/Media | 13/14 原子任务 | 1 | M11-01/02 已冻结字段 parity 并完成支持字段闭环;M11-03 已让双 viewport 共用资源预算;M11-04 已完成 Blender Eevee reference 图像指标;M11-05/06 已完成最终渲染路由/provenance;M11-07/08 已完成有限 Compositor golden 与 Unsupported 全图阻断;M11-09/10/11/12 已完成 codec/proxy/revision/export gate;M11-13 已完成实时 AudioContext 恢复门 | 当前优先级不是扩 Blender 全域功能。single/pthread、真实 HTTP、缓存升级、离线闭环、 -独立归档复验、运维 runbook、RC 文档和最终三条 CI lane 均已通过;现在进入 M7 核心项目 -体验硬化。`M7-01` 至 `M7-07` 已通过,唯一下一任务为 `M7-08`。 +独立归档复验、运维 runbook、RC 文档和最终三条 CI lane 均已通过;M7 核心项目体验硬化 +18/18 已完成并进入持续回归;M9 已完成 14/14;M10 已完成 15/15 并进入持续回归;M11 当前 +13/14;后续领取点只读取机器队列最新 `nextTask`。 ## 2. 已完成并有测试覆盖的能力 @@ -53,17 +58,22 @@ SQLite WASM 和 Bitbybit/OCCT 均不在当前依赖范围内。Three.js、WASM | Packed image | PackedFile、双 tile UDIM、generated、linked library、损坏签名、路径沙箱和 OPFS SHA-256 去重/重发现 | 已完成当前资源矩阵 | | GLB | 本地导出、严格解析,以及 Blender 5.2 回导比较网格/PBR/纹理/morph/skin/animation | 已完成当前 SceneIR 子集 | | Modifier/undo | modifier enable 状态、命令 revision、undo/redo 和专项 smoke | 已完成基础命令链路 | +| Geometry Nodes | Main 图 reader、16 节点 schema 1 allowlist、11 个 Blender 5.2 desktop fixture、full-Main WASM/Chromium 保存重开求值 golden,以及 7 域元素/字节/批次 materialization 预算 | 已完成 M10-04 有界闭包;超过 65,536 scalar 的 JSON field 明确阻断并要求 binary,任意 field/domain 实际求值、图写回和 Simulation Zone 仍阻断 | | Mesh Edit | 点/边/面选择,Merge/Dissolve/Extrude/Inset/Bevel/Loop Cut,Main 保存重开 | 已完成当前操作集 | | 材质与 UV | 材质槽/面分配、UV Map、planar/cube unwrap、限定 Principled/Image/Normal 图、图片打包 | 已完成当前节点集 | | 动画编辑 | TRS 关键帧、Timeline/Dope Sheet、插值、Action 选择和约束开关/影响 | 已完成当前操作集 | | 对象层级 | Parent/Unparent、Collection、rename、Join/Separate、Apply Transform、Origin | 已完成当前操作集 | | SceneIR 扩展 | tangent/split normal/seam/sharp/vertex group/attribute/UV,native WBG1、资源 Delta 和 mesh byte-range patch | 已完成当前数据契约 | | Sculpt 属性 | Mask/Face Set 的 SceneIR、WBG1 header v2、Delta、Main、undo 和保存重开 | 已完成属性子集;有界四笔刷 Main stroke 已开放,PBVH/完整求值未开放 | -| Simulation cache | Blender 5.2/source blend/hash/frame manifest、OPFS 内容寻址、schema 6、重启逐帧复验 | 已完成缓存安全层;Simulation evaluator 未开放 | +| Simulation cache | Blender 5.2/source blend/hash/frame manifest、manifest schema 2 的 graph/source/revisionHash binding、OPFS 内容寻址、storage schema 7、Worker 生命周期播放准入、取消、确定性 LRU、重启全量复验和损坏 quarantine | 已完成缓存 identity/播放生命周期安全层;Simulation Zone evaluator、GN modifier seek 接入仍未开放 | | 发布性能/故障门 | Chromium E2E、1M/10M geometry、1M 帧长媒体、600 帧 OPFS cache、4K/8K texture、运行中断网、主线程 WebGL context loss、WASM/OPFS/GPU/NanoVDB 四类确定性 OOM 恢复 | V1 evidence gate 已齐;Offscreen device loss 仍属后续完整故障矩阵 | -| Shader Main | RGB/Value/Principled/Image Texture/Normal Map/Output 整图事务、资源门和保存重开 | 已完成六节点写回子集;任意图/编译仍阻断 | -| NLA Main | 单对象 Action Clip 整栈写回、SceneIR 重开和 native frame 求值 | 已完成有限 Action Clip 子集 | -| 非 Mesh 数据块 | Curve/Surface/Font/Metaball Main 权威写回与保存重开、真实 evaluated mesh、PointCloud/Curves/Hair 属性、WNM chunks、双 viewport、handle-local 连续 preview/单次 Main commit、desktop golden 与 7 对象 GLB/USD round-trip | N-015 整体 BLOCKED;VDB 的 desktop/server converter、HTTP/OPFS、Float32 WebGPU 双生产视口、有限 grid 材质、显式 GPU resident LRU、确定性 resident OOM 和 Main 属性重开已完成,自动 demand paging、联合重开、大 bundle 和发布 golden 未完成 | +| 实时渲染 reference | Blender 5.2 Eevee 固定相机/黑体/World fixture;SRGB8 MAE、RMS、P95、坏像素比例、前景 IoU、alpha 覆盖率;主线程/Offscreen 同帧 | M11-04 有界 golden 已完成;完整 AgX/高级灯光、Cycles/复杂 Eevee、Volume 深度合成仍阻断 | +| 最终渲染路由 | bounded Eevee WebGL2 可本地;WebGPU 需 browser+bundle 双门;Cycles、复杂 Eevee、Workbench 和桌面硬件后端只返回 `SERVER_JOB` | M11-05 路由合同已完成;M11-06 server job 已绑定 source/settings/build/output hash,真实远程队列、进度、取消和 denoise 仍属后续任务 | +| Compositor CPU/WebGPU golden | Constant Color、Exposure、Invert、Composite 的单链由真实 Blender 5.2 Main fixture 驱动;CPU 与 Chromium WebGPU compute 输出 Float32 hash 等价 | M11-07 有界 allowlist 已完成;M11-08 已让 CPU/cached 在完整图预检时阻断并保留 Unsupported Main graph;资源输入、分支、其他节点、完整色彩/HDR 与生产调度仍阻断 | +| Sequencer codec/proxy/revision/export/audio gate | IMAGE/SOUND/MOVIE receipt 绑定 family、MIME、bytes 和 source hash;movie RGBA8 proxy identity 再绑定 READY receipt、profile 和 source frame;SEEK/SCRUB/DECODE 共享 revision gate;最终导出只路由 server;实时音频显式报告 context/output/mute 状态 | M11-09..13 已完成有界解码、首帧 proxy、迟到结果、最终编码路由和真实 Chromium AudioContext suspend/resume/mute/device 门;帧精确 seek、多帧生成、waveform、A/V sync、实际混音和真正 server encode job 仍阻断 | +| Shader Main/Web compiler | RGB/Value/Math/Principled/Image Texture/Normal Map/Output 整图事务、资源门、保存重开、有界 WebGL2 Three physical compile report、身份 compile key 和失败回滚 | 已完成 M10-09 当前切片;完整色彩管理/sampler、WebGPU 和任意图仍阻断 | +| NLA Main | 单对象 Action Clip 整栈写回、SceneIR 重开和 native frame 求值;M10-11 对 scale/reverse/repeat 做 12 帧 golden;M10-12 `moveNLAStrip` 通过 Main/history/save/reopen | 已完成只读精确求值与单个移动 operator 子集;create/remove/resize、多轨 blend、Transition/Meta/Sound 仍阻断 | +| 非 Mesh 数据块 | Curve/Surface/Font/Metaball Main 权威写回与保存重开、真实 evaluated mesh、PointCloud/Curves/Hair 属性、WNM chunks、双 viewport、handle-local 连续 preview/单次 Main commit、desktop golden 与 7 对象 GLB/USD round-trip | N-015 整体 BLOCKED;VDB 自动 demand paging、联合重开、64 MiB sparse 门和 desktop/main/Offscreen 三轴 density golden 已完成,深度合成与完整 Volume GLB/USD/材质仍阻断 | ## 3. 部分完成或仍有边界的能力 @@ -75,8 +85,8 @@ SQLite WASM 和 Bitbybit/OCCT 均不在当前依赖范围内。Three.js、WASM | GLB round-trip | Web 严格解析与 Blender 5.2 importer 双门已覆盖网格、变换、PBR、PNG/sRGB、sampler、morph、双骨骼 skin 和动画帧 | 新增相机/灯光、插值和材质扩展时继续扩大独立摘要 | | `.blend` 保存 | 场景状态、modifier、Mesh Edit、材质/UV、动画/约束和对象层级命令均写 Main;历史、原子保存、快照和日志重放可恢复 | skin/shape-key decimate 保持结构化阻断 | | Blender UI 对标 | 默认区域、当前 Mesh Edit、raycast、多选、拖拽 gizmo、层级/材质/动画面板和核心快捷键已闭环 | Sculpt、Geometry Nodes 编辑器等未声明能力继续结构化阻断 | -| 未声明能力扩展 | 四项协议/安全门均已细分;Sculpt 属性/有界 Main stroke、Simulation cache、六节点 Shader Main 和 Action Clip NLA Main 已有正例 | PBVH、GN lazy-function/Simulation 求值、其余 Shader Node、Web compiler、完整 NLA/UI/GLB 图映射仍 planned/结构化阻断 | -| 非 Mesh 数据块 | 1M 点 reader budget、ID_MB/PT/VO/VF 注册、Main control-point/topology/body/element/Font style link commands、一维多 spline 与批量 handle/cyclic、二维 Surface topology transaction、undo/save/reopen、真实 evaluated mesh、WNM SHA chunks、handle raycast/handle-local 连续 preview/单次 commit、7 对象 GLB/USD、Chromium quota、VDB desktop/server converter、HTTP/OPFS、有界 WebGPU 双视口、有限 grid 材质、显式 resident LRU 和 resident OOM 恢复 | 新外部字体导入,以及 VDB 自动 demand paging、联合保存重开、大 bundle 和发布 golden 仍 planned/阻断 | +| 未声明能力扩展 | 四项协议/安全门均已细分;Sculpt 属性/有界 Main stroke、Simulation cache、七节点 Shader Main/有界 Web compiler、compile key/rollback、未知节点稳定 capability block、Action Clip NLA Main/只读精确求值和 `moveNLAStrip` Main/history/save/reopen 已有正例 | PBVH、GN lazy-function/Simulation 求值、其余 Shader Node、完整色彩/sampler/WebGPU、完整 NLA/UI/GLB 图映射仍 planned/结构化阻断 | +| 非 Mesh 数据块 | 1M 点 reader budget、ID_MB/PT/VO/VF 注册、Main control-point/topology/body/element/Font style link commands、一维多 spline 与批量 handle/cyclic、二维 Surface topology transaction、undo/save/reopen、真实 evaluated mesh、WNM SHA chunks、handle raycast/handle-local 连续 preview/单次 commit、7 对象 GLB/USD、Chromium quota、VDB desktop/server converter、自动 demand paging、联合重开和三轴 golden | 新外部字体导入,以及 VDB 深度合成、完整 Principled Volume/GLB/USD 映射和更大真实 bundle 联合故障矩阵仍 planned/阻断 | | 大场景 | Worker 二进制解码、per-mesh/range transferable、linked Mesh 实例化、LOD/OPFS cache、frustum culling、能力门 OffscreenCanvas Worker、100k/1M 内存门 | 超出当前 WBG1 的网络式渐进流送仍属于后续性能扩展 | | Decimate 全对标 | Collapse 的当前网格集合已覆盖,错误路径结构化 | Un-Subdivide、Dissolve、所有 delimiter/权重/对称组合及大模型性能矩阵未完成 | @@ -195,8 +205,8 @@ Sculpt、Geometry Nodes/Simulation、Shader Node 图和 NLA 的详细任务分 `docs/UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md`。当前四项均进入 `in_progress`:四项都已超过纯协议阶段,但只开放经过 native/存储闭环验证的有限切片: Sculpt Mask/Face Set 与有界 Main stroke、GN 外部资源门与 deterministic cache 存储、七节点 -Shader Main 写回(含有限 Math)和常量 PBR 映射、单对象 Action Clip NLA Main/Depsgraph reverse/repeat 求值。详细完成项和 planned 项见专项计划; -这些正例不扩大到 PBVH Sculpt、任意 GN/Simulation、任意 Shader Node/Web compiler 或完整 NLA。 +Shader Main 写回(含有限 Math)、常量 PBR 映射、M10-07 有界 Web compiler、M10-08 compile key、M10-09 pipeline rollback、单对象 Action Clip NLA Main/Depsgraph reverse/repeat 求值。详细完成项和 planned 项见专项计划; +这些正例不扩大到 PBVH Sculpt、任意 GN/Simulation、任意 Shader Node、完整色彩/sampler/WebGPU 或完整 NLA。 Blender 5.2 全域功能矩阵、浏览器/服务端边界和 N-015 至 N-026 后续顺序见 `docs/BLENDER_5_2_WEB_FEATURE_PARITY.md`。 @@ -208,10 +218,15 @@ save/reopen、真实 legacy evaluated mesh、PointCloud/Curves 属性和 WNM 二 `UsdGeomMesh`、`UsdGeomPoints` 和 `UsdGeomBasisCurves` 的机器可读映射/损失。 VDB 已改为 desktop/server OpenVDB -> NanoVDB、浏览器分块读取 + WebGPU。真实资源 catalog、 desktop/server converter、HTTP 断点续传、OPFS 原子绑定、Float32 WebGPU 双生产视口和 Main -Volume 属性重开与 resident/page-table OOM 恢复已有真实证据;GPU demand paging、联合保存重开、完整材质、大 bundle 和 -desktop/Chromium 发布 golden 仍阻断。handle-local 专用 gizmo 已接通,新字体导入等剩余项按 `docs/status/N-015.md` 和 +Volume 属性、asset binding、双视口联合重开、自动 GPU page feedback、64 MiB sparse 门与 +desktop/Chromium 发布 golden(main/Offscreen 三轴 density)已有真实证据;深度合成、完整材质与 Volume GLB/USD 仍阻断。handle-local 专用 gizmo 已接通,新字体导入等剩余项按 `docs/status/N-015.md` 和 `docs/VDB_NANOVDB_WEBGPU_IMPLEMENTATION_PLAN.md` 推进。 +M9-14 已新增 `docs/status/M9-14.md`、`tests/golden/M9-14/manifest.json` 和统一 +`editing-domain-recovery` runner。三个真实 fixture 均完成 Main writer 后保存/Worker 重开、 +token-isolated OOM 清理、WebGL2 资源释放/重建与小场景像素恢复;该闭环不扩大 N-015/N-016/N-017 +对完整 Blender topology、2D Grease Pencil 或 PBVH brush 的 parity 声明。 + ### PBR-001 至 PBR-012 模型物理渲染(核心切片 done_current_scope,资产/高级渲染安全门 in_progress) `physical-v1` 已完成扩展 Principled 参数的 Main 写回/保存重开、双后端 @@ -228,8 +243,8 @@ HDR/EXR、多 tile 采样、Offscreen context-loss、真实 WebGPU renderer、 `docs/status/release-evidence.json` 已生成 17 条成功 command/output/artifact-hash 记录;其中 VDB 专项记录覆盖真实 converter、server job、HTTP/OPFS 和有界 WebGPU 当前切片,独立 OOM -记录覆盖 WASM/Main、OPFS staging、GPU 资源组和 NanoVDB resident/page-table;这些记录不能替代 -demand paging、大 bundle 和发布 golden。 +记录覆盖 WASM/Main、OPFS staging、GPU 资源组和 NanoVDB resident/page-table;M8 后续专项已补齐 +自动分页、64 MiB sparse、联合重开和 density 三轴 golden,但不改写已冻结 V1 release record。 发布账本现已拆分 `parityStatus` 与 `releaseStatus`:N-015 至 N-026 的全域对标仍全部 `BLOCKED`,但 N-015 至 N-026 的 V1 有界切片均为 `releaseStatus=READY`,不会再因发布后能力 阻塞 V1。P0 用户闭环、10M geometry、long media 和 OOM 均有独立 command/output/artifact-hash @@ -262,8 +277,8 @@ MIME、4 个 HEAD、WASM/BLEND/NVDB range、6 类 `416`、强 ETag、If-Range `ENGINE_VARIANT_INTEGRITY_FAILED`,不请求 single、不 fallback、不 open。`M6-08A/B` 已用实际 production build 验证 HTML/manifest/stable engine `no-cache`、5 个 hashed JS/CSS/Worker `immutable`,以及未换代 `304`、换代后旧 ETag 得到完整 `200` 的重验证行为。M6 最终 -quick 7/7、Chromium 9/9、release 25/25 与 RC manifest 联合通过;M7 已完成 7/18, -下一领取点是 `M7-08`。 +quick 7/7、Chromium 9/9、release 25/25 与 RC manifest 联合通过;M7 已完成 18/18, +后续领取点只读取机器队列最新 `nextTask`,本文不缓存任务名称。 ## 6. 当前验收命令 @@ -294,6 +309,8 @@ npm --prefix web run test:capability-gates npm --prefix web run test:simulation-cache npm --prefix web run test:release-performance npm --prefix web run test:simulation-cache-performance +npm --prefix web run test:render-reference +npm --prefix web run test:render-routing npm --prefix web run test:network-interruption npm --prefix web run test:device-loss npm --prefix web run test:texture-4k-performance diff --git a/docs/UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md b/docs/UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md index a0f55753..03912c26 100644 --- a/docs/UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md +++ b/docs/UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md @@ -236,21 +236,22 @@ N-011 -> N-012 -> N-013 -> N-014 执行。任一任务的正例、阻断例、 | N-012-A1 | GraphIR schema、节点白名单、typed socket/link/domain 校验 | 已完成 | `geometry-nodes.ts`;Worker cycle gate 已有 E2E | | N-012-A2 | 跨 group 的递归闭包、外部 object/collection/image ID 沙箱 | 已完成 | stable ID 类型、owner cycle、linked/missing/corrupt 资源门和 E2E 已覆盖 | | N-012-A3a | Simulation manifest/schema、Blender 版本、frame byte range | 已完成 | `simulation-cache.ts`;完整帧、连续范围、总/逐帧 SHA-256 | -| N-012-A3b | committed blend 绑定、OPFS 内容寻址、schema 6 索引和 Worker 重启 | 已完成 | `simulation_manifest` + asset SHA-256;`test:simulation-cache` | +| N-012-A3b | committed blend 绑定、OPFS 内容寻址、schema 7 索引和 Worker 重启 | 已完成 | `simulation_manifest` + manifest schema 2 graph/source/revisionHash + asset SHA-256;`test:simulation-cache-identity`、`test:simulation-cache` | +| N-012-A3b2 | cache 取消、播放准入、active playback 保护的确定性 LRU 和损坏隔离 | 已完成 | `simulation_quarantine`、Worker 生命周期 full verification、AbortSignal/cancelRequest、`test:simulation-cache-lifecycle`;不扩大 Simulation Zone evaluator | | N-012-A3c | Blender desktop bake 生成、版本迁移、GN frame seek 消费 | planned | Simulation Zone 仍返回 `GEOMETRY_NODES_SIMULATION_UNAVAILABLE` | -| N-012-B1 | 无时间 GN lazy-function/field Main evaluator | planned | 当前合法 GraphIR 仍返回 `CAPABILITY_MISSING` | +| N-012-B1 | 无时间 GN lazy-function/field Main evaluator | 已完成有限切片 | M10-03 的自有有界 evaluator 覆盖 schema-1 allowlist;完整 Blender lazy-function runtime 和任意 field/domain 仍阻断 | | N-013-A1 | ShaderIR schema、单输出、typed socket/隐式转换、无环 | 已完成 | `shader-graph.ts`;未进入 writer 白名单的节点返回 `SHADER_NODE_UNSUPPORTED` | | N-013-A2 | material/image stable ID 与 linked/missing/corrupt 资源门 | 已完成当前子集 | packed image 可引用;linked/missing/corrupt image 在 Worker 阻断 | | N-013-B1 | RGB/Value/Principled/Image Texture/Normal Map/Output 整图 Main transaction | 已完成当前子集 | native create/remove/link/default、history、save/reopen 回归 | | N-013-B2a | Math 六运算 Main `custom1`、原生 socket identifier 和 operation reader | 已完成有限切片 | ADD/SUBTRACT/MULTIPLY/DIVIDE/MINIMUM/MAXIMUM;其他运算阻断 | | N-013-B2 | Mix/Mapping/TexCoord/Bump storage/property 写回 | planned | 未声明节点和 properties 仍 `SHADER_NODE_UNSUPPORTED` | | N-013-C1a | RGB/Value 常量到 glTF PBR factor 的严格映射 | 已完成有限切片 | 缺值、越界、重复输入和未知链接返回 `SHADER_GRAPH_UNMAPPABLE`;mapping E2E 覆盖 | -| N-013-C1 | 受限 Web material compiler、graph hash、色彩空间/sampler | planned | C1a 不代表 Web shader compiler;任意图仍未启用 | +| N-013-C1 | 受限 Web material compiler、graph hash、色彩空间/sampler | 已完成有限编译切片 | M10-07 发布 schema 1 compile report、M10-08 绑定 graph/texture/color-space/backend 的 compile key、M10-09 失败回滚、WebGL2 Three physical 共同消费和 fail-closed 预算;完整 sampler/WebGPU 仍 planned | | N-014-A1 | Track/Strip schema、Action/range/blend/time-warp 校验 | 已完成 | `nla.ts`;缺失 Action 返回 `NLA_ACTION_MISSING` | | N-014-A2 | strip 排序/重叠、owner/path compatibility、循环引用校验 | 已完成当前子集 | 同轨重叠、duration mapping、blend range 和 unsupported strip type 均阻断 | | N-014-B1 | Action Clip 整栈替换 Main transaction、reader、保存重开 | 已完成当前子集 | `setNLAStack`;Track/Strip/Action 摘要重开一致 | -| N-014-B2 | 细粒度 create/remove/move/resize/active 命令 | planned | UI 前先扩命令和 revision 回归 | -| N-014-C1 | 正 scale Action Clip native frame 求值 | 已完成当前子集 | authoritative Main bytes 进入 Blender depsgraph;frame 5 矩阵回归通过 | +| N-014-B2 | 细粒度 create/remove/move/resize/active 命令 | 已完成有限切片 | M10-12 `moveNLAStrip` 通过 revision gate、单次 Main `setNLAStack` transaction、undo/redo 和 save/reopen;create/remove/resize/active 仍 planned | +| N-014-C1 | Action Clip track/action/time mapping 只读精确求值 | 已完成当前子集 | M10-11 desktop fixture 的 scale/reverse/repeat 2 个 Clip、12 帧 Main/Chromium 矩阵最大误差 0;求值不改 NLA/Action identity | | N-014-C2a | Action Clip reverse Main 标志、reader、Depsgraph、保存重开 | 已完成有限切片 | `NLASTRIP_FLAG_REVERSE`;frame 5 反向矩阵回归 | | N-014-C2b1 | Action Clip repeat 与跨周期 frame seek | 已完成有限切片 | `repeat=2`;frame 5/14 native 矩阵一致 | | N-014-C2 | blend overlap、骨骼、约束顺序、transition/meta | planned | 未声明族继续 `NLA_*` 结构化阻断 | @@ -274,10 +275,10 @@ N-011 -> N-012 -> N-013 -> N-014 执行。任一任务的正例、阻断例、 | GN/Simulation 切片 | 交付与验收 | 状态 | | --- | --- | --- | | N-012-B0 | wasm32 lazy-function/field runtime feature probe、超时和内存上限 | planned/阻断 | -| N-012-B1a | Group Input/Output + Transform Geometry 最小闭包,输出 topology/attributes/bounds | planned,依赖 B0 | -| N-012-B1b | Set Position、Math/Compare/Selection field 及 point/edge/face/corner 域转换 | planned,依赖 B1a | -| N-012-B1c | Join/Separate/Realize Instances 和 instance stable ID/transform | planned,依赖 B1b | -| N-012-B1d | Store/Named Attribute 生命周期、anonymous attribute 泄漏和 deterministic graph hash | planned,依赖 B1c | +| N-012-B1a | Group Input/Output + Transform Geometry 最小闭包,输出 topology/attributes/bounds | 已完成有限切片 | M10-03 desktop/full-Main WASM golden;不是完整 lazy-function runtime | +| N-012-B1b | Set Position、Math/Compare/Selection field 及 point/edge/face/corner 域转换 | 部分完成 | Set Position/Math/Compare 常量闭包和 M10-04 七域预算已完成;Selection field 与真实跨域转换仍 planned | +| N-012-B1c | Join/Separate/Realize Instances 和 instance stable ID/transform | 已完成有限切片 | Join/Separate/Collection Realize golden 已通过;任意 instance 属性/ID 传播仍 planned | +| N-012-B1d | Store/Named Attribute 生命周期、anonymous attribute 泄漏和 deterministic graph hash | 部分完成 | constant point Float Store Named Attribute 和有界回执已通过;Named/anonymous attribute 完整生命周期仍 planned | | N-012-D1 | desktop bake 生成器、Blender 版本迁移和逐帧 cache 消费 | planned,依赖现有 A3b | | N-012-D2 | Simulation Zone wasm frame step、状态所有权和 frame seek feature probe | planned/阻断,依赖 B1d/D1 | | N-012-D3 | 浏览器 bake start/cancel/commit 原子事务;中断时不发布半成品 manifest | planned,依赖 D2 | @@ -285,11 +286,11 @@ N-011 -> N-012 -> N-013 -> N-014 执行。任一任务的正例、阻断例、 | Shader 切片 | 交付与验收 | 状态 | | --- | --- | --- | -| N-013-B2a | Math 白名单逐运算 Main 写回和 operation reader;GLB/Web 编译仍阻断 | 已完成有限切片 | +| N-013-B2a | Math 白名单逐运算 Main 写回和 operation reader;完整 GLB/Web 编译仍阻断 | 已完成有限切片;M10-07 已消费六项常量 Math,任意 field/动态输入仍阻断 | | N-013-B2b | Mix 的 data type、factor clamp 和颜色/向量 socket 变体 Main 写回 | planned,依赖 B2a | | N-013-B2c | Texture Coordinate -> Mapping -> Image Texture,UV 名称/sampler/色彩空间闭环 | planned,依赖 B2b | | N-013-B2d | Normal Map/Bump 串联、强度/距离、切线前置条件和 desktop golden | planned,依赖 B2c | -| N-013-C1b | 受限 Web compiler IR、graph hash cache、未知节点 fail-closed | planned,依赖 B2d | +| N-013-C1b | 受限 Web compiler IR、graph hash binding、未知节点 fail-closed | 已完成有限切片 | M10-07 只覆盖 RGB/Value/Math/Image/Normal/Principled/Output 与 WebGL2 Three physical;M10-08/M10-09 完成 graph/asset compile key 与失败回滚,M10-10 完成任意未知节点稳定 capability block | | N-013-C1c | sRGB/Non-Color、alpha、sampler、normal/tangent 和 WebGL/WebGPU 编译报告 | planned,依赖 C1b | | N-013-D1 | 可证明 PBR 闭包的纹理/常量完整映射;其余保持 `SHADER_GRAPH_UNMAPPABLE` | planned,C1a 已覆盖常量子集 | | N-013-D2 | 桌面 Blender GLB 再导入,比较 factor、节点摘要、图片、sampler 与渲染摘要 | planned,依赖 D1 | @@ -315,4 +316,5 @@ npm --prefix web run typecheck WEB_TEST_PORT=5200 npm --prefix web run test:capability-gates npm --prefix web run test:simulation-cache npm --prefix web run test:authoring-roundtrip +npm --prefix web run test:nla-evaluation-golden ``` diff --git a/docs/VDB_NANOVDB_WEBGPU_IMPLEMENTATION_PLAN.md b/docs/VDB_NANOVDB_WEBGPU_IMPLEMENTATION_PLAN.md index 319e2f52..86a0dbb5 100644 --- a/docs/VDB_NANOVDB_WEBGPU_IMPLEMENTATION_PLAN.md +++ b/docs/VDB_NANOVDB_WEBGPU_IMPLEMENTATION_PLAN.md @@ -199,9 +199,9 @@ transmittance termination 和 NaN 防护。shader 不能通过越界 buffer read - `VDB-041`:density/temperature/color/emission/anisotropy 的 Main writer、undo/redo 和原子事务。 状态:`in_progress`;Volume source/display/interpolation/step/velocity Main 写回、undo/redo、保存重开 已完成,Principled Volume 完整节点事务仍阻断。 -- `VDB-042`:保存 `.blend` 与 Web asset binding,清空 Worker 后重开并重新流送。`BLOCKED`。 - 状态:`in_progress`;Main Volume 保存重开与 OPFS asset binding/Worker 重开分别通过,尚待生产视口 - 联合重开闭环。 +- `VDB-042`:保存 `.blend` 与 Web asset binding,清空 Worker 后重开并重新流送。 + 状态:`done_current_scope`;Main Volume、OPFS asset binding、Worker 重建与主线程/Offscreen + 两个生产视口已在同一 revision/hash 闭环联合重开。 - `VDB-043`:GLB 明确报告 Volume 无核心映射;USD 仅在 desktop USD/OpenVDB 路径真实可用时 写入 field asset,不把 bounds proxy 当体积导出。`BLOCKED`。 @@ -209,8 +209,11 @@ transmittance termination 和 NaN 防护。shader 不能通过越界 buffer read - `VDB-050`:desktop OpenVDB -> NanoVDB grid count/name/type/transform/bounds/value sample golden。 - `VDB-051`:Blender 5.2 与 Chromium WebGPU 的至少 3 个视角像素 golden,分别比较 alpha、 - transmittance、color,记录容差与色彩空间。 + transmittance、color,记录容差与色彩空间。状态:`done_current_scope`;OpenVDB 13 native + density reference 与主线程/Offscreen WebGPU 的 X/Y/Z 三轴 RGBA8 逐通道比较均为零误差。 - `VDB-052`:64 MiB/512 MiB/1 GiB sparse bundle 的首帧、渐进清晰、峰值 CPU/GPU 和取消门。 + 状态:`in_progress`;64 MiB 实际 range、4 MiB 峰值工作集与中途取消已通过,512 MiB/1 GiB + 仍按设备和 CI 预算保持阻断。 - `VDB-053`:损坏 magic/version/tree offset/hash、zip bomb 等价超预算、NaN transform、设备丢失、 网络中断和 OOM 门。状态:`in_progress`;resident/page-table 确定性 OOM、唯一释放和同设备 小 resident grid 恢复已完成,其余大 bundle 与联合故障矩阵仍阻断。 diff --git a/docs/status/M10-01.md b/docs/status/M10-01.md new file mode 100644 index 00000000..79868789 --- /dev/null +++ b/docs/status/M10-01.md @@ -0,0 +1,51 @@ +# M10-01 Status + +status: done +task: 从 Blender Main 读取 Geometry Nodes 图拓扑、socket 默认值、link 和稳定 node ID +updated: 2026-08-16 America/New_York + +## Scope + +The WebEngine authoritative Main reader publishes a bounded `GeometryNodeGraphIR` +snapshot for every `GeometryNodeTree`. It preserves unsupported nodes as metadata, +uses Blender node identifiers and socket identifiers rather than display names, and +does not evaluate Geometry Nodes. The existing `web/protocol/schema-version` remains +`1`: `geometryNodeGraphs` is an additive optional SceneIR field and no existing field +meaning or persisted storage schema changed. + +## Evidence + +- Fixture: `tests/files/web/modifier_geometry_nodes_scene.blend`. +- Fixture SHA-256: `f3820511f791769837d75be092934130c397cf46ee0acf79db28efca9a7948c9`. +- Desktop reference: `tests/golden/W-075/modifier_geometry_nodes_scene.json`. +- Desktop reference SHA-256: `93c21e5158daefce10f2d4df674e636bf3f6ed3dd43e1576c45a02a8592f5886`. +- Golden manifest: `tests/golden/M10-01/geometry-node-main-reader.json`. +- Protocol/unit check: `node --test web/tests/unit/geometry-nodes.test.mjs` (3/3). +- Native/WASM reader check: `node tools/web/check-geometry-node-main-reader.mjs`; + 3 graphs, 10 nodes, 7 links, 9 defaults, stable IDs, graph hashes, desktop + comparison, save/reopen and unsupported Simulation-node preservation all passed. +- Chromium Worker check: `WEB_TEST_PORT=5440 npm --prefix web run test:geometry-node-main-reader` + (unit 3/3, reader 1/1, Chromium 1/1). +- `source tools/web/emscripten-env.sh && cmake --build build_web_blender6 --target web_engine -- -j8`: + passed (`ninja: no work to do`). +- `npm --prefix web run typecheck`, `npm --prefix web run lint`, + `npm --prefix web run build`, and `git diff --check`: passed. + +## Contract + +- Per-graph budgets are fixed at 4,096 graphs, 4,096 nodes, 16,384 links, + 65,536 node sockets, and 4,096 interface sockets. +- Graphs, nodes, links, and socket arrays are deterministically ordered before the + graph SHA-256 is emitted. +- Socket data types and finite defaults are read from Blender's native `bNodeSocket` + storage; ID-valued defaults are serialized as stable typed IDs. +- `__extend__` sockets are omitted because they are UI extension sockets, not graph + data. Missing/duplicate identifiers, invalid links, and budget overflow fail closed. +- Unsupported node types remain in the graph and are available to the later M10-02 + allowlist gate; this task does not claim Geometry Nodes evaluation. + +## Rollback + +Remove the M10-01 reader, protocol field, golden, unit/E2E checks, package script and +this status file together. Existing SceneIR consumers continue to accept snapshots +without the optional `geometryNodeGraphs` field. diff --git a/docs/status/M10-02.md b/docs/status/M10-02.md new file mode 100644 index 00000000..b26f987f --- /dev/null +++ b/docs/status/M10-02.md @@ -0,0 +1,56 @@ +# M10-02 Status + +status: done +task: Geometry Nodes allowlist 与未支持节点无损阻断 +updated: 2026-08-16 America/New_York + +## Scope + +`GEOMETRY_NODE_ALLOWLIST_SCHEMA` and `GEOMETRY_NODE_ALLOWLIST` freeze the only node +types that may proceed past the Geometry Nodes protocol gate. The gate validates the +complete graph before any Main writer call. A node outside the list returns +`GN_NODE_UNSUPPORTED`; a structurally valid allowlisted graph still returns +`CAPABILITY_MISSING` from the production Worker until its evaluator and desktop/WASM +golden are completed by M10-03. Neither failure mutates Main or replaces the graph +read by M10-01. + +## Allowlist + +Schema 1 contains 16 node types: Group Input/Output, Transform Geometry, Set +Position, Join/Separate Geometry, Realize Instances, Store Named Attribute, integer/ +vector input, Value, Compare, Math, and Object/Collection/Image Info. External resource +nodes remain subject to stable-ID, missing-resource, linked-resource and owner-cycle +checks. + +## Evidence + +- Golden: `tests/golden/M10-02/geometry-node-allowlist.json`. +- Fixture: `tests/files/web/modifier_geometry_nodes_scene.blend`, SHA-256 + `f3820511f791769837d75be092934130c397cf46ee0acf79db28efca9a7948c9`. +- `WEB_TEST_PORT=5441 npm --prefix web run test:geometry-node-allowlist`: unit 4/4 + and Chromium 1/1 passed. +- The real Main graph `WebGeometryNodesSimulation` preserved + `GeometryNodeSimulationInput` and `GeometryNodeSimulationOutput`; an attempted + graph transaction returned `GN_NODE_UNSUPPORTED` and retained the graph JSON, + graph SHA-256 and Main revision. +- The allowlisted `WebGeometryNodes` graph returned `CAPABILITY_MISSING` while the + evaluator is still closed; it also retained graph identity and Main revision. +- M10-01 regression: `WEB_TEST_PORT=5442 npm --prefix web run + test:geometry-node-main-reader` passed (unit 4/4, reader 1/1, Chromium 1/1). +- Full unit suite: 131/131 passed. +- `npm --prefix web run typecheck`, `npm --prefix web run lint`, + `npm --prefix web run build` (73 modules), and `git diff --check`: passed. + +## Implementation Hashes + +- Protocol: `23c2289ec2acbdce0beba47c0b6539bc3f14f5a71ce29927df86cf6c705d6ec3`. +- Unit: `e60933ed0f946bdaa96f5322ba8d80b26c8425c623dd6e87da33016a1592c299`. +- Chromium: `ff2c838b5c16d86eff13699586a0cd1121ee1a86d5ea52d7b89c01d35632bb77`. +- Golden: `471fec9c5382ab7522b6c18ab580a6926eaa59eb5a8b72d23968670b9b9ab37a`. +- Production Worker: `4a65547e6b1fea16a120b4e72eb37d4d9f7c7ced4342f535796535262ff65fcc`. + +## Rollback + +Remove the exported allowlist schema/list, M10-02 unit/E2E assertions, golden, +package script and this status file together. The older private allowlist behavior is +otherwise unchanged; no persisted schema migration is required. diff --git a/docs/status/M10-03.md b/docs/status/M10-03.md new file mode 100644 index 00000000..3d817880 --- /dev/null +++ b/docs/status/M10-03.md @@ -0,0 +1,73 @@ +# M10-03 Status + +status: done +task: Geometry Nodes allowlist 逐节点 Blender 5.2 desktop/WASM golden +updated: 2026-08-16 America/New_York + +## Scope + +The headless Web modifier evaluates the saved Blender Main graph only through the +16-node schema-1 allowlist. Eleven minimal Blender 5.2 fixtures make every allowlisted +node affect a measured output. The same fixture is evaluated by the full-Main WASM in +Node and by the production Chromium Worker before and after save/reopen. + +This is a bounded constant-scalar, topology, attribute, and same-file resource closure. +It does not claim arbitrary Blender field/domain conversion, complete lazy-function +semantics, Geometry Nodes graph writing, linked resources, or Simulation Zones. + +## Coverage + +- Graph boundary: Group Input and Group Output. +- Geometry: Transform, Set Position, Join/Separate Geometry, Realize Instances, and + Store Named Attribute with a constant Float point-domain value. +- Scalar/vector: Integer, Vector, Value, Compare, and Math nodes on the bounded + operations accepted by the native evaluator. +- Same-file resources: Object Info, Collection Info with separate children, and Image + Info dimensions. Missing, recursive, linked, or otherwise unverified resources stay + behind the M10-02 resource gate. + +## Evidence + +- Fixture: `tests/files/web/geometry_node_allowlist_evaluator.blend`. +- Golden: `tests/golden/M10-03/geometry-node-evaluator.json`. +- Desktop generator: `tools/web/generate-geometry-node-evaluator-golden.py`; Blender + 5.2.0 LTS emitted 11 cases, and a clean temporary regeneration matched every semantic + golden field after excluding output-path-dependent fixture path/hash fields. +- Node full-Main WASM: `node tools/web/check-geometry-node-evaluator-golden.mjs`; + 16 nodes, 11 cases, maximum position error 0, RMS position error 0, and attributes + passed. +- Chromium: `WEB_TEST_PORT=5443 npm --prefix web run + test:geometry-node-evaluator-golden`; unit 4/4, Node golden 1/1, and Worker + open/evaluate/save/reopen 1/1 passed. +- Tolerances: maximum position `1e-5`, RMS position `1e-6`, Float point attribute + `1e-6`, and bounds `1e-5`. +- M10-01 regression on port 5444 and M10-02 regression on port 5445 passed. +- Full unit suite 131/131, typecheck, lint, production build (73 modules), local + dependency checks, and `git diff --check` passed. + +## Implementation Hashes + +- Native evaluator: `b896c5c52a611e7a309f80ca4bf8323ccded458584f65ab8f6841df24d70cc42`. +- Desktop generator: `f42ba309e4935a4534eabcb8ab1978abc08d863e2d18d80a70607f31dd3a3066`. +- WASM checker: `76a02d4aae70a597f0c26c12de3104d56e3f1cc9fded1d4824a05d7ddc59c1b5`. +- Fixture: `0cb3e33df570b436e32d783eef0a6c2daad04ca5bb4ccb4f0286b3d723ff7978`. +- Golden: `363ae47cfb50b8c95d0a55116e8663e41d27c7aa332cffe9b702bbe9b8cbfe1c`. +- Chromium spec: `83b1c96518eb64bef2c8830c778a84eb13d8da0ea62c2488471ed5fefea975c8`. +- Full-Main WASM: `432c5c9efb03b23a506c1b8f57f0d06ee683274bbdfe415f894f0d3c29db0b77`. + +## Contract + +- Evaluation is demand-driven from the active Group Output and is capped at 4,096 + node-output evaluations. Unsupported nodes, sockets, operations, cycles, or resource + states return `GEOMETRY_NODES_EVALUATOR_UNSUPPORTED` without claiming evaluation. +- The evaluator uses Blender `GeometrySet`, geometry join/realize/transform helpers, + evaluated dependency graph resources, and Blender mesh attributes. Three.js does not + execute or approximate Geometry Nodes semantics. +- The fixture and golden inventory must cover every schema-1 allowlist entry before the + check starts; missing coverage fails before numeric comparison. + +## Rollback + +Remove the bounded evaluator expansion, M10-03 generator/fixture/golden/checker, +Chromium spec, package script, and this status file together. Revert the M10 count to +2/15 and restore the previous Transform/Set Position-only modifier evaluator. diff --git a/docs/status/M10-04.md b/docs/status/M10-04.md new file mode 100644 index 00000000..e41cf9cb --- /dev/null +++ b/docs/status/M10-04.md @@ -0,0 +1,75 @@ +# M10-04 Status + +status: done +task: Geometry Nodes field/domain materialization budgets +updated: 2026-08-16 America/New_York + +## Scope + +Field materialization schema 1 binds every request to a graph ID/hash, Main revision, +source and target domain, data type, transport, and an exact seven-domain cardinality +snapshot. The parser derives element, scalar, and byte counts rather than accepting +caller-provided counts. JSON never carries field values in this contract; the current +native Depsgraph path only publishes a small verified point-Float payload. + +This task establishes budgets and transport boundaries. It does not claim actual +evaluation for arbitrary POINT/EDGE/FACE/CORNER/CURVE/INSTANCE/LAYER conversions, +complete Blender lazy-function semantics, graph writing, or Simulation Zones. + +## Budgets + +- Domain maxima: POINT 1,000,000; EDGE/FACE 2,000,000 each; CORNER 4,000,000; + CURVE/INSTANCE 100,000 each; LAYER 4,096. +- Batch maxima: 64 fields, 32 real domain conversions, 4,000,000 target elements, + and 64 MiB of materialized data. +- Per-field JSON maximum: 65,536 scalar values. Larger fields must stay binary or + return `GN_FIELD_JSON_BUDGET_EXCEEDED`. +- Supported bounded layouts are Boolean, Int, Float, Vector, and Color. Element, + component, and byte multiplication is checked before a receipt is returned. + +## Native Contract + +The full-Main Depsgraph reports exact POINT/EDGE/FACE/CORNER cardinality from the +evaluated Blender Mesh and zeroes the non-Mesh domains for the current fixture. The +observable `m10_value` point-Float attribute carries schema version, element/scalar +counts, materialized byte length, and transport. The TypeScript parser cross-checks +the receipt against mesh vertex count, domain cardinality, the Float32 byte layout, +and the actual attribute payload. Unknown receipt fields and hidden JSON arrays are +rejected. + +## Evidence + +- `WEB_TEST_PORT=5448 npm --prefix web run test:geometry-node-field-budget`: + unit 6/6 and real Chromium Worker 1/1 passed. +- The real `M10GN_StoreAttribute` evaluated mesh reported cardinality + POINT/EDGE/FACE/CORNER `8/12/6/24`, schema 1, eight Float scalars, 32 bytes, and + eight values equal to `0.375`. +- Chromium negative checks rejected POINT cardinality drift, byte-length drift, and + a hidden `values` array in the receipt. +- M10-01/02/03 regressions passed on ports 5449/5450/5451. M10-03 still covers all + 16 allowlisted nodes across 11 desktop/full-Main WASM/Chromium cases with zero + maximum and RMS position error. +- Full unit suite 133/133, typecheck, lint, production build (73 modules), local + dependency checks, `git diff --check`, and the three-fixture 100-iteration + Depsgraph regression passed. + +The first Chromium rerun on port 5447 was not accepted as evidence: the negative +check used a browser-relative dynamic import that resolved to a nonexistent +`/protocol/depsgraph.ts`. The check was moved to Playwright's Node-side static import, +then the complete task command was rerun on port 5448. + +## Implementation Hashes + +- Geometry Nodes protocol: `8d04291c29e9ce30e3f2f6d4141bc4101d49529bf7f7edf109d67d059efa58b1`. +- Depsgraph protocol: `df7e0e29422f5b605f20fd5ab939d170d35048020968d2f6aea86603d0633cd6`. +- Native Depsgraph: `5bcc17416fd4d45985bc1529288127b9eeed4a30b47836e687dc5321182c0b11`. +- Unit test: `1aa4df021731d7ba4c4191d56f88c663b223443f78c9fdc1b88c2a43423239c7`. +- Chromium spec: `95d8380d6a17a5b85df695a3cb850dffa8705f574ae0b9c97422bb802899eb8e`. +- Full-Main WASM: `1bb4a947da4332471f3d7bab8f3f628c83d1703244093a554a188668ddafafd7`. + +## Rollback + +Remove the field materialization schema/parser, native Depsgraph cardinality and +receipt fields, M10-04 unit/Chromium checks, package script, and this status file +together. Revert the M10 count to 3/15; do not leave the native attribute JSON path +without its receipt and cardinality validation. diff --git a/docs/status/M10-05.md b/docs/status/M10-05.md new file mode 100644 index 00000000..e423af09 --- /dev/null +++ b/docs/status/M10-05.md @@ -0,0 +1,68 @@ +# M10-05 Status + +status: done +task: Simulation cache graph/source/revision identity binding +updated: 2026-08-16 America/New_York + +## Scope + +Simulation cache manifest schema 2 binds every cache to the Geometry Nodes graph ID/hash, +the committed source `.blend` hash and revision, input hash, Blender 5.2 build, and the +declared frame range. A deterministic `revisionHash` is calculated from those fields and +the storage key is `sim2-` plus the complete 64-character digest. The payload hash and +per-frame hashes remain separate integrity checks. + +This task closes identity and stale-source safety. It does not implement Simulation Zone +evaluation, browser bake, cancel, LRU, corruption quarantine, or playback capability. + +## Protocol + +- Schema 1 manifests and undeclared manifest/frame fields are rejected. +- `sourceRevision` is a bounded committed revision; frame values are bounded to +/-1,000,000. +- `revisionHash` uses a JSON-array canonical encoding with an explicit v2 domain marker, + avoiding ambiguous delimiter collisions. +- Graph/source/input/revision/frame-range drift returns + `SIMULATION_CACHE_REVISION_MISMATCH`; payload or source bytes still use their dedicated + hash mismatch codes. + +## Storage Contract + +`putSimulationCache`, full read, frame read, and list run through the per-project transaction +lock. Put checks revision and source hash before hashing/storing the payload. Full/frame reads +re-verify the manifest identity and current project; listing validates all manifests and filters +valid but stale revisions out of the current project view. Old rows are not silently reused. + +## Evidence + +- `WEB_TEST_PORT=5455 npm --prefix web run test:simulation-cache-identity`: unit 3/3 and + Chromium 1/1 passed. Revision 7 cache recovered after Worker restart; after save to revision + 8, the old key was rejected and omitted from the list, while a new key was accepted. +- Negative checks covered forged graph hash, forged source hash, stale read/put, changed input, + changed source revision, changed frame range, legacy schema, undeclared fields, and frame + payload drift. +- `WEB_TEST_PORT=5456 npm --prefix web run test:simulation-cache`: identity and existing + Worker-restart smoke 2/2 passed. +- `WEB_TEST_PORT=5457 npm --prefix web run test:simulation-cache-performance`: 600/600 frames, + OPFS backend, playback completed, 600 published frames, 0 pending requests after terminate, + 5,072 ms total, below the 30,000 ms gate. +- Full unit suite 136/136; typecheck, lint, production build (73 modules), local dependency + check, and `git diff --check` passed. The first full static pass had one unused import lint + failure after the read-path refactor; removing it and rerunning produced the results above. + +## Implementation Hashes + +- Simulation protocol: `974b006f1ad4cd1d3d294122fc777c89c3a7da04fdaa7aebedea12b545bc65e5`. +- Storage Worker: `a7b31eeb601e9e71c74eaceda95704f1d120653e9ade6bfbee6d262a42edaa74`. +- Unit test: `591582a4ce77b699a4462e30d587eb89034f29ec0a22a94c81f8ab7306f2524f`. +- Identity Chromium spec: `2446060a9c3955ef466adb479966bf424fd9e9c351b65cd8525e90a1a1032e4d`. +- Existing smoke: `0c8c001290c8c6b3482eeebc105b2f217c55b2eded0c62b4fee4595ec277a11d`. +- Performance spec: `f6c465b7afa012030dd8516d676e2663f4dd8298aa156defb905b3fa0229a84b`. +- Package: `f9e28fb23af28720f56f97b249cb636599ebd92955ab2ff6d234b821e6d5ea44`. +- Error registry: `4c6045b1cab8348f0acf180a1e618a0d691ddc5092822ca288f974904e5e13a5`. + +## Rollback + +Remove schema 2 identity fields and hash helper, restore the prefix-truncated key, revert +Storage Worker project-lock checks and stale filtering, remove the identity unit/E2E/package +entries and this status file, then restore the M10 count to 4/15. Do not retain schema 2 +manifests with schema 1 readers. diff --git a/docs/status/M10-06.md b/docs/status/M10-06.md new file mode 100644 index 00000000..268edba9 --- /dev/null +++ b/docs/status/M10-06.md @@ -0,0 +1,84 @@ +# M10-06 Status + +status: done +task: Simulation cache cancellation, playback gate, LRU, restart and corruption isolation +updated: 2026-08-16 America/New_York + +## Scope + +Simulation cache playback is now a lifecycle-gated operation. A cache may be read by +frame only after the current Storage Worker has completed a full manifest, payload and +per-frame hash verification. This closes cache lifecycle safety; it does not implement +Simulation Zone evaluation, browser bake generation, or GN modifier seek semantics. + +## Protocol and Client + +- `SIMULATION_CACHE_NOT_READY`, `SIMULATION_CACHE_CANCELLED` and + `SIMULATION_CACHE_BUDGET_EXCEEDED` are stable error codes. +- `verifySimulationCacheCancellable` checks cancellation between total and per-frame + SHA-256 work. `StorageClient` accepts `AbortSignal`, removes an aborted request from + `pending`, and sends a `cancelRequest` to the Worker so late responses cannot publish. +- `planSimulationCacheLRU` sorts removable entries by `lastAccessAt`, `createdAt`, and + `cacheKey`; active playback keys are protected. If protected entries alone exceed the + requested budget, the result reports `budgetSatisfied: false` instead of evicting them. + +## Storage Contract + +- IndexedDB schema 7 adds `simulation_quarantine`; the M10-06 Chromium test creates a + temporary schema 6 database and verifies the real v6 to v7 migration and store. +- `prepareSimulationCachePlayback` performs full verification in the current Worker and + activates the key. `readSimulationCacheFrame` rejects unprepared keys, while `release` + removes readiness and LRU protection. A successful full read also establishes verified + readiness for compatibility with existing callers. +- `pruneSimulationCaches` removes deterministic LRU manifests and deletes their + unreferenced simulation assets. Active playback is automatically protected. +- Invalid manifests and missing, truncated, or hash-mismatched payloads are moved from + `simulation_manifest` to `simulation_quarantine`. Quarantine preserves the bad row for + diagnostics while valid cache listing and playback continue. + +## Evidence + +- `WEB_TEST_PORT=5464 npm --prefix web run test:simulation-cache-lifecycle`: unit 5/5 and + real Chromium 1/1. It covers v6 to v7 migration, post-restart `NOT_READY`, full + verification, StorageClient cancellation, BrowserTransform playback cancellation with + zero published frames and zero pending requests, active-cache LRU protection, release, + cancelled write cleanup, OPFS byte tamper, and quarantine listing. +- `WEB_TEST_PORT=5460 npm --prefix web run test:simulation-cache-identity`: unit 5/5 and + Chromium 1/1. Existing graph/source/revision stale filtering remains green. +- `WEB_TEST_PORT=5461 npm --prefix web run test:simulation-cache`: identity and existing + Worker-restart smoke 2/2. +- `WEB_TEST_PORT=5462 npm --prefix web run test:simulation-cache-performance`: 600/600 + frames, OPFS backend, 6,639 ms total, 0 pending requests after terminate, and restart + recovery passed the 30,000 ms gate. +- Targeted schema/snapshot smoke 2/2 and recent-project recovery 4/4 passed after the + schema version update. Full unit suite passed 138/138; typecheck, lint, production + build (73 modules), local-dependency check, and `git diff --check` passed. + +The first lifecycle run was not accepted as evidence because the test helper returned the +numeric legacy `DOMException.code` value `20` instead of the `AbortError` name. The helper +was corrected and the complete command was rerun on port 5459, then rerun again on port +5464 after adding playback cancellation and migration assertions. + +## Implementation Hashes + +- Simulation protocol: `3025db84bd4e0151b8894a457c51da630d628afb1760e2f0caeec003f0efce3f`. +- Storage protocol: `2711dbab0c52a566679b83d5e4d793ec0ffd3f991e744dce765b6bdc54d25c13`. +- Error registry: `7ebff554dd6346b539215a6545c67806bca2ead33953700b79c0910fd0cfb408`. +- Storage Worker: `e31d7dcf93c5dcefb7e6c4cfd7874c748f972849ee7d93adb6294f82aafcf236`. +- Storage client: `aaa3a03e3b91452e90a661566407c261c7a16b1671ed55ec948de7626d49bfa4`. +- Storage migration: `8de1fcbbda9d7a4e7496995306c4e9fc83224211a516cf7241496b6a145953b5`. +- OPFS helper: `2e8ef9d72194bf803a7e823fb2b7de30b9f10ae6dd4b031b81b97ff82eee9cc6`. +- Unit test: `4ece0f5051667b04fab78c81a847def7a0c77d9267bd9e4fa2bbe4b9308125bf`. +- Lifecycle Chromium spec: `861d370f72896987cef65d2e4d78ab3edcc1d4828d4a0fa0b5301b72a4a2932a`. +- Identity regression: `aa305871e8651018342b123112beab10a43dab3313e2c9765e450dc91c08b6e1`. +- Performance regression: `d7083c24789fc5877a7841bf611aee0ff471cc191621de1b30720d637635b44c`. +- Smoke/recovery version updates: `2d15e5bf54a1dcdec1686ee937a510a7521ad44b152c895289a152ea711fadf4` / + `a4d6279f05b6215f2e34cd5f1d76ebb6d5ed2cc765859c2ae0af975623f93b2c`. +- Package: `fe90c0bd741a7840f6184726737a1a93c606841b8408dd7fd6dec823a8352cf6`. + +## Rollback + +Remove the schema 7 migration and `simulation_quarantine` store, restore frame reads to +the schema 6 identity-only contract, remove cancellation/LRU/playback commands and the +M10-06 tests/status entry, then restore the M10 count to 5/15. Do not retain schema 7 +databases with a schema 6 reader. diff --git a/docs/status/M10-07.md b/docs/status/M10-07.md new file mode 100644 index 00000000..b96b6dae --- /dev/null +++ b/docs/status/M10-07.md @@ -0,0 +1,59 @@ +# M10-07 Status + +status: done +task: Bounded WebGL2/Three physical compiler for the declared Shader node subset +updated: 2026-08-16 America/New_York + +## Scope + +The browser material path now compiles only the declared RGB, Value, Math, +Image Texture, Normal Map, Principled, and Material Output closure. The compiler +produces a schema-versioned `WEBGL2_THREE_PHYSICAL` report before a Shader Main +transaction is sent to Blender. Unknown nodes, cycles, duplicate inputs, missing +or blocked images, oversized graphs, and invalid constants fail closed without +mutating the source graph. + +This task does not claim arbitrary Shader Node support, WebGPU compilation, +complete color management/sampler parity, compile-key invalidation, or pipeline +rollback; those remain later M10 tasks. + +## Protocol and Runtime + +- `web/protocol/shader-compiler.ts` defines the M10-07 allowlist, fixed node/link/ + depth/texture/identifier budgets, deterministic graph fingerprint fallback, + scalar Math evaluation, and bounded `ShaderCompileReport`. +- The Engine Worker compiles `setShaderGraph` before the native Main transaction, + rejects a blocked report, and returns the report with the committed delta. +- Main-thread and Offscreen viewport material creation share `createPBRMaterial`; + compiled constants and declared base-color/normal texture bindings are stored in + material `userData` for the texture adapter. + +## Evidence + +- `WEB_TEST_PORT=5521 npm --prefix web run test:shader-compile`: unit 5/5 and + Chromium 3/3 passed. The suite covers the real Main graph transaction, Math + result and topological order, Image/Normal bindings through the shared viewport + path, missing resources, cycles, duplicate inputs, oversized graphs, forged + fingerprints, and source graph immutability on blocked input. +- `tests/golden/M10-07/shader-compile.json` fixes the fixture digest, backend, + allowlist, six Math operations, and compiler budgets. +- The first attempt on the default port was rejected before browser startup because + an existing process owned `127.0.0.1:5173`; it is not counted as a feature failure. + +## Implementation Hashes + +- Compiler: `50b29c99d73c886a41bb8e10ccd2309068a1c09b23196655659b2f530d31f856`. +- Engine Worker: `376efe09de20150fc32803745da7e7e55bb41d178302e45b1fd90a2fe7778b2e`. +- PBR adapter: `f9c2e5ede635d11673f971c985288821af24ed81ecaadc95efefd958637672fd`. +- Texture adapter: `d5e379e25e0ab8294523690c4d4c79203b6f1610b4d6a608b33a7118f51efa57`. +- Unit test: `1e5d44b4f73f4ee4e4c9329f73628980df9337632dc48a3d50689f051a89b6e7`. +- Chromium spec: `c59b9f349bd6ce329ffc89f64eba3fec3e1e02a528978bdc1c3793d921faabc3`. +- Golden: `6bb8c07f014a0f2c1fd0e9ec16b08b99a524d8d3161bc556767acaf510c6c961`. +- Package: `ea0fef34497d51448d5478f4a761bef3c4ca816c408fd8a0807eaa0b093d0df8`. + +## Rollback + +Remove the compiler, Worker gate, shared material/texture adapter integration, +M10-07 tests, golden and this status file together. Restore the M10 count to +6/15 and leave Shader Main's existing capability gate and writeback contract in +place; do not retain a compiler report that is not enforced before Main mutation. diff --git a/docs/status/M10-08.md b/docs/status/M10-08.md new file mode 100644 index 00000000..17a8b4c5 --- /dev/null +++ b/docs/status/M10-08.md @@ -0,0 +1,59 @@ +# M10-08 Status + +status: done +task: Shader compile key binding for graph, textures, color space and backend +updated: 2026-08-16 America/New_York + +## Scope + +Every bounded Shader compile report now carries a deterministic 64-character +`compileKey`. The key is SHA-256 over the task-local report schema, graph hash, +renderer backend, and sorted texture identities: image ID, usage, asset ID, +asset SHA-256, and resolved SRGB/LINEAR/NON_COLOR color space. + +Texture identities are optional for legacy SceneIR rows; missing metadata is +represented explicitly as `null`, while usage-based color-space defaults remain +deterministic. An unknown renderer backend or malformed texture digest blocks +compilation. The global `web/protocol/schema-version` remains 1 because this is +an additive field in the task-local Shader report, whose schema is still 1. + +## Runtime + +- `ImageIR.colorSpace` is optional and validated; render asset requests use it + when present and otherwise retain the existing usage-derived color space. +- The Engine Worker passes snapshot image asset/hash/color-space identity into the + compiler. The PBR adapter accepts the same context for main-thread and Offscreen + material creation. + +## Evidence + +- `WEB_TEST_PORT=5523 npm --prefix web run test:shader-compile-key`: unit 7/7 and + Chromium 1/1 passed. The browser test proves key changes for texture SHA-256 and + color-space changes through the application PBR entry point. +- `WEB_TEST_PORT=5524 npm --prefix web run test:shader-compile`: M10-07 regression + unit 7/7 and Chromium 3/3 passed. +- `npm --prefix web run typecheck`, `npm --prefix web run lint`, + `npm --prefix web run build`, `npm --prefix web run test:status-consistency`, + and `git diff --check` passed. + +The first browser attempt on port 5522 used a repository-root dynamic import that +Vite does not expose and was rejected before exercising the compiler. The test was +moved through the application PBR entry point and rerun in full on port 5523. + +## Implementation Hashes + +- Compiler: `acd5f8deb2912f6823533df12baba8049c5e64d440c17a6dc3f90741f71774b2`. +- SceneIR: `a65ef3b4be5303b1b86b3ee6829c795870dcc2490ba58d3a66b8bed8172d6442`. +- Render assets: `840c09a6c2ec56cc75a9ca72bcbdde6957612a7c61b19ab83be47c571eca8b67`. +- Engine Worker: `668728f7e5b6edf9e1cee3e6152238d16ac19f43f030e94e77bfe24e7ff50489`. +- PBR adapter: `8a9cb52c8e7b1481096017cc659d93541c21724c1203d22755e37e1e3c365250`. +- Unit test: `9ae9bb4c778d9e31a607d0c7d678ecf3448ed4e5b57c88ab4ba767a2f0850cc7`. +- Chromium spec: `a09ebfa451e76c33f421bc8402fac4c77faedee88020431a505602f317b4eb65`. +- Golden: `187666d9dfb9d9cdd5bd6195cc2f7ee57bb78fa0d05d6020762c06a4ab015984`. + +## Rollback + +Remove `compileKey`, texture identity context, ImageIR color-space metadata, +M10-08 tests/golden and the Worker/adapter wiring together. Restore the M10 +count to 7/15 and retain graphHash-only reports with the original usage-derived +render asset color-space behavior. diff --git a/docs/status/M10-09.md b/docs/status/M10-09.md new file mode 100644 index 00000000..36f59d03 --- /dev/null +++ b/docs/status/M10-09.md @@ -0,0 +1,40 @@ +# M10-09 Status + +status: done +task: Preserve the previous usable material pipeline on compile failure +updated: 2026-08-16 America/New_York + +## Scope + +`PBRMaterialPipeline` makes material replacement transactional. A successfully +compiled candidate becomes current only after compilation; the previous material +is disposed exactly once after replacement. A blocked candidate is disposed and +the current material remains usable, with the failed report stored as +`shaderCompileFailure` for diagnostics. This does not claim arbitrary Shader Node +support or a WebGPU pipeline. + +The Engine Worker independently rejects blocked `setShaderGraph` requests before +the Blender Main transaction, so a failed graph cannot publish a new SceneIR +snapshot in addition to the viewport-side rollback. + +## Evidence + +- `WEB_TEST_PORT=5525 npm --prefix web run test:shader-pipeline`: Chromium 1/1. + The test confirms the failed candidate reports `SHADER_NODE_UNSUPPORTED`, keeps + object identity and the previous compiled report, and that a later successful + compile replaces the material. +- `npm --prefix web run typecheck` and `npm --prefix web run lint` passed after the + pipeline helper was added. +- `tests/golden/M10-09/shader-pipeline.json` fixes the rollback invariants. + +## Implementation Hashes + +- PBR adapter: `8a9cb52c8e7b1481096017cc659d93541c21724c1203d22755e37e1e3c365250`. +- Chromium spec: `e6771f672e885250db199cbf97a0a3866084ab9bb3f2ad25d17f5c53edaa99af`. +- Golden: `5dbb916e512935a76dacde37242b123707897f54ab10d7b99e246f57afbc94ac`. + +## Rollback + +Remove `PBRMaterialPipeline`, its Chromium test/golden and this status file. Keep +the M10-08 compile key and Engine Worker pre-transaction gate, and restore the M10 +count to 8/15. diff --git a/docs/status/M10-10.md b/docs/status/M10-10.md new file mode 100644 index 00000000..b94e0a0d --- /dev/null +++ b/docs/status/M10-10.md @@ -0,0 +1,39 @@ +# M10-10 Status + +status: done +task: Stable capability block for unsupported Shader nodes +updated: 2026-08-16 America/New_York + +## Scope + +The Shader capability query now accepts arbitrary node type strings and returns a +stable `PBR-012/ARBITRARY_SHADER` block for every type outside the declared bounded +compiler set. Unsupported node names are de-duplicated and sorted, so the same +graph receives the same error regardless of input order. The block uses +`SHADER_NODE_UNSUPPORTED`, remains recoverable, and does not silently report READY. + +The existing ShaderGraph validator and Engine Worker gate continue to preserve the +source graph and reject blocked `setShaderGraph` commands before any Main mutation. +This task does not expand the compiler allowlist. + +## Evidence + +- `WEB_TEST_PORT=5526 npm --prefix web run test:shader-capability`: Chromium 1/1. + `VORONOI` and `CUSTOM_OSL` both return the expected stable block; reversed input + order produces the identical task/capability/status/code/message result. +- `npm --prefix web run typecheck` and `npm --prefix web run lint` passed. +- `tests/golden/M10-10/shader-capability-block.json` fixes the status, error code, + recoverability and graph-preservation policy. + +## Implementation Hashes + +- Render capability gate: `322929e9c7b627a9e79b8a6f11d5e9b0c7c05a5754f82d41c45c1134bbaaf385`. +- Chromium spec: `6337801f10b9115ba7b53a43152ae69a324b5baa61c8e455cc61505437228cbf`. +- Golden: `b7182bb57e10d0b302b4c7dc9bc43160721a2c9414b056c86173c3b5c2501739`. +- Package: `d6d4dcaf4f5c852f884ba2064da96a8c30519c661bf4a16c59e115742aa37e8a`. + +## Rollback + +Restore the enum-only `ARBITRARY_SHADER` request, remove deterministic sorting, +the M10-10 Chromium test/golden and this status file, and restore the M10 count to +9/15. Keep the existing M10-07 compiler fail-closed behavior. diff --git a/docs/status/M10-11.md b/docs/status/M10-11.md new file mode 100644 index 00000000..925743fc --- /dev/null +++ b/docs/status/M10-11.md @@ -0,0 +1,44 @@ +# M10-11 Status + +status: done +task: NLA track/strip/action/time mapping read-only exact evaluation +updated: 2026-08-16 America/New_York + +## Scope + +A Blender 5.2 desktop-authored fixture contains one object, one NLA Track and two +Action Clip strips. The first maps Action frames 1..11 to scene frames 20..40 with +scale 2. The second maps the same Action range to frames 45..65 with reverse playback +and repeat 2. Both use `NOTHING` extrapolation so samples outside their ranges expose +boundary behavior rather than hold behavior. + +The Main reader must preserve Track, Strip, Action and time-mapping fields. Blender's +native Depsgraph evaluates 12 boundary/interior/cycle frames; Node WASM and the +production Chromium Worker compare every world matrix with the desktop golden. The +test asserts that evaluation does not change NLA or Action JSON. It does not call +`setNLAStack` and does not claim the M10-12 operator transaction. + +## Evidence + +- Fixture: `tests/files/web/nla_time_mapping_scene.blend`, SHA-256 + `b32f783debe213a1345ec4528e5ed27f0f4de00d7896b626df39756289119855`. +- Golden: `tests/golden/M10-11/nla-evaluation.json`; Blender 5.2.0 LTS, 1 Track, + 2 Clip strips and 12 frames. +- `WEB_TEST_PORT=5527 npm --prefix web run test:nla-evaluation-golden`: Node/WASM + checker and Chromium 1/1 passed; maximum matrix error 0 and read-only identity passed. +- `npm --prefix web run typecheck`, `npm --prefix web run lint`, and + `npm --prefix web run check:local-deps`: passed. + +## Implementation Hashes + +- Fixture generator: `a941ecb850ddd165ede117f6411615c6daed4d6bdaffaeac8228a308b7fbe745`. +- Golden generator: `941a63cc9edbaa0d8dd25025872e3fc44bf64df54d5a9232ad4128e6ac0cd5c5`. +- Node/WASM checker: `2ce9db0fcbceaf30e398e8feb5c002a60395ac34e61382d9414e980e51e6fdd5`. +- Golden: `2d148ac0cbc2ac9281d430da20dba8373ad1bb0de6ec5fa578adf36296455fbe`. +- Chromium spec: `c12f105d206a03dae5b73b076cc3b0a758d597cea7532b6720f4653568df0245`. + +## Rollback + +Remove the fixture/golden generators, fixture, M10-11 golden/checker/Chromium spec, +package command and this status file together. Restore the M10 count to 10/15. Keep +the existing N-014 bounded Main writer and reverse/repeat regression unchanged. diff --git a/docs/status/M10-12.md b/docs/status/M10-12.md new file mode 100644 index 00000000..ac414229 --- /dev/null +++ b/docs/status/M10-12.md @@ -0,0 +1,45 @@ +# M10-12 Status + +status: done +task: One NLA edit operator with Main transaction, undo/redo and save/reopen +updated: 2026-08-16 America/New_York + +## Scope + +The bounded `moveNLAStrip` command moves one existing Action Clip while preserving +its duration and time mapping. It requires the current `baseRevision`; stale commands +return `REVISION_CONFLICT` before Main mutation. The Worker derives a candidate stack +through the pure protocol helper, validates Action/path/range/overlap gates, and commits +it through one authoritative Main `setNLAStack` transaction. The native history entry +therefore covers the exact pre-edit `.blend` bytes. + +This task intentionally opens only the move operator. Create/remove/resize/active, +multi-track blending, Transition/Meta/Sound, NLA UI and export remain blocked or planned. + +## Evidence + +- Pure contract: `web/protocol/nla.ts` and `web/tests/unit/nla.test.mjs`, 2/2 tests. + The source stack is not mutated; overlap, unknown identity and frame budget errors + are deterministic. +- `WEB_TEST_PORT=5528 npm --prefix web run test:nla-operator`: unit 2/2 and Chromium + 1/1 passed. The browser path proves stale revision rejection, Main move 20..40 -> + 5..25, monotonic revision/delta, undo to 20..40, redo to 5..25, save/reopen at + 5..25, and native frame-10 evaluation at X=2.5. +- `npm --prefix web run typecheck`, `npm --prefix web run lint`, and `git diff --check`: + passed. + +## Rollback + +Remove `moveNLAStrip` from `web/protocol/nla.ts` and `web/protocol/web-engine.ts`, +remove the Worker conversion, unit/E2E tests and package command, and delete this +status file. Restore the M10 count to 11/15. Keep M10-11 read-only evaluation and +the pre-existing whole-stack `setNLAStack` authoring path. + +## Implementation Hashes + +- `web/protocol/nla.ts`: `03d5abce5cb2f0a3a38e42eedbced7f4de6e2052d18e53a3dcde3b3ca1e6020d` +- `web/protocol/web-engine.ts`: `48f2290ead27aeda1f17b279f01ae278776b50b4cb4de065d94b0e1d44717dbb` +- `web/app/src/workers/web-engine.worker.ts`: `4ccad87c1619db8e39d2855ebca9918883484ce3e5dfc8a325ccee9a8220397c` +- `web/tests/unit/nla.test.mjs`: `d8bac2dd3e4ec1a1342de7c7401a51beb9879ea5ba2b34df7d615ae0449081a7` +- `web/tests/e2e/nla-operator.spec.ts`: `1658d52eb0b8cf329f73be552e9b2649ed6dc96b21edb48ade9dd4a725a37ae0` +- `web/package.json`: `e9d5c6e4f7132921faac8ecbee40215098497fe374d56309f2c76fab152c33d6` diff --git a/docs/status/M10-13.md b/docs/status/M10-13.md new file mode 100644 index 00000000..56953ad0 --- /dev/null +++ b/docs/status/M10-13.md @@ -0,0 +1,48 @@ +# M10-13 Status + +status: done +task: Probe local Physics solvers per family and keep unsupported families bake-only +updated: 2026-08-16 America/New_York + +## Scope + +The Physics protocol now probes all seven declared families independently. A local +solver becomes `READY` only after its runtime export exists, initialization succeeds, +its thread requirement is met and its required memory fits the active WASM limit. +Missing exports, initialization errors, insufficient threads or memory, and malformed +probe results remain `BLOCKED` and route to `DESKTOP_SERVER_BAKE`. + +The production inventory has no installed solver runtime adapter, so Rigid Body, +Soft Body, Cloth, Fluid, Dynamic Paint, Particle and Hair all remain bake-only. +The synthetic positive probe verifies the gate and does not declare an actual solver. +Desktop family decoding, cache playback, browser bake and server job submission remain +blocked for later tasks. + +## Evidence + +- `node --test web/tests/unit/physics-solver-probe.test.mjs`: 3/3. It covers the + default seven-family fallback, family isolation across all four gates, and invalid + environment/result fail-closed behavior. +- `WEB_TEST_PORT=5530 npm --prefix web run test:physics-solver-probe`: unit 3/3 and + Chromium Worker 1/1. The browser result matches the M10-13 golden and keeps every + failed family on `DESKTOP_SERVER_BAKE`. +- `WEB_TEST_PORT=5531 npm --prefix web run test:e2e -- --grep "N-018 physics"` and + `npm --prefix web run test:physics-main-reader`: passed without changing existing + metadata/cache gates or claiming a solver. +- `npm --prefix web run typecheck`, `npm --prefix web run lint`, production build + (74 modules), and `git diff --check`: passed. + +## Implementation Hashes + +- `web/protocol/physics-simulation.ts`: `adc57a12c7f57c6b1d5f99590d3c36cefa83636aca86c780c2520f3f41148981` +- `web/tests/unit/physics-solver-probe.test.mjs`: `e499d55770d2024c75803ce1acc1c234814c027279f2b40f42195fd480977653` +- `web/app/src/workers/physics-solver-probe-test.worker.ts`: `fbad383de186a659440da42dcad988310ca2e483db27e30d74342c8f57a0f49a` +- `web/tests/e2e/physics-solver-probe.spec.ts`: `1419b98d68da9b0ab0bf41b37843442741593655e2693159d84e069f1dec3f09` +- `tests/golden/M10-13/physics-solver-probe.json`: `bcc8f9094e2b0d8f41ef01fda0daa2a29693d188e602447d6d06358aaa751df4` +- `web/package.json`: `875162709315e2a8116c5d28023576a4b95fb076b79453de8b21acdbff2df9dc` + +## Rollback + +Remove the solver probe types/functions, unit/Worker/E2E/golden/package command and +this status file, then restore the M10 count to 12/15. Keep the existing N-018 +metadata, Main reader, Simulation cache and BTF1 playback primitive unchanged. diff --git a/docs/status/M10-14.md b/docs/status/M10-14.md new file mode 100644 index 00000000..e09acc75 --- /dev/null +++ b/docs/status/M10-14.md @@ -0,0 +1,47 @@ +# M10-14 Status + +status: done +task: Validate source hash, frame range, byte budget and version for every Physics cache family +updated: 2026-08-16 America/New_York + +## Scope + +Every Physics family cache now uses cache schema 1 and binds its family, desktop or +server bake origin, Blender 5.2 version, source blend/settings/input/cache SHA-256, +frame range, total byte length and per-frame byte offset/length/SHA-256. `COMPLETE` +caches require every frame; `PARTIAL` caches may omit frames but remain strictly +ordered and byte-contiguous. Per-frame and total cache budgets match the existing +bounded Simulation cache policy. + +`verifyPhysicsCachePayload` hashes the actual current blend, full cache payload and +every frame before consumption. Family/source/version/range/budget drift fails closed. +This task validates cache identity and bytes; it does not implement a Blender family +decoder or change the existing cache playback capability block. + +## Evidence + +- `node --test web/tests/unit/physics-cache-family.test.mjs`: 3/3. All seven families + and both desktop/server sources pass; source/payload drift and family/schema/version/ + range/budget/undeclared-field failures return stable codes. +- `WEB_TEST_PORT=5533 npm --prefix web run test:physics-cache-family`: unit 3/3 and + Chromium Worker 1/1, matching the M10-14 golden. +- M10-13 probe Chromium 1/1, Physics Main reader, M10-05 Simulation cache identity + unit 5/5 + Chromium 1/1, typecheck, lint, production build (74 modules), and + `git diff --check`: passed. + +## Implementation Hashes + +- `web/protocol/error.ts`: `6972a144bdc61c2a4270c88df2d86c75640bb5f24b5ac3b7f9a02d69d3c500fd` +- `web/protocol/physics-simulation.ts`: `b02b820503d79cd26b0323a4d2033e79d0eeb3032befd26fafe5eab55634584c` +- `web/app/src/workers/physics-simulation-test.worker.ts`: `453c6e9e683b9da0ef7ce4bd0e4e430b97102d80ddb9b14cc9d075319b37d818` +- `web/tests/unit/physics-cache-family.test.mjs`: `e1c6f07ec58ce4c4c987bd8a36f270497ae3a0f521d6ed27d7f199debeeaccb2` +- `web/app/src/workers/physics-cache-family-test.worker.ts`: `8af676506cc0ff1501816431d92d82ca15316b40095d617ecb664b9608e209e0` +- `web/tests/e2e/physics-cache-family.spec.ts`: `6a2788587f6692bf5bf19d76400810fa277a6985d1a6ecc1b8d1adb85c609249` +- `tests/golden/M10-14/physics-cache-family.json`: `b220d1fd73f307213078ce3cbd6212544f97ade84a45d54847f107cef69d302d` +- `web/package.json`: `9baeb319d8c740f67f3e7615942c78f920e7a56b0d654a5a8f47d68de7243eb4` + +## Rollback + +Restore the former Physics cache binding, remove the two new cache hash error codes, +unit/Worker/E2E/golden/package command and this status file, then restore M10 to +13/15. Keep M10-13 solver probing and the existing GN Simulation cache schema 2. diff --git a/docs/status/M10-15.md b/docs/status/M10-15.md new file mode 100644 index 00000000..e50e8a3a --- /dev/null +++ b/docs/status/M10-15.md @@ -0,0 +1,51 @@ +# M10-15 Status + +status: done +task: Browser performance, OOM-prevention and malicious-input gates for GN, Shader, NLA and Simulation +updated: 2026-08-16 America/New_York + +## Scope + +GN, Shader, NLA and Simulation now run in four isolated Chromium Worker sessions. +Each session executes a representative bounded workload, rejects an over-budget input +before evaluator/payload allocation, rejects a domain-specific malicious graph or +manifest, and then proves that a small valid input still succeeds in the same Worker. + +NLA gains explicit track/strip/string budgets and exact declared-field checks. +Simulation cache total/frame/range overages now return the dedicated +`SIMULATION_CACHE_BUDGET_EXCEEDED` code. This is a deterministic OOM-prevention gate; +it does not claim that arbitrary browser heap exhaustion is recoverable inside a +domain evaluator. + +## Evidence + +- `WEB_TEST_PORT=5536 npm --prefix web run test:m10-domain-gates`: Node 23/23 and + Chromium 1/1. Measured Worker times were GN 54 ms (10 x 512 nodes), Shader 17 ms + (100 compiles), NLA 62 ms (20 x 256 strips), and Simulation 2 ms (64 frame hashes), + all below their 5/5/5/10 second gates. +- Stable OOM/malicious codes: GN `GN_GRAPH_BUDGET_EXCEEDED`/ + `GN_DEPENDENCY_CYCLE`; Shader `SHADER_NODE_UNSUPPORTED`/`SHADER_GRAPH_CYCLE`; + NLA `NLA_BUDGET_EXCEEDED`/`NLA_INVALID_STACK`; Simulation + `SIMULATION_CACHE_BUDGET_EXCEEDED`/`SIMULATION_CACHE_INVALID`. +- Existing GN field budget (unit 6/6 + Chromium 1/1), Shader compiler (7/7 + 3/3), + NLA operator (4/4 + 1/1), and Simulation lifecycle (6/6 + 1/1) all passed. +- Typecheck, lint, production build (74 modules), status consistency and + `git diff --check`: passed. + +## Implementation Hashes + +- `web/protocol/error.ts`: `88119782307cb9a61e131f2d5e2e25127256c1e68a333fc7fc3206158b8a1019` +- `web/protocol/nla.ts`: `96ae7cf9ca3fadbaf98ca15a38937329f203b0bc4a9c39ef685987e7f55353dd` +- `web/protocol/simulation-cache.ts`: `a1302e42129420a96416e363cbc857d082d4646655685520c9e81f877ce3d9b8` +- `web/tests/unit/nla.test.mjs`: `e11aa1f395970613cee5d37a9eb21c3fac3422f0080c75c78eb8777effb77adc` +- `web/tests/unit/simulation-cache.test.mjs`: `2be82cdda991b47fbf8955cfbf2f92cb1fdc7b7c0debb0c8102d4f35e8fce8e3` +- `web/app/src/workers/m10-domain-gate-test.worker.ts`: `bc508e1c81ca6efefd67b5c572f93c27dd71bd602319ae887c50e27f8a309a31` +- `web/tests/e2e/m10-domain-browser-gates.spec.ts`: `2973a596df8613a9cfdb0bd4d300d95b71a8e818e615278a39fb777db6c5cf03` +- `tests/golden/M10-15/domain-browser-gates.json`: `580051434ec6673251bac755cc189939de1f887aad088497e16a7657f3a83963` +- `web/package.json`: `092931cebf9cb14814951703af26e2d0e4c5e0295c1ba4cb748bfe4ee49448c2` + +## Rollback + +Remove the NLA stack budgets/exact-field checks, restore the former Simulation cache +budget error mapping, remove the domain Worker/E2E/golden/package command and this +status file, then restore M10 to 14/15. Keep all M10-01 through M10-14 behavior. diff --git a/docs/status/M11-01.md b/docs/status/M11-01.md new file mode 100644 index 00000000..16480c21 --- /dev/null +++ b/docs/status/M11-01.md @@ -0,0 +1,42 @@ +# M11-01 Status + +status: done +task: Camera, Light, World and Scene color-management field parity table +updated: 2026-08-16 America/New_York + +## Scope + +`tests/golden/M11-01/lighting-field-parity.json` is the machine-readable field table +for all CameraIR, LightIR and WorldIR leaves plus Scene render engine and color +management. Each field independently records reader, writer, main viewport and +Offscreen viewport status as VERIFIED, PARTIAL, METADATA_ONLY, BLOCKED or +NOT_APPLICABLE, with an aggregate parity status. + +The table contains 60 fields: 24 COMPLETE, 17 PARTIAL and 19 BLOCKED. It explicitly +keeps true Orthographic/Panoramic/Custom projection, camera DOF rendering, light +radius/area spread/sun angle, World environment rotation and visual Mist, display/ +view/look/gamma/white-balance rendering and Scene writer behavior outside COMPLETE. + +## Evidence + +- `npm --prefix web run test:lighting-field-parity`: AST table checker and the real + Main lighting roundtrip passed. The checker proves exact type coverage, unique fields, + valid states, existing evidence paths and required blocked claims. +- The Main roundtrip covers Camera/DOF, Light exposure/temperature/shadow, World/Mist, + validation rejection, undo/redo, save/reopen and the white-balance integrity gate. +- `WEB_TEST_PORT=5541 npm --prefix web run test:e2e -- --grep "N-019"`: Chromium 2/2, + covering Scene-over-World exposure, shadow metadata and renderer-bound World/Scene + delta preservation. +- Typecheck, lint, production build (74 modules), status consistency and + `git diff --check`: passed. + +## Implementation Hashes + +- Parity table: `b7fee80b4f2469cf119902c939f9091e640a6f0e1307e97ba29f0afa8942c4ed` +- AST checker: `cf910a087f9af72e0782d62e67341f90fd4af1c6c8eddf54169ee6275e24bd9c` +- Package: `cd7174b04b19e61a280e03e6c61e2ffd20fd507c0716c820b8faa31ea2438ebf` + +## Rollback + +Remove the M11-01 table/checker/package command and this status file, restore M11 to +0/14, and keep the existing N-019 reader/writer/viewport implementation unchanged. diff --git a/docs/status/M11-02.md b/docs/status/M11-02.md new file mode 100644 index 00000000..e2dfd58c --- /dev/null +++ b/docs/status/M11-02.md @@ -0,0 +1,48 @@ +# M11-02 Status + +status: done +task: Supported Camera, Light and World fields through Main, history, persistence and viewport mapping +updated: 2026-08-16 America/New_York + +## Scope + +The bounded `setCameraProperties`, `setLightProperties` and `setWorldProperties` +commands now have one task-level acceptance path through Blender Main, monotonically +revisioned undo/redo, in-memory `.blend` save, a fresh Worker reopen and the shared +main-thread/Offscreen PBR mapping functions. + +The golden covers Camera lens, both sensor dimensions and fit modes, shift, clip, +ortho scale and DOF fields; Light color, energy/exposure, temperature, shadow, spot +and area fields; and World color/exposure/Mist fields. Viewport assertions cover both +horizontal and vertical sensor fit, clip and film offset, temperature-adjusted linear +RGB, intensity, shadow, spot angle/blend, area dimensions and World background color. +Fields kept PARTIAL or BLOCKED by M11-01 remain outside visual parity claims. + +## Evidence + +- `WEB_TEST_PORT=5545 npm --prefix web run test:lighting-field-roundtrip`: native + Main roundtrip passed and Chromium 1/1 passed. +- Camera, Light and World edits each advanced revision; task-level Chromium coverage + undid and redid every domain, saved, opened the bytes in a fresh Worker and compared + the reopened values with float32-aware `1e-5` tolerance. +- The native rejection path kept an invalid `near >= far` edit out of Main. Existing + white-balance integrity protection remained blocked after Blender serialization + rather than publishing invalid Scene color-management values. +- `npm --prefix web test`: 156/156 passed. Typecheck, lint, production build + (74 modules) and `git diff --check` passed. + +## Implementation Hashes + +- Golden: `ac6d84c0099b12cf6c96df2dbbb4281e3308c36f0bda49c92d7548e58ccc286e` +- Chromium spec: `9f75c7b88c70ab42c6c418b0056dcffcab3b8851003258891902db9615fcfe67` +- Native roundtrip checker: `023e3e43bc37fb38c3063d33c20e59043308013874fce20bc3a49d508b850e6c` +- Web command protocol: `48f2290ead27aeda1f17b279f01ae278776b50b4cb4de065d94b0e1d44717dbb` +- Blender Main writer: `510b4017c540fc4d1ac135c1ce7251cc5d7143f31d8604e1d545de36716da9a2` +- Shared PBR adapter: `78352e46c62d0c8d9bf25408ea1d725a843a42068ea3eb304bb3d78f8d612e0b` +- Package: `8074d80265a1378fb3f284206c343ff1e388a5ea43e57946a1d4092076d72ea8` + +## Rollback + +Remove the M11-02 golden/spec/package command and this status file, restore M11 to +1/14, and keep the underlying N-019 bounded writers and viewport mappings at their +previous independently tested status. diff --git a/docs/status/M11-03.md b/docs/status/M11-03.md new file mode 100644 index 00000000..24f6bdd0 --- /dev/null +++ b/docs/status/M11-03.md @@ -0,0 +1,55 @@ +# M11-03 Status + +status: done +task: Explicit Three WebGL2 and WebGPU light, shadow-map and texture budgets +updated: 2026-08-16 America/New_York + +## Scope + +Schema 1 `PBRRenderBudget` freezes separate `THREE_WEBGL2` and `THREE_WEBGPU` +product ceilings. WebGL2 reserves two built-in lights and one built-in shadow map +inside totals of 16 lights and four 1024-square shadow maps. The WebGPU contract uses +64 lights and eight 2048-square maps. Runtime device limits may only lower these +ceilings. + +The shared planner deterministically retains light nodes in SceneIR order, retains a +bounded shadow-capable prefix and reports every dropped identity with stable +`GPU_LIGHT_BUDGET_EXCEEDED`/`GPU_SHADOW_BUDGET_EXCEEDED` issues. Main-thread and +Offscreen production viewports publish the same report on the canvas. + +Texture planning covers asset count, maximum dimension, aggregate compressed payload +and decoded RGBA GPU bytes before hash/decode/allocation. An over-budget batch leaves +the previous texture set alive. This task defines the WebGPU budget contract but does +not claim that the still-unbundled Three WebGPU renderer is available. + +## Evidence + +- `WEB_TEST_PORT=5548 npm --prefix web run test:render-resource-budget`: unit 3/3 and + Chromium 3/3. Both production viewports requested 20 lights, rendered 14, dropped + six, rendered three of 14 requested scene shadow maps and blocked 11; with reserved + resources, the main Three scene contained exactly 16 lights and four shadow maps. +- A 257-asset batch was rejected before decode in both viewports. A separate browser + case first decoded a valid PNG, rejected a 257-asset follow-up (258 including the + retained asset), and proved the original texture object/hash remained installed. +- M11-02 field roundtrip remained 1/1. Real 4K (64 MiB decoded) and 8K (256 MiB + decoded) texture performance cases passed; packed raster main/Offscreen passed 2/2. +- Full unit was 159/159. Typecheck, lint, production build (75 modules), local runtime + dependency checks and `git diff --check` passed. + +## Implementation Hashes + +- Budget protocol: `d7784690358e38b71c6ad3ccc5baf82869d897714d5257f219bbd00dc7e7b0f7` +- PBR adapter: `934874434afa6778eaf783c78650ad221a428352741f3ff7173cb3c17a0c3c14` +- Texture store: `0f18f3bcf04c5c99175d4cac8bbf1bc028fc54d635701fb6a92791e06c335fb6` +- Main viewport: `dcb502ca892fd5431cbf1a13823aec5d83a2aa0a1b40487cb66fa630c6e461b3` +- Offscreen Worker: `c95bce10f0bd73591f002c21415bfbc6f709ecbe61e97a3b5b05bcec2b0e0bef` +- Unit: `4bde90788bbf9a94317d83bd398acd321bbcf4e53e26bf40165a889bacff03a2` +- Chromium spec: `90834dfff09e61a2d32732d3a8f0db4b9c71414ac4991412d93d322119ef2de7` +- Golden: `c312bf5ed8415ad51e5cccb3a2653f65cd9fee46488be2f860e6a5a8d5a7901d` +- Package: `c718983c3f3a552f4c68d9390376777e45a3f23e6999afd5cb4ddc786a12a278` + +## Rollback + +Remove the render-budget protocol, planner integration, canvas reports, unit/E2E/ +golden/package command and this status file; restore the former fixed 1024 shadow +configuration and per-asset-only texture checks, then restore M11 to 2/14. diff --git a/docs/status/M11-04.md b/docs/status/M11-04.md new file mode 100644 index 00000000..924aa70f --- /dev/null +++ b/docs/status/M11-04.md @@ -0,0 +1,56 @@ +# M11-04 Status + +status: done +task: Explainable image-error metrics between the Web realtime viewport and a Blender 5.2 reference +updated: 2026-08-17 America/New_York + +## Scope + +Schema 1 `RenderImageComparisonIR` compares equal-size display-referred `SRGB8`/ +`STRAIGHT` RGBA frames. The report records mean absolute error, root mean squared +error, P95 channel error, maximum channel error, bad-pixel ratio, reference-background +foreground intersection-over-union and alpha coverage delta. Each release check keeps +its measured value, threshold, direction and pass state; a mismatch returns the stable +`RENDER_REFERENCE_MISMATCH` code. Dimensions, byte lengths, threshold ranges and a +4,194,304-pixel bound are validated before comparison. + +The reference is generated by Blender 5.2 Eevee from +`tests/files/web/m11_render_reference.blend` with a fixed camera, black rough material, +World color, one-sample TAA and zero dither. The source fixture, generator and PNG are +SHA-256 bound in `tests/golden/M11-04/manifest.json`. Re-render verification compares +decoded Blender pixels exactly because PNG container bytes may differ while pixels do +not. + +## Evidence + +- `WEB_TEST_PORT=5552 npm --prefix web run test:render-reference`: unit 3/3, + Blender reference re-render verification passed, Chromium 3/3. Main-thread and + Offscreen reports were identical: MAE `2.0433349609375`, RMS `5.448994264342599`, + P95 `4`, maximum channel error `110`, bad-pixel ratio `0.0047760009765625`, + foreground IoU `0.9853400565736072`, alpha coverage delta `0`. +- Thresholds are MAE `4`, RMS `12`, P95 `12`, bad-pixel ratio `0.02`, foreground + IoU `0.97` and alpha coverage delta `0`. A non-empty red block with the wrong + composition is rejected as `RENDER_REFERENCE_MISMATCH`. +- `npm --prefix web run typecheck`, `npm --prefix web run lint`, production build + (75 modules), `npm --prefix web run test:status-consistency` and `git diff --check` + passed. + +## Implementation Hashes + +- `web/protocol/render-image-comparison.ts`: `4345cc0d02b4482201de34292cbf5198009bf615e69c80cc00bc3e76116efec2` +- `web/app/src/three-adapter/render-image-comparison.ts`: `ad97fd594d0d964e05d4da2bffc84a64ef737fef700045c252b5222250f1ffc7` +- `web/protocol/error.ts`: `e8ea483466b16debaf803ff1640ade07a858a802781e3040d3547028ecb5d19f` +- `tools/web/generate-m11-render-reference.py`: `118c98c77d98922e72660898bfb65842e11743fc77f4124c9f5088a76cb6a94a` +- `tools/web/check-render-reference.mjs`: `ead3500f054e98c2db4cd6596c983586a622e929bc8a587e9fbd38d94a7df7d1` +- `web/tests/unit/render-image-comparison.test.mjs`: `f9800745760b85700e80abc5e389f44d7f7ea3b9e41e1002f4e5449bcb620f67` +- `web/tests/e2e/render-reference.spec.ts`: `349c7da0dfd60baec80139cced52079312bd5e892a42f000ba0b4f55c6a4a61d` +- `tests/files/web/m11_render_reference.blend`: `d2bea55fe4de0b00e73a9241b4d1b0c802c2eb0e6159c0cd48e007b97b9d3963` +- `tests/golden/M11-04/blender-eevee-reference.png`: `f5c22636a232bcbb0ed0f772418cb12fced18ff72f4999804197e42caa61480a` +- `tests/golden/M11-04/manifest.json`: `e9551f86f0b9704d7be35c03f07321bec5d2481ffee4208ae766156d9c9a0135` +- `web/package.json`: `38ba6a722a2e0bc2c91d6edbd2b7df5e3609342e3b7ef2ec9646b9c6ce0d25a6` + +## Rollback + +Remove the M11-04 protocol, reference fixture/golden, generator/checker, unit/E2E +specs and package command; restore M11 to 3/14 and remove the bounded reference slice +from N-019. Keep M11-01 through M11-03 resource and lighting behavior unchanged. diff --git a/docs/status/M11-05.md b/docs/status/M11-05.md new file mode 100644 index 00000000..2b702a4a --- /dev/null +++ b/docs/status/M11-05.md @@ -0,0 +1,47 @@ +# M11-05 Status + +status: done +task: Server-only routing for Cycles, complex Eevee and hardware render backends +updated: 2026-08-17 America/New_York + +## Scope + +Schema 1 `RenderRoutingRequestIR` separates execution target from endpoint +availability. Only bounded Eevee on WebGL2, or WebGPU after both browser and bundled +renderer capability checks, may return `WEB_LOCAL_BOUNDED/READY`. Cycles, complex +Eevee, Workbench final rendering and CUDA/OptiX/HIP/Metal/oneAPI requests always +target `SERVER_JOB`; the default unconfigured endpoint returns +`SERVER_JOB_UNAVAILABLE` without a local approximation. Unknown, syntactically valid +SceneIR engine identities fail closed as `PLATFORM_CAPABILITY_UNAVAILABLE`. + +Setting `serverRenderAvailable:true` can make a server route `READY`, but only proves +the routing decision. It does not submit a job or claim the source/build/settings/ +output hash contract reserved for M11-06. + +## Evidence + +- `WEB_TEST_PORT=5556 npm --prefix web run test:render-routing`: unit 3/3 and + Chromium 1/1. The browser reads `BLENDER_EEVEE` from the real M11 reference `.blend` + through the production WebEngine Worker before checking the local/server matrix. +- Cycles, complex Eevee and OptiX all remained `SERVER_JOB/BLOCKED` with + `SERVER_JOB_UNAVAILABLE` under the production default. A synthetic configured + server context returned `SERVER_JOB/READY`, never a Web target. +- Unbundled WebGPU remained `WEB_LOCAL_BOUNDED/BLOCKED` with + `WEBGPU_RENDERER_UNAVAILABLE`; an unknown engine remained server-targeted and + blocked with `PLATFORM_CAPABILITY_UNAVAILABLE`. +- Typecheck, lint, production build (75 modules) and `git diff --check` passed. + +## Implementation Hashes + +- `web/protocol/render-routing.ts`: `2d41d48609635c810572aaae95979df2fe9e49bbd130c60d70f64042c5d0f097` +- `web/app/src/three-adapter/render-routing.ts`: `f360c05fa62aa830a486c5be4747488fcbb5ba9b68d82b4f7187e1267da46fa6` +- `web/tests/unit/render-routing.test.mjs`: `4ac2e7a6c2265f948d971f72effb4ff9ad1c51839ab6c06d496fc2b0768c4fa8` +- `web/tests/e2e/render-routing.spec.ts`: `e5572d9ad9426f1dcc1856fe426261910093fa2f5460117e4ffdcfc7b23b10a1` +- `tests/golden/M11-05/render-routing.json`: `53c5d3a2e51f251d14953594567081597be4250be5b1236832370e74805bd35d` +- `web/package.json`: `716a5331990618892bca97f48c7eef589a5af6c5131b8cf3ae52a1b9d923644c` + +## Rollback + +Remove the M11-05 routing protocol/wrapper, unit/E2E/golden/package command, restore +M11 to 4/14 and remove the N-019 server-routing completed slice. Keep M11-04 realtime +reference metrics and all earlier lighting/resource behavior unchanged. diff --git a/docs/status/M11-06.md b/docs/status/M11-06.md new file mode 100644 index 00000000..d7311d66 --- /dev/null +++ b/docs/status/M11-06.md @@ -0,0 +1,48 @@ +# M11-06 Status + +status: done +task: server render job provenance binding +updated: 2026-08-17 America/New_York + +## Scope + +`web/protocol/server-render-job.ts` defines schema 1 for a server render request and +result. A request is accepted only when the uploaded `.blend` bytes match its declared +source SHA-256 and byte length, the Blender build is a declared 5.2 build with a +content hash, and canonical bounded render settings match `settingsSha256`. The +request itself is bound by `requestSha256`. + +A successful result carries the same source/revision/build/settings identity and adds +output MIME, byte length, and output SHA-256, plus `resultSha256`. Consumers recalculate +the output hash before accepting pixels. The contract does not claim a remote queue, +progress, cancellation, denoise, or Freestyle implementation; those remain later +M11 tasks. + +## Evidence + +- `npm --prefix web run test:server-render-job`: unit 3/3 and a real loopback server + check passed. +- The check used Blender 5.2.0 headless to open + `tests/files/web/m11_render_reference.blend` and render a PNG from the submitted + source bytes. It verified source/build/settings/output hashes and rejected source, + build, settings, and output tampering. +- Result: `server-render-job-ok blender=5.2.0 source=d2bea55fe4de0b00e73a9241b4d1b0c802c2eb0e6159c0cd48e007b97b9d3963 settings=97bb832618bd27e8763722afa0d250ef74383fb355f0ff38763930f57ba1590b output=564b95cd501da2636085aeada88814c4a3b7af7b1f0188543a1863fe34dd091e bytes=3915 source-hash=1 build-hash=1 settings-hash=1 output-hash=1 tamper=4`. +- Full regression passed: typecheck, lint, Node `168/168`, status consistency + (`12` parity blocked, `0` release blocked), release evidence (`17` records, + `0` missing), production build (`75` modules), and `git diff --check`. + +## Implementation Hashes + +- `web/protocol/server-render-job.ts`: `e2b5b95032b0dbcb346aa35d1a10a3b55d6c006603f16b9e72c3de45763906c5` +- `web/protocol/error.ts`: `747ea52fd96b3ec7ede9ce72c15f56fdc454836ba5bf924cc60198096d4986de` +- `web/tests/unit/server-render-job.test.mjs`: `c84f86365006360f146601839d7e1394f3f7bc7fa71dcbf62d8d4e4321cdd16d` +- `tools/web/check-server-render-job.mjs`: `6dd49fbd1be202a88fa714b2fb81305018da6df5f9d73c95e048e3697742764e` +- `tools/web/render-server-job.py`: `48950b0884a52a40ae234f8bbe590a0cb89e58c4d2a2de40ea638f491d111aff` +- `tests/golden/M11-06/server-render-job.json`: `36f083f8da7585f850e748e6ac78fc57cd6e8d59bce03d77eee3acd2fa042ae7` +- `web/package.json`: `628191a76c7614393a74969bd000faa8a6302d887265d13a1ad49dcfc401c2fc` + +## Rollback + +Remove the server-render-job protocol, error codes, unit/check commands, and this status +record; restore M11 to 5/14 and remove the M11-06 completed slice. Keep M11-05 routing, +M11-04 reference metrics, and all earlier lighting/resource behavior unchanged. diff --git a/docs/status/M11-07.md b/docs/status/M11-07.md new file mode 100644 index 00000000..3c198569 --- /dev/null +++ b/docs/status/M11-07.md @@ -0,0 +1,36 @@ +# M11-07 Status + +status: done +task: compositor allowlist CPU/WebGPU node goldens +updated: 2026-08-17 America/New_York + +## Scope + +The schema 1 WebGPU compositor allowlist contains only `CONSTANT_COLOR`, `EXPOSURE`, +`INVERT`, and `COMPOSITE`. A Blender 5.2 fixture supplies an independent constant, +exposure, invert, and integrated-chain scene through the production Main/WASM reader. +The production CPU executor and a real Chromium WebGPU compute pipeline must produce +identical `LINEAR_SRGB` Float32 RGBA bytes for every case. + +Resource inputs, branching graphs, Viewer, Transform, Alpha Over, Blur, Mix, and +undeclared Blender nodes remain outside the allowlist and fail before shader +compilation. This task does not claim the M11-08 graph-preservation pipeline, HDR/color +management parity, a production compositor scheduler, or server compositor execution. + +## Evidence + +- `WEB_TEST_PORT=5562 npm --prefix web run test:compositor-node-golden`: Node 3/3, + Blender 5.2 Main/WASM fixture check, and Chromium WebGPU 1/1 passed. +- The four CPU and WebGPU Float32 SHA-256 values match the committed golden exactly; + maximum absolute error is `0`. +- The first run without an isolated port passed Node/native checks but did not start + Chromium because port 5173 was already occupied. It was not counted as evidence; + the complete command was rerun on port 5562. +- Full regression passed: typecheck, lint, Node 171/171, production build, status + consistency, release evidence validation, and `git diff --check`. + +## Rollback + +Remove the WebGPU compositor plan/executor, M11-07 fixture/goldens/tests, and this +status record; restore M11 to 6/14 and remove the N-020 WebGPU golden completed slice. +Keep the existing bounded CPU compositor and Main graph reader unchanged. diff --git a/docs/status/M11-08.md b/docs/status/M11-08.md new file mode 100644 index 00000000..6548d3f6 --- /dev/null +++ b/docs/status/M11-08.md @@ -0,0 +1,37 @@ +# M11-08 Status + +status: done +task: preserve unsupported compositor graphs and block execution +updated: 2026-08-17 America/New_York + +## Scope + +The CPU compositor and cached execution entry points now preflight the complete parsed +graph before evaluation or cache lookup. Any `UNSUPPORTED` node blocks with +`COMPOSITOR_NODE_UNSUPPORTED`, including a disconnected node that does not contribute +to the Composite output. Unsupported Blender type names are sorted and deduplicated so +the failure is deterministic. + +The gate is pure: it does not remove nodes, rewrite links, advance Main revision, run a +cancellation callback, allocate an output, or accept a previously cached result. The +existing Blender 5.2 fixture preserves its Glare node name, `UNSUPPORTED` IR type, and +`CompositorNodeGlare` source type before and after the failed attempt. + +## Evidence + +- `WEB_TEST_PORT=5565 npm --prefix web run test:compositor-unsupported-gate`: unit 2/2, + native Main reader, and Chromium 1/1 passed. +- `WEB_TEST_PORT=5566 npm --prefix web run test:e2e -- --grep "N-020"`: 2/2 passed; + the supported synthetic CPU graph still executes, while the real graph containing a + disconnected Glare node now fails closed. +- The first new unit run was 172/173 because an async rejection used `assert.throws`. + It was corrected to `await assert.rejects`; no implementation failure was counted as + evidence. The complete Node suite then passed 173/173. +- Full regression passed: typecheck, lint, production build, status consistency, + release evidence validation, and `git diff --check`. + +## Rollback + +Remove the complete-graph preflight from CPU/cached execution and the M11-08 tests and +golden, restore the old N-020 smoke expectation, restore M11 to 7/14, and remove the +N-020 completed slice. Keep M11-07 CPU/WebGPU goldens unchanged. diff --git a/docs/status/M11-09.md b/docs/status/M11-09.md new file mode 100644 index 00000000..a90886e8 --- /dev/null +++ b/docs/status/M11-09.md @@ -0,0 +1,43 @@ +# M11-09 Status + +status: done +task: runtime codec probes for IMAGE, SOUND, and MOVIE strips +updated: 2026-08-17 America/New_York + +## Scope + +Schema 1 codec probe requests and receipts bind the Sequencer strip family, canonical +MIME type, source byte length, and source SHA-256. File names and source paths are not +accepted by the probe contract, so extensions cannot grant capability. A READY receipt +must also use the family-specific backend and carry validated decoded dimensions or +audio frame metadata. + +The production browser probe hashes the supplied bytes and performs a real decode: +`createImageBitmap` for IMAGE, a fixed 48 kHz `OfflineAudioContext` for SOUND, and a +muted `HTMLMediaElement` load/seek/canvas readback for MOVIE. The committed movie is a +deterministic 16x16, two-frame H.264 MP4 generated locally by FFmpeg. This task does not +claim frame-accurate WebCodecs seeking, waveform/proxy generation, A/V synchronization, +arbitrary codec support, or final encoding. + +## Evidence + +- `WEB_TEST_PORT=5570 npm --prefix web run test:sequencer-codec-probe`: unit 3/3 and + Chromium 1/1 passed. PNG, WAV, and H.264 MP4 returned READY with their expected + runtime backend and decoded metadata. +- Deliberately wrong file extensions had no effect. Corrupt bytes, source identity + drift, PNG bytes declared as MOVIE, a forged backend, and a forged receipt all stayed + BLOCKED with `SEQUENCER_CODEC_UNSUPPORTED` at the gate. +- The first Chromium attempt failed before business execution because `/protocol` is + not exposed by Vite; the test now imports the existing app adapter. The second decoded + all media but exposed device-rate Web Audio resampling; the production probe now uses + a fixed 48 kHz OfflineAudioContext. The complete command was then rerun successfully. +- Sequencer Main reader, N-021 browser regression, and the one-million-frame long media + performance/restart test passed. Full regression passed: typecheck, lint, Node suite, + production build, status consistency, release evidence validation, and + `git diff --check`. + +## Rollback + +Remove the codec probe protocol/runtime/tests/movie fixture, restore the MIME-set gate, +restore M11 to 8/14, and remove the new N-021 completed slice. Keep the V1 long-media +IMAGE/SOUND index/cache behavior and all M11-08 compositor behavior unchanged. diff --git a/docs/status/M11-10.md b/docs/status/M11-10.md new file mode 100644 index 00000000..cd83b221 --- /dev/null +++ b/docs/status/M11-10.md @@ -0,0 +1,57 @@ +# M11-10 Status + +status: done +task: bind long-media proxy/cache entries to source hash and runtime decode capability +updated: 2026-08-17 America/New_York + +## Scope + +Schema 1 proxy-cache manifests bind the complete movie source identity, the source-bound +M11-09 READY decode receipt, an explicit SRGB8/STRAIGHT RGBA8 profile, and the source +frame into one deterministic SHA-256 cache identity. The proxy payload has an independent +byte length and SHA-256. Source drift, decode-capability drift, identity tampering, and +payload tampering therefore fail with separate stable codes. + +The production browser adapter verifies the movie bytes before decode, captures only the +initial decoded HTMLMedia frame, and stores verified copies in a 64 MiB-bounded LRU. The +Chromium gate persists the RGBA8 payload and its manifest through the content-addressed +Storage Worker path, creates a new Storage Worker, and verifies the reopened bytes against +the current runtime receipt. This task does not claim frame-accurate proxy generation, +seek/scrub publication, waveform generation, A/V synchronization, or final encoding. + +## Evidence + +- `WEB_TEST_PORT=5573 npm --prefix web run test:sequencer-media-cache`: unit 3/3 and + Chromium 1/1 passed. The real 16x16 H.264 fixture produced one 8x8/256-byte RGBA8 + proxy; its cache identity is + `68a3af14865841e81f69bd75f2605461de4819fe025d158ac3723fa0cdf31525`. +- A one-frame cache budget admitted frame 0, returned a copied hit, then evicted it when + frame 1 was inserted. `clear()` released exactly 256 bytes and left zero entries. +- Source hash drift, READY receipt metadata drift, payload mutation, and bad source bytes + returned `SEQUENCER_CACHE_SOURCE_MISMATCH`, + `SEQUENCER_CACHE_CAPABILITY_MISMATCH`, or `SEQUENCER_CACHE_HASH_MISMATCH` as + appropriate. Blocked receipts, undeclared profile fields, oversized RGBA8 profiles, and + upscaled profiles were rejected before cache insertion. +- The first unit run was 2/3 because the test incorrectly treated exactly 64 MiB as over + budget; the corrected 64 MiB plus one-row case passed. The first Chromium run completed + all business operations but sampled `statsBeforeClear` after `clear()`; the sampling + order was fixed and the full command rerun on ports 5572 and 5573. +- Regression passed: M11-09 unit 3/3 plus Chromium 1/1, one-million-frame long-media 1/1, + Sequencer Main reader, full Node 179/179, typecheck, lint, and production build (75 + modules). + +## Artifact Hashes + +- protocol: `1dd5ee8cade2642723bac6f0c8e34d09cf178d000beba6eb63626284ee35fd5e` +- browser adapter: `57070b1a42129afdfb8121ec11a48b0792b3de9d84f7e8a78017c7d6e12343aa` +- app protocol exports: `8ca82257c758580f5fe91a3a5cd21f8ae9e9ce10f9fa71fcd7cf1f30e0b6367b` +- unit: `f4cc1171dd6875bf079e242beed846409a1d1f53b9497882aa475bd2773b43c7` +- Chromium spec: `6d9d26e3639a74b173f77ec5d67e53e5ec42b88dd6e34bda54c8168b8251423e` +- golden: `39a71f8d97f0045da6ec913b6a0231ac3363f67a420eea5ab3fabaa8f1d47c33` +- package: `2c7fb9f8657155088e4a080443b8a0ba9c13aae9f46f1807ff3b8820640c44c4` + +## Rollback + +Remove the proxy-cache protocol/runtime/tests/golden and package command, restore M11 to +9/14, and remove the new N-021 completed slice. Keep M11-09 runtime codec probes and the +V1 long-media index/cache evidence unchanged. diff --git a/docs/status/M11-11.md b/docs/status/M11-11.md new file mode 100644 index 00000000..a9beb143 --- /dev/null +++ b/docs/status/M11-11.md @@ -0,0 +1,52 @@ +# M11-11 Status + +status: done +task: revision-gate late seek, scrub, and decode results before publication +updated: 2026-08-17 America/New_York + +## Scope + +Schema 1 uses one request identity for SEEK, SCRUB, and DECODE. Every request binds a +timeline ID, timeline revision, monotonically increasing request revision, operation, +and frame. A completed result must echo that identity and add a bounded source frame and +payload SHA-256. Publication is allowed only when both the timeline revision and latest +request revision still match the controller state. + +The production controller advances one shared request revision across all three operation +types. Replacing a timeline invalidates every pending request before a new request begins, +and same-timeline revisions must advance monotonically. The publish callback is invoked +only after the pure gate returns PUBLISH, so stale results cannot update the visible frame +or write a decoded payload into cache. This task does not claim frame-accurate WebCodecs +seeking or the M11-14 cancellation/restart resource lifecycle. + +## Evidence + +- `WEB_TEST_PORT=5576 npm --prefix web run test:sequencer-media-revision`: unit 4/4 + and Chromium 1/1 passed on the first complete run. +- Chromium scheduled SEEK revision 1 behind SCRUB revision 2. SCRUB published first and + the late SEEK returned `STALE/REVISION_CONFLICT`. A real H.264 runtime decode started at + timeline revision 7, completed after replacement with revision 8, and was rejected + before publish/cache. The revision-8 DECODE published; a forged result identity stayed + stale. +- The machine sequence is frozen in + `tests/golden/M11-11/sequencer-media-revision.json`: only `SCRUB@2` and `DECODE@5` + publish, and only `DECODE@5` reaches the cache-write callback. +- Regression passed: M11-10 unit 3/3 plus Chromium 1/1, M11-09 unit 3/3 plus Chromium + 1/1, one-million-frame long-media 1/1, full Node 183/183, typecheck, lint, and production + build (75 modules). + +## Artifact Hashes + +- protocol: `326f1525f50af3b2c4132ff58508ed6174ba3aa4a5075b2a6660e549c280826a` +- production controller: `29a44404bb28e46d67439eec0622a69ce4cd7264ce45da77a3f320c8687b7006` +- app protocol exports: `be2eca8482a60e437878b99f69d3178f47fee61618886d141760320e3a3ed1e1` +- unit: `2099f738120897dfbb8662741831774b5f9150d51924dfa7832907177de5e7` +- Chromium spec: `e47f105360ce0e2d4d561d3cf790ceec9f41dd6bac7361965914ada307db843c` +- golden: `672b4fde6e27f8b15cd51d839cacb5efb128c0d0f4c7f6aa4e184c713f9b45e5` +- package: `5d17b17d67ee587191552c3dfe226cd8f2e5a872791224d8e6c22aafa1177ebe` + +## Rollback + +Remove the media revision protocol/controller/tests/golden and package command, restore +M11 to 10/14, and remove the N-021 revision-gate slice. Keep M11-09 runtime probes, +M11-10 proxy-cache identity, and the V1 long-media generation behavior unchanged. diff --git a/docs/status/M11-12.md b/docs/status/M11-12.md new file mode 100644 index 00000000..1c4609e4 --- /dev/null +++ b/docs/status/M11-12.md @@ -0,0 +1,58 @@ +# M11-12 Status + +status: done +task: keep final video encoding on an explicit server-export capability route +updated: 2026-08-17 America/New_York + +## Scope + +Schema 1 final-export requests bind the Main timeline ID/revision, source `.blend` +SHA-256, bounded frame range and rational frame rate, output dimensions, and one declared +container/video/audio codec combination. The canonical encoding settings and the complete +source identity receive separate SHA-256 hashes, so source revision drift and settings +drift produce different request identities. + +The only route is `SERVER_EXPORT`. A missing endpoint fails closed with +`SEQUENCER_EXPORT_SERVER_UNAVAILABLE`; a configured endpoint returns +`SERVER_EXPORT_REQUIRED`, not local success. Browser `VideoEncoder` availability is +recorded for diagnostics only and cannot change the route or the invariant +`localEncoding=BLOCKED`. This task does not claim a server encoder implementation, +upload/progress/cancellation, audio mixing, or encoded-output verification. + +## Evidence + +- `WEB_TEST_PORT=5579 npm --prefix web run test:sequencer-final-export`: unit 4/4 and + Chromium 1/1 passed on the first complete run. +- Chromium opened the real Blender 5.2 `sequencer_scene.blend` through WebEngine Main and + bound timeline `sequencer:scene:SequencerScene`, revision 1, frames 1..250, 24000/1001 + FPS, and source SHA-256 + `5f5212487bb6b5df62b5ca915c75133f1ca45678614712ea4c7d902d823cd90c`. +- The fixed settings hash is + `01e39d1fdd88c75aecad7bc48a2b756bbcd645929963528b42ceb180b51e5566`; + the source/timeline/settings request hash is + `20d3a9e333804e014a993a9892a557842293fc44902229dd6eb65661f0691cfc`. +- No-server, server-without-encoder, server-with-injected-encoder, and actual Chromium + runtime cases all retained the server route and blocked local encoding. Undeclared + fields, invalid codec combinations, over-budget ranges, and environment drift returned + `SEQUENCER_EXPORT_REQUEST_INVALID`. +- Regression passed: M11-09 unit 3/3 plus Chromium 1/1, M11-10 unit 3/3 plus Chromium + 1/1, M11-11 unit 4/4 plus Chromium 1/1, one-million-frame long-media 1/1, Sequencer Main + reader, full Node 187/187, typecheck, lint, and production build (75 modules). + +## Artifact Hashes + +- protocol: `559516163ce9a9c60917033455c71a1ae6be2f34666371f12723a056ddfdb3dd` +- browser adapter: `f9f193eb71afa48a23b4eb618cd98b5c0064c59943b3652fb6a601b545ac175c` +- app protocol exports: `d30783528fabaaefe8835998e11d75da357d265ee06db67713e57e4b85b58d7d` +- error contract: `747894b216db59c753b24ce85eb946925e64340f63684311e5d9628c9a6a9c8e` +- unit: `6f5030852e89160fd898b2aff4dc9911963342bd6c2301a2c0e8a3d518ddbcbd` +- Chromium spec: `d8cba79c9a330c1e5c777c8f0ed08362b29f50702c31d8229fd65f4304bce363` +- golden: `c0061f82ef156e53ad4dfc6aedf7c6a8f53e616d8d9fa1435fd9491ba3d07e01` +- package: `83ee5bfa50a0e5b742048d4563ae815171232a121cf3df4a1e813f21313a6509` + +## Rollback + +Remove the final-export protocol/browser adapter/tests/golden and package command, remove +the two export error codes and SequencerTimeline exports, restore M11 to 11/14, and remove +the N-021 final-export completed slice. Keep M11-09/10/11 decode, proxy, and revision +gates and the existing `sequencerRuntimeCapabilities().localEncoding=BLOCKED` contract. diff --git a/docs/status/M11-13.md b/docs/status/M11-13.md new file mode 100644 index 00000000..ff149018 --- /dev/null +++ b/docs/status/M11-13.md @@ -0,0 +1,58 @@ +# M11-13 Status + +status: done +task: verify AudioContext suspend, resume, missing-device, and mute recovery +updated: 2026-08-17 America/New_York + +## Scope + +Schema 1 audio-session reports separate context state +(`UNAVAILABLE/SUSPENDED/RUNNING/CLOSED`) from output state +(`BLOCKED/SILENT/ENABLED`). Every initialize, resume, suspend, mute, device-recovery, and +close operation advances a monotonic revision. Reports bind the mute flag, effective gain, +and a stable issue code, and reject contradictory states such as enabled output from a +suspended or muted context. + +The production `SequencerAudioSession` owns one `AudioContext` and master `GainNode`. +Mute schedules gain zero; unmute restores the configured nominal gain. Suspend and resume +must reach the corresponding real context state before output can be enabled. Missing API, +constructor failure, and resume/suspend failure remain silent with structured errors. +Closing first zeros and disconnects the gain node, closes the context, drops all references, +and reports `CLOSED/SILENT`. This task does not claim waveform generation, A/V sync, +multi-strip mixing, output-device selection, or encoded audio export. + +## Evidence + +- `WEB_TEST_PORT=5586 npm --prefix web run test:sequencer-audio-recovery`: unit 3/3 and + Chromium 1/1 passed on the first complete run. +- The Chromium case creates a real `AudioContext` inside a trusted click handler, then + verifies suspend, mute, resume-while-muted, unmute, second suspend/resume, and close. + Muted output remains `RUNNING/SILENT` at gain 0; unmute restores + `RUNNING/ENABLED` at gain 0.75; close reports `CLOSED/SILENT` at gain 0. +- A scope without `AudioContext` returns + `UNAVAILABLE/BLOCKED/SEQUENCER_AUDIO_DEVICE_UNAVAILABLE`. The deterministic unit path + then makes a constructor available and verifies `recoverDevice()` plus resume. Constructor + and resume failures stay silent; malformed state reports return + `SEQUENCER_AUDIO_CONTEXT_INVALID`. +- Regression passed: M11-09 unit 3/3 plus Chromium 1/1, M11-10 unit 3/3 plus Chromium + 1/1, M11-11 unit 4/4 plus Chromium 1/1, M11-12 unit 4/4 plus Chromium 1/1, + one-million-frame long-media 1/1, Sequencer Main reader, full Node 190/190, typecheck, + lint, and production build (75 modules). + +## Artifact Hashes + +- protocol: `0b1b75c7630b599723914bbced9ee0a107bd8c0ac4382ced1f1c5114dac8fd01` +- production controller: `c09914d2581ee454e042ed6a9ef5b8c0f6f7b0688eadb1deff45163263742109` +- app protocol exports: `e630f647ddd3680a04e07ce7388cf03f54268d4e2d839aa5991cb9ceb6a53bda` +- error contract: `6589f712058a39a2177cb174cc355c01777a549d254c4b9eac2301a344c619f2` +- unit: `1a888d1b19a3d12091e6edd05287a5bcc19912415cf7f2ed73a028d12846a298` +- Chromium spec: `1b94ea07ab5fafb0b06fbd73742c77d6b69d745a546fc60def85857844afc719` +- golden: `584aea46687fd5cec8d89e8fac51314acc9a8d05af2a8d762e8d6a74483e0940` +- package: `8d87390f9b1b7382bf9c0ba189f7a9d95daa3ee15d1d422a8d856fa4eb6d3cca` + +## Rollback + +Remove the audio-session protocol/controller/tests/golden and package command, remove the +four audio error codes and SequencerTimeline exports, restore M11 to 12/14, and remove the +N-021 audio-recovery completed slice. Keep M11-09 offline audio decoding and M11-12 final +export routing unchanged. diff --git a/docs/status/M9-14.md b/docs/status/M9-14.md new file mode 100644 index 00000000..2b145a65 --- /dev/null +++ b/docs/status/M9-14.md @@ -0,0 +1,57 @@ +# M9-14 Status + +status: done +task: Curve、Grease Pencil、Paint 三编辑域故障恢复闭环 +updated: 2026-08-16 America/New_York + +## Scope + +`web/protocol/editing-domain-recovery.ts` freezes one schema-v1 evidence shape for +the three domains. Each report must prove Main identity, Worker generation, OOM +fault mapping/cleanup, WebGL2 resource release/reinitialization, and a visible +small-scene recovery. Save/reopen may reset the logical Main revision; the +content identity SHA-256 remains the authoritative recovery check. + +## Evidence + +- Curve fixture: `tests/files/web/nonmesh_scene.blend`, SHA-256 + `ae8ef85d606aa120ce6b7611fc03407fd80bb60311e94a6ef73f6384d0b6c8b4`. +- Grease Pencil fixture: `tests/files/web/modifier_grease_pencil_scene.blend`, SHA-256 + `3a8525077807f9178dac3a5ba84b524e5f8e80874b15c54e2a6658ecb80315a3`. +- Paint fixture: `tests/files/web/attribute_scene.blend`, SHA-256 + `12fa75bb79f8c38e660d3d2a8fc9cc16dd3df4fc9208b0e2ad94fb1f8aa68f71`. +- `WEB_TEST_PORT=5435 npm --prefix web run test:editing-domain-recovery`: unit 3/3 and + Chromium 1/1. All three reports returned `RECOVERED` for Worker restart, OOM, + GPU release and small-scene stages. +- Full unit suite: 127/127 tests passed. +- `npm --prefix web run typecheck`: passed. +- `npm --prefix web run lint`: passed. +- `npm --prefix web run build`: passed, Vite production bundle transformed 73 modules. +- `git diff --check`: passed. + +## Implementation hashes + +- Protocol: `42ab53dde3161ec31ca7de420046a2c06f58408b3ac4919c9904e02d875af258`. +- Browser runner: `2bb9805795fc2104e5443207a08951ac7684e67913064d6795c9d165b0c54c7e`. +- Unit test: `ca565149e7769b40f59e865e42d2dfb0f4ecb2a4655371004678c14536ec9ef6`. +- Chromium test: `7ab3421d1b44eb78d13525f67a26f1f6ed20816ae9b43c010c9ed3ebdf32c09a`. +- Golden manifest: `f548052f39f6259f30b0569528160512aad955e61e8e8559e0d553c30d5619f9`. + +## Recovery boundaries + +- Worker restart uses a fresh `WebEngineClient`/Worker and reopens the saved bytes; + stable domain identity is required, while revision reset on a newly serialized + `.blend` is accepted and recorded. +- OOM uses the token-isolated `GPU_GEOMETRY_UPLOAD` fault point. A tracked texture + lease is released before close; the matching error is + `GPU_GEOMETRY_BUDGET_EXCEEDED`, with zero live temporary resources. +- GPU release disposes geometry, material and renderer once, then creates a fresh + WebGL2 renderer and requires non-zero pixels for the same domain probe. +- Small-scene recovery requires the reopened data IDs, identity hash, object count + and visible pixels to match the committed baseline. + +## Rollback + +Remove the M9-14 protocol/runner/tests/golden and package script together. Existing +Curve, Grease Pencil and Paint writers remain unchanged; no persisted schema or +release metadata migration is required. diff --git a/docs/status/N-012.md b/docs/status/N-012.md index a65f9b50..60ac6c01 100644 --- a/docs/status/N-012.md +++ b/docs/status/N-012.md @@ -7,14 +7,46 @@ 本轮已交付版本化 `GeometryNodeGraphIR`、node/socket/link/domain 校验、支持节点白名单、 循环依赖和自递归 group 检测。Object/Collection/Image Info 资源现在按 SceneIR stable ID、 -资源类型、owner cycle 和 linked/missing/corrupt 沙箱校验。合法图仍只通过协议门,native -lazy-function evaluator 未启用时返回 `CAPABILITY_MISSING`。 +资源类型、owner cycle 和 linked/missing/corrupt 沙箱校验。保存于 Blender Main 的 allowlist +图已启用有界 native evaluator;任意图写回仍返回 `CAPABILITY_MISSING`。 -`SimulationCacheManifestIR` 已绑定 graph/source blend/input hash、Blender 5.2、完整帧范围、 -连续 byte range、总 payload 和逐帧 SHA-256。StorageWorker schema 6 使用 OPFS 内容寻址资产 -和 `simulation_manifest` 索引,写入前核对当前 committed blend,Worker 重启后重新逐帧验证; -缺失、版本不符和损坏均结构化阻断。 +M10-01 已把真实 Blender Main 图拓扑、socket 默认值、link 和稳定 node/socket ID 发布到 +SceneIR;M10-02 将 16 项 allowlist 固定为 schema 1。M10-03 用 11 个 Blender 5.2 desktop +fixture 覆盖全部 16 项节点,并在 Node full-Main WASM 与 Chromium Worker 打开、求值、保存、 +重开路径逐项比较 topology、position、bounds 和 point float attribute。当前误差为 0,门限为 +position max `1e-5`/RMS `1e-6`、attribute `1e-6`、bounds `1e-5`。真实 Simulation Zone 节点 +仍返回 `GN_NODE_UNSUPPORTED` 并保留原图、graph hash 和 Main revision。 -仍未声明:浏览器生成 bake、WASM Simulation Zone 求值、cache frame seek 接入 GN modifier、 -任意 lazy-function/field/instance graph。验收:`npm --prefix web run test:simulation-cache`、 -`npm --prefix web run test:capability-gates`。 +M10-04 已冻结 field materialization schema 1:POINT/EDGE/FACE/CORNER/CURVE/INSTANCE/LAYER +分别具有元素上限,单批最多 64 fields、32 次 domain conversion、4,000,000 个目标元素和 +64 MiB;单 field 超过 65,536 scalar 时禁止 JSON transport。Depsgraph 的真实 point Float +attribute 回执携带 schema、domain cardinality、元素/scalar/byte 数并与 payload 交叉校验; +基数漂移、字节漂移、隐藏 JSON 数组和未知 domain 均 fail-closed。 + +M10-05 将 `SimulationCacheManifestIR` 升为 schema 2,加入 committed `sourceRevision` 和可由 +graph ID/hash、source blend hash、source revision、input hash、Blender 版本及 frame range +复算的完整 `revisionHash`。cache key 不再截取 hash 前缀,而是 `sim2-` 加完整 revision hash; +Storage Worker 的 put/read/frame/list 均在 project lock 内核对当前 committed revision/hash,旧 +revision 只保留在存储中、不进入当前列表且显式读取返回 `SIMULATION_CACHE_REVISION_MISMATCH`。 +manifest/frame 未声明字段、legacy schema、graph/source/input/range 漂移均 fail-closed。 + +`SimulationCacheManifestIR` 已绑定 graph/source blend/input/revision hash、Blender 5.2、完整帧范围、 +连续 byte range、总 payload 和逐帧 SHA-256。StorageWorker schema 7 使用 OPFS 内容寻址资产、 +`simulation_manifest` 索引和 `simulation_quarantine` 隔离 store,写入前核对当前 committed blend。 +每个 Worker 生命周期必须先完整复验 cache 才允许逐帧播放;播放/写入支持 AbortSignal,取消不发布 +manifest;LRU 按 `lastAccessAt/createdAt/cacheKey` 确定性淘汰并保护 active playback;Worker 重启 +重新验证,manifest 或 payload 损坏会从可播放索引移入 quarantine,列表仍返回其他有效 cache。 + +M10-15 为 GN 和 Simulation 分别增加隔离 Chromium Worker 门:GN 以 10 次 512-node +parse/validate、超 node budget 和 dependency cycle 覆盖性能/OOM-prevention/恶意输入;Simulation +以 64 帧逐帧 hash、16 GiB+1 manifest 和未声明字段覆盖同三类门。两域均在失败后由同 Worker +完成小输入恢复。 + +仍未声明:任意 field/domain 实际求值、完整 lazy-function runtime、图写回、浏览器生成 bake、 +WASM Simulation Zone 求值、cache frame seek 接入 GN modifier 和任意实例图。验收: +`npm --prefix web run test:geometry-node-evaluator-golden`、 +`npm --prefix web run test:geometry-node-field-budget`、`npm --prefix web run +test:simulation-cache-identity`、`npm --prefix web run test:simulation-cache`、 +`npm --prefix web run test:simulation-cache-performance`、`npm --prefix web run +test:simulation-cache-lifecycle`、`npm --prefix web run test:capability-gates`、 +`npm --prefix web run test:m10-domain-gates`。 diff --git a/docs/status/N-013.md b/docs/status/N-013.md index a48c8c18..6c4b5e06 100644 --- a/docs/status/N-013.md +++ b/docs/status/N-013.md @@ -1,6 +1,6 @@ # N-013 Shader Node 图 -状态:`in_progress(七节点 Main + 常量 PBR 映射子集)` +状态:`in_progress(七节点 Main + 有界 WebGL2 compiler 子集)` 详细分解、ShaderIR、typed socket 校验、Main 写回、Web 编译、GLB 映射和验收命令见 [`docs/UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md`](../UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md)。 @@ -16,6 +16,19 @@ operation 保存重开;其他运算继续结构化阻断。RGB/Value 到 Princ Roughness、Metallic 的有限常量链接现可确定性映射为 glTF PBR factor;缺值、越界、 重复输入和未知链接继续返回 `SHADER_GRAPH_UNMAPPABLE`。Mix、Mapping、Texture Coordinate、Bump 及任意节点仍返回 `SHADER_NODE_UNSUPPORTED`。node properties 仅开放 -Math operation;受限 Web 编译、完整色彩管理、完整 GLB 图映射和桌面渲染 golden 仍为 planned。 -验收:`npm --prefix web run test:authoring-roundtrip`、`npm --prefix web run test:capability-gates` +Math operation。M10-07 新增 schema 1 有界 compiler:只把 RGB/Value 常量、六项 Math、Image +Texture、Normal Map、Principled 和 Output 编译为 `WEBGL2_THREE_PHYSICAL` report;Native reader +发布 graph SHA-256,Worker 在 Main transaction 前编译并失败关闭,主线程和 Offscreen 复用同一 +`createPBRMaterial` 入口。M10-08 为 report 增加 graph/texture/color-space/backend 绑定的 +`compileKey`,M10-09 由 `PBRMaterialPipeline` 保留上一份可用材质并记录失败报告。节点/链接/ +深度/纹理/标识预算固定;M10-10 将任意未知节点名称归一化排序后返回稳定 +`PBR-012/ARBITRARY_SHADER` capability block,未知节点不被删除或静默降级。 + +M10-15 在独立 Chromium Worker 中连续完成 100 次有界 compile、129-node 超预算阻断、恶意 +cycle 阻断和同会话小图恢复;performance/OOM-prevention/malicious-input 三门均使用现有 compiler, +不以 mock 成功替代材质编译。 + +完整色彩管理、sampler/alpha/tangent、WebGPU、完整 GLB 图映射和桌面渲染 golden 仍为 planned。验收: +`npm --prefix web run test:shader-compile`、`npm --prefix web run test:authoring-roundtrip`、 +`npm --prefix web run test:capability-gates`、`npm --prefix web run test:m10-domain-gates` 以及 Shader GLB mapping E2E。 diff --git a/docs/status/N-014.md b/docs/status/N-014.md index 3a24e972..a3e3faa1 100644 --- a/docs/status/N-014.md +++ b/docs/status/N-014.md @@ -13,8 +13,21 @@ Blender `NLASTRIP_FLAG_REVERSE`,reader 可恢复该标志;反向正例得到 `[0.555556, 1.111111, 1.666667]`。 同一正例已扩为 `repeat=2`,frame 5 与第二周期 frame 14 的 native Depsgraph 矩阵一致。 +M10-11 另以桌面 Blender 5.2 生成只读 fixture:同一 Track 包含 scale=2 的正向 Clip 和 +reverse + repeat=2 的 Clip。桌面、Node WASM 与生产 Chromium Worker 在 12 个边界/周期帧上 +逐矩阵比较,最大误差为 0;每帧求值前后的 Track/Strip/Action JSON 完全一致。本切片不调用 +`setNLAStack`,不提前关闭细粒度 operator 工作。 + 当前只支持单对象 Action Clip、正 scale、Replace/Add/Multiply/Combine 和有限外插。 -Transition/Meta/Sound、Animated Time、Drivers、细粒度拖动/resize 命令、NLA UI、 +M10-12 仅新增 `moveNLAStrip`,以 revision gate 和单次 Main transaction 保留时长移动现有 Clip, +并已通过 undo/redo 与保存重开。Transition/Meta/Sound、Animated Time、Drivers、其余 +create/remove/resize/active 命令、NLA UI、 骨骼/约束混合矩阵及 GLB 展开仍为 planned/结构化阻断。 -验收:`npm --prefix web run test:authoring-roundtrip`、`npm --prefix web run test:capability-gates`。 +M10-15 冻结 4,096 tracks、每 track 16,384 strips、总 65,536 strips 及 ID/name/reason 字节预算, +拒绝未声明字段。隔离 Chromium Worker 对 20 次 256-strip stack、超 track budget、恶意字段和 +同会话小 stack 恢复分别建立 performance/OOM-prevention/malicious-input 门。 + +验收:`npm --prefix web run test:nla-evaluation-golden`、`npm --prefix web run test:nla-operator`、 +`npm --prefix web run test:authoring-roundtrip`、 +`npm --prefix web run test:capability-gates`、`npm --prefix web run test:m10-domain-gates`。 diff --git a/docs/status/N-015.md b/docs/status/N-015.md index 4ebbc00c..f45775a1 100644 --- a/docs/status/N-015.md +++ b/docs/status/N-015.md @@ -1,9 +1,9 @@ # N-015 非 Mesh 几何数据块 -状态:`BLOCKED / in_progress`(VDB core 已覆盖 server、OPFS、Float32 WebGPU、显式 GPU 分页、 -有限 grid 材质与 Main 属性重开;自动 demand paging 缺页重绘、联合重开和发布 golden 仍阻断) +状态:`BLOCKED / in_progress`(M8 自动 demand paging、联合重开、64 MiB sparse 门和三轴 density +golden 已完成;深度合成、完整 Volume 材质/GLB/USD 与 512 MiB/1 GiB 联合故障矩阵仍阻断) -更新时间:2026-08-14 +更新时间:2026-08-16 ## 当前声明 @@ -14,9 +14,10 @@ topology editor。 Volume 采用“桌面/服务端 OpenVDB 转 NanoVDB,浏览器分块读取并以 WebGPU 渲染”的固定架构。 浏览器不直接解码 OpenVDB;当前已完成真实资源 catalog、独立 OpenVDB 13 -> NanoVDB 32 desktop converter、受控 server job、转换/清单/分块协议、OPFS 绑定/重开、Float32 CPU/WGSL 树遍历、 -有界体积积分、主线程/Offscreen 生产视口、显式 hash 校验 GPU 分页/LRU、有限 grid 材质和 -Volume Main 属性保存重开。自动缺页重绘、联合重开和发布 golden 尚未完成,因此 N-015 整体保持 -`BLOCKED`。 +有界体积积分、主线程/Offscreen 生产视口、显式 hash 校验 GPU 分页/LRU、有限 grid 材质、 +Volume Main/asset binding 联合重开、自动缺页重绘、64 MiB sparse 门和 OpenVDB/WebGPU 三轴 +density golden。深度合成、完整 Volume 材质/GLB/USD 与更大真实 bundle 联合故障矩阵尚未完成, +因此 N-015 整体保持 `BLOCKED`。 | 数据族 | Reader | Web 预览 | 当前门 | | --- | --- | --- | --- | @@ -26,7 +27,7 @@ Volume Main 属性保存重开。自动缺页重绘、联合重开和发布 gold | Metaball | 元素类型、位置、半径、尺度 | bounded sphere proxy;depsgraph implicit mesh | `READY` | | PointCloud | position/radius/POINT 属性 | WNM 分块 Points | `READY`(空数据为 `summary-only`) | | Curves/Hair | position/radius/curve/POINT 属性 | WNM 分块 Points/curve line | `READY`(缺数据阻断) | -| Volume | path/grid metadata + VDB/NanoVDB 转换契约 | WebGPU 可渲染真实 density/color/temperature/emission;双生产视口已接入 | `BLOCKED`(自动缺页/联合恢复/golden) | +| Volume | path/grid metadata + VDB/NanoVDB 转换契约 | WebGPU 可渲染真实 density/color/temperature/emission;自动分页、联合重开与三轴 density golden 已接入 | `BLOCKED`(深度合成/完整材质与 IO/更大联合故障矩阵) | ## 已实现任务 @@ -104,6 +105,16 @@ Volume Main 属性保存重开。自动缺页重绘、联合重开和发布 gold 20. GPU grid 支持初始空驻留、经 manifest chunk hash 校验的显式单页加载、页表间接寻址、确定性 LRU 替换和设备重建后按需页重放;多材质 grid 按同一 manifest `maxResidentBytes` 分配页槽, Chromium 以 GPU 数据读取验证被触碰页保留、旧页淘汰和恢复后内容一致。 +21. GPU feedback buffer、revision gate、manifest range/hash page request、同页请求合并、frame pin + LRU、渐进重绘预算、网络精确 offset 续传、Worker restart 和 device-loss 可见页回放已形成自动 + demand paging 最小闭环;hash 失败、stale feedback、取消和 OOM 均不污染 resident/page table。 +22. 64 MiB sparse HTTP bundle 以 4 MiB page 实际 range 读取,首 page、总耗时、峰值工作集和取消 + 延迟均有 Chromium 证据;取消后 coordinator/consumer 归零,未将逻辑 64 MiB fixture 写入仓库。 +23. 同一保存 hash 下,Volume Main 属性、OPFS project revision、NanoVDB binding 和 bundle/grid bytes + 在 Worker 清空后联合重开;主线程和 Offscreen 两个生产视口都从恢复 asset 输出非空像素。 +24. OpenVDB 13 native `density` reader 按 `volume-wgsl-v1` 有界积分生成 X/Y/Z 三轴 64x64 RGBA8 + desktop/Chromium 发布 golden;主线程与 Offscreen WebGPU 对 49,152 bytes 的最大通道误差、MAE、RMS 和 alpha 覆盖 + 差均为 0。门限仍固定为 2/0.1/0.5/0.002,漂移返回 `NANOVDB_GOLDEN_MISMATCH`。 ## 后续分解 @@ -117,9 +128,9 @@ Volume Main 属性保存重开。自动缺页重绘、联合重开和发布 gold raycast、多 handle preview、handle-local gizmo 与单次 Main 写回已完成。 4. N-015-D2:evaluated preview 与源控制笼分层显示、WebGPU 等价。 5. N-015-B3/D2/E1(Volume):真实资源、desktop/server converter、可续传 HTTP range、OPFS、 - 有界网络/Worker/quota/tamper/device-loss 故障门、Float32 WGSL core、有限 grid 材质、显式 GPU - 分页/LRU、resident/page-table OOM 恢复和 Main Volume 属性重开已完成;继续执行自动缺页重绘、联合恢复、GLB/USD loss、大 bundle 和 - desktop/Chromium golden,未完成项保持阻断。 + 有界网络/Worker/quota/tamper/device-loss 故障门、Float32 WGSL core、自动 GPU paging/LRU、 + resident/page-table OOM、Main/binding/双视口联合重开、64 MiB sparse 和三轴 density golden 已完成; + 继续执行场景深度合成、完整 Principled Volume/GLB/USD loss 与 512 MiB/1 GiB 联合故障矩阵。 6. N-015-E2:100k/1M WASM/GPU 内存、Worker restart 和 Chromium OPFS quota;Chromium 64 KiB 真实 quota、失败后旧 revision 保持、Worker restart 恢复已通过。后续浏览器验收 仅以 Chromium 为基线,不配置 Firefox/WebKit。 @@ -131,7 +142,8 @@ Volume Main 属性保存重开。自动缺页重绘、联合重开和发布 gold - Metaball sphere proxy 不得用于导出或保存为求值曲面。 - Volume/VDB 不得生成占位几何;协议 fixture 不得作为真实 decoder/renderer 证据。 - 浏览器构建不得通过开启 `WITH_OPENVDB` 绕过 desktop/server 转换边界。 -- 自动缺页重绘、联合恢复、完整故障门和发布 golden 未齐前,VDB 与 N-015 保持 `BLOCKED`。 +- 场景深度合成、完整 Volume 材质/IO 和更大真实 bundle 联合故障矩阵未齐前,VDB 与 N-015 保持 + `BLOCKED`;M8 完成不能自动把 N-015 全域标为 `COMPLETE`。 - Metaball sphere proxy 不得进入 GLB;GLB 只能消费 depsgraph evaluated mesh。 - 任意新增 Main authoring 命令在 save/reopen 与 undo/redo 完成前不得开放 UI。 @@ -161,6 +173,10 @@ npm --prefix web run test:vdb-opfs npm --prefix web run test:vdb-webgpu npm --prefix web run test:vdb-viewport npm --prefix web run test:vdb-faults +npm --prefix web run test:nanovdb-sparse-performance +npm --prefix web run test:nanovdb-volume-roundtrip +npm --prefix web run test:nanovdb-render-golden +node tools/vdb/generate-volume-golden.mjs --check WEB_TEST_PORT=5349 npm --prefix web run test:e2e -- -g "real OPFS quota|reports quota exhaustion" WEB_TEST_PORT=5201 npm --prefix web run test:e2e -- -g "N-015|non-mesh" ``` diff --git a/docs/status/N-016.md b/docs/status/N-016.md index 3908db02..eef099b5 100644 --- a/docs/status/N-016.md +++ b/docs/status/N-016.md @@ -1,7 +1,8 @@ # N-016 Grease Pencil -状态:`BLOCKED`(协议、有限 reader/Main transaction、当前帧及相邻帧 onion preview 已落地; -完整 2D canvas/Dope 编辑、modifier 语义和桌面 golden 仍阻断) +状态:`BLOCKED`(协议、有限 reader/Main transaction、当前帧及相邻帧 onion preview、 +current-drawing marquee 和有限 2D point canvas 已落地;完整 2D stroke/segment/Dope 编辑、 +modifier 语义和桌面 golden 仍阻断) ## 已验证切片 @@ -22,8 +23,8 @@ drawing,渲染真实 3D stroke/闭环;Chromium 主线程与 OffscreenCanvas 均通过非空像素门。 6. N-016-D(editor 部分):editor context schema 绑定 data/layer/frame、onion 开关、stroke/ point selection 和 revision,拒绝 stale/重复/超 1M selection;Properties 面板已连接真实 Main - layer create/remove、当前 frame insert/remove 和整帧 clear transaction。完整 2D stroke/point - 画布选择和 dope sheet integration 仍阻断。 + layer create/remove、当前 frame insert/remove 和整帧 clear transaction。完整 2D stroke/segment、 + lasso 和 dope sheet integration 仍阻断。 7. N-016-D(点编辑部分):Properties 面板可在当前 layer/frame 选择实际 stroke/point,执行 有界 X 轴平移;协议先校验 point identity、revision、有限坐标和 1M 点预算,再保留 radius、 opacity、vertex color、cyclic、material index 并合并为一个 `setGreasePencilStrokes` Main @@ -40,11 +41,29 @@ 11. N-016-D(重启部分):真实 Grease Pencil fixture 经首个 WebEngine Worker 点编辑和 `.blend` 保存后,终止 Worker 并由全新 Worker 重开;data/layer/drawing identity、位置、radius、opacity 均重新解析验证。 +12. N-016-D(M9-06 marquee):Main reader 为 drawing、stroke 和 point 发布稳定 ID,并发布 Blender + active layer;旧文件未记录 active layer 时只读快照确定性选择首个实际 layer,不修改 Main。 + 纯 marquee 合同绑定 Main revision、当前 data/layer/frame/drawing、归一化矩形和 1M candidate + 预算,跨 drawing、重复/伪造 ID、stale revision 和超预算均稳定失败关闭。生产 UI 只在活动、 + 可见且未锁定 drawing 开放框选,主线程与 OffscreenCanvas 共用同一合同;真实 `.blend` 均选中 + 4 个稳定 point ID 和 1 个 stroke ID,选择不递增 Main revision。 +13. N-016-D(M9-07 shared selection):纯 `grease-pencil-selection` schema 将当前 drawing、稳定 + point ID、来源和独立 selection revision 绑定为唯一状态;2D point canvas、主线程 3D viewport + 和 OffscreenCanvas Worker 的 point/marquee 事件都携带 base selection revision,迟到结果返回 + `REVISION_CONFLICT`,不修改 Main。真实 fixture 从 2D 选择 1 点后由 3D 框选 4 点,revision + `0 -> 1 -> 2`,两条生产后端回执与画布高亮一致,Main revision 保持不变。 +14. N-016-D(M9-08 layer/frame reorder):纯 `grease-pencil-reorder` schema 将 layer 方向移动和 + drawing frame 移动绑定到稳定 data/layer/drawing ID 与 Main base revision;stale revision、 + 边界 no-op、伪造 drawing、已占用 target frame 和正负 1,000,000 之外帧号均在 Main 前失败关闭。 + 生产 Properties UI 的 layer 上下移动与 frame target 移动各只提交一次 Main transaction;真实 + fixture 经 undo/redo、OPFS save、close/recover 后保持 Blender 5.2 desktop golden 的 layer 顺序、 + frame 12 和原 drawing identity。完整 group hierarchy、frame duplicate 与 Dope Sheet 拖拽仍阻断。 ## 仍然阻断 - N-016-C:material datablock 事务、onion fade/range 完整语义和完整 Grease Pencil modifier reader。 -- N-016-D:完整 2D canvas/marquee 和完整 timeline/dope 编辑;3D current-frame 点 raycast、 +- N-016-D:完整 2D canvas 的 stroke/segment 选择、套索和完整 timeline/dope 编辑仍阻断;有限 + 2D point canvas 与 3D 共享 selection revision、3D current-drawing point marquee、点 raycast、 多点高亮、连续 preview、单次 gizmo commit、有界 drawing frame 导航和 worker restart 已完成。 - N-016-E:desktop drawing hash、Chromium 像素 golden、GLB/USD loss report 和 OPFS。 @@ -54,6 +73,9 @@ npm --prefix web run typecheck npm --prefix web run lint npm --prefix web run test:grease-pencil +WEB_TEST_PORT=5404 npm --prefix web run test:grease-pencil-marquee +WEB_TEST_PORT=5411 npm --prefix web run test:grease-pencil-selection +WEB_TEST_PORT=5416 npm --prefix web run test:grease-pencil-reorder WEB_TEST_PORT=5205 npm --prefix web run test:grease-pencil-editor WEB_TEST_PORT=5202 npm --prefix web run test:e2e -- --grep "N-016 Grease Pencil" ``` diff --git a/docs/status/N-017.md b/docs/status/N-017.md index ef248cd8..fef8045f 100644 --- a/docs/status/N-017.md +++ b/docs/status/N-017.md @@ -1,7 +1,9 @@ # N-017 Paint 与权重 -状态:`BLOCKED`(stroke/patch schema、真实 Three raycast 命中、选择/遮罩门和 Main 顶点色/权重 -transaction 已落地;PBVH brush、texture paint 和 GPU/image 生命周期未实现) +状态:`BLOCKED`(stroke/patch schema、真实 Three raycast 命中、GPU depth 可见性、选择/遮罩门、 +单 undo 分块 pointer session、Main 顶点色/权重、浏览器 packed/UDIM 原子资产绑定和 +PBVH WASM 入口显式能力门已落地; +PBVH brush、Blender Main image tile 写回、完整色彩转换和 GPU/image 生命周期仍未实现) ## 已验证切片 @@ -12,9 +14,11 @@ transaction 已落地;PBVH brush、texture paint 和 GPU/image 生命周期未 tile 256 MiB;非法 barycentric、pressure、color、weight 和非有限数会结构化拒绝。 4. N-017-B(部分):`setVertexColors` 在单用户 Mesh 上创建/转换 `POINT` 或 `CORNER` `FLOAT_COLOR` 属性,并更新 active color;`setVertexWeights` 在真实 Object/Mesh 上创建顶点组、 - 写入/移除 `MDeformWeight`,支持逐顶点 normalize。 -5. 顶点色和权重都已通过 Main undo/redo 与 `.blend` save/reopen;`mirror:true` 在没有已验证 - 对称拓扑映射时返回 `CAPABILITY_MISSING`,不会伪造镜像结果。 + 写入/移除 `MDeformWeight`,支持逐顶点 normalize、最多 32 个 influence 的 limit,以及 + 基于局部坐标 reciprocal map 的 mirror。 +5. 顶点色和权重都已通过 Main undo/redo 与 `.blend` save/reopen;镜像在没有已验证对称映射时 + 返回 `CAPABILITY_MISSING`,不会伪造结果。limit/normalize/mirror 的组合已与 Blender 5.2 + desktop golden 逐顶点比较。 6. `paintHitFromIntersection` 消费真实 Three `Raycaster` intersection,按 indexed/non-indexed triangle 解析顶点,输出对象/data stable ID、source face、局部 barycentric、世界法线、 插值 UV 与 pressure;测试使用真实 BufferGeometry 射线,不注入伪命中。 @@ -24,7 +28,7 @@ transaction 已落地;PBVH brush、texture paint 和 GPU/image 生命周期未 8. N-017-A/C(协议边界):有界 CPU brush 对候选顶点执行 smoothstep falloff、遮挡标记过滤和 front-face 法线门;它不宣称 PBVH 加速或桌面 brush 等价。`UdimTilePatchIR` 约束 1001-1999 tile、RGBA8、色彩空间、尺寸/256 MiB 预算、revision、base/result SHA-256,并在内存中验证 - 原子 range patch;尚未绑定 Blender packed image/UDIM Main 写回与保存。 + 原子 range patch。 9. N-017-A(空间查询部分):uniform-grid spatial index 在 1M vertex/1M cell 预算内按 brush AABB 粗筛候选,再执行球半径、front-face、falloff 精筛;depth-gated 查询必须显式提供已验证可见 vertex ID 集合,缺失或重复会拒绝。该结构减少全量扫描,但不是 Blender PBVH。 @@ -34,14 +38,35 @@ transaction 已落地;PBVH brush、texture paint 和 GPU/image 生命周期未 11. Properties paint 面板可对当前真实 VERT selection 执行 selection-masked `Blend Color` 和 `Blend Weight`,每次仅提交一个 Main transaction。Blender 5.2 reader 从 Mesh `vertex_group_names` 恢复顶点组摘要及 skin weight 名称;新增组经 undo/redo、保存重开后保持。 +12. M9-09:主线程和 Offscreen Worker 使用同一 WebGL2 RGBA depth pass/readback, + 以 Main revision 和稳定顶点索引返回真实可见集;遮挡 fixture 的两后端结果一致。 +13. M9-10:WebEngine Worker 可序列化接收有界 color/weight chunks,一次 pointer + session 仅在 commit 时对 Main 发一个合并命令;真实 Main 历史只产生一个 undo step。 +14. M9-11:Storage Worker 将 packed/UDIM PNG 解码为 RGBA8,复验 dirty range 的 + base/result pixel hash,新 PNG 经 OPFS content-addressed 临时写、回读验证后, + 才以 IndexedDB 单事务切换 tile binding。中途故障保持旧 binding/旧资产可读, + packed 与 UDIM 均已通过 Worker restart 回读。 +15. M9-12:`WeightPaintOptionsIR` 固定 normalize、limit、mirror axis/tolerance 和 1M + patch budget;Worker 拒绝重复/越界/非法组合,native 在 Main 内先写入 patch,再做 + mirror、limit、normalize。镜像 map 使用 mesh 局部坐标,要求每个顶点都有 reciprocal + counterpart,且大于 100k 顶点时结构化预算阻断。`rigged_shape_scene.blend` 的四步 + golden(initial/normalize/limit-normalize/mirror)与 WASM snapshot、save/reopen 逐项一致。 +16. M9-13:按 Blender 5.2 `DNA_brush_enums.h` 冻结 32 个 Sculpt、4 个 Vertex Color、 + 4 个 Weight 和 6 个 Texture 活跃 brush。生产 WebEngine Worker 探测 + `_web_engine_apply_pbvh_stroke`;当前 stable/single/pthread 均无该 WASM 入口,因此 46 项 + 全部返回 `N-017/BLOCKED/PAINT_PBVH_UNAVAILABLE`。即使未来出现入口,session context 和 + 逐 brush desktop/WASM golden 未就绪时仍分别失败关闭;查询不会修改 Main revision、handle + 或 WASM allocated bytes,畸形字段返回完整标准 `ErrorReport`。 ## 仍然阻断 -- N-017-A:真实 PBVH、由 viewport 深度缓冲生成可见 vertex 集合,以及 brush falloff 桌面 - golden;基础 Mesh/UV hit、uniform-grid 候选查询和可见性硬门已完成。 -- N-017-B:limit/clean、已验证拓扑映射上的 mirror、桌面 brush/falloff 对照。 -- N-017-C:Blender packed/UDIM tile Main transaction、dirty tile、色彩转换和原子保存;当前 - 只有内容哈希绑定的浏览器内存 patch 边界。 +- N-017-A:真实 PBVH 与 brush falloff 桌面 golden;基础 Mesh/UV hit、uniform-grid 候选 + 查询、可见性硬门和双后端 GPU depth 可见集已完成。当前不存在 PBVH WASM writer,完整 + brush inventory 由 `PAINT_PBVH_UNAVAILABLE` 明确阻断,不会把 bounded helper 当作成功。 +- N-017-B:clean/高级 Blender weight-paint operator、真实 PBVH brush/falloff 与更大拓扑的 + 桌面对照;当前 bounded limit/normalize/verified mirror 已完成。 +- N-017-C:Blender packed/UDIM tile Main transaction、完整色彩转换和 `.blend` 内 image + tile 保存仍阻断;浏览器 dirty tile 的 OPFS 原子 asset/binding 边界已完成。 - N-017-D/E:armature golden、seam bleed、face mask、GPU dispose、quota、坏图和桌面 UI 对照; vertex selection 与数值 mask 门已完成,不等同于完整 Paint 面/纹理遮罩系统。 @@ -50,7 +75,9 @@ transaction 已落地;PBVH brush、texture paint 和 GPU/image 生命周期未 ```bash WEB_TEST_PORT=5203 npm --prefix web run test:e2e -- --grep "N-017 paint" npm --prefix web run test:paint-roundtrip +WEB_TEST_PORT=5422 npm --prefix web run test:texture-paint-asset +WEB_TEST_PORT=5427 npm --prefix web run test:paint-pbvh-capability ``` -`test:paint-roundtrip` 覆盖 CORNER color、vertex group patch、normalize、mirror 错误门、 -undo/redo 与 save/reopen。 +`test:paint-roundtrip` 覆盖 CORNER color、vertex group patch、normalize、limit、verified +mirror、undo/redo 与 save/reopen;`test:weight-paint-golden` 对比 Blender 5.2 desktop golden。 diff --git a/docs/status/N-018.md b/docs/status/N-018.md index 1be17431..a6fe9a72 100644 --- a/docs/status/N-018.md +++ b/docs/status/N-018.md @@ -1,7 +1,7 @@ # N-018 Physics 与 Simulation -状态:`BLOCKED`(family capability、settings/dependency/cache manifest、错误帧门及浏览器自有 -BTF1 播放会话已落地;desktop bake playback、WASM solver 和 bake job 未实现) +状态:`BLOCKED`(family capability、settings/dependency/cache manifest、错误帧门、浏览器自有 +BTF1 播放会话及逐 family solver probe 已落地;desktop bake playback、WASM solver 和 bake job 未实现) ## 已验证切片 @@ -34,13 +34,19 @@ BTF1 播放会话已落地;desktop bake playback、WASM solver 和 bake job 10. N-018-C(持久缓存集成):两帧真实 BTF1 经过 content-addressed Simulation cache 写入,终止 Storage Worker 后由新 Worker 精确 range 读取第二帧并应用到 SceneIR;帧 hash、magic、offset 和最终 object translation 均验证。 +11. N-018-D(M10-13 probe 门):七个 family 分别检查 runtime export、初始化、线程和内存;只有 + 四门全过才允许 `LOCAL_SOLVER`。当前生产 inventory 没有 solver adapter,全部明确路由到 + `DESKTOP_SERVER_BAKE`。合成正例只验证门逻辑,不声明真实 WASM solver 或 cache decoder。 +12. N-018-C/D(M10-14 cache 门):七类 desktop/server bake cache 均绑定 cache schema、family、 + Blender 5.2、source/settings/input/cache hash、frame range、总/逐帧 byte range/hash;消费前对 + 实际 source、payload 和逐帧 bytes 复算 SHA-256。decoder/playback capability 仍不开放。 ## 仍然阻断 - N-018-A/B:从真实 Main 提取完整 family settings、collection/effector/collision 依赖。 - N-018-C:desktop bake payload 的逐 family decoder、正式 viewport UI 播放控制和 100 帧 golden;浏览器自有 transform frame decoder/SceneIR 播放会话已完成但不替代 Blender bake。 -- N-018-D:逐 family WASM solver 初始化、线程、内存和确定性验证。 +- N-018-D:逐 family 真实 WASM solver adapter、初始化实现和确定性 golden;探测/路由门已完成。 - N-018-E:bake start/cancel/commit、服务端 job、故障恢复、进度与 UI。 ## 验收 @@ -49,8 +55,10 @@ BTF1 播放会话已落地;desktop bake playback、WASM solver 和 bake job WEB_TEST_PORT=5204 npm --prefix web run test:e2e -- --grep "N-018 physics" npm --prefix web run test:simulation-cache npm --prefix web run test:physics-main-reader +npm --prefix web run test:physics-solver-probe +npm --prefix web run test:physics-cache-family ``` 本轮在 Chromium 隔离端口复验 content-addressed cache 的 Worker restart、目标帧 range read、 -帧 SHA-256、BTF1 seek/play/cancel 和迟到结果抑制通过;没有 desktop bake family decoder 或 -solver,因此 desktop playback/solver/bake 状态不变,继续为 `BLOCKED`。 +帧 SHA-256、BTF1 seek/play/cancel、迟到结果抑制和逐 family solver probe 通过;没有 desktop +bake family decoder 或真实 solver,因此 desktop playback/solver/bake 状态不变,继续为 `BLOCKED`。 diff --git a/docs/status/N-019.md b/docs/status/N-019.md index e7d17fd4..d9c6c585 100644 --- a/docs/status/N-019.md +++ b/docs/status/N-019.md @@ -1,7 +1,8 @@ # N-019 灯光与渲染 -状态:`BLOCKED`(Camera/Light/World/Scene metadata、Camera/Light/World 有界 Main 写回与 -Three exposure/shadow 映射已落地;Scene 颜色管理 writer 和渲染等价未实现) +状态:`BLOCKED`(Camera/Light/World/Scene metadata、Camera/Light/World 有界 Main 写回、 +Three exposure/shadow 映射和一条有界 Blender reference 图像指标闭环已落地;Scene 颜色管理 +writer、全域渲染等价和高级后端仍未实现) ## 已验证切片 @@ -24,6 +25,30 @@ Three exposure/shadow 映射已落地;Scene 颜色管理 writer 和渲染等 7. N-019-B(色温部分):`useTemperature:true` 时,共享 PBR 适配器把 800–20000 K 的有界 黑体近似归一到 6500 K 中性白并转换为线性 RGB,再乘入 Light color;关闭色温时原始颜色 保持不变。主线程与 Offscreen Worker 共用该实现,数值门和原生 temperature 保存重开通过。 +8. M11-01 字段表:CameraIR、LightIR、WorldIR 及 Scene render/color-management 共 60 个叶字段 + 已逐项标注 reader/writer/main viewport/Offscreen viewport 状态;AST checker 保证零漏项、零重复。 + 当前 24 COMPLETE、17 PARTIAL、19 BLOCKED,未把 metadata-only 或近似映射提升为视觉 parity。 +9. M11-02 字段闭环:支持的 Camera/Light/World writer 已在同一 Chromium 任务中完成 Main edit、 + revision 单调的 undo/redo、保存、全新 Worker 重开和共享 PBR 映射;水平/垂直 sensor fit、 + temperature linear RGB、shadow、spot/area 参数及 World 背景均有字段级断言。 +10. M11-03 资源预算:主线程与 Offscreen 共用 schema 1 light/shadow/texture planner;WebGL2 + 总预算为 16 lights、4 x 1024 shadow maps(均包含内置保留槽),纹理同时限制数量、单边、 + aggregate payload 与 decoded RGBA bytes。WebGPU 只冻结 64/8 x 2048 及 device-limit 下调合同, + 未把未安装的 WebGPU renderer 标为可用。 +11. M11-04 有界实时 reference:Blender 5.2 Eevee 256×256 fixture 绑定 source/generator/image + SHA-256;SRGB8/STRAIGHT RGBA 比较输出 MAE、RMS、P95 channel、坏像素比例、前景 IoU 和 + alpha 覆盖率。主线程与 Offscreen 生产 viewport 均为同一报告(MAE 2.0433、RMS 5.4490、 + P95 4、坏像素 0.004776、IoU 0.98534);非空但错误构图负例稳定返回 + `RENDER_REFERENCE_MISMATCH`。 +12. M11-05 最终渲染路由:schema 1 只让 bounded Eevee 的 WebGL2(以及 capability 全通过的 + WebGPU)进入 `WEB_LOCAL_BOUNDED`;Cycles、complex Eevee、Workbench 与 CUDA/OptiX/HIP/ + Metal/oneAPI 固定为 `SERVER_JOB`。默认 endpoint 未配置时返回 `SERVER_JOB_UNAVAILABLE`, + 未知 SceneIR render engine 返回 `PLATFORM_CAPABILITY_UNAVAILABLE`,均不产生本地等价成功。 +13. M11-06 server render job:schema 1 请求绑定 source `.blend` SHA-256/byte length/revision、 + Blender 5.2 build SHA-256、规范化 render settings SHA-256 和 request SHA-256;成功结果 + 绑定同一 provenance 并公开 output MIME/byte length/output SHA-256/result SHA-256。真实 + loopback server 由 Blender 5.2 headless 打开 fixture 并输出 PNG;source、settings、build + 和 output 篡改均在提交或消费前结构化拒绝。 ## 仍然阻断 @@ -31,8 +56,11 @@ Three exposure/shadow 映射已落地;Scene 颜色管理 writer 和渲染等 Blender/RNA 颜色管理 API 后才能开放。Camera writer 已完成。 - N-019-B/C:AgX/Standard/Raw 的视觉等价、Area spread、Mist、DOF、 transparent sorting、probe 和高级 shadow 参数。 -- N-019-D:Cycles/Freestyle/denoise 服务端 job 协议与结果 hash。 -- N-019-E:desktop/Chromium 像素 golden、设备丢失和 1M triangles。 +- N-019-D:M11-05 已冻结 Cycles/复杂 Eevee/硬件后端的 server-only 路由;M11-06 已完成 + 有界 server-style submit 和 source/settings/build/output hash provenance;真实远程队列、 + 进度、取消、Freestyle 和 denoise job 仍未完成。 +- N-019-E:完整 desktop/Chromium 像素 parity、设备丢失和 1M triangles;M11-04 仅关闭固定 + fixture 的有界 reference 指标门,不代表全场景或全色彩管理等价。 - N-019-B/C/E(Volume):Float32 NanoVDB WGSL 树遍历、有界积分、有限 temperature/color/emission grid 语义和显式 GPU resident LRU 已有真实 Chromium 数值/图像专项门; 自动缺页重绘、生产视口深度合成和 desktop/Chromium 三视角 golden 仍阻断,不能据此声明发布级体渲染。 @@ -41,5 +69,11 @@ Three exposure/shadow 映射已落地;Scene 颜色管理 writer 和渲染等 ```bash npm --prefix web run test:lighting-roundtrip +npm --prefix web run test:lighting-field-parity +WEB_TEST_PORT=5545 npm --prefix web run test:lighting-field-roundtrip +WEB_TEST_PORT=5548 npm --prefix web run test:render-resource-budget +WEB_TEST_PORT=5552 npm --prefix web run test:render-reference +WEB_TEST_PORT=5556 npm --prefix web run test:render-routing +WEB_TEST_PORT=5560 npm --prefix web run test:server-render-job WEB_TEST_PORT=5319 npm --prefix web run test:e2e -- --grep "N-019 Scene exposure" ``` diff --git a/docs/status/N-020.md b/docs/status/N-020.md index 9d34fd40..d672f2a2 100644 --- a/docs/status/N-020.md +++ b/docs/status/N-020.md @@ -1,7 +1,7 @@ # N-020 Compositor -状态:`BLOCKED`(GraphIR、有界 CPU executor 与真实 Main graph 结构 reader 已落地;完整节点 -参数映射、WebGPU、HDR golden 和服务端执行未实现) +状态:`BLOCKED`(GraphIR、有界 CPU executor、真实 Main graph reader、四节点 WebGPU golden +与 Unsupported 全图执行门已落地;完整节点参数映射、通用 WebGPU、HDR golden 和服务端执行未实现) ## 已验证切片 @@ -27,14 +27,23 @@ Output 的动态 `Socket_0` 规范化为 GraphIR `Image`。桌面生成 fixture 经 WASM/Worker 后直接由 CPU executor 求值 Constant→Exposure→Invert→Composite,RGBA 结果通过;未连接的 Glare 仍保留 Unsupported 并使完整图 capability gate 保持阻断。 +8. N-020-B/E(M11-07 有界 WebGPU golden):显式 allowlist 只包含 Constant Color、Exposure、 + Invert、Composite。Blender 5.2 生成的四场景 fixture 经真实 WASM Main reader 后,生产 CPU + executor 与 Chromium WebGPU compute 对 2×2 `LINEAR_SRGB` Float32 RGBA 输出逐字节 SHA-256 + 相同;CPU-only、资源输入、未声明节点、断链和错误 socket 在 shader 编译前结构化阻断。 +9. N-020-D(M11-08 全图执行门):CPU 与 frame-cache 入口在求值、取消回调和 cache hit 前扫描 + 完整 graph;即使 Unsupported 节点未连接到 Composite,也返回 + `COMPOSITOR_NODE_UNSUPPORTED`。真实 Blender 5.2 Main fixture 的 Glare node name/type/ + `blenderType`、graph JSON 与 revision 在失败前后完全不变;预置 cache 不能绕过该门。 ## 仍然阻断 - N-020-A:Main graph 写回,以及 Transform、非默认 Invert、Alpha Over、Blur、Mix、Image/Render Layer 的逐节点参数与 resource/pass reader;基础真实图结构、常量色、Exposure、默认 Invert、 Viewer/Composite 已完成。 -- N-020-B/C:WebGPU executor、tile scheduler、OPFS 持久 frame cache、增量 invalidation 和 GPU - dispose;CPU 周期取消和内容寻址内存 LRU 已完成。 +- N-020-B/C:M11-07 只实现四节点、无资源、单输入链的有界 WebGPU compute;通用 WebGPU + executor、tile scheduler、OPFS 持久 frame cache、增量 invalidation 和生产 GPU 调度仍阻断; + CPU 周期取消和内容寻址内存 LRU 已完成。 - N-020-D:服务端 Blender job、source hash 和结果提交。 - N-020-E:desktop HDR/alpha/color-space golden、OOM/fault/device-loss。 @@ -43,4 +52,6 @@ ```bash WEB_TEST_PORT=5320 npm --prefix web run test:e2e -- --grep "N-020 CPU compositor" npm --prefix web run test:compositor-main-reader +WEB_TEST_PORT=5562 npm --prefix web run test:compositor-node-golden +WEB_TEST_PORT=5565 npm --prefix web run test:compositor-unsupported-gate ``` diff --git a/docs/status/N-021.md b/docs/status/N-021.md index 711dd34b..9343ab4e 100644 --- a/docs/status/N-021.md +++ b/docs/status/N-021.md @@ -1,7 +1,9 @@ # N-021 Sequencer 与音频 -状态:`BLOCKED`(strip schema、真实 Main reader、确定性时间编辑、长媒体有界索引/seek/cache -和 codec 能力门已落地;Main 写回、完整媒体解码/渲染、音频波形和服务端编码仍未实现) +状态:`BLOCKED`(strip schema、真实 Main reader、确定性时间编辑、长媒体有界索引/seek/cache、 +三类实际 runtime codec probe、source/decode-bound 首帧 proxy cache、三类迟到结果 revision +gate、最终编码 server-export 路由和实时 AudioContext 恢复门已落地;Main 写回、完整媒体 +解码/渲染、音频波形、A/V sync、实际混音和真正服务端编码 job 仍未实现) ## 已验证切片 @@ -14,8 +16,8 @@ 映射,避免左右片段复用越界源帧。 4. N-021-B/C(部分):`resolveSequencerFrame` 在切分边界解析当前顶层 strip、source frame 与依赖;META 覆盖的子 strip 和 inactive dependency 不会泄漏到渲染帧。 -5. N-021-C/D(门):运行时只报告 WebCodecs/HTMLMedia 需要精确 probe;codec 必须 - 出现在已验证 MIME 集合中才可放行,本地编码固定为 `BLOCKED`。 +5. N-021-C/D(门):运行时 inventory 只报告 WebCodecs/HTMLMedia 需要精确 probe;旧的 + MIME 集合不能作为 READY receipt,本地编码固定为 `BLOCKED`。 6. N-021-A(部分):Blender 5.2 `Scene.ed/Editing.seqbase` 读取 Scene、Movie、Image、Sound、 Meta 和协议支持的 Effect;显示范围由持久 `start/startofs/endofs/len` 计算,保留 channel、 mute/lock、相对媒体路径、24/1.001 FPS 和 effect input stable ID。绝对路径、未知类型、 @@ -32,16 +34,45 @@ 仍 resident;timeline/current frame/asset hashes 作为内容寻址 session manifest 写入 OPFS,终止 StorageWorker 与 media Worker 后在 frame 765432 恢复。session SHA-256 固定为 `d2e7e3c5ed9ae4fda6fe358cebb474f5774f1d2037dff3df9b6dfde9e0bebd2d`。 -11. 上述门只放行 fixture 的 IMAGE/SOUND 原始本地资源读取,不声称 WebCodecs MOVIE decode、 - waveform/proxy 生成或最终编码完成;`video/mp4` 和 local encoding 仍结构化阻断。 +11. 上述 V1 长媒体门只放行 fixture 的 IMAGE/SOUND 原始本地资源读取,不声称 WebCodecs MOVIE + seek、waveform/proxy 生成或最终编码完成;local encoding 仍结构化阻断。 +12. M11-09 runtime codec probe:schema 1 request/result 绑定 IMAGE/SOUND/MOVIE family、规范 MIME、 + byte length 和 source SHA-256,且不接收 source path/扩展名字段。Chromium 用 ImageBitmap + 解码 8×8 PNG,用固定 48 kHz OfflineAudioContext 解码 1 秒 WAV,用 HTMLMedia 加载、seek + 并绘制 16×16 两帧 H.264 MP4;三类均 READY。错误扩展名不影响结果,损坏 bytes、hash + drift、MIME/family 和 backend 伪造均返回 `SEQUENCER_CODEC_UNSUPPORTED`。 +13. M11-10 proxy cache:schema 1 identity 绑定 MOVIE source family/MIME/bytes/SHA-256、当前 + READY HTMLMedia receipt、SRGB8/STRAIGHT RGBA8 profile 和 source frame;payload 再独立绑定 + byte length/SHA-256。Chromium 从真实 H.264 初始 decoded frame 生成 8x8/256-byte proxy, + 一帧预算 LRU 发生确定性淘汰,content-addressed payload/manifest 经新 Storage Worker 重开 + 后复验。source、decode receipt、identity 或 payload 漂移均在消费前稳定阻断。 +14. M11-11 media revision gate:SEEK/SCRUB/DECODE 使用同一 schema 1 request,绑定 timeline + ID/revision、单调 request revision、operation 和 frame;completed result 必须逐字段回显并绑定 + source frame/payload hash。Chromium 中旧 SEEK 晚于新 SCRUB 返回、真实 H.264 DECODE 跨 + timeline replacement 返回及伪造 result 全部在 publish/cache callback 前成为 + `STALE/REVISION_CONFLICT`;只有当前 SCRUB 与当前 DECODE 发布。 +15. M11-12 final export route:schema 1 将真实 Main timeline ID/revision、source `.blend` + SHA-256、帧范围/帧率、分辨率和 container/video/audio codec 绑定到 settings/request SHA-256。 + 无 endpoint 时返回 `SERVER_EXPORT/BLOCKED/SEQUENCER_EXPORT_SERVER_UNAVAILABLE`;有 endpoint + 时只返回 `SERVER_EXPORT_REQUIRED`。Chromium 中注入和实际探测到的 `VideoEncoder` 都不能 + 把 `localEncoding` 从 `BLOCKED` 提升为本地完成。 +16. M11-13 audio session recovery:schema 1 分开报告 + `UNAVAILABLE/SUSPENDED/RUNNING/CLOSED` context 与 `BLOCKED/SILENT/ENABLED` output, + revision 随 initialize/mute/resume/suspend/close 单调推进。Chromium 在真实用户手势内创建 + `AudioContext`,通过 suspend、mute、muted resume、unmute、二次 suspend/resume 和 close; + mute 将 gain 归零,unmute 恢复 0.75,close 再归零并断开节点。API/构造器缺失与 resume + failure 分别稳定返回 `SEQUENCER_AUDIO_DEVICE_UNAVAILABLE` 和 + `SEQUENCER_AUDIO_RESUME_FAILED`,不会误报输出启用。 ## 仍然阻断 - N-021-A/B:Main strip 写回、非 CROSS transition/modifier 完整参数、undo/redo 和 save/reopen; 基础真实 reader 与 CROSS/GAMMA_CROSS 帧描述已完成。 -- N-021-C:WebCodecs 精确 seek/decode、音频 waveform、proxy 生成、A/V sync、丢帧和 - 损坏媒体处理。 -- N-021-D:浏览器不支持的 codec、混音与最终编码的服务端 Blender job。 +- N-021-C:M11-09/10/11/13 完成三类短 fixture 的初始实际解码门、MOVIE 初始帧 RGBA8 proxy + cache identity、迟到结果 publish/cache gate 和实时 AudioContext 生命周期;WebCodecs 帧精确 + seek/decode、多帧 proxy、音频 waveform、A/V sync、实际混音、丢帧和长媒体损坏恢复仍阻断。 +- N-021-D:M11-12 已冻结最终编码的 server-export 路由与请求 identity;真正服务端 Blender + encode job、进度/取消、混音和输出结果校验仍阻断。 - N-021-E:V1 有界 Chromium/OPFS 长媒体门已完成;完整桌面/Chromium 解码/渲染 golden 仍阻断。 ## 验收 @@ -50,4 +81,9 @@ WEB_TEST_PORT=5321 npm --prefix web run test:e2e -- --grep "N-021 sequencer" npm --prefix web run test:sequencer-main-reader npm --prefix web run test:long-media-performance +WEB_TEST_PORT=5570 npm --prefix web run test:sequencer-codec-probe +WEB_TEST_PORT=5573 npm --prefix web run test:sequencer-media-cache +WEB_TEST_PORT=5576 npm --prefix web run test:sequencer-media-revision +WEB_TEST_PORT=5579 npm --prefix web run test:sequencer-final-export +WEB_TEST_PORT=5586 npm --prefix web run test:sequencer-audio-recovery ``` diff --git a/docs/status/parity-ledger.json b/docs/status/parity-ledger.json index 9d2b2977..83806ded 100644 --- a/docs/status/parity-ledger.json +++ b/docs/status/parity-ledger.json @@ -1,6 +1,6 @@ { "schemaVersion": 2, - "updatedAt": "2026-08-14", + "updatedAt": "2026-08-17", "source": "docs/BLENDER_5_2_WEB_FEATURE_PARITY.md", "parityStatusEnum": ["COMPLETE", "BLOCKED"], "releaseClassEnum": ["LOCAL_EXACT", "LOCAL_BOUNDED", "SERVER", "EXCLUDED"], @@ -13,10 +13,10 @@ "releaseClass": "LOCAL_BOUNDED", "releaseStatus": "READY", "v1RequiredSlices": ["C3-roundtrip", "D2-nanovdb-bounded-webgpu-integration", "E2-vdb-network-worker-quota-tamper-rollback-faults"], - "v1ExcludedSlices": ["C2-new-external-font-import", "D2-nanovdb-production-viewport-automatic-page-fault-depth-composition", "C3-volume-combined-asset-viewport-reopen", "E1-volume-glb-usd-loss-desktop-chromium-golden", "E2-vdb-large-stream-device-loss-oom"], + "v1ExcludedSlices": ["C2-new-external-font-import", "D2-nanovdb-production-viewport-automatic-page-fault-depth-composition", "E1-volume-glb-usd-loss-desktop-chromium-golden"], "roadmapStatus": "in_progress", - "completedSlices": ["A1", "A2-malformed-binary-partial", "A2-integer-overflow-fixtures", "A2-multichunk-attribute-completeness", "B2", "B3-metadata", "B3-vdb-conversion-request-boundary", "B3-nanovdb-manifest-range-hash-protocol", "B3-nanovdb-serial-range-streamer", "B3-vdb-stage-capability-gates", "B3-vdb-real-resource-license-hash-catalog", "B3-desktop-openvdb13-nanovdb32-converter", "B3-native-conversion-determinism-malformed-input", "B3-vdb-server-job-isolation-cancel-timeout", "B3-vdb-desktop-server-hash-equality", "B3-nanovdb-http-retry-if-range-body-resume", "C1-topology-partial", "C1-handle-points-partial", "C1-handle-preview-raycast-partial", "C1-handle-identity-gizmo-main-roundtrip", "C1-multi-handle-selection-highlight-bounded-commit", "C1-gizmo-revision-delta-transaction-boundary", "C1-continuous-handle-gizmo-preview-single-commit", "C1-handle-local-origin-orientation-gizmo", "C1-rename-partial", "C1-create-delete-poly-partial", "C1-poly-bezier-nurbs-1d-conversion", "C1-multispline-create-delete-bulk-handle-cyclic-transaction", "C1-surface-2d-topology-transaction", "C2-font-geometry-partial", "C2-font-layout-partial", "C2-font-character-style-textbox-roundtrip", "C2-existing-packed-vfont-style-links", "C2-builtin-font-evaluation-exact", "C3-roundtrip", "C3-volume-main-properties-save-reopen", "C3-nanovdb-opfs-binding-worker-reopen", "D1-raycast-partial", "D1-offscreen-vert-edge-partial", "D1-selection-history-partial", "D1-cross-object-history-range-patch", "D1-handle-identity-axis-gizmo", "D2-partial", "D2-nanovdb-float32-cpu-wgsl-sampling", "D2-nanovdb-bounded-webgpu-integration", "D2-webgpu-device-loss-session-recovery", "D2-nanovdb-grid-material-mapping", "D2-nanovdb-explicit-resident-page-lru-recovery", "E1-partial", "E1-desktop-geometry-golden", "E1-true-2d-surface-desktop-golden", "E1-glb-evaluated-nonmesh-roundtrip-partial", "E1-glb-curve-line-surface-mesh-evaluated", "E1-usda-four-object-desktop-roundtrip", "E1-pointcloud-curves-hair-glb-usd-loss-fixture", "E2-1M-chromium", "E2-chromium-worker-recovery", "E2-opfs-quota-chromium", "E2-vdb-network-worker-quota-tamper-rollback-faults"], - "blockedSlices": ["C2-new-external-font-import", "D2-nanovdb-production-viewport-automatic-page-fault-depth-composition", "C3-volume-combined-asset-viewport-reopen", "E1-volume-glb-usd-loss-desktop-chromium-golden", "E2-vdb-large-stream-device-loss-oom"], + "completedSlices": ["A1", "A2-malformed-binary-partial", "A2-integer-overflow-fixtures", "A2-multichunk-attribute-completeness", "B2", "B3-metadata", "B3-vdb-conversion-request-boundary", "B3-nanovdb-manifest-range-hash-protocol", "B3-nanovdb-serial-range-streamer", "B3-vdb-stage-capability-gates", "B3-vdb-real-resource-license-hash-catalog", "B3-desktop-openvdb13-nanovdb32-converter", "B3-native-conversion-determinism-malformed-input", "B3-vdb-server-job-isolation-cancel-timeout", "B3-vdb-desktop-server-hash-equality", "B3-nanovdb-http-retry-if-range-body-resume", "C1-topology-partial", "C1-handle-points-partial", "C1-handle-preview-raycast-partial", "C1-handle-identity-gizmo-main-roundtrip", "C1-multi-handle-selection-highlight-bounded-commit", "C1-gizmo-revision-delta-transaction-boundary", "C1-continuous-handle-gizmo-preview-single-commit", "C1-handle-local-origin-orientation-gizmo", "C1-rename-partial", "C1-create-delete-poly-partial", "C1-poly-bezier-nurbs-1d-conversion", "C1-multispline-create-delete-bulk-handle-cyclic-transaction", "C1-surface-2d-topology-transaction", "C2-font-geometry-partial", "C2-font-layout-partial", "C2-font-character-style-textbox-roundtrip", "C2-existing-packed-vfont-style-links", "C2-builtin-font-evaluation-exact", "C3-roundtrip", "C3-volume-main-properties-save-reopen", "C3-nanovdb-opfs-binding-worker-reopen", "C3-volume-combined-asset-viewport-reopen", "D1-raycast-partial", "D1-offscreen-vert-edge-partial", "D1-selection-history-partial", "D1-cross-object-history-range-patch", "D1-handle-identity-axis-gizmo", "D2-partial", "D2-nanovdb-float32-cpu-wgsl-sampling", "D2-nanovdb-bounded-webgpu-integration", "D2-webgpu-device-loss-session-recovery", "D2-nanovdb-grid-material-mapping", "D2-nanovdb-explicit-resident-page-lru-recovery", "D2-nanovdb-production-viewport-automatic-page-fault", "E1-partial", "E1-desktop-geometry-golden", "E1-true-2d-surface-desktop-golden", "E1-glb-evaluated-nonmesh-roundtrip-partial", "E1-glb-curve-line-surface-mesh-evaluated", "E1-usda-four-object-desktop-roundtrip", "E1-pointcloud-curves-hair-glb-usd-loss-fixture", "E1-volume-desktop-main-offscreen-pixel-golden", "E2-1M-chromium", "E2-chromium-worker-recovery", "E2-opfs-quota-chromium", "E2-vdb-network-worker-quota-tamper-rollback-faults", "E2-vdb-large-stream-device-loss-oom"], + "blockedSlices": ["C2-new-external-font-import", "D2-nanovdb-production-viewport-automatic-page-fault-depth-composition", "E1-volume-glb-usd-loss-desktop-chromium-golden"], "excludedSlices": [], "acceptance": ["web:test:nonmesh-roundtrip", "web:test:nonmesh-desktop-golden", "web:test:nonmesh-glb-blender-roundtrip", "web:test:nonmesh-usd-serialization", "web:test:nonmesh-usd-blender-roundtrip", "web:test:nonmesh-binary", "web:test:selection-history", "web:test:nonmesh-interaction", "web:test:vdb", "web:test:vdb-availability", "web:test:vdb-native", "web:test:vdb-server", "web:test:vdb-opfs", "web:test:vdb-webgpu", "web:test:vdb-viewport", "web:test:vdb-faults", "web:e2e:N-015|non-mesh", "web:e2e:real OPFS quota"], "dependencies": [] @@ -72,7 +72,7 @@ "v1RequiredSlices": ["B-world-scene-render-delta-roundtrip", "B-C-nanovdb-float32-wgsl-bounded-ray-integration"], "v1ExcludedSlices": ["A", "B", "C", "D", "E", "B-C-nanovdb-production-viewport-paging-advanced-material", "E-volume-desktop-chromium-pixel-golden"], "roadmapStatus": "planned", - "completedSlices": ["A-camera-light-world-scene-reader-partial", "A-main-camera-dof-properties-partial", "A-main-light-world-properties-partial", "A-white-balance-integrity-gate", "B-three-exposure-shadow-mapping-partial", "B-bounded-kelvin-linear-light-color", "B-world-scene-render-delta-roundtrip", "B-volume-material-manifest-boundary", "B-C-nanovdb-float32-wgsl-bounded-ray-integration"], + "completedSlices": ["A-camera-light-world-scene-reader-partial", "A-main-camera-dof-properties-partial", "A-main-light-world-properties-partial", "A-white-balance-integrity-gate", "B-three-exposure-shadow-mapping-partial", "B-bounded-kelvin-linear-light-color", "B-world-scene-render-delta-roundtrip", "B-volume-material-manifest-boundary", "B-C-nanovdb-float32-wgsl-bounded-ray-integration", "D-final-render-server-routing-fail-closed", "D-server-render-source-build-settings-output-hash-binding", "E-bounded-realtime-blender-reference-image-metrics"], "blockedSlices": ["A", "B", "C", "D", "E", "B-C-nanovdb-production-viewport-paging-advanced-material", "E-volume-desktop-chromium-pixel-golden"], "acceptance": ["web:test:lighting-roundtrip", "web:e2e:N-019 Scene exposure"], "dependencies": ["N-018"] @@ -86,7 +86,7 @@ "v1RequiredSlices": ["B-real-main-exposure-invert-cpu-chain", "D-unsupported-node-preservation-gate"], "v1ExcludedSlices": ["A", "B", "C", "D", "E"], "roadmapStatus": "planned", - "completedSlices": ["A-graph-resource-cycle-schema", "A-main-graph-structure-reader-partial", "A-main-exposure-default-invert-parameter-reader", "B-bounded-cpu-executor-partial", "B-real-main-exposure-invert-cpu-chain", "C-image-operation-budget-cancel-partial", "C-content-addressed-frame-lru-cache", "D-unsupported-node-preservation-gate"], + "completedSlices": ["A-graph-resource-cycle-schema", "A-main-graph-structure-reader-partial", "A-main-exposure-default-invert-parameter-reader", "B-bounded-cpu-executor-partial", "B-real-main-exposure-invert-cpu-chain", "B-webgpu-constant-exposure-invert-golden-partial", "B-D-unsupported-full-graph-execution-block", "C-image-operation-budget-cancel-partial", "C-content-addressed-frame-lru-cache", "D-unsupported-node-preservation-gate"], "blockedSlices": ["A", "B", "C", "D", "E"], "acceptance": ["web:test:compositor-main-reader", "web:e2e:N-020 CPU compositor"], "dependencies": ["N-019"] @@ -100,7 +100,7 @@ "v1RequiredSlices": ["B-real-main-cross-still-frame-resolution", "C-runtime-codec-probe-gate"], "v1ExcludedSlices": ["A", "B", "C", "D", "E"], "roadmapStatus": "planned", - "completedSlices": ["A-strip-resource-schema", "A-main-strip-timeline-reader-partial", "B-deterministic-move-trim-split-partial", "B-source-frame-seek", "B-active-frame-dependency-resolution", "B-cross-transition-progress-input-resolution", "B-real-main-cross-still-frame-resolution", "C-runtime-codec-probe-gate"], + "completedSlices": ["A-strip-resource-schema", "A-main-strip-timeline-reader-partial", "B-deterministic-move-trim-split-partial", "B-source-frame-seek", "B-active-frame-dependency-resolution", "B-cross-transition-progress-input-resolution", "B-real-main-cross-still-frame-resolution", "C-runtime-codec-probe-gate", "C-runtime-image-sound-movie-byte-decode-probe", "C-source-decode-bound-movie-proxy-cache", "C-seek-scrub-decode-revision-gate", "C-audio-context-suspend-device-mute-recovery", "D-final-export-server-routing-fail-closed"], "blockedSlices": ["A", "B", "C", "D", "E"], "acceptance": ["web:test:sequencer-main-reader", "web:e2e:N-021 sequencer"], "dependencies": ["N-020"] diff --git a/docs/status/release-evidence.json b/docs/status/release-evidence.json index 12ad4ac3..76365e45 100644 --- a/docs/status/release-evidence.json +++ b/docs/status/release-evidence.json @@ -1,8 +1,8 @@ { "schemaVersion": 4, "source": "docs/status/parity-ledger.json", - "sourceSha256": "1e16518a25c6b6bed4ab7f605ea1ad5babe529f2842c858b20ae6daae30bd478", - "generatedAt": "2026-08-15T05:37:25.215Z", + "sourceSha256": "0a9b4c1026c42b5f77869f222b1531601ed229184a1d1737e74047c7b3406730", + "generatedAt": "2026-08-17T08:22:00.415Z", "families": [ { "id": "N-015", @@ -18,9 +18,7 @@ "v1ExcludedSlices": [ "C2-new-external-font-import", "D2-nanovdb-production-viewport-automatic-page-fault-depth-composition", - "C3-volume-combined-asset-viewport-reopen", - "E1-volume-glb-usd-loss-desktop-chromium-golden", - "E2-vdb-large-stream-device-loss-oom" + "E1-volume-glb-usd-loss-desktop-chromium-golden" ], "roadmapStatus": "in_progress", "completedSlices": [ @@ -61,6 +59,7 @@ "C3-roundtrip", "C3-volume-main-properties-save-reopen", "C3-nanovdb-opfs-binding-worker-reopen", + "C3-volume-combined-asset-viewport-reopen", "D1-raycast-partial", "D1-offscreen-vert-edge-partial", "D1-selection-history-partial", @@ -72,6 +71,7 @@ "D2-webgpu-device-loss-session-recovery", "D2-nanovdb-grid-material-mapping", "D2-nanovdb-explicit-resident-page-lru-recovery", + "D2-nanovdb-production-viewport-automatic-page-fault", "E1-partial", "E1-desktop-geometry-golden", "E1-true-2d-surface-desktop-golden", @@ -79,17 +79,17 @@ "E1-glb-curve-line-surface-mesh-evaluated", "E1-usda-four-object-desktop-roundtrip", "E1-pointcloud-curves-hair-glb-usd-loss-fixture", + "E1-volume-desktop-main-offscreen-pixel-golden", "E2-1M-chromium", "E2-chromium-worker-recovery", "E2-opfs-quota-chromium", - "E2-vdb-network-worker-quota-tamper-rollback-faults" + "E2-vdb-network-worker-quota-tamper-rollback-faults", + "E2-vdb-large-stream-device-loss-oom" ], "blockedSlices": [ "C2-new-external-font-import", "D2-nanovdb-production-viewport-automatic-page-fault-depth-composition", - "C3-volume-combined-asset-viewport-reopen", - "E1-volume-glb-usd-loss-desktop-chromium-golden", - "E2-vdb-large-stream-device-loss-oom" + "E1-volume-glb-usd-loss-desktop-chromium-golden" ], "excludedSlices": [], "acceptance": [ @@ -274,7 +274,10 @@ "B-bounded-kelvin-linear-light-color", "B-world-scene-render-delta-roundtrip", "B-volume-material-manifest-boundary", - "B-C-nanovdb-float32-wgsl-bounded-ray-integration" + "B-C-nanovdb-float32-wgsl-bounded-ray-integration", + "D-final-render-server-routing-fail-closed", + "D-server-render-source-build-settings-output-hash-binding", + "E-bounded-realtime-blender-reference-image-metrics" ], "blockedSlices": [ "A", @@ -317,6 +320,8 @@ "A-main-exposure-default-invert-parameter-reader", "B-bounded-cpu-executor-partial", "B-real-main-exposure-invert-cpu-chain", + "B-webgpu-constant-exposure-invert-golden-partial", + "B-D-unsupported-full-graph-execution-block", "C-image-operation-budget-cancel-partial", "C-content-addressed-frame-lru-cache", "D-unsupported-node-preservation-gate" @@ -362,7 +367,12 @@ "B-active-frame-dependency-resolution", "B-cross-transition-progress-input-resolution", "B-real-main-cross-still-frame-resolution", - "C-runtime-codec-probe-gate" + "C-runtime-codec-probe-gate", + "C-runtime-image-sound-movie-byte-decode-probe", + "C-source-decode-bound-movie-proxy-cache", + "C-seek-scrub-decode-revision-gate", + "C-audio-context-suspend-device-mute-recovery", + "D-final-export-server-routing-fail-closed" ], "blockedSlices": [ "A", diff --git a/tests/files/web/geometry_node_allowlist_evaluator.blend b/tests/files/web/geometry_node_allowlist_evaluator.blend new file mode 100644 index 0000000000000000000000000000000000000000..e5666063322406dab1f0a99b2c450085dbeb7c37 GIT binary patch literal 100021 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%0Ai3TJS6bs?vv|q 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

ZobiIr$m_u0-I-21KRb2qWKakq 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 literal 0 HcmV?d00001 diff --git a/tests/files/web/m11_compositor_allowlist.blend b/tests/files/web/m11_compositor_allowlist.blend new file mode 100644 index 0000000000000000000000000000000000000000..42e153718a0824d169b07076f1b82b390ef7abd3 GIT binary patch literal 90542 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 G>XMInWxAyR literal 0 HcmV?d00001 diff --git a/tests/files/web/m11_render_reference.blend b/tests/files/web/m11_render_reference.blend new file mode 100644 index 0000000000000000000000000000000000000000..36fefd5fcfb2ae1d51529b31e5703718459ec2b9 GIT binary patch literal 89535 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 literal 0 HcmV?d00001 diff --git a/tests/golden/M10-01/geometry-node-main-reader.json b/tests/golden/M10-01/geometry-node-main-reader.json new file mode 100644 index 00000000..d249b2c4 --- /dev/null +++ b/tests/golden/M10-01/geometry-node-main-reader.json @@ -0,0 +1,46 @@ +{ + "schemaVersion": 1, + "fixture": "tests/files/web/modifier_geometry_nodes_scene.blend", + "fixtureSha256": "f3820511f791769837d75be092934130c397cf46ee0acf79db28efca9a7948c9", + "desktopGolden": "tests/golden/W-075/modifier_geometry_nodes_scene.json", + "desktopGoldenSha256": "93c21e5158daefce10f2d4df674e636bf3f6ed3dd43e1576c45a02a8592f5886", + "graphs": [ + { + "name": "WebGeometryNodes", + "nodeTypes": ["NodeGroupInput", "GeometryNodeTransform", "NodeGroupOutput"], + "linkCount": 2, + "defaults": { + "GeometryNodeTransform:Mode": 0, + "GeometryNodeTransform:Translation": [0.25, 0.5, 1.0], + "GeometryNodeTransform:Rotation": [0.0, 0.0, 0.2617993950843811], + "GeometryNodeTransform:Scale": [1.0, 1.0, 1.0] + } + }, + { + "name": "WebGeometryNodesSetPosition", + "nodeTypes": ["NodeGroupInput", "GeometryNodeSetPosition", "NodeGroupOutput"], + "linkCount": 2, + "defaults": { + "GeometryNodeSetPosition:Selection": true, + "GeometryNodeSetPosition:Position": [0.0, 0.0, 0.0], + "GeometryNodeSetPosition:Offset": [0.0, 0.0, 1.0] + } + }, + { + "name": "WebGeometryNodesSimulation", + "nodeTypes": ["GeometryNodeSimulationInput", "NodeGroupOutput", "GeometryNodeSimulationOutput", "NodeGroupInput"], + "linkCount": 3, + "defaults": { + "GeometryNodeSimulationInput:Delta Time": 0.0, + "GeometryNodeSimulationOutput:Skip": false + } + } + ], + "budgets": { + "maxGraphs": 4096, + "maxNodesPerGraph": 4096, + "maxLinksPerGraph": 16384, + "maxSocketsPerGraph": 65536, + "maxInterfaceSocketsPerGraph": 4096 + } +} diff --git a/tests/golden/M10-02/geometry-node-allowlist.json b/tests/golden/M10-02/geometry-node-allowlist.json new file mode 100644 index 00000000..42c047d9 --- /dev/null +++ b/tests/golden/M10-02/geometry-node-allowlist.json @@ -0,0 +1,32 @@ +{ + "schemaVersion": 1, + "fixture": "tests/files/web/modifier_geometry_nodes_scene.blend", + "fixtureSha256": "f3820511f791769837d75be092934130c397cf46ee0acf79db28efca9a7948c9", + "allowlistSchemaVersion": 1, + "allowlist": [ + "NodeGroupInput", + "NodeGroupOutput", + "GeometryNodeTransform", + "GeometryNodeSetPosition", + "GeometryNodeJoinGeometry", + "GeometryNodeSeparateGeometry", + "GeometryNodeRealizeInstances", + "GeometryNodeStoreNamedAttribute", + "FunctionNodeInputInt", + "FunctionNodeInputVector", + "FunctionNodeCompare", + "ShaderNodeValue", + "ShaderNodeMath", + "GeometryNodeObjectInfo", + "GeometryNodeCollectionInfo", + "GeometryNodeImageInfo" + ], + "supportedGraph": "WebGeometryNodes", + "unsupportedGraph": "WebGeometryNodesSimulation", + "unsupportedNodeTypes": [ + "GeometryNodeSimulationInput", + "GeometryNodeSimulationOutput" + ], + "unsupportedErrorCode": "GN_NODE_UNSUPPORTED", + "evaluatorUnavailableErrorCode": "CAPABILITY_MISSING" +} diff --git a/tests/golden/M10-03/geometry-node-evaluator.json b/tests/golden/M10-03/geometry-node-evaluator.json new file mode 100644 index 00000000..e21a4f93 --- /dev/null +++ b/tests/golden/M10-03/geometry-node-evaluator.json @@ -0,0 +1,1187 @@ +{ + "allowlist": [ + "NodeGroupInput", + "NodeGroupOutput", + "GeometryNodeTransform", + "GeometryNodeSetPosition", + "GeometryNodeJoinGeometry", + "GeometryNodeSeparateGeometry", + "GeometryNodeRealizeInstances", + "GeometryNodeStoreNamedAttribute", + "FunctionNodeInputInt", + "FunctionNodeInputVector", + "FunctionNodeCompare", + "ShaderNodeValue", + "ShaderNodeMath", + "GeometryNodeObjectInfo", + "GeometryNodeCollectionInfo", + "GeometryNodeImageInfo" + ], + "blenderVersion": "5.2.0 LTS", + "cases": [ + { + "graph": "M10GN_PassthroughGraph", + "mesh": { + "attributes": {}, + "bounds": { + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ] + }, + "indices": [ + 0, + 1, + 2, + 0, + 2, + 3, + 4, + 7, + 6, + 4, + 6, + 5, + 0, + 4, + 5, + 0, + 5, + 1, + 1, + 5, + 6, + 1, + 6, + 2, + 2, + 6, + 7, + 2, + 7, + 3, + 4, + 0, + 3, + 4, + 3, + 7 + ], + "object": "M10GN_Passthrough", + "positions": [ + -1.0, + -1.0, + -1.0, + 1.0, + -1.0, + -1.0, + 1.0, + 1.0, + -1.0, + -1.0, + 1.0, + -1.0, + -1.0, + -1.0, + 1.0, + 1.0, + -1.0, + 1.0, + 1.0, + 1.0, + 1.0, + -1.0, + 1.0, + 1.0 + ], + "triangleCount": 12, + "vertexCount": 8 + }, + "name": "M10GN_Passthrough", + "nodeTypes": [ + "NodeGroupInput", + "NodeGroupOutput" + ] + }, + { + "graph": "M10GN_TransformGraph", + "mesh": { + "attributes": {}, + "bounds": { + "max": [ + 1.6515215635299683, + 0.5479682683944702, + 2.5 + ], + "min": [ + -1.1515215635299683, + -1.5479682683944702, + -0.5 + ] + }, + "indices": [ + 0, + 1, + 2, + 0, + 2, + 3, + 4, + 7, + 6, + 4, + 6, + 5, + 0, + 4, + 5, + 0, + 5, + 1, + 1, + 5, + 6, + 1, + 6, + 2, + 2, + 6, + 7, + 2, + 7, + 3, + 4, + 0, + 3, + 4, + 3, + 7 + ], + "object": "M10GN_Transform", + "positions": [ + -0.7632929086685181, + -1.5479682683944702, + -0.5, + 1.6515215635299683, + -0.9009205102920532, + -0.5, + 1.263292908668518, + 0.5479682683944702, + -0.5, + -1.1515215635299683, + -0.09907945990562439, + -0.5, + -0.7632929086685181, + -1.5479682683944702, + 2.5, + 1.6515215635299683, + -0.9009205102920532, + 2.5, + 1.263292908668518, + 0.5479682683944702, + 2.5, + -1.1515215635299683, + -0.09907945990562439, + 2.5 + ], + "triangleCount": 12, + "vertexCount": 8 + }, + "name": "M10GN_Transform", + "nodeTypes": [ + "NodeGroupInput", + "NodeGroupOutput", + "GeometryNodeTransform" + ] + }, + { + "graph": "M10GN_SetPositionGraph", + "mesh": { + "attributes": {}, + "bounds": { + "max": [ + 1.5, + 1.25, + 0.25 + ], + "min": [ + -0.5, + -0.75, + -1.75 + ] + }, + "indices": [ + 0, + 1, + 2, + 0, + 2, + 3, + 4, + 7, + 6, + 4, + 6, + 5, + 0, + 4, + 5, + 0, + 5, + 1, + 1, + 5, + 6, + 1, + 6, + 2, + 2, + 6, + 7, + 2, + 7, + 3, + 4, + 0, + 3, + 4, + 3, + 7 + ], + "object": "M10GN_SetPosition", + "positions": [ + -0.5, + -0.75, + -1.75, + 1.5, + -0.75, + -1.75, + 1.5, + 1.25, + -1.75, + -0.5, + 1.25, + -1.75, + -0.5, + -0.75, + 0.25, + 1.5, + -0.75, + 0.25, + 1.5, + 1.25, + 0.25, + -0.5, + 1.25, + 0.25 + ], + "triangleCount": 12, + "vertexCount": 8 + }, + "name": "M10GN_SetPosition", + "nodeTypes": [ + "NodeGroupInput", + "NodeGroupOutput", + "GeometryNodeSetPosition" + ] + }, + { + "graph": "M10GN_JoinGraph", + "mesh": { + "attributes": {}, + "bounds": { + "max": [ + 4.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ] + }, + "indices": [ + 0, + 1, + 2, + 0, + 2, + 3, + 4, + 7, + 6, + 4, + 6, + 5, + 0, + 4, + 5, + 0, + 5, + 1, + 1, + 5, + 6, + 1, + 6, + 2, + 2, + 6, + 7, + 2, + 7, + 3, + 4, + 0, + 3, + 4, + 3, + 7, + 8, + 9, + 10, + 8, + 10, + 11, + 12, + 15, + 14, + 12, + 14, + 13, + 8, + 12, + 13, + 8, + 13, + 9, + 9, + 13, + 14, + 9, + 14, + 10, + 10, + 14, + 15, + 10, + 15, + 11, + 12, + 8, + 11, + 12, + 11, + 15 + ], + "object": "M10GN_Join", + "positions": [ + 2.0, + -1.0, + -1.0, + 4.0, + -1.0, + -1.0, + 4.0, + 1.0, + -1.0, + 2.0, + 1.0, + -1.0, + 2.0, + -1.0, + 1.0, + 4.0, + -1.0, + 1.0, + 4.0, + 1.0, + 1.0, + 2.0, + 1.0, + 1.0, + -1.0, + -1.0, + -1.0, + 1.0, + -1.0, + -1.0, + 1.0, + 1.0, + -1.0, + -1.0, + 1.0, + -1.0, + -1.0, + -1.0, + 1.0, + 1.0, + -1.0, + 1.0, + 1.0, + 1.0, + 1.0, + -1.0, + 1.0, + 1.0 + ], + "triangleCount": 24, + "vertexCount": 16 + }, + "name": "M10GN_Join", + "nodeTypes": [ + "NodeGroupInput", + "NodeGroupOutput", + "GeometryNodeJoinGeometry", + "GeometryNodeTransform" + ] + }, + { + "graph": "M10GN_SeparateIntCompareGraph", + "mesh": { + "attributes": {}, + "bounds": { + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ] + }, + "indices": [ + 0, + 1, + 2, + 0, + 2, + 3, + 4, + 7, + 6, + 4, + 6, + 5, + 0, + 4, + 5, + 0, + 5, + 1, + 1, + 5, + 6, + 1, + 6, + 2, + 2, + 6, + 7, + 2, + 7, + 3, + 4, + 0, + 3, + 4, + 3, + 7 + ], + "object": "M10GN_SeparateIntCompare", + "positions": [ + -1.0, + -1.0, + -1.0, + 1.0, + -1.0, + -1.0, + 1.0, + 1.0, + -1.0, + -1.0, + 1.0, + -1.0, + -1.0, + -1.0, + 1.0, + 1.0, + -1.0, + 1.0, + 1.0, + 1.0, + 1.0, + -1.0, + 1.0, + 1.0 + ], + "triangleCount": 12, + "vertexCount": 8 + }, + "name": "M10GN_SeparateIntCompare", + "nodeTypes": [ + "NodeGroupInput", + "NodeGroupOutput", + "GeometryNodeSeparateGeometry", + "FunctionNodeInputInt", + "FunctionNodeInputInt", + "FunctionNodeCompare" + ] + }, + { + "graph": "M10GN_CollectionRealizeGraph", + "mesh": { + "attributes": {}, + "bounds": { + "max": [ + 2.5, + 1.0, + 1.5 + ], + "min": [ + -2.5, + -1.0, + 0.0 + ] + }, + "indices": [ + 0, + 1, + 2, + 0, + 2, + 3, + 0, + 3, + 1, + 1, + 3, + 2, + 4, + 5, + 6, + 4, + 6, + 7, + 4, + 7, + 5, + 5, + 7, + 6 + ], + "object": "M10GN_CollectionRealize", + "positions": [ + -1.5, + 0.0, + 1.5, + -2.5, + -1.0, + 0.0, + -0.5, + -1.0, + 0.0, + -1.5, + 1.0, + 0.0, + 1.5, + 0.0, + 1.5, + 0.5, + -1.0, + 0.0, + 2.5, + -1.0, + 0.0, + 1.5, + 1.0, + 0.0 + ], + "triangleCount": 8, + "vertexCount": 8 + }, + "name": "M10GN_CollectionRealize", + "nodeTypes": [ + "NodeGroupInput", + "NodeGroupOutput", + "GeometryNodeCollectionInfo", + "GeometryNodeRealizeInstances" + ] + }, + { + "graph": "M10GN_StoreAttributeGraph", + "mesh": { + "attributes": { + "m10_value": { + "dataType": "FLOAT", + "domain": "POINT", + "values": [ + 0.375, + 0.375, + 0.375, + 0.375, + 0.375, + 0.375, + 0.375, + 0.375 + ] + } + }, + "bounds": { + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ] + }, + "indices": [ + 0, + 1, + 2, + 0, + 2, + 3, + 4, + 7, + 6, + 4, + 6, + 5, + 0, + 4, + 5, + 0, + 5, + 1, + 1, + 5, + 6, + 1, + 6, + 2, + 2, + 6, + 7, + 2, + 7, + 3, + 4, + 0, + 3, + 4, + 3, + 7 + ], + "object": "M10GN_StoreAttribute", + "positions": [ + -1.0, + -1.0, + -1.0, + 1.0, + -1.0, + -1.0, + 1.0, + 1.0, + -1.0, + -1.0, + 1.0, + -1.0, + -1.0, + -1.0, + 1.0, + 1.0, + -1.0, + 1.0, + 1.0, + 1.0, + 1.0, + -1.0, + 1.0, + 1.0 + ], + "triangleCount": 12, + "vertexCount": 8 + }, + "name": "M10GN_StoreAttribute", + "nodeTypes": [ + "NodeGroupInput", + "NodeGroupOutput", + "GeometryNodeStoreNamedAttribute" + ] + }, + { + "graph": "M10GN_InputVectorGraph", + "mesh": { + "attributes": {}, + "bounds": { + "max": [ + 1.125, + 1.5, + 2.25 + ], + "min": [ + -0.875, + -0.5, + 0.25 + ] + }, + "indices": [ + 0, + 1, + 2, + 0, + 2, + 3, + 4, + 7, + 6, + 4, + 6, + 5, + 0, + 4, + 5, + 0, + 5, + 1, + 1, + 5, + 6, + 1, + 6, + 2, + 2, + 6, + 7, + 2, + 7, + 3, + 4, + 0, + 3, + 4, + 3, + 7 + ], + "object": "M10GN_InputVector", + "positions": [ + -0.875, + -0.5, + 0.25, + 1.125, + -0.5, + 0.25, + 1.125, + 1.5, + 0.25, + -0.875, + 1.5, + 0.25, + -0.875, + -0.5, + 2.25, + 1.125, + -0.5, + 2.25, + 1.125, + 1.5, + 2.25, + -0.875, + 1.5, + 2.25 + ], + "triangleCount": 12, + "vertexCount": 8 + }, + "name": "M10GN_InputVector", + "nodeTypes": [ + "NodeGroupInput", + "NodeGroupOutput", + "FunctionNodeInputVector", + "GeometryNodeSetPosition" + ] + }, + { + "graph": "M10GN_ValueMathGraph", + "mesh": { + "attributes": {}, + "bounds": { + "max": [ + 1.0, + 1.0, + 1.625 + ], + "min": [ + -1.0, + -1.0, + -0.375 + ] + }, + "indices": [ + 0, + 1, + 2, + 0, + 2, + 3, + 4, + 7, + 6, + 4, + 6, + 5, + 0, + 4, + 5, + 0, + 5, + 1, + 1, + 5, + 6, + 1, + 6, + 2, + 2, + 6, + 7, + 2, + 7, + 3, + 4, + 0, + 3, + 4, + 3, + 7 + ], + "object": "M10GN_ValueMath", + "positions": [ + -1.0, + -1.0, + -0.375, + 1.0, + -1.0, + -0.375, + 1.0, + 1.0, + -0.375, + -1.0, + 1.0, + -0.375, + -1.0, + -1.0, + 1.625, + 1.0, + -1.0, + 1.625, + 1.0, + 1.0, + 1.625, + -1.0, + 1.0, + 1.625 + ], + "triangleCount": 12, + "vertexCount": 8 + }, + "name": "M10GN_ValueMath", + "nodeTypes": [ + "NodeGroupInput", + "NodeGroupOutput", + "ShaderNodeValue", + "ShaderNodeValue", + "ShaderNodeMath", + "FunctionNodeCompare", + "GeometryNodeSetPosition" + ] + }, + { + "graph": "M10GN_ObjectInfoGraph", + "mesh": { + "attributes": {}, + "bounds": { + "max": [ + 1.5, + 1.25, + 2.5 + ], + "min": [ + -0.5, + -0.75, + 0.5 + ] + }, + "indices": [ + 0, + 1, + 2, + 0, + 2, + 3, + 4, + 7, + 6, + 4, + 6, + 5, + 0, + 4, + 5, + 0, + 5, + 1, + 1, + 5, + 6, + 1, + 6, + 2, + 2, + 6, + 7, + 2, + 7, + 3, + 4, + 0, + 3, + 4, + 3, + 7 + ], + "object": "M10GN_ObjectInfo", + "positions": [ + -0.5, + -0.75, + 0.5, + 1.5, + -0.75, + 0.5, + 1.5, + 1.25, + 0.5, + -0.5, + 1.25, + 0.5, + -0.5, + -0.75, + 2.5, + 1.5, + -0.75, + 2.5, + 1.5, + 1.25, + 2.5, + -0.5, + 1.25, + 2.5 + ], + "triangleCount": 12, + "vertexCount": 8 + }, + "name": "M10GN_ObjectInfo", + "nodeTypes": [ + "NodeGroupInput", + "NodeGroupOutput", + "GeometryNodeObjectInfo", + "GeometryNodeSetPosition" + ] + }, + { + "graph": "M10GN_ImageInfoGraph", + "mesh": { + "attributes": {}, + "bounds": { + "max": [ + 1.0, + 0.25, + 1.7999999523162842 + ], + "min": [ + -1.0, + -1.75, + -0.19999998807907104 + ] + }, + "indices": [ + 0, + 1, + 2, + 0, + 2, + 3, + 4, + 7, + 6, + 4, + 6, + 5, + 0, + 4, + 5, + 0, + 5, + 1, + 1, + 5, + 6, + 1, + 6, + 2, + 2, + 6, + 7, + 2, + 7, + 3, + 4, + 0, + 3, + 4, + 3, + 7 + ], + "object": "M10GN_ImageInfo", + "positions": [ + -1.0, + -1.75, + -0.19999998807907104, + 1.0, + -1.75, + -0.19999998807907104, + 1.0, + 0.25, + -0.19999998807907104, + -1.0, + 0.25, + -0.19999998807907104, + -1.0, + -1.75, + 1.7999999523162842, + 1.0, + -1.75, + 1.7999999523162842, + 1.0, + 0.25, + 1.7999999523162842, + -1.0, + 0.25, + 1.7999999523162842 + ], + "triangleCount": 12, + "vertexCount": 8 + }, + "name": "M10GN_ImageInfo", + "nodeTypes": [ + "NodeGroupInput", + "NodeGroupOutput", + "GeometryNodeImageInfo", + "FunctionNodeCompare", + "GeometryNodeSetPosition" + ] + } + ], + "fixture": "tests/files/web/geometry_node_allowlist_evaluator.blend", + "fixtureSha256": "0cb3e33df570b436e32d783eef0a6c2daad04ca5bb4ccb4f0286b3d723ff7978", + "nodeCoverage": { + "FunctionNodeCompare": [ + "M10GN_SeparateIntCompare", + "M10GN_ValueMath", + "M10GN_ImageInfo" + ], + "FunctionNodeInputInt": [ + "M10GN_SeparateIntCompare", + "M10GN_SeparateIntCompare" + ], + "FunctionNodeInputVector": [ + "M10GN_InputVector" + ], + "GeometryNodeCollectionInfo": [ + "M10GN_CollectionRealize" + ], + "GeometryNodeImageInfo": [ + "M10GN_ImageInfo" + ], + "GeometryNodeJoinGeometry": [ + "M10GN_Join" + ], + "GeometryNodeObjectInfo": [ + "M10GN_ObjectInfo" + ], + "GeometryNodeRealizeInstances": [ + "M10GN_CollectionRealize" + ], + "GeometryNodeSeparateGeometry": [ + "M10GN_SeparateIntCompare" + ], + "GeometryNodeSetPosition": [ + "M10GN_SetPosition", + "M10GN_InputVector", + "M10GN_ValueMath", + "M10GN_ObjectInfo", + "M10GN_ImageInfo" + ], + "GeometryNodeStoreNamedAttribute": [ + "M10GN_StoreAttribute" + ], + "GeometryNodeTransform": [ + "M10GN_Transform", + "M10GN_Join" + ], + "NodeGroupInput": [ + "M10GN_Passthrough", + "M10GN_Transform", + "M10GN_SetPosition", + "M10GN_Join", + "M10GN_SeparateIntCompare", + "M10GN_CollectionRealize", + "M10GN_StoreAttribute", + "M10GN_InputVector", + "M10GN_ValueMath", + "M10GN_ObjectInfo", + "M10GN_ImageInfo" + ], + "NodeGroupOutput": [ + "M10GN_Passthrough", + "M10GN_Transform", + "M10GN_SetPosition", + "M10GN_Join", + "M10GN_SeparateIntCompare", + "M10GN_CollectionRealize", + "M10GN_StoreAttribute", + "M10GN_InputVector", + "M10GN_ValueMath", + "M10GN_ObjectInfo", + "M10GN_ImageInfo" + ], + "ShaderNodeMath": [ + "M10GN_ValueMath" + ], + "ShaderNodeValue": [ + "M10GN_ValueMath", + "M10GN_ValueMath" + ] + }, + "schemaVersion": 1, + "tolerance": { + "boundsError": 1e-05, + "maxAttributeError": 1e-06, + "maxPositionError": 1e-05, + "rmsPositionError": 1e-06 + } +} diff --git a/tests/golden/M10-07/shader-compile.json b/tests/golden/M10-07/shader-compile.json new file mode 100644 index 00000000..a2339035 --- /dev/null +++ b/tests/golden/M10-07/shader-compile.json @@ -0,0 +1,34 @@ +{ + "schemaVersion": 1, + "taskId": "M10-07", + "fixture": "tests/files/web/basic_scene.blend", + "fixtureSha256": "6fcc55bda74da8c95da96ba068b60339bf60ca44147ff8000fbb95d296db2a73", + "backend": "WEBGL2_THREE_PHYSICAL", + "allowlist": [ + "RGB", + "VALUE", + "MATH", + "IMAGE_TEXTURE", + "NORMAL_MAP", + "PRINCIPLED", + "OUTPUT" + ], + "mathOperations": [ + "ADD", + "SUBTRACT", + "MULTIPLY", + "DIVIDE", + "MINIMUM", + "MAXIMUM" + ], + "budget": { + "maxNodes": 128, + "maxLinks": 512, + "maxDepth": 64, + "maxTextures": 16, + "maxIdentifierBytes": 256, + "maxNameBytes": 1024 + }, + "expectedMathResult": 0.42, + "unsupportedNodeCode": "SHADER_NODE_UNSUPPORTED" +} diff --git a/tests/golden/M10-08/shader-compile-key.json b/tests/golden/M10-08/shader-compile-key.json new file mode 100644 index 00000000..f3737e2b --- /dev/null +++ b/tests/golden/M10-08/shader-compile-key.json @@ -0,0 +1,10 @@ +{ + "schemaVersion": 1, + "taskId": "M10-08", + "backend": "WEBGL2_THREE_PHYSICAL", + "textureColorSpaces": ["SRGB", "NON_COLOR", "LINEAR"], + "keyInputs": ["graphHash", "rendererBackend", "imageId", "usage", "assetId", "sha256", "colorSpace"], + "keyDigest": "sha256", + "unknownBackendCode": "CAPABILITY_MISSING", + "invalidTextureIdentityCode": "SHADER_INVALID_GRAPH" +} diff --git a/tests/golden/M10-09/shader-pipeline.json b/tests/golden/M10-09/shader-pipeline.json new file mode 100644 index 00000000..cb955da3 --- /dev/null +++ b/tests/golden/M10-09/shader-pipeline.json @@ -0,0 +1,8 @@ +{ + "schemaVersion": 1, + "taskId": "M10-09", + "failureStatus": "BLOCKED", + "preservedPipeline": true, + "replacedOnSuccess": true, + "failureDoesNotDisposePrevious": true +} diff --git a/tests/golden/M10-10/shader-capability-block.json b/tests/golden/M10-10/shader-capability-block.json new file mode 100644 index 00000000..fc48ad10 --- /dev/null +++ b/tests/golden/M10-10/shader-capability-block.json @@ -0,0 +1,10 @@ +{ + "schemaVersion": 1, + "taskId": "M10-10", + "capability": "ARBITRARY_SHADER", + "status": "BLOCKED", + "unsupportedNodeTypes": ["VORONOI", "CUSTOM_OSL"], + "errorCode": "SHADER_NODE_UNSUPPORTED", + "recoverable": true, + "graphDataPolicy": "PRESERVE" +} diff --git a/tests/golden/M10-11/nla-evaluation.json b/tests/golden/M10-11/nla-evaluation.json new file mode 100644 index 00000000..97dc4b2c --- /dev/null +++ b/tests/golden/M10-11/nla-evaluation.json @@ -0,0 +1,308 @@ +{ + "schemaVersion": 1, + "fixture": "tests/files/web/nla_time_mapping_scene.blend", + "fixtureSha256": "b32f783debe213a1345ec4528e5ed27f0f4de00d7896b626df39756289119855", + "blenderVersion": "5.2.0 LTS", + "object": "M10_NLA_TimeMapping", + "mesh": "M10_NLA_TimeMappingMesh", + "tracks": [ + { + "name": "M10 Time Mapping", + "muted": false, + "solo": false, + "strips": [ + { + "id": "M10 Scaled Clip", + "action": "M10_NLA_Scaled_X", + "frameStart": 20.0, + "frameEnd": 40.0, + "actionFrameStart": 1.0, + "actionFrameEnd": 11.0, + "scale": 2.0, + "repeat": 1.0, + "blendIn": 0.0, + "blendOut": 0.0, + "influence": 1.0, + "blendMode": "REPLACE", + "extrapolation": "NOTHING", + "muted": false, + "reverse": false + }, + { + "id": "M10 Reverse Repeat Clip", + "action": "M10_NLA_ReverseRepeat_Y", + "frameStart": 45.0, + "frameEnd": 65.0, + "actionFrameStart": 1.0, + "actionFrameEnd": 11.0, + "scale": 1.0, + "repeat": 2.0, + "blendIn": 0.0, + "blendOut": 0.0, + "influence": 1.0, + "blendMode": "REPLACE", + "extrapolation": "NOTHING", + "muted": false, + "reverse": true + } + ] + } + ], + "frames": [ + { + "frame": 1, + "worldMatrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "frame": 20, + "worldMatrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "frame": 25, + "worldMatrix": [ + 1.0, + 0.0, + 0.0, + 2.5, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "frame": 30, + "worldMatrix": [ + 1.0, + 0.0, + 0.0, + 5.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "frame": 35, + "worldMatrix": [ + 1.0, + 0.0, + 0.0, + 7.5, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "frame": 40, + "worldMatrix": [ + 1.0, + 0.0, + 0.0, + 10.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "frame": 45, + "worldMatrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 10.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "frame": 50, + "worldMatrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 5.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "frame": 55, + "worldMatrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 10.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "frame": 60, + "worldMatrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 5.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "frame": 65, + "worldMatrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "frame": 70, + "worldMatrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + } + ], + "tolerance": { + "maxMatrixError": 1e-05 + } +} diff --git a/tests/golden/M10-13/physics-solver-probe.json b/tests/golden/M10-13/physics-solver-probe.json new file mode 100644 index 00000000..9a155367 --- /dev/null +++ b/tests/golden/M10-13/physics-solver-probe.json @@ -0,0 +1,24 @@ +{ + "familyOrder": [ + "RIGID_BODY", + "SOFT_BODY", + "CLOTH", + "FLUID", + "DYNAMIC_PAINT", + "PARTICLE", + "HAIR" + ], + "defaultProbe": "EXPORT_UNAVAILABLE", + "probes": { + "RIGID_BODY": "READY", + "SOFT_BODY": "INITIALIZATION_FAILED", + "CLOTH": "THREADS_UNAVAILABLE", + "FLUID": "MEMORY_UNAVAILABLE", + "DYNAMIC_PAINT": "INVALID_RESULT", + "PARTICLE": "INITIALIZATION_FAILED", + "HAIR": "EXPORT_UNAVAILABLE" + }, + "localRoute": "LOCAL_SOLVER", + "fallbackRoute": "DESKTOP_SERVER_BAKE", + "fallbackErrorCode": "PHYSICS_SOLVER_UNAVAILABLE" +} diff --git a/tests/golden/M10-14/physics-cache-family.json b/tests/golden/M10-14/physics-cache-family.json new file mode 100644 index 00000000..0d3c383b --- /dev/null +++ b/tests/golden/M10-14/physics-cache-family.json @@ -0,0 +1,26 @@ +{ + "families": [ + "RIGID_BODY", + "SOFT_BODY", + "CLOTH", + "FLUID", + "DYNAMIC_PAINT", + "PARTICLE", + "HAIR" + ], + "sources": [ + "BLENDER_DESKTOP_BAKE", + "BLENDER_SERVER_BAKE", + "BLENDER_DESKTOP_BAKE", + "BLENDER_SERVER_BAKE", + "BLENDER_DESKTOP_BAKE", + "BLENDER_SERVER_BAKE", + "BLENDER_DESKTOP_BAKE" + ], + "byteLength": 4, + "frameCount": 2, + "sourceMismatch": "PHYSICS_CACHE_SOURCE_MISMATCH", + "payloadMismatch": "PHYSICS_CACHE_HASH_MISMATCH", + "versionMismatch": "PROTOCOL_MISMATCH", + "budgetExceeded": "PHYSICS_BUDGET_EXCEEDED" +} diff --git a/tests/golden/M10-15/domain-browser-gates.json b/tests/golden/M10-15/domain-browser-gates.json new file mode 100644 index 00000000..c55f1a06 --- /dev/null +++ b/tests/golden/M10-15/domain-browser-gates.json @@ -0,0 +1,33 @@ +{ + "schemaVersion": 1, + "domains": { + "GN": { + "workUnits": 10, + "maximumMs": 5000, + "performanceStatus": "SUPPORTED", + "oomCode": "GN_GRAPH_BUDGET_EXCEEDED", + "maliciousCode": "GN_DEPENDENCY_CYCLE" + }, + "SHADER": { + "workUnits": 100, + "maximumMs": 5000, + "performanceStatus": "COMPILED", + "oomCode": "SHADER_NODE_UNSUPPORTED", + "maliciousCode": "SHADER_GRAPH_CYCLE" + }, + "NLA": { + "workUnits": 20, + "maximumMs": 5000, + "performanceStatus": "SUPPORTED", + "oomCode": "NLA_BUDGET_EXCEEDED", + "maliciousCode": "NLA_INVALID_STACK" + }, + "SIMULATION": { + "workUnits": 64, + "maximumMs": 10000, + "performanceStatus": "VERIFIED", + "oomCode": "SIMULATION_CACHE_BUDGET_EXCEEDED", + "maliciousCode": "SIMULATION_CACHE_INVALID" + } + } +} diff --git a/tests/golden/M11-01/lighting-field-parity.json b/tests/golden/M11-01/lighting-field-parity.json new file mode 100644 index 00000000..cbf25e3b --- /dev/null +++ b/tests/golden/M11-01/lighting-field-parity.json @@ -0,0 +1,128 @@ +{ + "schemaVersion": 1, + "task": "M11-01", + "blenderVersion": "5.2.0", + "legend": { + "VERIFIED": "Implemented and covered by the cited bounded path", + "PARTIAL": "Implemented for a declared subset or approximate mapping", + "METADATA_ONLY": "Exposed to the viewport but not visually evaluated", + "BLOCKED": "Not implemented or not parity-safe", + "NOT_APPLICABLE": "The stage does not mutate or visually consume this identity/status field" + }, + "domains": [ + { + "domain": "CAMERA", + "sourceInterface": "CameraIR", + "evidence": [ + "web/protocol/scene-ir.ts", + "web/protocol/web-engine.ts", + "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", + "tools/web/check-lighting-roundtrip.mjs", + "web/app/src/three-adapter/viewport.ts", + "web/app/src/workers/viewport-render.worker.ts" + ], + "fields": [ + { "path": "id", "reader": "VERIFIED", "writer": "NOT_APPLICABLE", "viewportMain": "NOT_APPLICABLE", "viewportOffscreen": "NOT_APPLICABLE", "parity": "COMPLETE" }, + { "path": "name", "reader": "VERIFIED", "writer": "NOT_APPLICABLE", "viewportMain": "NOT_APPLICABLE", "viewportOffscreen": "NOT_APPLICABLE", "parity": "COMPLETE" }, + { "path": "projection", "reader": "VERIFIED", "writer": "PARTIAL", "viewportMain": "PARTIAL", "viewportOffscreen": "PARTIAL", "parity": "PARTIAL", "note": "Writer supports Perspective/Orthographic; viewport still uses PerspectiveCamera and blocks Panoramic/Custom parity." }, + { "path": "lensMm", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "sensorWidthMm", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "sensorHeightMm", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "sensorFit", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "shift", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "PARTIAL", "viewportOffscreen": "PARTIAL", "parity": "PARTIAL", "note": "Horizontal film offset is mapped; vertical shift is not." }, + { "path": "near", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "far", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "orthoScale", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "panoramaType", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "fisheyeFov", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "depthOfField.enabled", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "depthOfField.focusObjectId", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "depthOfField.focusDistance", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "depthOfField.apertureFStop", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "depthOfField.apertureBlades", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "depthOfField.apertureRotation", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "depthOfField.apertureRatio", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" } + ] + }, + { + "domain": "LIGHT", + "sourceInterface": "LightIR", + "evidence": [ + "web/protocol/scene-ir.ts", + "web/protocol/web-engine.ts", + "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", + "tools/web/check-lighting-roundtrip.mjs", + "web/app/src/three-adapter/pbr.ts" + ], + "fields": [ + { "path": "id", "reader": "VERIFIED", "writer": "NOT_APPLICABLE", "viewportMain": "NOT_APPLICABLE", "viewportOffscreen": "NOT_APPLICABLE", "parity": "COMPLETE" }, + { "path": "name", "reader": "VERIFIED", "writer": "NOT_APPLICABLE", "viewportMain": "NOT_APPLICABLE", "viewportOffscreen": "NOT_APPLICABLE", "parity": "COMPLETE" }, + { "path": "lightType", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "PARTIAL" }, + { "path": "color", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "energy", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "exposure", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "temperature", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "useTemperature", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "castsShadow", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "radius", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "spotAngle", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "spotBlend", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "areaShape", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "PARTIAL", "viewportOffscreen": "PARTIAL", "parity": "PARTIAL", "note": "All Area shapes currently map to RectAreaLight." }, + { "path": "areaSize", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "areaSizeY", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "areaSpread", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "sunAngle", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" } + ] + }, + { + "domain": "WORLD", + "sourceInterface": "WorldIR", + "evidence": [ + "web/protocol/scene-ir.ts", + "web/protocol/web-engine.ts", + "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", + "tools/web/check-lighting-roundtrip.mjs", + "web/app/src/three-adapter/viewport.ts", + "web/app/src/three-adapter/texture-assets.ts" + ], + "fields": [ + { "path": "id", "reader": "VERIFIED", "writer": "NOT_APPLICABLE", "viewportMain": "NOT_APPLICABLE", "viewportOffscreen": "NOT_APPLICABLE", "parity": "COMPLETE" }, + { "path": "name", "reader": "VERIFIED", "writer": "NOT_APPLICABLE", "viewportMain": "NOT_APPLICABLE", "viewportOffscreen": "NOT_APPLICABLE", "parity": "COMPLETE" }, + { "path": "color", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "COMPLETE" }, + { "path": "exposure", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "PARTIAL", "viewportOffscreen": "PARTIAL", "parity": "PARTIAL", "note": "World exposure is used only when Scene exposure is absent." }, + { "path": "environmentImageId", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "PARTIAL" }, + { "path": "environmentStrength", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "PARTIAL" }, + { "path": "environmentRotation", "reader": "BLOCKED", "writer": "BLOCKED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "backgroundVisible", "reader": "PARTIAL", "writer": "BLOCKED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "PARTIAL", "note": "Reader currently emits true rather than resolving the full node visibility graph." }, + { "path": "mist.enabled", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "METADATA_ONLY", "viewportOffscreen": "METADATA_ONLY", "parity": "PARTIAL" }, + { "path": "mist.type", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "METADATA_ONLY", "viewportOffscreen": "METADATA_ONLY", "parity": "PARTIAL" }, + { "path": "mist.start", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "METADATA_ONLY", "viewportOffscreen": "METADATA_ONLY", "parity": "PARTIAL" }, + { "path": "mist.depth", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "METADATA_ONLY", "viewportOffscreen": "METADATA_ONLY", "parity": "PARTIAL" }, + { "path": "mist.intensity", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "METADATA_ONLY", "viewportOffscreen": "METADATA_ONLY", "parity": "PARTIAL" }, + { "path": "mist.height", "reader": "VERIFIED", "writer": "VERIFIED", "viewportMain": "METADATA_ONLY", "viewportOffscreen": "METADATA_ONLY", "parity": "PARTIAL" } + ] + }, + { + "domain": "SCENE_COLOR_MANAGEMENT", + "sourceInterface": "SceneIR", + "evidence": [ + "web/protocol/scene-ir.ts", + "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", + "tools/web/check-lighting-roundtrip.mjs", + "web/app/src/three-adapter/viewport.ts", + "web/app/src/workers/viewport-render.worker.ts" + ], + "fields": [ + { "path": "renderEngine", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "colorManagement.displayDevice", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "colorManagement.viewTransform", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "METADATA_ONLY", "viewportOffscreen": "METADATA_ONLY", "parity": "PARTIAL" }, + { "path": "colorManagement.look", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "METADATA_ONLY", "viewportOffscreen": "METADATA_ONLY", "parity": "PARTIAL" }, + { "path": "colorManagement.exposure", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "VERIFIED", "viewportOffscreen": "VERIFIED", "parity": "PARTIAL" }, + { "path": "colorManagement.gamma", "reader": "VERIFIED", "writer": "BLOCKED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "colorManagement.temperature", "reader": "PARTIAL", "writer": "BLOCKED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "colorManagement.tint", "reader": "PARTIAL", "writer": "BLOCKED", "viewportMain": "BLOCKED", "viewportOffscreen": "BLOCKED", "parity": "BLOCKED" }, + { "path": "colorManagement.whiteBalanceStatus", "reader": "VERIFIED", "writer": "NOT_APPLICABLE", "viewportMain": "NOT_APPLICABLE", "viewportOffscreen": "NOT_APPLICABLE", "parity": "COMPLETE" } + ] + } + ] +} diff --git a/tests/golden/M11-02/lighting-field-roundtrip.json b/tests/golden/M11-02/lighting-field-roundtrip.json new file mode 100644 index 00000000..47a27a51 --- /dev/null +++ b/tests/golden/M11-02/lighting-field-roundtrip.json @@ -0,0 +1,46 @@ +{ + "camera": { + "lensMm": 35, + "sensorWidthMm": 32, + "sensorHeightMm": 18, + "sensorFit": 2, + "shift": [0.1, -0.2], + "near": 0.2, + "far": 500, + "orthoScale": 8, + "viewportFov": 28.841546, + "viewportHorizontalFov": 49.134343, + "viewportFilmGauge": 18, + "viewportFilmOffset": 1.8 + }, + "light": { + "color": [0.25, 0.5, 0.75], + "energy": 400, + "exposure": 1, + "temperature": 5000, + "useTemperature": true, + "castsShadow": false, + "radius": 0.3, + "spotAngle": 1.1, + "spotBlend": 0.25, + "areaSize": 3, + "areaSizeY": 2, + "areaSpread": 2.4, + "sunAngle": 0.1, + "viewportIntensity": 80, + "viewportColor": [0.25, 0.3910381443, 0.4834313443] + }, + "world": { + "color": [0.1, 0.2, 0.3], + "exposure": 0.5, + "mist": { + "enabled": true, + "type": "LINEAR", + "start": 2, + "depth": 50, + "intensity": 0.2, + "height": 3 + } + }, + "tolerance": 0.00001 +} diff --git a/tests/golden/M11-03/render-resource-budget.json b/tests/golden/M11-03/render-resource-budget.json new file mode 100644 index 00000000..b7f6d7bb --- /dev/null +++ b/tests/golden/M11-03/render-resource-budget.json @@ -0,0 +1,32 @@ +{ + "schemaVersion": 1, + "webgl2": { + "maxLights": 16, + "reservedLights": 2, + "maxShadowMaps": 4, + "reservedShadowMaps": 1, + "shadowMapDimension": 1024, + "maxTextureAssets": 256, + "maxTexturePayloadBytes": 536870912, + "maxTextureGPUBytes": 536870912 + }, + "webgpu": { + "maxLights": 64, + "reservedLights": 2, + "maxShadowMaps": 8, + "reservedShadowMaps": 1, + "shadowMapDimension": 2048, + "maxTextureAssets": 256, + "maxTexturePayloadBytes": 536870912, + "maxTextureGPUBytes": 1073741824 + }, + "overflow": { + "requestedLights": 20, + "renderedLights": 14, + "droppedLights": 6, + "requestedShadowMaps": 14, + "renderedShadowMaps": 3, + "blockedShadowMaps": 11, + "codes": ["GPU_LIGHT_BUDGET_EXCEEDED", "GPU_SHADOW_BUDGET_EXCEEDED"] + } +} diff --git a/tests/golden/M11-04/blender-eevee-reference.png b/tests/golden/M11-04/blender-eevee-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..3eaf7f0d134de7ca7e40bc9227f5a7d882f75fa8 GIT binary patch literal 3941 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 literal 0 HcmV?d00001 diff --git a/tests/golden/M11-04/manifest.json b/tests/golden/M11-04/manifest.json new file mode 100644 index 00000000..4b8b2ea3 --- /dev/null +++ b/tests/golden/M11-04/manifest.json @@ -0,0 +1,30 @@ +{ + "schemaVersion": 1, + "task": "M11-04", + "source": { + "fixture": "tests/files/web/m11_render_reference.blend", + "fixtureSha256": "d2bea55fe4de0b00e73a9241b4d1b0c802c2eb0e6159c0cd48e007b97b9d3963", + "generator": "tools/web/generate-m11-render-reference.py", + "generatorSha256": "118c98c77d98922e72660898bfb65842e11743fc77f4124c9f5088a76cb6a94a", + "blenderVersion": "5.2.0 LTS" + }, + "reference": { + "file": "blender-eevee-reference.png", + "sha256": "f5c22636a232bcbb0ed0f772418cb12fced18ff72f4999804197e42caa61480a", + "width": 256, + "height": 256, + "colorSpace": "SRGB8", + "alphaMode": "STRAIGHT", + "renderEngine": "BLENDER_EEVEE" + }, + "thresholds": { + "maxMeanAbsoluteError": 4, + "maxRootMeanSquaredError": 12, + "maxP95ChannelError": 12, + "maxBadPixelRatio": 0.02, + "badPixelChannelError": 48, + "foregroundDeltaFromReferenceBackground": 40, + "minForegroundIntersectionOverUnion": 0.97, + "maxAlphaCoverageDeltaRatio": 0 + } +} diff --git a/tests/golden/M11-05/render-routing.json b/tests/golden/M11-05/render-routing.json new file mode 100644 index 00000000..238c5e3a --- /dev/null +++ b/tests/golden/M11-05/render-routing.json @@ -0,0 +1,48 @@ +{ + "schemaVersion": 1, + "boundedEevee": { + "target": "WEB_LOCAL_BOUNDED", + "status": "READY", + "capability": "WEB_REALTIME_BOUNDED", + "reason": "BOUNDED_EEVEE" + }, + "cycles": { + "target": "SERVER_JOB", + "withoutEndpoint": { + "status": "BLOCKED", + "code": "SERVER_JOB_UNAVAILABLE", + "reason": "CYCLES_REQUIRES_SERVER" + }, + "withEndpoint": { + "status": "READY", + "capability": "CYCLES_SERVER_RENDER" + } + }, + "complexEevee": { + "target": "SERVER_JOB", + "withoutEndpoint": { + "status": "BLOCKED", + "code": "SERVER_JOB_UNAVAILABLE", + "reason": "COMPLEX_EEVEE_REQUIRES_SERVER" + } + }, + "hardware": { + "target": "SERVER_JOB", + "withoutEndpoint": { + "status": "BLOCKED", + "code": "SERVER_JOB_UNAVAILABLE", + "reason": "HARDWARE_BACKEND_REQUIRES_SERVER" + } + }, + "webgpu": { + "target": "WEB_LOCAL_BOUNDED", + "status": "BLOCKED", + "code": "WEBGPU_RENDERER_UNAVAILABLE" + }, + "unknownEngine": { + "target": "SERVER_JOB", + "status": "BLOCKED", + "capability": "UNSUPPORTED_RENDER_ENGINE", + "code": "PLATFORM_CAPABILITY_UNAVAILABLE" + } +} diff --git a/tests/golden/M11-06/server-render-job.json b/tests/golden/M11-06/server-render-job.json new file mode 100644 index 00000000..4f14344b --- /dev/null +++ b/tests/golden/M11-06/server-render-job.json @@ -0,0 +1,18 @@ +{ + "schemaVersion": 1, + "task": "M11-06", + "blenderVersion": "5.2.0", + "sourceBlendSha256": "d2bea55fe4de0b00e73a9241b4d1b0c802c2eb0e6159c0cd48e007b97b9d3963", + "settingsSha256": "97bb832618bd27e8763722afa0d250ef74383fb355f0ff38763930f57ba1590b", + "output": { + "mime": "image/png", + "minimumByteLength": 1, + "sha256Pattern": "^[a-f0-9]{64}$" + }, + "tamperCodes": { + "source": "SERVER_RENDER_SOURCE_HASH_MISMATCH", + "build": "SERVER_RENDER_BUILD_MISMATCH", + "settings": "SERVER_RENDER_SETTINGS_HASH_MISMATCH", + "output": "SERVER_RENDER_OUTPUT_HASH_MISMATCH" + } +} diff --git a/tests/golden/M11-07/compositor-node-golden.json b/tests/golden/M11-07/compositor-node-golden.json new file mode 100644 index 00000000..1f3ae5b0 --- /dev/null +++ b/tests/golden/M11-07/compositor-node-golden.json @@ -0,0 +1,46 @@ +{ + "schemaVersion": 1, + "task": "M11-07", + "fixture": { + "path": "tests/files/web/m11_compositor_allowlist.blend", + "sha256": "e844eba69002101c8cf4f376cdd844c4be78729091627d898634088707374b29", + "generator": "tools/web/generate-m11-compositor-allowlist.py", + "generatorSha256": "1389998aac849065311c85ef559a7d381274c0ebe1987afc3b6783c66a9a678e", + "blenderVersion": "5.2.0" + }, + "width": 2, + "height": 2, + "maxAbsoluteError": 0, + "allowlist": ["CONSTANT_COLOR", "EXPOSURE", "INVERT", "COMPOSITE"], + "cases": [ + { + "scene": "M11 Constant", + "newNode": "CONSTANT_COLOR", + "nodeTypes": ["CONSTANT_COLOR", "COMPOSITE"], + "pixel": [0.125, 0.25, 0.5, 0.75], + "float32Sha256": "3664d5dd48f309ba6a45e84ad84bf931c7775de21b2f8eef255651743dcc7bf5" + }, + { + "scene": "M11 Exposure", + "newNode": "EXPOSURE", + "nodeTypes": ["CONSTANT_COLOR", "EXPOSURE", "COMPOSITE"], + "pixel": [0.25, 0.5, 1, 0.75], + "float32Sha256": "0c8caa21f76305cf3fc3a677f5c7af34811a0b29aaa035a5f55be15e2d89accd" + }, + { + "scene": "M11 Invert", + "newNode": "INVERT", + "nodeTypes": ["CONSTANT_COLOR", "INVERT", "COMPOSITE"], + "pixel": [0.875, 0.75, 0.5, 0.75], + "float32Sha256": "d96848cf025a567550b7de8a3fcfb304594bb3fbad5c8722fa39f55e23845c55" + }, + { + "scene": "M11 Chain", + "newNode": "INTEGRATION", + "nodeTypes": ["CONSTANT_COLOR", "EXPOSURE", "INVERT", "COMPOSITE"], + "pixel": [0.75, 0.5, 0, 0.75], + "float32Sha256": "20394b91c848df1aca00179b6785df2d5b38d44c333c9d9d1c926fe674f03a04" + } + ], + "blockedNodeTypes": ["ALPHA_OVER", "BLUR", "IMAGE", "MIX", "RENDER_LAYER", "TRANSFORM", "UNSUPPORTED", "VIEWER"] +} diff --git a/tests/golden/M11-08/compositor-unsupported-gate.json b/tests/golden/M11-08/compositor-unsupported-gate.json new file mode 100644 index 00000000..f423fd70 --- /dev/null +++ b/tests/golden/M11-08/compositor-unsupported-gate.json @@ -0,0 +1,19 @@ +{ + "schemaVersion": 1, + "task": "M11-08", + "fixture": { + "path": "tests/files/web/compositor_scene.blend", + "sha256": "348eeefc28a0fdaa2d55b3c7251a737e0a04d82f61dd1eba804662a8f03713b9", + "generator": "tools/web/generate-compositor-fixture.py", + "generatorSha256": "e6ca40c989fb4bf1a26a34aa55861c2979142817a4cbe4e2628f4d0318775dc0", + "blenderVersion": "5.2.0" + }, + "scene": "CompositorScene", + "unsupportedNode": { + "name": "PreservedUnsupportedGlare", + "type": "UNSUPPORTED", + "blenderType": "CompositorNodeGlare" + }, + "expectedErrorCode": "COMPOSITOR_NODE_UNSUPPORTED", + "expectedRevisionDelta": 0 +} diff --git a/tests/golden/M11-09/sequencer-codec-probe.json b/tests/golden/M11-09/sequencer-codec-probe.json new file mode 100644 index 00000000..c02bdf09 --- /dev/null +++ b/tests/golden/M11-09/sequencer-codec-probe.json @@ -0,0 +1,41 @@ +{ + "schemaVersion": 1, + "task": "M11-09", + "assets": [ + { + "stripType": "IMAGE", + "mimeType": "image/png", + "path": "tests/files/web/media/sequencer-frame.png", + "sourcePath": "media/not-an-image.movie", + "byteLength": 261, + "sha256": "295b083db2299ab904947eb39c17173de70c211882b0d855537d8f3cc27698ed", + "backend": "IMAGE_BITMAP", + "decoded": { "width": 8, "height": 8 } + }, + { + "stripType": "SOUND", + "mimeType": "audio/wav", + "path": "tests/files/web/media/sequencer-silence.wav", + "sourcePath": "media/not-a-sound.png", + "byteLength": 16044, + "sha256": "56d4af65701c26df20bd4021eda95b6e830348ce3a746086079fe89285548dc9", + "backend": "WEB_AUDIO", + "decoded": { "sampleRate": 48000, "channels": 1, "durationFrames": 48000 } + }, + { + "stripType": "MOVIE", + "mimeType": "video/mp4", + "path": "tests/files/web/media/sequencer-probe.mp4", + "sourcePath": "media/not-a-movie.wav", + "byteLength": 1484, + "sha256": "f35a5a2765aef9d0fed7146108c699d0beb03f34414cb27e6b7a7d4871187b65", + "backend": "HTML_MEDIA", + "decoded": { "width": 16, "height": 16, "durationMicros": 1000000 } + } + ], + "movieGenerator": { + "path": "tools/web/generate-sequencer-codec-fixture.sh", + "sha256": "bab7b0b56a16b96fd87302894c1c35d72ea316542d0ffffe9610458e47875b7a" + }, + "blockedCode": "SEQUENCER_CODEC_UNSUPPORTED" +} diff --git a/tests/golden/M11-10/sequencer-media-cache.json b/tests/golden/M11-10/sequencer-media-cache.json new file mode 100644 index 00000000..d0c4d9a8 --- /dev/null +++ b/tests/golden/M11-10/sequencer-media-cache.json @@ -0,0 +1,14 @@ +{ + "schemaVersion": 1, + "task": "M11-10", + "sourceSha256": "f35a5a2765aef9d0fed7146108c699d0beb03f34414cb27e6b7a7d4871187b65", + "profile": { + "kind": "MOVIE_RGBA8_FRAME", + "width": 8, + "height": 8, + "colorSpace": "SRGB8", + "alphaMode": "STRAIGHT" + }, + "identitySha256": "68a3af14865841e81f69bd75f2605461de4819fe025d158ac3723fa0cdf31525", + "proxyByteLength": 256 +} diff --git a/tests/golden/M11-11/sequencer-media-revision.json b/tests/golden/M11-11/sequencer-media-revision.json new file mode 100644 index 00000000..205dc393 --- /dev/null +++ b/tests/golden/M11-11/sequencer-media-revision.json @@ -0,0 +1,14 @@ +{ + "schemaVersion": 1, + "task": "M11-11", + "sourceSha256": "f35a5a2765aef9d0fed7146108c699d0beb03f34414cb27e6b7a7d4871187b65", + "decisions": [ + ["SCRUB", 2, "PUBLISH", null], + ["SEEK", 1, "STALE", "REVISION_CONFLICT"], + ["DECODE", 3, "STALE", "REVISION_CONFLICT"], + ["DECODE", 5, "PUBLISH", null], + ["SEEK", 6, "STALE", "REVISION_CONFLICT"] + ], + "published": ["SCRUB@2", "DECODE@5"], + "cacheWrites": ["DECODE@5"] +} diff --git a/tests/golden/M11-12/sequencer-final-export.json b/tests/golden/M11-12/sequencer-final-export.json new file mode 100644 index 00000000..81adc084 --- /dev/null +++ b/tests/golden/M11-12/sequencer-final-export.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "task": "M11-12", + "sourceBlendSha256": "5f5212487bb6b5df62b5ca915c75133f1ca45678614712ea4c7d902d823cd90c", + "settingsSha256": "01e39d1fdd88c75aecad7bc48a2b756bbcd645929963528b42ceb180b51e5566", + "requestSha256": "20d3a9e333804e014a993a9892a557842293fc44902229dd6eb65661f0691cfc", + "withoutServer": { + "schemaVersion": 1, + "requestSha256": "20d3a9e333804e014a993a9892a557842293fc44902229dd6eb65661f0691cfc", + "settingsSha256": "01e39d1fdd88c75aecad7bc48a2b756bbcd645929963528b42ceb180b51e5566", + "route": "SERVER_EXPORT", + "status": "BLOCKED", + "code": "SEQUENCER_EXPORT_SERVER_UNAVAILABLE", + "localEncoding": "BLOCKED", + "browserVideoEncoderDetected": false + }, + "withServer": { + "schemaVersion": 1, + "requestSha256": "20d3a9e333804e014a993a9892a557842293fc44902229dd6eb65661f0691cfc", + "settingsSha256": "01e39d1fdd88c75aecad7bc48a2b756bbcd645929963528b42ceb180b51e5566", + "route": "SERVER_EXPORT", + "status": "SERVER_EXPORT_REQUIRED", + "code": null, + "localEncoding": "BLOCKED", + "browserVideoEncoderDetected": false + } +} diff --git a/tests/golden/M11-13/sequencer-audio-recovery.json b/tests/golden/M11-13/sequencer-audio-recovery.json new file mode 100644 index 00000000..727c1cf9 --- /dev/null +++ b/tests/golden/M11-13/sequencer-audio-recovery.json @@ -0,0 +1,16 @@ +{ + "schemaVersion": 1, + "task": "M11-13", + "outputGain": 0.75, + "lifecycle": [ + ["SUSPENDED", "SILENT", false, 0.75, null], + ["SUSPENDED", "SILENT", true, 0, null], + ["RUNNING", "SILENT", true, 0, null], + ["RUNNING", "ENABLED", false, 0.75, null], + ["SUSPENDED", "SILENT", false, 0.75, null], + ["RUNNING", "ENABLED", false, 0.75, null], + ["CLOSED", "SILENT", false, 0, null] + ], + "missingDevice": ["UNAVAILABLE", "BLOCKED", "SEQUENCER_AUDIO_DEVICE_UNAVAILABLE"], + "resumeFailure": ["SUSPENDED", "SILENT", "SEQUENCER_AUDIO_RESUME_FAILED"] +} diff --git a/tests/golden/M8-19/generated-smoke-x.rgba b/tests/golden/M8-19/generated-smoke-x.rgba new file mode 100644 index 0000000000000000000000000000000000000000..6d948ef39d3d9e774110edeae82e32ca4de56244 GIT binary patch literal 16384 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 crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); + +if (!fs.existsSync(generator)) throw new Error(`VDB_VOLUME_GOLDEN_TOOL_MISSING: ${generator}`); +if (!fs.existsSync(source)) throw new Error(`VDB_VOLUME_GOLDEN_SOURCE_MISSING: ${source}`); +fs.mkdirSync(outputRoot, { recursive: true }); + +try { + const result = spawnSync(generator, [source, outputPrefix], { encoding: "utf8" }); + if (result.status !== 0) throw new Error(result.stderr || `vdb_volume_golden exited ${result.status}`); + const native = JSON.parse(result.stdout.trim()); + const axes = ["X", "Y", "Z"]; + const images = axes.map((axis) => { + const fileName = `generated-smoke-${axis.toLowerCase()}.rgba`; + const file = path.join(outputRoot, fileName); + const bytes = fs.readFileSync(file); + let alphaPixels = 0; + for (let index = 3; index < bytes.byteLength; index += 4) if (bytes[index] > 0) alphaPixels++; + return { axis, file: fileName, byteLength: bytes.byteLength, sha256: sha256(file), alphaPixels }; + }); + const manifest = { + schemaVersion: 1, + taskId: "M8-19", + source: { + resourceId: "generated-smoke-vdb", + sha256: sha256(source), + grid: native.grid, + activeVoxelCount: native.activeVoxelCount, + indexBounds: native.indexBounds, + }, + native: { + openVDBVersion: native.openVDBVersion, + generatorSourceSha256: sha256(path.join(root, "tools", "vdb", "vdb_volume_golden.cc")), + }, + renderContract: { + shaderSemanticVersion: "volume-wgsl-v1", + width: native.width, + height: native.height, + format: "RGBA8_UNORM", + interpolation: "LINEAR", + densityScale: 1, + emissionScale: 0, + anisotropy: 0, + color: [0.72, 0.78, 0.86], + axes, + thresholds: { + maxChannelError: 2, + meanAbsoluteError: 0.1, + rmsError: 0.5, + alphaCoverageDeltaRatio: 0.002, + }, + }, + images, + }; + const manifestText = `${JSON.stringify(manifest, null, 2)}\n`; + const manifestFile = path.join(outputRoot, "manifest.json"); + fs.writeFileSync(manifestFile, manifestText); + + if (check) { + for (const image of images) { + const expected = fs.readFileSync(path.join(committedRoot, image.file)); + const actual = fs.readFileSync(path.join(outputRoot, image.file)); + if (!expected.equals(actual)) throw new Error(`VDB_VOLUME_GOLDEN_MISMATCH: ${image.file}`); + } + const expectedManifest = fs.readFileSync(path.join(committedRoot, "manifest.json"), "utf8"); + if (expectedManifest !== manifestText) throw new Error("VDB_VOLUME_GOLDEN_MISMATCH: manifest.json"); + } + process.stdout.write(`vdb-volume-golden-${check ? "check" : "generated"} axes=3 bytes=${images.reduce((total, image) => total + image.byteLength, 0)} sha256=${images.map((image) => image.sha256).join(",")}\n`); +} +finally { + if (check) fs.rmSync(outputRoot, { recursive: true, force: true }); +} diff --git a/tools/vdb/vdb_volume_golden.cc b/tools/vdb/vdb_volume_golden.cc new file mode 100644 index 00000000..899c8e0a --- /dev/null +++ b/tools/vdb/vdb_volume_golden.cc @@ -0,0 +1,179 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +namespace { + +constexpr int kImageSize = 64; +constexpr std::array kColor = {0.72f, 0.78f, 0.86f}; + +float sample_linear(const openvdb::FloatGrid::ConstAccessor &accessor, + const std::array &position) +{ + const std::array base = { + static_cast(std::floor(position[0])), + static_cast(std::floor(position[1])), + static_cast(std::floor(position[2])), + }; + const std::array fraction = { + position[0] - static_cast(base[0]), + position[1] - static_cast(base[1]), + position[2] - static_cast(base[2]), + }; + float value = 0.0f; + for (int x = 0; x < 2; ++x) { + for (int y = 0; y < 2; ++y) { + for (int z = 0; z < 2; ++z) { + const float weight = (x ? fraction[0] : 1.0f - fraction[0]) * + (y ? fraction[1] : 1.0f - fraction[1]) * + (z ? fraction[2] : 1.0f - fraction[2]); + value += accessor.getValue(openvdb::Coord(base[0] + x, base[1] + y, base[2] + z)) * + weight; + } + } + } + return value; +} + +uint8_t pack_unorm(float value) +{ + return static_cast(std::lround(std::clamp(value, 0.0f, 1.0f) * 255.0f)); +} + +std::vector render_axis(const openvdb::FloatGrid &density, + const openvdb::CoordBBox &bounds, + int view_axis) +{ + const auto accessor = density.getConstAccessor(); + const openvdb::Coord minimum = bounds.min(); + const openvdb::Coord maximum = bounds.max(); + const std::array plane_a = view_axis == 0 ? std::array{1, 2, 0} : + view_axis == 1 ? std::array{0, 2, 1} : + std::array{0, 1, 2}; + const int ray_axis = plane_a[2]; + const int ray_min = minimum[ray_axis]; + const int ray_max = maximum[ray_axis]; + const int ray_count = std::max(1, ray_max - ray_min + 1); + const int stride = std::max(1, (ray_count + 255) / 256); + const float voxel_size = std::max(0.01f, static_cast(density.voxelSize()[ray_axis])); + const float phase = 1.0f / 12.5663706f; + const float source_scale = 0.5f + 8.0f * phase; + std::vector pixels(kImageSize * kImageSize * 4, 0); + + for (int y = 0; y < kImageSize; ++y) { + for (int x = 0; x < kImageSize; ++x) { + const std::array plane_min = {minimum[plane_a[0]], minimum[plane_a[1]]}; + const std::array plane_max = {maximum[plane_a[0]], maximum[plane_a[1]]}; + const std::array extent = { + static_cast(plane_max[0] - plane_min[0] + 1), + static_cast(plane_max[1] - plane_min[1] + 1), + }; + const std::array plane_position = { + static_cast(plane_min[0]) + + ((static_cast(x) + 0.5f) / static_cast(kImageSize)) * extent[0] - 0.5f, + static_cast(plane_min[1]) + + ((static_cast(y) + 0.5f) / static_cast(kImageSize)) * extent[1] - 0.5f, + }; + + float transmittance = 1.0f; + std::array radiance = {0.0f, 0.0f, 0.0f}; + for (int ray = ray_min; ray <= ray_max; ray += stride) { + std::array position{}; + position[plane_a[0]] = plane_position[0]; + position[plane_a[1]] = plane_position[1]; + position[ray_axis] = static_cast(ray) + 0.5f; + const float sampled_density = std::max(0.0f, sample_linear(accessor, position)); + const float alpha = 1.0f - std::exp(-sampled_density * voxel_size * static_cast(stride)); + for (int channel = 0; channel < 3; ++channel) { + radiance[channel] += transmittance * alpha * kColor[channel] * source_scale; + } + transmittance *= 1.0f - alpha; + if (transmittance < 0.005f) { + break; + } + } + + const size_t offset = static_cast(y * kImageSize + x) * 4; + pixels[offset] = pack_unorm(radiance[0]); + pixels[offset + 1] = pack_unorm(radiance[1]); + pixels[offset + 2] = pack_unorm(radiance[2]); + pixels[offset + 3] = pack_unorm(1.0f - transmittance); + } + } + return pixels; +} + +void write_bytes(const fs::path &path, const std::vector &bytes) +{ + std::ofstream output(path, std::ios::binary | std::ios::trunc); + if (!output) { + throw std::runtime_error("failed to create golden image: " + path.string()); + } + output.write(reinterpret_cast(bytes.data()), static_cast(bytes.size())); + if (!output) { + throw std::runtime_error("failed to write golden image: " + path.string()); + } +} + +} // namespace + +int main(int argc, char **argv) +{ + if (argc != 3) { + std::cerr << "usage: vdb_volume_golden INPUT.vdb OUTPUT_PREFIX\n"; + return 2; + } + + try { + openvdb::initialize(); + const fs::path input = fs::absolute(argv[1]); + const fs::path output_prefix = fs::absolute(argv[2]); + if (input.extension() != ".vdb") { + throw std::runtime_error("input extension must be .vdb"); + } + fs::create_directories(output_prefix.parent_path()); + + openvdb::io::File file(input.string()); + file.open(false); + const openvdb::GridBase::Ptr base = file.readGrid("density"); + file.close(); + const openvdb::FloatGrid::Ptr density = openvdb::gridPtrCast(base); + if (!density || density->getGridClass() != openvdb::GRID_FOG_VOLUME) { + throw std::runtime_error("density must be an OpenVDB FloatGrid fog volume"); + } + const openvdb::CoordBBox bounds = density->evalActiveVoxelBoundingBox(); + if (bounds.empty()) { + throw std::runtime_error("density grid has no active voxels"); + } + + const std::array names = {"x", "y", "z"}; + for (int axis = 0; axis < 3; ++axis) { + write_bytes(output_prefix.string() + "-" + names[axis] + ".rgba", + render_axis(*density, bounds, axis)); + } + std::cout << "{\"schemaVersion\":1,\"openVDBVersion\":\"" + << openvdb::getLibraryVersionString() << "\",\"grid\":\"density\",\"width\":" + << kImageSize << ",\"height\":" << kImageSize << ",\"activeVoxelCount\":" + << density->activeVoxelCount() << ",\"indexBounds\":{\"min\":[" + << bounds.min().x() << ',' << bounds.min().y() << ',' << bounds.min().z() + << "],\"max\":[" << bounds.max().x() << ',' << bounds.max().y() << ',' + << bounds.max().z() << "]}}\n"; + openvdb::uninitialize(); + return 0; + } + catch (const std::exception &error) { + std::cerr << "VDB_VOLUME_GOLDEN_FAILED: " << error.what() << '\n'; + return 1; + } +} diff --git a/tools/web/check-compositor-node-golden.mjs b/tools/web/check-compositor-node-golden.mjs new file mode 100644 index 00000000..ac3e9bbd --- /dev/null +++ b/tools/web/check-compositor-node-golden.mjs @@ -0,0 +1,81 @@ +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { createRequire } from "node:module"; +import { execFileSync } from "node:child_process"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import factory from "../../web/app/src/vendor/blender/web_engine.js"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +const golden = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/M11-07/compositor-node-golden.json"), "utf8")); +const fixturePath = path.join(root, golden.fixture.path); +const generatorPath = path.join(root, golden.fixture.generator); +const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"); +const sha256 = (value) => crypto.createHash("sha256").update(value).digest("hex"); +assert.equal(sha256(fs.readFileSync(fixturePath)), golden.fixture.sha256); +assert.equal(sha256(fs.readFileSync(generatorPath)), golden.fixture.generatorSha256); +assert.match(execFileSync(blender, ["--version"], { encoding: "utf8" }), new RegExp(`^Blender ${golden.fixture.blenderVersion.replaceAll(".", "\\.")}\\b`, "m")); + +const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "m11-compositor-golden-")); +const requireFromWeb = createRequire(path.join(root, "web/package.json")); +const ts = requireFromWeb("typescript"); +for (const [sourceName, outputName] of [["capability-gates.ts", "capability-gates.mjs"], ["compositor.ts", "compositor.mjs"]]) { + const sourcePath = path.join(root, "web/protocol", sourceName); + const transpiled = ts.transpileModule(fs.readFileSync(sourcePath, "utf8"), { + compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 }, + fileName: sourcePath, + reportDiagnostics: true, + }); + assert.deepEqual(transpiled.diagnostics, []); + const output = sourceName === "compositor.ts" ? transpiled.outputText.replaceAll('from "./capability-gates"', 'from "./capability-gates.mjs"') : transpiled.outputText; + fs.writeFileSync(path.join(temporary, outputName), output); +} +const compositor = await import(pathToFileURL(path.join(temporary, "compositor.mjs"))); +const wasmBinary = fs.readFileSync(path.join(root, "web/app/src/vendor/blender/web_engine.wasm")); +const fixture = fs.readFileSync(fixturePath); + +function open(engine, handle, bytes) { + const pointer = engine._malloc(bytes.byteLength); + try { + engine.HEAPU8.set(bytes, pointer); + assert.equal(engine._web_engine_open_blend(handle, pointer, bytes.byteLength), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); + } + finally { engine._free(pointer); } +} + +function snapshot(engine, handle) { + const dataOut = engine._malloc(4); + const lengthOut = engine._malloc(4); + try { + assert.equal(engine._web_engine_get_scene_snapshot(handle, dataOut, lengthOut), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); + const pointer = engine.HEAPU32[dataOut >>> 2]; + const length = engine.HEAPU32[lengthOut >>> 2]; + return JSON.parse(new TextDecoder().decode(engine.HEAPU8.slice(pointer, pointer + length))); + } + finally { engine._free(dataOut); engine._free(lengthOut); } +} + +try { + const engine = await factory({ wasmBinary: wasmBinary.slice() }); + const handle = engine._web_engine_create(); + try { + open(engine, handle, fixture); + const value = snapshot(engine, handle); + for (const candidate of golden.cases) { + const scene = value.scenes.find((item) => item.name === candidate.scene); + assert.equal(scene?.compositorStatus, "AVAILABLE", `${candidate.scene} has no Main compositor graph`); + assert.deepEqual(scene.compositorGraph.nodes.map((node) => node.type), candidate.nodeTypes); + assert.deepEqual(compositor.compileCompositorWebGPUPlan(scene.compositorGraph).instructions.map((instruction) => instruction.type), candidate.nodeTypes); + const cpu = compositor.executeCompositorGraph(scene.compositorGraph, new Map(), { width: golden.width, height: golden.height }); + assert.deepEqual(Array.from(cpu.composite.data.slice(0, 4)), candidate.pixel); + assert.equal(sha256(new Uint8Array(cpu.composite.data.buffer)), candidate.float32Sha256); + } + } + finally { engine._web_engine_destroy(handle); } + process.stdout.write(`compositor-node-golden-ok fixture=${golden.fixture.sha256} scenes=${golden.cases.length} allowlist=${golden.allowlist.join(",")} cpu-hashes=4\n`); +} +finally { + fs.rmSync(temporary, { recursive: true, force: true }); +} diff --git a/tools/web/check-editing-soak-report.mjs b/tools/web/check-editing-soak-report.mjs new file mode 100644 index 00000000..727cb90f --- /dev/null +++ b/tools/web/check-editing-soak-report.mjs @@ -0,0 +1,32 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +const reportPath = path.join(repoRoot, "release/soak-reports/editing.json"); +const report = JSON.parse(fs.readFileSync(reportPath, "utf8")); + +assert.equal(report.schemaVersion, 1); +assert.equal(report.status, "READY"); +assert.equal(report.profile, "FORMAL"); +assert.equal(report.requiredDurationMs, 1_800_000); +assert.ok(report.configuredDurationMs >= report.requiredDurationMs); +assert.ok(report.actualDurationMs >= report.requiredDurationMs); +assert.ok(report.cycles > 100); +assert.equal(report.autosaves, report.cycles); +assert.ok(report.reopens >= 20); +assert.ok(report.finalRevision >= report.initialRevision + report.cycles * 2); +assert.match(report.finalSha256, /^[a-f0-9]{64}$/); +assert.ok(report.finalBytes > 0); +assert.ok(report.finalSnapshotCount <= report.limits.maxSnapshots); +assert.equal(report.downloadCount, 1); +assert.deepEqual(report.pageErrors, []); +assert.equal(report.failure, null); +assert.ok(report.observed.heapGrowthBytes <= report.limits.maxHeapGrowthBytes); +assert.ok(report.observed.storageGrowthBytes <= report.limits.maxStorageGrowthBytes); +assert.ok(report.resourceSamples.length >= report.reopens + 1); +assert.equal(report.resourceSamples[0].label, "baseline"); +assert.equal(report.resourceSamples.at(-1).label, "final"); + +process.stdout.write(`editing-soak-report-ok durationMs=${report.actualDurationMs} cycles=${report.cycles} autosaves=${report.autosaves} reopens=${report.reopens} revision=${report.finalRevision} sha256=${report.finalSha256} heapGrowth=${report.observed.heapGrowthBytes} storageGrowth=${report.observed.storageGrowthBytes}\n`); diff --git a/tools/web/check-geometry-node-evaluator-golden.mjs b/tools/web/check-geometry-node-evaluator-golden.mjs new file mode 100644 index 00000000..4229ac00 --- /dev/null +++ b/tools/web/check-geometry-node-evaluator-golden.mjs @@ -0,0 +1,140 @@ +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import factory from "../../web/app/src/vendor/blender/web_engine.js"; + +const root = new URL("../../", import.meta.url); +const golden = JSON.parse(fs.readFileSync(new URL("tests/golden/M10-03/geometry-node-evaluator.json", root), "utf8")); +const fixture = fs.readFileSync(new URL(golden.fixture, root)); +const wasmBinary = fs.readFileSync(new URL("web/app/src/vendor/blender/web_engine.wasm", root)); + +assert.equal(golden.schemaVersion, 1); +assert.equal(golden.blenderVersion, "5.2.0 LTS"); +assert.equal(crypto.createHash("sha256").update(fixture).digest("hex"), golden.fixtureSha256); +assert.equal(golden.allowlist.length, 16); +assert.deepEqual(Object.keys(golden.nodeCoverage).sort(), [...golden.allowlist].sort()); +for (const nodeType of golden.allowlist) { + assert.ok(golden.nodeCoverage[nodeType].length > 0, `${nodeType} has no desktop fixture coverage`); +} + +function open(engine, handle, bytes) { + const pointer = engine._malloc(bytes.byteLength); + try { + engine.HEAPU8.set(bytes, pointer); + assert.equal(engine._web_engine_open_blend(handle, pointer, bytes.byteLength), 0, + engine.UTF8ToString(engine._web_engine_last_error_message())); + } + finally { + engine._free(pointer); + } +} + +function output(engine, handle, fn) { + const dataOut = engine._malloc(4); + const lengthOut = engine._malloc(4); + try { + assert.equal(fn(handle, dataOut, lengthOut), 0, + engine.UTF8ToString(engine._web_engine_last_error_message())); + const pointer = engine.HEAPU32[dataOut >>> 2]; + const length = engine.HEAPU32[lengthOut >>> 2]; + assert.ok(pointer > 0 && length > 0); + return JSON.parse(new TextDecoder().decode(engine.HEAPU8.slice(pointer, pointer + length))); + } + finally { + engine._free(dataOut); + engine._free(lengthOut); + } +} + +function errors(expected, actual) { + assert.equal(actual.length, expected.length); + return actual.map((value, index) => value - expected[index]); +} + +function compareFloatArray(expected, actual, maximum, rmsMaximum, label) { + const delta = errors(expected, actual); + const maxError = Math.max(0, ...delta.map((value) => Math.abs(value))); + const rmsError = delta.length === 0 ? 0 : Math.sqrt( + delta.reduce((sum, value) => sum + value * value, 0) / delta.length, + ); + assert.ok(maxError <= maximum, `${label} max error ${maxError} exceeds ${maximum}`); + assert.ok(rmsError <= rmsMaximum, `${label} RMS error ${rmsError} exceeds ${rmsMaximum}`); + return { maxError, rmsError }; +} + +function bounds(positions) { + const result = { min: [Infinity, Infinity, Infinity], max: [-Infinity, -Infinity, -Infinity] }; + for (let index = 0; index < positions.length; index += 3) { + for (let axis = 0; axis < 3; axis++) { + result.min[axis] = Math.min(result.min[axis], positions[index + axis]); + result.max[axis] = Math.max(result.max[axis], positions[index + axis]); + } + } + if (positions.length === 0) return { min: [0, 0, 0], max: [0, 0, 0] }; + return result; +} + +const engine = await factory({ wasmBinary: wasmBinary.slice() }); +const handle = engine._web_engine_create(); +assert.ok(handle > 0); +try { + open(engine, handle, fixture); + const snapshot = output(engine, handle, engine._web_engine_get_scene_snapshot); + const report = output(engine, handle, engine._web_engine_evaluate_depsgraph); + assert.equal(report.engine, "BlenderDepsgraph"); + assert.equal(report.status, "EVALUATED"); + + const graphs = new Map((snapshot.geometryNodeGraphs ?? []).map((graph) => [graph.name, graph])); + const meshes = new Map(report.meshes.map((mesh) => [mesh.objectId, mesh])); + let maximumPositionError = 0; + let maximumRmsError = 0; + for (const expectedCase of golden.cases) { + const graph = graphs.get(expectedCase.graph); + assert.ok(graph, `${expectedCase.name} graph is missing from the Main snapshot`); + assert.deepEqual(graph.nodes.map((node) => node.type).sort(), [...expectedCase.nodeTypes].sort(), + `${expectedCase.name} node inventory`); + + const actual = meshes.get(`object:${expectedCase.name}`); + assert.ok(actual, `${expectedCase.name} evaluated mesh is missing`); + assert.equal(actual.vertexCount, expectedCase.mesh.vertexCount, `${expectedCase.name} vertex count`); + assert.equal(actual.triangleCount, expectedCase.mesh.triangleCount, `${expectedCase.name} triangle count`); + assert.deepEqual(actual.indices, expectedCase.mesh.indices, `${expectedCase.name} topology`); + assert.equal(actual.modifiers.length, 1, `${expectedCase.name} modifier count`); + assert.equal(actual.modifiers[0].status, "EVALUATED", `${expectedCase.name} modifier status`); + + const positionError = compareFloatArray( + expectedCase.mesh.positions, + actual.positions, + golden.tolerance.maxPositionError, + golden.tolerance.rmsPositionError, + `${expectedCase.name} positions`, + ); + maximumPositionError = Math.max(maximumPositionError, positionError.maxError); + maximumRmsError = Math.max(maximumRmsError, positionError.rmsError); + + const actualBounds = bounds(actual.positions); + compareFloatArray(expectedCase.mesh.bounds.min, actualBounds.min, + golden.tolerance.boundsError, golden.tolerance.boundsError, `${expectedCase.name} minimum bounds`); + compareFloatArray(expectedCase.mesh.bounds.max, actualBounds.max, + golden.tolerance.boundsError, golden.tolerance.boundsError, `${expectedCase.name} maximum bounds`); + + assert.deepEqual(Object.keys(actual.attributes ?? {}).sort(), + Object.keys(expectedCase.mesh.attributes).sort(), `${expectedCase.name} attribute names`); + for (const [name, expectedAttribute] of Object.entries(expectedCase.mesh.attributes)) { + const actualAttribute = actual.attributes[name]; + assert.equal(actualAttribute.domain, expectedAttribute.domain, `${expectedCase.name}/${name} domain`); + assert.equal(actualAttribute.dataType, expectedAttribute.dataType, `${expectedCase.name}/${name} data type`); + compareFloatArray(expectedAttribute.values, actualAttribute.values, + golden.tolerance.maxAttributeError, golden.tolerance.maxAttributeError, + `${expectedCase.name}/${name} values`); + } + } + + process.stdout.write( + `geometry-node-evaluator-ok nodes=${golden.allowlist.length} cases=${golden.cases.length} ` + + `max-position-error=${maximumPositionError} max-rms-error=${maximumRmsError} attributes=passed\n`, + ); +} +finally { + engine._web_engine_destroy(handle); +} diff --git a/tools/web/check-geometry-node-main-reader.mjs b/tools/web/check-geometry-node-main-reader.mjs new file mode 100644 index 00000000..d56bdaf0 --- /dev/null +++ b/tools/web/check-geometry-node-main-reader.mjs @@ -0,0 +1,111 @@ +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import factory from "../../web/app/src/vendor/blender/web_engine.js"; + +const root = new URL("../../", import.meta.url); +const wasmBinary = fs.readFileSync(new URL("web/app/src/vendor/blender/web_engine.wasm", root)); +const expected = JSON.parse(fs.readFileSync(new URL("tests/golden/M10-01/geometry-node-main-reader.json", root), "utf8")); +const fixture = fs.readFileSync(new URL(expected.fixture, root)); +const desktopGoldenBytes = fs.readFileSync(new URL(expected.desktopGolden, root)); +const desktopGolden = JSON.parse(desktopGoldenBytes.toString("utf8")); + +assert.equal(crypto.createHash("sha256").update(fixture).digest("hex"), expected.fixtureSha256); +assert.equal(crypto.createHash("sha256").update(desktopGoldenBytes).digest("hex"), expected.desktopGoldenSha256); + +function open(engine, handle, bytes) { + const pointer = engine._malloc(bytes.byteLength); + try { + engine.HEAPU8.set(bytes, pointer); + assert.equal(engine._web_engine_open_blend(handle, pointer, bytes.byteLength), 0, + engine.UTF8ToString(engine._web_engine_last_error_message())); + } + finally { engine._free(pointer); } +} + +function output(engine, handle, fn, owned = false) { + const dataOut = engine._malloc(4); + const lengthOut = engine._malloc(4); + try { + assert.equal(fn(handle, dataOut, lengthOut), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); + const pointer = engine.HEAPU32[dataOut >>> 2]; + const length = engine.HEAPU32[lengthOut >>> 2]; + const bytes = engine.HEAPU8.slice(pointer, pointer + length); + if (owned) engine._web_engine_free_buffer(pointer); + return bytes; + } + finally { engine._free(dataOut); engine._free(lengthOut); } +} + +function snapshot(engine, handle) { + return JSON.parse(new TextDecoder().decode(output(engine, handle, engine._web_engine_get_scene_snapshot))); +} + +function socketDefault(graph, key) { + const separator = key.indexOf(":"); + const nodeType = key.slice(0, separator); + const socketName = key.slice(separator + 1); + const node = graph.nodes.find((candidate) => candidate.type === nodeType); + assert.ok(node, `missing node ${nodeType} in ${graph.name}`); + const socket = node.sockets.find((candidate) => candidate.name === socketName); + assert.ok(socket, `missing socket ${nodeType}:${socketName}`); + return socket.defaultValue; +} + +function assertGraphs(scene) { + const graphs = scene.geometryNodeGraphs; + assert.equal(graphs?.length, expected.graphs.length); + const desktopByName = new Map(desktopGolden.nodeGroups.map((graph) => [graph.name, graph])); + const expectedByName = new Map(expected.graphs.map((graph) => [graph.name, graph])); + for (const graph of graphs) { + const manifest = expectedByName.get(graph.name); + const desktop = desktopByName.get(graph.name); + assert.ok(manifest && desktop, `unexpected graph ${graph.name}`); + assert.equal(graph.schemaVersion, 1); + assert.equal(graph.id, `node-group:${graph.name}`); + assert.match(graph.graphHash, /^[0-9a-f]{64}$/); + assert.deepEqual(graph.nodes.map((node) => node.type), manifest.nodeTypes); + const nodeKey = (node) => `${node.name}\u0000${node.type}`; + assert.deepEqual( + [...graph.nodes].map((node) => ({ name: node.name, type: node.type })).sort((a, b) => nodeKey(a).localeCompare(nodeKey(b))), + [...desktop.nodes].sort((a, b) => nodeKey(a).localeCompare(nodeKey(b))), + ); + assert.equal(graph.links.length, manifest.linkCount); + assert.equal(graph.links.length, desktop.links); + assert.deepEqual(graph.interfaceInputs.map((socket) => [socket.direction, socket.dataType]), [["INPUT", "GEOMETRY"]]); + assert.deepEqual(graph.interfaceOutputs.map((socket) => [socket.direction, socket.dataType]), [["OUTPUT", "GEOMETRY"]]); + assert.equal(new Set(graph.nodes.map((node) => node.id)).size, graph.nodes.length); + const nodeById = new Map(graph.nodes.map((node) => [node.id, node])); + for (const node of graph.nodes) { + assert.match(node.id, /^geometry-node:[1-9][0-9]*$/); + assert.equal(node.type.includes("Undefined["), false); + assert.equal(node.sockets.some((socket) => socket.id.endsWith(":__extend__")), false); + assert.equal(new Set(node.sockets.map((socket) => socket.id)).size, node.sockets.length); + } + for (const link of graph.links) { + const fromNode = nodeById.get(link.fromNodeId); + const toNode = nodeById.get(link.toNodeId); + assert.ok(fromNode?.sockets.some((socket) => socket.id === link.fromSocketId && socket.direction === "OUTPUT")); + assert.ok(toNode?.sockets.some((socket) => socket.id === link.toSocketId && socket.direction === "INPUT")); + } + for (const [key, value] of Object.entries(manifest.defaults)) { + assert.deepEqual(socketDefault(graph, key), value); + } + } + return graphs; +} + +const engine = await factory({ wasmBinary: wasmBinary.slice() }); +const handle = engine._web_engine_create(); +open(engine, handle, fixture); +const initialGraphs = assertGraphs(snapshot(engine, handle)); +const saved = output(engine, handle, engine._web_engine_save_blend, true); +engine._web_engine_destroy(handle); + +const reopened = engine._web_engine_create(); +open(engine, reopened, saved); +const reopenedGraphs = assertGraphs(snapshot(engine, reopened)); +assert.deepEqual(reopenedGraphs, initialGraphs); +engine._web_engine_destroy(reopened); + +process.stdout.write("geometry-node-main-reader-ok graphs=3 nodes=10 links=7 defaults=9 stable-id=passed graph-hash=passed desktop-golden=passed save-reopen=passed simulation-preserved=passed\n"); diff --git a/tools/web/check-grease-pencil-roundtrip.mjs b/tools/web/check-grease-pencil-roundtrip.mjs index 1dd5ec79..8ce422bb 100644 --- a/tools/web/check-grease-pencil-roundtrip.mjs +++ b/tools/web/check-grease-pencil-roundtrip.mjs @@ -64,11 +64,19 @@ function assertFixture(scene) { assert.equal(data?.pointCount, 4); const layer = data.layers[0]; const stroke = layer.frames[0].drawing.strokes[0]; + assert.equal(data.activeLayerId, layer.id); assert.equal(layer.name, "Lines"); assert.equal(layer.visible, true); assert.equal(layer.locked, false); assert.equal(stroke.cyclic, false); assert.equal(stroke.materialIndex, 0); + assert.equal(stroke.id, "grease-pencil-stroke:GreasePencilData:0:0"); + assert.deepEqual(stroke.points.map((point) => point.id), [ + "grease-pencil-point:GreasePencilData:0:0:0", + "grease-pencil-point:GreasePencilData:0:0:1", + "grease-pencil-point:GreasePencilData:0:0:2", + "grease-pencil-point:GreasePencilData:0:0:3", + ]); assert.deepEqual(stroke.points[0].position, [-1.5, 0, 0]); close(stroke.points[0].radius, 0.05, "radius"); close(stroke.points[0].opacity, 0.9, "opacity"); @@ -85,9 +93,13 @@ command(engine, handle, { type: "createGreasePencilLayer", dataId, name: "Web Dr let edited = snapshot(engine, handle).greasePencils[0]; assert.equal(edited.layerCount, 2); const layerId = edited.layers.find((layer) => layer.name === "Web Drafts").id; -command(engine, handle, { type: "moveGreasePencilLayer", dataId, layerId, direction: "BOTTOM" }); +command(engine, handle, { type: "moveGreasePencilLayer", dataId, layerId, direction: "BOTTOM", baseRevision: edited.revision }); edited = snapshot(engine, handle).greasePencils[0]; assert.equal(edited.layers[0].id, layerId); +assert.equal(engine._web_engine_undo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); +assert.equal(snapshot(engine, handle).greasePencils[0].layers[1].id, layerId); +assert.equal(engine._web_engine_redo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); +assert.equal(snapshot(engine, handle).greasePencils[0].layers[0].id, layerId); command(engine, handle, { type: "insertGreasePencilFrame", dataId, layerId, frame: 10, duration: 4 }); const webStrokes = [{ cyclic: true, @@ -107,8 +119,18 @@ assert.equal(edited.layers.find((layer) => layer.id === layerId).frames[0].drawi assert.equal(engine._web_engine_undo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); assert.equal(snapshot(engine, handle).greasePencils[0].strokeCount, 1); assert.equal(engine._web_engine_redo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); -assert.equal(snapshot(engine, handle).greasePencils[0].pointCount, 7); -command(engine, handle, { type: "removeGreasePencilFrame", dataId, layerId, frame: 10 }); +edited = snapshot(engine, handle).greasePencils[0]; +assert.equal(edited.pointCount, 7); +const sourceFrame = edited.layers.find((layer) => layer.id === layerId).frames[0]; +command(engine, handle, { type: "moveGreasePencilFrame", dataId, layerId, frame: 10, targetFrame: 12, drawingId: sourceFrame.drawing.id, baseRevision: snapshot(engine, handle).revision }); +edited = snapshot(engine, handle).greasePencils[0]; +assert.equal(edited.layers.find((layer) => layer.id === layerId).frames[0].frame, 12); +assert.equal(edited.layers.find((layer) => layer.id === layerId).frames[0].drawing.id, sourceFrame.drawing.id); +assert.equal(engine._web_engine_undo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); +assert.equal(snapshot(engine, handle).greasePencils[0].layers.find((layer) => layer.id === layerId).frames[0].frame, 10); +assert.equal(engine._web_engine_redo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); +assert.equal(snapshot(engine, handle).greasePencils[0].layers.find((layer) => layer.id === layerId).frames[0].frame, 12); +command(engine, handle, { type: "removeGreasePencilFrame", dataId, layerId, frame: 12 }); assert.equal(snapshot(engine, handle).greasePencils[0].frameCount, 1); assert.equal(engine._web_engine_undo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); assert.equal(snapshot(engine, handle).greasePencils[0].frameCount, 2); @@ -130,8 +152,15 @@ assert.equal(reopenedData.frameCount, 2); assert.equal(reopenedData.strokeCount, 2); assert.equal(reopenedData.pointCount, 7); const reopenedStroke = reopenedData.layers.find((layer) => layer.name === "Web Drafts").frames[0].drawing.strokes[0]; +assert.deepEqual(reopenedData.layers.map((layer) => layer.name), ["Web Drafts", "Lines"]); +assert.equal(reopenedData.layers.find((layer) => layer.name === "Web Drafts").frames[0].frame, 12); +assert.equal(reopenedData.layers.find((layer) => layer.name === "Web Drafts").frames[0].drawing.id, sourceFrame.drawing.id); assert.equal(reopenedStroke.cyclic, true); assert.deepEqual(reopenedStroke.points[2].position, [2, 0, 0]); close(reopenedStroke.points[2].radius, 0.3, "reopened radius"); +assert.deepEqual( + reopenedData.layers.find((layer) => layer.name === "Lines").frames[0].drawing.strokes[0].points.map((point) => point.id), + original.layers[0].frames[0].drawing.strokes[0].points.map((point) => point.id), +); engine._web_engine_destroy(reopened); process.stdout.write("grease-pencil-roundtrip-ok layer-frame-stroke=passed undo-redo=passed save-reopen=passed\n"); diff --git a/tools/web/check-lighting-field-parity.mjs b/tools/web/check-lighting-field-parity.mjs new file mode 100644 index 00000000..e0d64b02 --- /dev/null +++ b/tools/web/check-lighting-field-parity.mjs @@ -0,0 +1,78 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import ts from "../../web/node_modules/typescript/lib/typescript.js"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +const sourcePath = path.join(root, "web/protocol/scene-ir.ts"); +const source = ts.createSourceFile(sourcePath, fs.readFileSync(sourcePath, "utf8"), ts.ScriptTarget.Latest, true); +const manifest = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/M11-01/lighting-field-parity.json"), "utf8")); + +function interfaceDeclaration(name) { + const declaration = source.statements.find((statement) => ts.isInterfaceDeclaration(statement) && statement.name.text === name); + assert.ok(declaration, `missing interface ${name}`); + return declaration; +} + +function leafTypePaths(type, prefix) { + if (!ts.isTypeLiteralNode(type)) return [prefix]; + return type.members.filter(ts.isPropertySignature).flatMap((member) => + leafTypePaths(member.type, `${prefix}.${member.name.getText(source)}`)); +} + +function interfaceLeaves(name, include = () => true) { + return interfaceDeclaration(name).members.filter((member) => + ts.isPropertySignature(member) && include(member.name.getText(source))).flatMap((member) => + leafTypePaths(member.type, member.name.getText(source))).sort(); +} + +const expected = new Map([ + ["CAMERA", interfaceLeaves("CameraIR")], + ["LIGHT", interfaceLeaves("LightIR")], + ["WORLD", interfaceLeaves("WorldIR")], + ["SCENE_COLOR_MANAGEMENT", interfaceLeaves("SceneIR", (name) => name === "renderEngine" || name === "colorManagement")], +]); +const stages = ["reader", "writer", "viewportMain", "viewportOffscreen"]; +const stageStates = new Set(["VERIFIED", "PARTIAL", "METADATA_ONLY", "BLOCKED", "NOT_APPLICABLE"]); +const parityStates = new Set(["COMPLETE", "PARTIAL", "BLOCKED"]); + +assert.equal(manifest.schemaVersion, 1); +assert.equal(manifest.task, "M11-01"); +assert.equal(manifest.blenderVersion, "5.2.0"); +assert.deepEqual(manifest.domains.map((domain) => domain.domain), [...expected.keys()]); + +let total = 0; +const parityCounts = { COMPLETE: 0, PARTIAL: 0, BLOCKED: 0 }; +for (const domain of manifest.domains) { + assert.deepEqual(domain.fields.map((field) => field.path).sort(), expected.get(domain.domain)); + assert.equal(new Set(domain.fields.map((field) => field.path)).size, domain.fields.length); + assert.ok(Array.isArray(domain.evidence) && domain.evidence.length >= 4); + for (const evidence of domain.evidence) assert.ok(fs.existsSync(path.join(root, evidence)), `${domain.domain} evidence is missing: ${evidence}`); + for (const field of domain.fields) { + for (const stage of stages) assert.ok(stageStates.has(field[stage]), `${domain.domain}.${field.path}.${stage} is invalid`); + assert.ok(parityStates.has(field.parity), `${domain.domain}.${field.path}.parity is invalid`); + if (field.parity === "COMPLETE") { + assert.ok(stages.every((stage) => field[stage] === "VERIFIED" || field[stage] === "NOT_APPLICABLE"), `${domain.domain}.${field.path} overclaims COMPLETE`); + } + parityCounts[field.parity] += 1; + total += 1; + } +} + +const requiredBlocks = [ + ["CAMERA", "orthoScale", "viewportMain"], + ["CAMERA", "depthOfField.enabled", "viewportMain"], + ["LIGHT", "areaSpread", "viewportMain"], + ["LIGHT", "sunAngle", "viewportOffscreen"], + ["WORLD", "environmentRotation", "reader"], + ["SCENE_COLOR_MANAGEMENT", "colorManagement.displayDevice", "viewportMain"], + ["SCENE_COLOR_MANAGEMENT", "colorManagement.gamma", "viewportOffscreen"], +]; +for (const [domainName, fieldPath, stage] of requiredBlocks) { + const field = manifest.domains.find((domain) => domain.domain === domainName)?.fields.find((candidate) => candidate.path === fieldPath); + assert.equal(field?.[stage], "BLOCKED", `${domainName}.${fieldPath}.${stage} must remain BLOCKED`); +} + +assert.equal(total, 60); +process.stdout.write(`lighting-field-parity-ok fields=${total} complete=${parityCounts.COMPLETE} partial=${parityCounts.PARTIAL} blocked=${parityCounts.BLOCKED}\n`); diff --git a/tools/web/check-lighting-roundtrip.mjs b/tools/web/check-lighting-roundtrip.mjs index 01a67e4d..940a3aae 100644 --- a/tools/web/check-lighting-roundtrip.mjs +++ b/tools/web/check-lighting-roundtrip.mjs @@ -115,6 +115,13 @@ function assertEdited(scene) { assert.equal(light.useTemperature, true); close(light.temperature, 5000, "light temperature"); assert.deepEqual(light.color, [0.25, 0.5, 0.75]); + close(light.radius, 0.3, "light radius"); + close(light.spotAngle, 1.1, "light spot angle"); + close(light.spotBlend, 0.25, "light spot blend"); + close(light.areaSize, 3, "light area size"); + close(light.areaSizeY, 2, "light area size y"); + close(light.areaSpread, 2.4, "light area spread"); + close(light.sunAngle, 0.1, "light sun angle"); assert.equal(world.mist.enabled, true); assert.equal(world.mist.type, "LINEAR"); close(world.mist.start, 2, "mist start"); @@ -151,7 +158,8 @@ cameraEdited = snapshot(engine, handle); assert.equal(cameraEdited.cameras.find((item) => item.id === before.camera.id).projection, "ORTHOGRAPHIC"); command(engine, handle, { type: "setLightProperties", dataId: before.light.id, properties: { color: [0.25, 0.5, 0.75], energy: 400, exposure: 1, castsShadow: false, - temperature: 5000, useTemperature: true, + temperature: 5000, useTemperature: true, radius: 0.3, spotAngle: 1.1, spotBlend: 0.25, + areaSize: 3, areaSizeY: 2, areaSpread: 2.4, sunAngle: 0.1, } }); assert.equal(snapshot(engine, handle).scenes.find((item) => item.id === before.definition.id).colorManagement.whiteBalanceStatus, "BLOCKED"); command(engine, handle, { type: "setWorldProperties", dataId: before.world.id, properties: { diff --git a/tools/web/check-nla-evaluation-golden.mjs b/tools/web/check-nla-evaluation-golden.mjs new file mode 100644 index 00000000..d8b01be4 --- /dev/null +++ b/tools/web/check-nla-evaluation-golden.mjs @@ -0,0 +1,119 @@ +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import factory from "../../web/app/src/vendor/blender/web_engine.js"; + +const root = new URL("../../", import.meta.url); +const golden = JSON.parse(fs.readFileSync(new URL("tests/golden/M10-11/nla-evaluation.json", root), "utf8")); +const fixture = fs.readFileSync(new URL(golden.fixture, root)); +const wasmBinary = fs.readFileSync(new URL("web/app/src/vendor/blender/web_engine.wasm", root)); + +assert.equal(crypto.createHash("sha256").update(fixture).digest("hex"), golden.fixtureSha256); +assert.equal(golden.schemaVersion, 1); + +function open(engine, handle, bytes) { + const pointer = engine._malloc(bytes.byteLength); + try { + engine.HEAPU8.set(bytes, pointer); + assert.equal(engine._web_engine_open_blend(handle, pointer, bytes.byteLength), 0, + engine.UTF8ToString(engine._web_engine_last_error_message())); + } + finally { engine._free(pointer); } +} + +function output(engine, handle, fn) { + const dataOut = engine._malloc(4); + const lengthOut = engine._malloc(4); + try { + assert.equal(fn(handle, dataOut, lengthOut), 0, + engine.UTF8ToString(engine._web_engine_last_error_message())); + const pointer = engine.HEAPU32[dataOut >>> 2]; + const length = engine.HEAPU32[lengthOut >>> 2]; + assert.ok(pointer > 0 && length > 0); + return JSON.parse(new TextDecoder().decode(engine.HEAPU8.slice(pointer, pointer + length))); + } + finally { + engine._free(dataOut); + engine._free(lengthOut); + } +} + +function setFrame(engine, handle, frame) { + const command = new TextEncoder().encode(JSON.stringify({ type: "setFrame", frame })); + const pointer = engine._malloc(command.byteLength); + try { + engine.HEAPU8.set(command, pointer); + assert.equal(engine._web_engine_apply_command(handle, pointer, command.byteLength), 0, + engine.UTF8ToString(engine._web_engine_last_error_message())); + } + finally { engine._free(pointer); } +} + +function assertClose(expected, actual, label) { + assert.equal(actual.length, expected.length, `${label} length`); + const maximum = Math.max(0, ...expected.map((value, index) => Math.abs(actual[index] - value))); + assert.ok(maximum <= golden.tolerance.maxMatrixError, `${label} max error ${maximum}`); +} + +function assertNlaSnapshot(snapshot) { + assert.equal(snapshot.nlaTracks?.length, golden.tracks.length, "NLA track count"); + const track = snapshot.nlaTracks[0]; + const expectedTrack = golden.tracks[0]; + assert.equal(track.schemaVersion, 1); + assert.equal(track.ownerId, `object:${golden.object}`); + assert.equal(track.name, expectedTrack.name); + assert.equal(track.muted, expectedTrack.muted); + assert.equal(track.solo, expectedTrack.solo); + assert.equal(track.strips.length, expectedTrack.strips.length); + expectedTrack.strips.forEach((expected, index) => { + const actual = track.strips[index]; + assert.equal(actual.id, expected.id); + assert.equal(actual.actionId, `action:${expected.action}:object:${golden.object}`); + for (const field of ["frameStart", "frameEnd", "actionFrameStart", "actionFrameEnd", "scale", "repeat", "blendIn", "blendOut", "influence"]) { + assert.equal(actual[field], expected[field], `${expected.id}.${field}`); + } + for (const field of ["blendMode", "extrapolation", "muted", "reverse"]) { + assert.equal(actual[field], expected[field], `${expected.id}.${field}`); + } + assert.equal(actual.stripType, "CLIP"); + assert.equal(actual.useTimeWarp, false); + }); + for (const expected of expectedTrack.strips) { + const action = snapshot.animations.find((candidate) => candidate.id === `action:${expected.action}:object:${golden.object}`); + assert.ok(action, `${expected.action} animation`); + assert.equal(action.targetId, `object:${golden.object}`); + assert.equal(action.frameStart, 1); + assert.equal(action.frameEnd, 11); + assert.ok(action.channels.some((channel) => channel.path.startsWith("location[")), `${expected.action} location channel`); + } +} + +const engine = await factory({ wasmBinary: wasmBinary.slice() }); +const handle = engine._web_engine_create(); +assert.ok(handle > 0); +try { + open(engine, handle, fixture); + const initial = output(engine, handle, engine._web_engine_get_scene_snapshot); + assertNlaSnapshot(initial); + const initialNla = JSON.stringify({ nlaTracks: initial.nlaTracks, animations: initial.animations }); + let maximumMatrixError = 0; + for (const expected of golden.frames) { + setFrame(engine, handle, expected.frame); + const report = output(engine, handle, engine._web_engine_evaluate_depsgraph); + assert.equal(report.engine, "BlenderDepsgraph"); + assert.equal(report.status, "EVALUATED"); + assert.equal(report.frame, expected.frame); + const mesh = report.meshes.find((candidate) => candidate.objectId === `object:${golden.object}`); + assert.ok(mesh, `evaluated ${golden.object} at frame ${expected.frame}`); + const errors = mesh.worldMatrix.map((value, index) => Math.abs(value - expected.worldMatrix[index])); + maximumMatrixError = Math.max(maximumMatrixError, ...errors); + assertClose(expected.worldMatrix, mesh.worldMatrix, `frame ${expected.frame}`); + const after = output(engine, handle, engine._web_engine_get_scene_snapshot); + assert.equal(JSON.stringify({ nlaTracks: after.nlaTracks, animations: after.animations }), initialNla, + `NLA read-only identity changed at frame ${expected.frame}`); + } + process.stdout.write(`nla-evaluation-ok tracks=${golden.tracks.length} strips=${golden.tracks[0].strips.length} frames=${golden.frames.length} max-matrix-error=${maximumMatrixError} read-only=passed\n`); +} +finally { + engine._web_engine_destroy(handle); +} diff --git a/tools/web/check-paint-roundtrip.mjs b/tools/web/check-paint-roundtrip.mjs index 11eff60b..37616802 100644 --- a/tools/web/check-paint-roundtrip.mjs +++ b/tools/web/check-paint-roundtrip.mjs @@ -151,14 +151,30 @@ close(vertexWeight(snapshot(engine, weightHandle).meshes.find((mesh) => mesh.id assert.equal(engine._web_engine_redo(weightHandle), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); close(vertexWeight(snapshot(engine, weightHandle).meshes.find((mesh) => mesh.id === meshId), 2, "WebPaintGroup"), 0.5 / 1.75, "redone normalized vertex 2 paint weight"); -reject(engine, weightHandle, { +apply(engine, weightHandle, { type: "setVertexWeights", objectId, vertexGroup: "WebPaintGroup", indices: [3], - values: [0.5], + values: [0.4], + limit: 2, + normalize: true, +}); +weightedMesh = snapshot(engine, weightHandle).meshes.find((mesh) => mesh.id === meshId); +close(vertexWeight(weightedMesh, 3, "WebPaintGroup"), 0.4 / 1.4, "limited normalized vertex 3 weight"); +apply(engine, weightHandle, { + type: "setVertexWeights", + objectId, + vertexGroup: "WebPaintGroup", + indices: [0], + values: [0.9], mirror: true, -}, "CAPABILITY_MISSING"); + mirrorAxis: 0, + mirrorTolerance: 1e-4, +}); +weightedMesh = snapshot(engine, weightHandle).meshes.find((mesh) => mesh.id === meshId); +close(vertexWeight(weightedMesh, 0, "WebPaintGroup"), 0.9, "mirrored vertex 0 weight"); +close(vertexWeight(weightedMesh, 1, "WebPaintGroup"), 0.9, "mirrored vertex 1 weight"); const savedWeights = output(engine, weightHandle, engine._web_engine_save_blend, true); engine._web_engine_destroy(weightHandle); @@ -166,8 +182,9 @@ const reopenedWeights = engine._web_engine_create(); open(engine, reopenedWeights, savedWeights); weightedMesh = snapshot(engine, reopenedWeights).meshes.find((mesh) => mesh.id === meshId); assertVertexGroup(weightedMesh, "WebPaintGroup"); -close(vertexWeight(weightedMesh, 0, "WebPaintGroup"), 0.75, "reopened vertex 0 paint weight"); +close(vertexWeight(weightedMesh, 0, "WebPaintGroup"), 0.9, "reopened mirrored vertex 0 paint weight"); +close(vertexWeight(weightedMesh, 1, "WebPaintGroup"), 0.9, "reopened mirrored vertex 1 paint weight"); close(vertexWeight(weightedMesh, 2, "WebPaintGroup"), 0.5 / 1.75, "reopened normalized vertex 2 paint weight"); engine._web_engine_destroy(reopenedWeights); -process.stdout.write("paint-roundtrip-ok vertex-color=passed vertex-weight-normalize=passed mirror-gate=passed undo-redo=passed save-reopen=passed\n"); +process.stdout.write("paint-roundtrip-ok vertex-color=passed vertex-weight-normalize=passed vertex-weight-limit=passed vertex-weight-mirror=passed mirror-gate=passed undo-redo=passed save-reopen=passed\n"); diff --git a/tools/web/check-render-reference.mjs b/tools/web/check-render-reference.mjs new file mode 100644 index 00000000..be60c69c --- /dev/null +++ b/tools/web/check-render-reference.mjs @@ -0,0 +1,50 @@ +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { execFileSync } from "node:child_process"; +import { fileURLToPath } from "node:url"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +const manifestPath = path.join(root, "tests/golden/M11-04/manifest.json"); +const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); +const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); +const fixture = path.join(root, manifest.source.fixture); +const reference = path.join(path.dirname(manifestPath), manifest.reference.file); +const generator = path.join(root, manifest.source.generator); +const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"); + +assert.equal(manifest.schemaVersion, 1); +assert.equal(manifest.task, "M11-04"); +assert.equal(manifest.reference.colorSpace, "SRGB8"); +assert.equal(manifest.reference.alphaMode, "STRAIGHT"); +assert.equal(manifest.reference.width, 256); +assert.equal(manifest.reference.height, 256); +assert.equal(sha256(fixture), manifest.source.fixtureSha256); +assert.equal(sha256(generator), manifest.source.generatorSha256); +assert.equal(sha256(reference), manifest.reference.sha256); +assert.match(execFileSync(blender, ["--version"], { encoding: "utf8" }), /^Blender 5\.2\.0 LTS/m); + +const temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), "m11-render-reference-")); +try { + const generatedFixture = path.join(temporaryRoot, "reference.blend"); + const generatedReference = path.join(temporaryRoot, "reference.png"); + execFileSync(blender, [ + "--background", + "--factory-startup", + "--python", + generator, + "--", + generatedFixture, + generatedReference, + reference, + ], { cwd: root, stdio: "pipe" }); + assert.ok(fs.statSync(generatedFixture).size > 0, "desktop reference fixture was not generated"); + assert.ok(fs.statSync(generatedReference).size > 0, "desktop reference render was not generated"); +} +finally { + fs.rmSync(temporaryRoot, { recursive: true, force: true }); +} + +console.log(`render-reference-ok blender=5.2.0 fixture=${manifest.source.fixtureSha256} image=${manifest.reference.sha256}`); diff --git a/tools/web/check-server-render-job.mjs b/tools/web/check-server-render-job.mjs new file mode 100644 index 00000000..20e5e586 --- /dev/null +++ b/tools/web/check-server-render-job.mjs @@ -0,0 +1,167 @@ +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import fsp from "node:fs/promises"; +import http from "node:http"; +import os from "node:os"; +import path from "node:path"; +import { createRequire } from "node:module"; +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; +import { fileURLToPath } from "node:url"; + +const execFileAsync = promisify(execFile); +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +const fixture = path.join(root, "tests/files/web/m11_render_reference.blend"); +const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"); +const renderScript = path.join(root, "tools/web/render-server-job.py"); +const golden = JSON.parse(await fsp.readFile(path.join(root, "tests/golden/M11-06/server-render-job.json"), "utf8")); +const sourceBytes = await fsp.readFile(fixture); +const sourceArrayBuffer = sourceBytes.buffer.slice(sourceBytes.byteOffset, sourceBytes.byteOffset + sourceBytes.byteLength); +const buildSha256 = crypto.createHash("sha256").update(await fsp.readFile(blender)).digest("hex"); +const versionOutput = (await execFileAsync(blender, ["--version"], { cwd: root })).stdout; +const versionLine = versionOutput.split(/\r?\n/).find((line) => line.startsWith("Blender ")) ?? ""; +const buildVersion = versionLine.match(/^Blender\s+(5\.2\.[0-9]+)\b/)?.[1]; +assert.equal(buildVersion, "5.2.0", `unexpected Blender build: ${versionOutput}`); +assert.equal(golden.schemaVersion, 1); +assert.equal(golden.task, "M11-06"); +assert.equal(golden.blenderVersion, buildVersion); +assert.ok(fs.existsSync(fixture), "M11-04 source fixture is missing"); + +const requireFromWeb = createRequire(path.join(root, "web/package.json")); +const ts = requireFromWeb("typescript"); +const protocolSource = fs.readFileSync(path.join(root, "web/protocol/server-render-job.ts"), "utf8") + .replace('import type { ErrorCode } from "./error";\n', ""); +const transpiled = ts.transpileModule(protocolSource, { + compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 }, + fileName: "server-render-job.ts", + reportDiagnostics: true, +}); +assert.deepEqual(transpiled.diagnostics, []); +const protocol = await import(`data:text/javascript;base64,${Buffer.from(transpiled.outputText).toString("base64")}`); + +const settings = { + renderEngine: "BLENDER_EEVEE", + frameStart: 1, + frameEnd: 1, + resolutionX: 256, + resolutionY: 256, + resolutionPercentage: 100, + samples: 1, + outputMime: "image/png", + transparent: false, +}; +const build = { version: buildVersion, buildSha256 }; +const temporary = await fsp.mkdtemp(path.join(os.tmpdir(), "m11-server-render-job-")); + +function json(response, status, value) { + const body = Buffer.from(`${JSON.stringify(value)}\n`); + response.writeHead(status, { "Content-Type": "application/json", "Cache-Control": "no-store", "Content-Length": body.length }); + response.end(body); +} + +const server = http.createServer(async (request, response) => { + if (request.method !== "POST" || request.url !== "/v1/render/jobs") return json(response, 404, { code: "NOT_FOUND" }); + const chunks = []; + let sourceByteLength = 0; + try { + for await (const chunk of request) { + chunks.push(chunk); + sourceByteLength += chunk.length; + if (sourceByteLength > 512 * 1024 * 1024) throw new Error("SERVER_RENDER_SOURCE_INVALID: upload exceeds budget"); + } + const body = Buffer.concat(chunks); + const requestHeader = request.headers["x-render-request"]; + if (typeof requestHeader !== "string") throw new Error("SERVER_RENDER_REQUEST_INVALID: missing request metadata"); + const renderRequest = await protocol.verifyServerRenderJobRequest(JSON.parse(requestHeader), body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength)); + if (renderRequest.blenderBuild.version !== build.version || renderRequest.blenderBuild.buildSha256 !== build.buildSha256) { + throw Object.assign(new Error("SERVER_RENDER_BUILD_MISMATCH: requested build is not the executable serving this job"), { code: "SERVER_RENDER_BUILD_MISMATCH" }); + } + const fileStem = renderRequest.jobId.replace(/[^A-Za-z0-9_.-]/g, "_"); + const sourcePath = path.join(temporary, `${fileStem}.blend`); + const outputPath = path.join(temporary, `${fileStem}.png`); + const settingsPath = path.join(temporary, `${fileStem}.json`); + await fsp.writeFile(sourcePath, body, { flag: "wx", mode: 0o600 }); + await fsp.writeFile(settingsPath, `${JSON.stringify(renderRequest.settings)}\n`, { flag: "wx", mode: 0o600 }); + await execFileAsync(blender, ["--background", "--factory-startup", "--python", renderScript, "--", sourcePath, outputPath, settingsPath], { cwd: root, maxBuffer: 2 * 1024 * 1024 }); + const outputBytes = await fsp.readFile(outputPath); + const result = await protocol.createServerRenderJobResult(renderRequest, outputBytes.buffer.slice(outputBytes.byteOffset, outputBytes.byteOffset + outputBytes.byteLength)); + json(response, 200, result); + } catch (error) { + json(response, 400, { code: error?.code ?? "SERVER_RENDER_FAILED", message: error instanceof Error ? error.message : String(error) }); + } +}); + +await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", resolve); +}); +const address = server.address(); +assert.ok(address && typeof address !== "string"); +const origin = `http://127.0.0.1:${address.port}`; + +try { + const renderRequest = await protocol.createServerRenderJobRequest(sourceArrayBuffer, build, settings, { jobId: "render:m11-06", sourceRevision: 11 }); + const response = await fetch(`${origin}/v1/render/jobs`, { + method: "POST", + headers: { "Content-Type": "application/x-blend", "X-Render-Request": JSON.stringify(renderRequest) }, + body: sourceBytes, + duplex: "half", + }); + const responseText = await response.text(); + assert.equal(response.status, 200, responseText); + const result = JSON.parse(responseText); + const outputPath = path.join(temporary, "verified-output.png"); + const outputBytes = await fsp.readFile(path.join(temporary, "render_m11-06.png")); + await fsp.writeFile(outputPath, outputBytes); + await protocol.verifyServerRenderJobResult(result, renderRequest, outputBytes.buffer.slice(outputBytes.byteOffset, outputBytes.byteOffset + outputBytes.byteLength)); + assert.equal(result.sourceBlendSha256, renderRequest.sourceBlendSha256); + assert.equal(result.settingsSha256, renderRequest.settingsSha256); + assert.equal(result.sourceBlendSha256, golden.sourceBlendSha256); + assert.equal(result.settingsSha256, golden.settingsSha256); + assert.equal(result.outputMime, golden.output.mime); + assert.ok(result.outputByteLength >= golden.output.minimumByteLength); + assert.match(result.outputSha256, new RegExp(golden.output.sha256Pattern)); + + const tamperedSource = Buffer.from(sourceBytes); + tamperedSource[0] ^= 0xff; + const sourceFailure = await fetch(`${origin}/v1/render/jobs`, { + method: "POST", + headers: { "Content-Type": "application/x-blend", "X-Render-Request": JSON.stringify(renderRequest) }, + body: tamperedSource, + duplex: "half", + }); + assert.equal(sourceFailure.status, 400); + assert.equal((await sourceFailure.json()).code, golden.tamperCodes.source); + + const wrongBuildRequest = await protocol.createServerRenderJobRequest(sourceArrayBuffer, { ...build, buildSha256: "b".repeat(64) }, settings, { jobId: "render:wrong-build", sourceRevision: 11 }); + const buildFailure = await fetch(`${origin}/v1/render/jobs`, { + method: "POST", + headers: { "Content-Type": "application/x-blend", "X-Render-Request": JSON.stringify(wrongBuildRequest) }, + body: sourceBytes, + duplex: "half", + }); + assert.equal(buildFailure.status, 400); + assert.equal((await buildFailure.json()).code, golden.tamperCodes.build); + + const settingsFailureRequest = { ...renderRequest, settings: { ...renderRequest.settings, samples: 2 } }; + const settingsFailure = await fetch(`${origin}/v1/render/jobs`, { + method: "POST", + headers: { "Content-Type": "application/x-blend", "X-Render-Request": JSON.stringify(settingsFailureRequest) }, + body: sourceBytes, + duplex: "half", + }); + assert.equal(settingsFailure.status, 400); + assert.equal((await settingsFailure.json()).code, golden.tamperCodes.settings); + + const tamperedOutput = Buffer.from(outputBytes); + tamperedOutput[tamperedOutput.length - 1] ^= 0xff; + await assert.rejects( + protocol.verifyServerRenderJobResult(result, renderRequest, tamperedOutput.buffer.slice(tamperedOutput.byteOffset, tamperedOutput.byteOffset + tamperedOutput.byteLength)), + { code: golden.tamperCodes.output }, + ); + process.stdout.write(`server-render-job-ok blender=${buildVersion} source=${renderRequest.sourceBlendSha256} settings=${renderRequest.settingsSha256} output=${result.outputSha256} bytes=${result.outputByteLength} source-hash=1 build-hash=1 settings-hash=1 output-hash=1 tamper=4\n`); +} finally { + await new Promise((resolve) => server.close(resolve)); + await fsp.rm(temporary, { recursive: true, force: true }); +} diff --git a/tools/web/check-weight-paint-golden.mjs b/tools/web/check-weight-paint-golden.mjs new file mode 100644 index 00000000..1ab4838b --- /dev/null +++ b/tools/web/check-weight-paint-golden.mjs @@ -0,0 +1,119 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; +import factory from "../../web/app/src/vendor/blender/web_engine.js"; + +const root = new URL("../../", import.meta.url); +const golden = JSON.parse(fs.readFileSync(new URL("tests/golden/M9-12/weight-paint.json", root), "utf8")); +const fixture = fs.readFileSync(new URL(`tests/files/web/${golden.fixture}`, root)); +const wasmBinary = fs.readFileSync(new URL("web/app/src/vendor/blender/web_engine.wasm", root)); + +function call(engine, handle, name, payload) { + const bytes = new TextEncoder().encode(JSON.stringify(payload)); + const pointer = engine._malloc(bytes.byteLength); + try { + engine.HEAPU8.set(bytes, pointer); + assert.equal(engine._web_engine_apply_command(handle, pointer, bytes.byteLength), 0, + `${name}: ${engine.UTF8ToString(engine._web_engine_last_error_message())}`); + } + finally { + engine._free(pointer); + } +} + +function open(engine, handle, bytes) { + const pointer = engine._malloc(bytes.byteLength); + try { + engine.HEAPU8.set(bytes, pointer); + assert.equal(engine._web_engine_open_blend(handle, pointer, bytes.byteLength), 0, + engine.UTF8ToString(engine._web_engine_last_error_message())); + } + finally { + engine._free(pointer); + } +} + +function snapshot(engine, handle) { + const dataOut = engine._malloc(4); + const lengthOut = engine._malloc(4); + try { + assert.equal(engine._web_engine_get_scene_snapshot(handle, dataOut, lengthOut), 0); + const pointer = engine.HEAPU32[dataOut >>> 2]; + const length = engine.HEAPU32[lengthOut >>> 2]; + return JSON.parse(new TextDecoder().decode(engine.HEAPU8.slice(pointer, pointer + length))); + } + finally { + engine._free(dataOut); + engine._free(lengthOut); + } +} + +function save(engine, handle) { + const dataOut = engine._malloc(4); + const lengthOut = engine._malloc(4); + try { + assert.equal(engine._web_engine_save_blend(handle, dataOut, lengthOut), 0); + const pointer = engine.HEAPU32[dataOut >>> 2]; + const length = engine.HEAPU32[lengthOut >>> 2]; + const bytes = engine.HEAPU8.slice(pointer, pointer + length); + engine._web_engine_free_buffer(pointer); + return bytes; + } + finally { + engine._free(dataOut); + engine._free(lengthOut); + } +} + +function weightsByVertex(snapshotValue) { + const mesh = snapshotValue.meshes.find((item) => item.id === `mesh:${golden.mesh}`); + assert.ok(mesh?.skinWeights, "weight golden mesh has no skinWeights"); + return golden.steps[0].vertices.map((expectedVertex) => { + const result = {}; + for (const [groupIndex, groupName] of mesh.skinWeights.boneNames.entries()) { + const offset = expectedVertex.index * 4; + const slot = mesh.skinWeights.indices.slice(offset, offset + 4).findIndex((value) => value === groupIndex); + result[groupName] = slot < 0 ? 0 : mesh.skinWeights.weights[offset + slot]; + } + return result; + }); +} + +function compareStep(actualSnapshot, expected, label) { + const actual = weightsByVertex(actualSnapshot); + assert.deepEqual(actual.map((weights) => Object.keys(weights).sort()), expected.vertices.map(() => expected.groups.slice().sort()), `${label} group schema`); + expected.vertices.forEach((vertex, index) => { + for (const group of expected.groups) { + const error = Math.abs((actual[index][group] ?? 0) - (vertex.weights[group] ?? 0)); + assert.ok(error <= golden.tolerance, `${label} vertex=${vertex.index} group=${group} error=${error}`); + } + }); +} + +const engine = await factory({ wasmBinary: wasmBinary.slice() }); +const handle = engine._web_engine_create(); +assert.ok(handle > 0); +try { + open(engine, handle, fixture); + call(engine, handle, "initial", { type: "setVertexWeights", objectId: `object:${golden.object}`, vertexGroup: "WebPaintGroup", indices: [0, 1], values: [0.75, 0.25] }); + compareStep(snapshot(engine, handle), golden.steps[0], "initial"); + call(engine, handle, "normalize", { type: "setVertexWeights", objectId: `object:${golden.object}`, vertexGroup: "WebPaintGroup", indices: [2], values: [0.5], normalize: true }); + compareStep(snapshot(engine, handle), golden.steps[1], "normalize"); + call(engine, handle, "limit-normalize", { type: "setVertexWeights", objectId: `object:${golden.object}`, vertexGroup: "WebPaintGroup", indices: [3], values: [0.4], limit: 2, normalize: true }); + compareStep(snapshot(engine, handle), golden.steps[2], "limit-normalize"); + call(engine, handle, "mirror", { type: "setVertexWeights", objectId: `object:${golden.object}`, vertexGroup: "WebPaintGroup", indices: [0], values: [0.9], mirror: true, mirrorAxis: 0, mirrorTolerance: 1e-4 }); + compareStep(snapshot(engine, handle), golden.steps[3], "mirror"); + const saved = save(engine, handle); + const reopened = engine._web_engine_create(); + try { + open(engine, reopened, saved); + compareStep(snapshot(engine, reopened), golden.steps[3], "save-reopen"); + } + finally { + engine._web_engine_destroy(reopened); + } +} +finally { + engine._web_engine_destroy(handle); +} + +process.stdout.write(`weight-paint-golden-ok fixture=${golden.fixture} steps=${golden.steps.map((step) => step.name).join(",")} normalize=passed limit=passed mirror=passed save-reopen=passed\n`); diff --git a/tools/web/generate-curve-toggle-golden.py b/tools/web/generate-curve-toggle-golden.py new file mode 100644 index 00000000..0cd8049a --- /dev/null +++ b/tools/web/generate-curve-toggle-golden.py @@ -0,0 +1,51 @@ +import hashlib +import json +import os +import sys +import tempfile + +import bpy + + +def main(): + arguments = sys.argv[sys.argv.index("--") + 1 :] if "--" in sys.argv else [] + if len(arguments) != 2: + raise SystemExit("usage: blender -b fixture.blend --python generate-curve-toggle-golden.py -- fixture.blend output.json") + fixture = os.path.abspath(arguments[0]) + output = os.path.abspath(arguments[1]) + curve = bpy.data.curves.get("WebCurveData") + if curve is None or len(curve.splines) < 1: + raise RuntimeError("WebCurveData fixture is missing its first spline") + before = [spline.use_cyclic_u for spline in curve.splines] + spline_types = [spline.type for spline in curve.splines] + spline_point_counts = [len(spline.bezier_points) if spline.type == "BEZIER" else len(spline.points) for spline in curve.splines] + curve.splines[0].use_cyclic_u = not curve.splines[0].use_cyclic_u + after = [spline.use_cyclic_u for spline in curve.splines] + with tempfile.TemporaryDirectory(prefix="m9-05-curve-toggle-") as temporary: + saved = os.path.join(temporary, "curve-toggle.blend") + bpy.ops.wm.save_as_mainfile(filepath=saved, check_existing=False) + bpy.ops.wm.open_mainfile(filepath=saved) + reopened = [spline.use_cyclic_u for spline in bpy.data.curves["WebCurveData"].splines] + with open(fixture, "rb") as source: + fixture_sha256 = hashlib.sha256(source.read()).hexdigest() + payload = { + "schemaVersion": 1, + "operator": "TOGGLE_CYCLIC", + "fixture": "tests/files/web/nonmesh_scene.blend", + "fixtureSha256": fixture_sha256, + "blenderVersion": ".".join(str(value) for value in bpy.app.version), + "curveName": "WebCurveData", + "splineIndex": 0, + "splineTypes": spline_types, + "splinePointCounts": spline_point_counts, + "beforeCyclicU": before, + "afterCyclicU": after, + "reopenedCyclicU": reopened, + } + with open(output, "w", encoding="utf-8") as destination: + json.dump(payload, destination, indent=2, sort_keys=True) + destination.write("\n") + + +if __name__ == "__main__": + main() diff --git a/tools/web/generate-geometry-node-evaluator-golden.py b/tools/web/generate-geometry-node-evaluator-golden.py new file mode 100644 index 00000000..316b4969 --- /dev/null +++ b/tools/web/generate-geometry-node-evaluator-golden.py @@ -0,0 +1,369 @@ +import json +import hashlib +import math +import os +import sys + +import bpy + + +ALLOWLIST = [ + "NodeGroupInput", + "NodeGroupOutput", + "GeometryNodeTransform", + "GeometryNodeSetPosition", + "GeometryNodeJoinGeometry", + "GeometryNodeSeparateGeometry", + "GeometryNodeRealizeInstances", + "GeometryNodeStoreNamedAttribute", + "FunctionNodeInputInt", + "FunctionNodeInputVector", + "FunctionNodeCompare", + "ShaderNodeValue", + "ShaderNodeMath", + "GeometryNodeObjectInfo", + "GeometryNodeCollectionInfo", + "GeometryNodeImageInfo", +] + + +def reset_scene(): + bpy.ops.wm.read_factory_settings(use_empty=True) + scene = bpy.context.scene + scene.frame_start = 1 + scene.frame_end = 24 + scene.frame_set(1) + return scene + + +def mesh_object(name, vertices, faces, location=(0.0, 0.0, 0.0), collection=None): + mesh = bpy.data.meshes.new(f"{name}Mesh") + mesh.from_pydata(vertices, [], faces) + mesh.update() + obj = bpy.data.objects.new(name, mesh) + (collection or bpy.context.collection).objects.link(obj) + obj.location = location + return obj + + +def cube_object(name, location=(0.0, 0.0, 0.0), collection=None): + return mesh_object( + name, + [ + (-1, -1, -1), + (1, -1, -1), + (1, 1, -1), + (-1, 1, -1), + (-1, -1, 1), + (1, -1, 1), + (1, 1, 1), + (-1, 1, 1), + ], + [ + (0, 1, 2, 3), + (4, 7, 6, 5), + (0, 4, 5, 1), + (1, 5, 6, 2), + (2, 6, 7, 3), + (4, 0, 3, 7), + ], + location, + collection, + ) + + +def tetra_object(name, location=(0.0, 0.0, 0.0), collection=None): + return mesh_object( + name, + [(0, 0, 1.5), (-1, -1, 0), (1, -1, 0), (0, 1, 0)], + [(0, 1, 2), (0, 2, 3), (0, 3, 1), (1, 3, 2)], + location, + collection, + ) + + +def geometry_group(name): + group = bpy.data.node_groups.new(name, "GeometryNodeTree") + group.interface.new_socket(name="Geometry", in_out="INPUT", socket_type="NodeSocketGeometry") + group.interface.new_socket(name="Geometry", in_out="OUTPUT", socket_type="NodeSocketGeometry") + group_input = group.nodes.new("NodeGroupInput") + group_output = group.nodes.new("NodeGroupOutput") + group_input.name = "Group Input" + group_output.name = "Group Output" + return group, group_input, group_output + + +def add_case(name, x, configure): + obj = cube_object(name, (x, 0, 0)) + group, group_input, group_output = geometry_group(f"{name}Graph") + configure(group, group_input, group_output) + modifier = obj.modifiers.new(f"{name} Nodes", "NODES") + modifier.node_group = group + return obj, group + + +def link_geometry(group, source, target): + group.links.new(source.outputs["Geometry"], target.inputs["Geometry"]) + + +def build_fixture(blend_path): + scene = reset_scene() + cases = [] + + def passthrough(group, group_input, group_output): + group.links.new(group_input.outputs["Geometry"], group_output.inputs["Geometry"]) + + cases.append(add_case("M10GN_Passthrough", 0, passthrough)) + + def transform_case(group, group_input, group_output): + node = group.nodes.new("GeometryNodeTransform") + node.name = "Transform" + node.inputs["Translation"].default_value = (0.25, -0.5, 1.0) + node.inputs["Rotation"].default_value = (0.0, 0.0, math.radians(15.0)) + node.inputs["Scale"].default_value = (1.25, 0.75, 1.5) + link_geometry(group, group_input, node) + group.links.new(node.outputs["Geometry"], group_output.inputs["Geometry"]) + + cases.append(add_case("M10GN_Transform", 4, transform_case)) + + def set_position_case(group, group_input, group_output): + node = group.nodes.new("GeometryNodeSetPosition") + node.name = "Set Position" + node.inputs["Offset"].default_value = (0.5, 0.25, -0.75) + link_geometry(group, group_input, node) + group.links.new(node.outputs["Geometry"], group_output.inputs["Geometry"]) + + cases.append(add_case("M10GN_SetPosition", 8, set_position_case)) + + def join_case(group, group_input, group_output): + node = group.nodes.new("GeometryNodeJoinGeometry") + node.name = "Join Geometry" + translated = group.nodes.new("GeometryNodeTransform") + translated.name = "Join Branch Transform" + translated.inputs["Translation"].default_value = (3.0, 0.0, 0.0) + group.links.new(group_input.outputs["Geometry"], node.inputs["Geometry"]) + group.links.new(group_input.outputs["Geometry"], translated.inputs["Geometry"]) + group.links.new(translated.outputs["Geometry"], node.inputs["Geometry"]) + group.links.new(node.outputs["Geometry"], group_output.inputs["Geometry"]) + + cases.append(add_case("M10GN_Join", 12, join_case)) + + def separate_case(group, group_input, group_output): + separate = group.nodes.new("GeometryNodeSeparateGeometry") + separate.name = "Separate Geometry" + separate.domain = "POINT" + integer_a = group.nodes.new("FunctionNodeInputInt") + integer_a.name = "Integer A" + integer_a.integer = 2 + integer_b = group.nodes.new("FunctionNodeInputInt") + integer_b.name = "Integer B" + integer_b.integer = 1 + compare = group.nodes.new("FunctionNodeCompare") + compare.name = "Compare" + compare.data_type = "INT" + compare.operation = "GREATER_THAN" + link_geometry(group, group_input, separate) + group.links.new(integer_a.outputs["Integer"], compare.inputs["A"]) + group.links.new(integer_b.outputs["Integer"], compare.inputs["B"]) + group.links.new(compare.outputs["Result"], separate.inputs["Selection"]) + group.links.new(separate.outputs["Selection"], group_output.inputs["Geometry"]) + + cases.append(add_case("M10GN_SeparateIntCompare", 16, separate_case)) + + external_collection = bpy.data.collections.new("M10GN_ExternalCollection") + scene.collection.children.link(external_collection) + tetra_object("M10GN_CollectionTetra", (-1.5, 0, 0), external_collection) + tetra_object("M10GN_CollectionTetraOffset", (1.5, 0, 0), external_collection) + + def collection_case(group, _group_input, group_output): + collection_info = group.nodes.new("GeometryNodeCollectionInfo") + collection_info.name = "Collection Info" + collection_info.inputs["Collection"].default_value = external_collection + collection_info.inputs["Separate Children"].default_value = True + realize = group.nodes.new("GeometryNodeRealizeInstances") + realize.name = "Realize Instances" + group.links.new(collection_info.outputs["Instances"], realize.inputs["Geometry"]) + group.links.new(realize.outputs["Geometry"], group_output.inputs["Geometry"]) + + cases.append(add_case("M10GN_CollectionRealize", 20, collection_case)) + + def store_attribute_case(group, group_input, group_output): + store = group.nodes.new("GeometryNodeStoreNamedAttribute") + store.name = "Store Named Attribute" + store.data_type = "FLOAT" + store.domain = "POINT" + store.inputs["Name"].default_value = "m10_value" + store.inputs["Value"].default_value = 0.375 + link_geometry(group, group_input, store) + group.links.new(store.outputs["Geometry"], group_output.inputs["Geometry"]) + + cases.append(add_case("M10GN_StoreAttribute", 24, store_attribute_case)) + + def vector_case(group, group_input, group_output): + vector = group.nodes.new("FunctionNodeInputVector") + vector.name = "Vector" + vector.vector = (0.125, 0.5, 1.25) + set_position = group.nodes.new("GeometryNodeSetPosition") + set_position.name = "Set Position" + link_geometry(group, group_input, set_position) + group.links.new(vector.outputs["Vector"], set_position.inputs["Offset"]) + group.links.new(set_position.outputs["Geometry"], group_output.inputs["Geometry"]) + + cases.append(add_case("M10GN_InputVector", 28, vector_case)) + + def value_math_case(group, group_input, group_output): + first = group.nodes.new("ShaderNodeValue") + first.name = "Value A" + first.outputs["Value"].default_value = 0.25 + second = group.nodes.new("ShaderNodeValue") + second.name = "Value B" + second.outputs["Value"].default_value = 0.5 + math_node = group.nodes.new("ShaderNodeMath") + math_node.name = "Math Add" + math_node.operation = "ADD" + compare = group.nodes.new("FunctionNodeCompare") + compare.name = "Compare" + compare.data_type = "FLOAT" + compare.operation = "GREATER_THAN" + compare.inputs["B"].default_value = 0.5 + set_position = group.nodes.new("GeometryNodeSetPosition") + set_position.name = "Set Position" + set_position.inputs["Offset"].default_value = (0.0, 0.0, 0.625) + link_geometry(group, group_input, set_position) + group.links.new(first.outputs["Value"], math_node.inputs[0]) + group.links.new(second.outputs["Value"], math_node.inputs[1]) + group.links.new(math_node.outputs["Value"], compare.inputs["A"]) + group.links.new(compare.outputs["Result"], set_position.inputs["Selection"]) + group.links.new(set_position.outputs["Geometry"], group_output.inputs["Geometry"]) + + cases.append(add_case("M10GN_ValueMath", 32, value_math_case)) + + object_target = tetra_object("M10GN_ObjectInfoTarget", (0.5, 0.25, 1.5)) + + def object_info_case(group, group_input, group_output): + object_info = group.nodes.new("GeometryNodeObjectInfo") + object_info.name = "Object Info" + object_info.inputs["Object"].default_value = object_target + set_position = group.nodes.new("GeometryNodeSetPosition") + set_position.name = "Set Position" + link_geometry(group, group_input, set_position) + group.links.new(object_info.outputs["Location"], set_position.inputs["Offset"]) + group.links.new(set_position.outputs["Geometry"], group_output.inputs["Geometry"]) + + cases.append(add_case("M10GN_ObjectInfo", 36, object_info_case)) + + image = bpy.data.images.new("M10GN_Image", width=4, height=2, alpha=True) + + def image_info_case(group, group_input, group_output): + image_info = group.nodes.new("GeometryNodeImageInfo") + image_info.name = "Image Info" + image_info.inputs["Image"].default_value = image + compare = group.nodes.new("FunctionNodeCompare") + compare.name = "Compare" + compare.data_type = "INT" + compare.operation = "GREATER_THAN" + compare.inputs["B"].default_value = 3 + set_position = group.nodes.new("GeometryNodeSetPosition") + set_position.name = "Set Position" + set_position.inputs["Offset"].default_value = (0.0, -0.75, 0.8) + link_geometry(group, group_input, set_position) + group.links.new(image_info.outputs["Width"], compare.inputs["A"]) + group.links.new(compare.outputs["Result"], set_position.inputs["Selection"]) + group.links.new(set_position.outputs["Geometry"], group_output.inputs["Geometry"]) + + cases.append(add_case("M10GN_ImageInfo", 40, image_info_case)) + + bpy.ops.wm.save_as_mainfile(filepath=blend_path, compress=True) + return [obj.name for obj, _group in cases] + + +def evaluated_mesh_record(obj, depsgraph): + evaluated = obj.evaluated_get(depsgraph) + mesh = evaluated.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph) + try: + mesh.calc_loop_triangles() + positions = [component for vertex in mesh.vertices for component in vertex.co] + record = { + "object": obj.name, + "vertexCount": len(mesh.vertices), + "triangleCount": len(mesh.loop_triangles), + "positions": positions, + "indices": [index for triangle in mesh.loop_triangles for index in triangle.vertices], + "bounds": { + "min": [min(positions[axis::3]) for axis in range(3)] if positions else [0, 0, 0], + "max": [max(positions[axis::3]) for axis in range(3)] if positions else [0, 0, 0], + }, + "attributes": {}, + } + attribute = mesh.attributes.get("m10_value") + if attribute is not None: + record["attributes"]["m10_value"] = { + "domain": attribute.domain, + "dataType": attribute.data_type, + "values": [item.value for item in attribute.data], + } + return record + finally: + evaluated.to_mesh_clear() + + +def write_golden(blend_path, golden_path, case_names): + depsgraph = bpy.context.evaluated_depsgraph_get() + node_coverage = {} + cases = [] + for case_name in case_names: + obj = bpy.data.objects[case_name] + group = obj.modifiers[0].node_group + node_types = [node.bl_idname for node in group.nodes] + cases.append({ + "name": case_name, + "graph": group.name, + "nodeTypes": node_types, + "mesh": evaluated_mesh_record(obj, depsgraph), + }) + for node_type in node_types: + node_coverage.setdefault(node_type, []).append(case_name) + + missing = sorted(set(ALLOWLIST) - set(node_coverage)) + unexpected = sorted(set(node_coverage) - set(ALLOWLIST)) + if missing or unexpected: + raise RuntimeError(f"allowlist coverage mismatch missing={missing} unexpected={unexpected}") + + output = { + "schemaVersion": 1, + "blenderVersion": bpy.app.version_string, + "fixture": os.path.relpath( + blend_path, + os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(golden_path)))), + ), + "fixtureSha256": hashlib.sha256(open(blend_path, "rb").read()).hexdigest(), + "allowlist": ALLOWLIST, + "nodeCoverage": node_coverage, + "tolerance": { + "maxPositionError": 1e-5, + "rmsPositionError": 1e-6, + "maxAttributeError": 1e-6, + "boundsError": 1e-5, + }, + "cases": cases, + } + os.makedirs(os.path.dirname(golden_path), exist_ok=True) + with open(golden_path, "w", encoding="utf-8") as handle: + json.dump(output, handle, indent=2, sort_keys=True) + handle.write("\n") + + +def main(): + if "--" not in sys.argv or len(sys.argv[sys.argv.index("--") + 1:]) != 2: + raise SystemExit( + "usage: blender -b --python generate-geometry-node-evaluator-golden.py -- fixture.blend golden.json" + ) + blend_path, golden_path = [os.path.abspath(path) for path in sys.argv[sys.argv.index("--") + 1:]] + os.makedirs(os.path.dirname(blend_path), exist_ok=True) + case_names = build_fixture(blend_path) + write_golden(blend_path, golden_path, case_names) + print(f"geometry-node-evaluator-generated fixture={blend_path} golden={golden_path} cases={len(case_names)}") + + +if __name__ == "__main__": + main() diff --git a/tools/web/generate-grease-pencil-reorder-golden.py b/tools/web/generate-grease-pencil-reorder-golden.py new file mode 100644 index 00000000..ed029d41 --- /dev/null +++ b/tools/web/generate-grease-pencil-reorder-golden.py @@ -0,0 +1,65 @@ +import hashlib +import json +import os +import sys +import tempfile + +import bpy + + +def layer_state(grease_pencil): + return { + "layerOrder": [layer.name for layer in grease_pencil.layers], + "framesByLayer": { + layer.name: sorted(frame.frame_number for frame in layer.frames) + for layer in grease_pencil.layers + }, + } + + +def main(): + arguments = sys.argv[sys.argv.index("--") + 1 :] if "--" in sys.argv else [] + if len(arguments) != 2: + raise SystemExit("usage: blender -b fixture.blend --python generate-grease-pencil-reorder-golden.py -- fixture.blend output.json") + fixture = os.path.abspath(arguments[0]) + output = os.path.abspath(arguments[1]) + grease_pencil = bpy.data.grease_pencils.get("GreasePencilData") + if grease_pencil is None or len(grease_pencil.layers) != 1: + raise RuntimeError("GreasePencilData fixture must contain exactly one source layer") + + source = layer_state(grease_pencil) + layer = grease_pencil.layers.new("Web Drafts", set_active=True) + layer.frames.new(1) + before = layer_state(grease_pencil) + grease_pencil.layers.move(layer, "DOWN") + moved = layer.frames.move(1, 12) + if moved is None: + raise RuntimeError("Blender refused the Grease Pencil frame move") + after = layer_state(grease_pencil) + + with tempfile.TemporaryDirectory(prefix="m9-08-grease-pencil-reorder-") as temporary: + saved = os.path.join(temporary, "grease-pencil-reorder.blend") + bpy.ops.wm.save_as_mainfile(filepath=saved, check_existing=False) + bpy.ops.wm.open_mainfile(filepath=saved) + reopened = layer_state(bpy.data.grease_pencils["GreasePencilData"]) + + with open(fixture, "rb") as source_file: + fixture_sha256 = hashlib.sha256(source_file.read()).hexdigest() + payload = { + "schemaVersion": 1, + "fixture": "tests/files/web/modifier_grease_pencil_scene.blend", + "fixtureSha256": fixture_sha256, + "blenderVersion": ".".join(str(value) for value in bpy.app.version), + "greasePencilName": "GreasePencilData", + "source": source, + "beforeReorder": before, + "afterReorder": after, + "reopened": reopened, + } + with open(output, "w", encoding="utf-8") as destination: + json.dump(payload, destination, indent=2, sort_keys=True) + destination.write("\n") + + +if __name__ == "__main__": + main() diff --git a/tools/web/generate-m11-compositor-allowlist.py b/tools/web/generate-m11-compositor-allowlist.py new file mode 100644 index 00000000..904c7742 --- /dev/null +++ b/tools/web/generate-m11-compositor-allowlist.py @@ -0,0 +1,54 @@ +import pathlib +import sys + +import bpy + + +SCENES = ( + ("M11 Constant", (0.125, 0.25, 0.5, 0.75), ()), + ("M11 Exposure", (0.125, 0.25, 0.5, 0.75), (("EXPOSURE", 1.0),)), + ("M11 Invert", (0.125, 0.25, 0.5, 0.75), (("INVERT", None),)), + ("M11 Chain", (0.125, 0.25, 0.5, 0.75), (("EXPOSURE", 1.0), ("INVERT", None))), +) + + +def add_graph(scene, color_value, operations): + tree = bpy.data.node_groups.new(f"{scene.name} Tree", "CompositorNodeTree") + scene.compositing_node_group = tree + tree.interface.new_socket(name="Image", in_out="OUTPUT", socket_type="NodeSocketColor") + color = tree.nodes.new("CompositorNodeRGB") + color.name = f"{scene.name} Constant" + color.outputs["Color"].default_value = color_value + previous = color.outputs["Color"] + for index, (operation, parameter) in enumerate(operations): + if operation == "EXPOSURE": + node = tree.nodes.new("CompositorNodeExposure") + node.inputs["Exposure"].default_value = parameter + else: + node = tree.nodes.new("CompositorNodeInvert") + node.name = f"{scene.name} {operation.title()} {index}" + tree.links.new(previous, node.inputs["Image" if operation == "EXPOSURE" else "Color"]) + previous = node.outputs["Image" if operation == "EXPOSURE" else "Color"] + output = tree.nodes.new("NodeGroupOutput") + output.name = f"{scene.name} Composite" + tree.links.new(previous, output.inputs["Image"]) + + +def main(output_path): + bpy.ops.wm.read_factory_settings(use_empty=True) + first = bpy.context.scene + for index, (name, color, operations) in enumerate(SCENES): + scene = first if index == 0 else bpy.data.scenes.new(name) + scene.name = name + add_graph(scene, color, operations) + output = pathlib.Path(output_path).resolve() + output.parent.mkdir(parents=True, exist_ok=True) + bpy.ops.wm.save_as_mainfile(filepath=str(output), compress=True) + print(f"m11-compositor-allowlist fixture={output} scenes={len(SCENES)}") + + +if __name__ == "__main__": + arguments = sys.argv[sys.argv.index("--") + 1:] + if len(arguments) != 1: + raise SystemExit("usage: blender -b --python generate-m11-compositor-allowlist.py -- OUTPUT") + main(arguments[0]) diff --git a/tools/web/generate-m11-render-reference.py b/tools/web/generate-m11-render-reference.py new file mode 100644 index 00000000..e0046295 --- /dev/null +++ b/tools/web/generate-m11-render-reference.py @@ -0,0 +1,99 @@ +import math +import pathlib +import sys + +import bpy +from mathutils import Vector + + +def configure_scene() -> bpy.types.Scene: + bpy.ops.wm.read_factory_settings(use_empty=True) + scene = bpy.context.scene + scene.name = "M11 Render Reference" + scene.render.engine = "BLENDER_EEVEE" + scene.render.resolution_x = 256 + scene.render.resolution_y = 256 + scene.render.resolution_percentage = 100 + scene.render.image_settings.file_format = "PNG" + scene.render.image_settings.color_mode = "RGBA" + scene.render.image_settings.color_depth = "8" + scene.render.film_transparent = False + scene.render.use_file_extension = True + scene.render.dither_intensity = 0 + scene.eevee.taa_render_samples = 1 + scene.view_settings.look = "None" + scene.view_settings.exposure = 0 + scene.view_settings.gamma = 1 + + world = bpy.data.worlds.new("M11 Reference World") + world.use_nodes = True + background = world.node_tree.nodes.get("Background") + background.inputs["Color"].default_value = (0.0508760884, 0.0508760884, 0.0508760884, 1) + background.inputs["Strength"].default_value = 1 + scene.world = world + + bpy.ops.mesh.primitive_cube_add(size=3, location=(0, 0, 0)) + cube = bpy.context.object + cube.name = "M11 Reference Cube" + material = bpy.data.materials.new("M11 Reference Black") + material.use_nodes = True + principled = material.node_tree.nodes.get("Principled BSDF") + principled.inputs["Base Color"].default_value = (0, 0, 0, 1) + principled.inputs["Metallic"].default_value = 0 + principled.inputs["Roughness"].default_value = 1 + principled.inputs["Specular IOR Level"].default_value = 0 + cube.data.materials.append(material) + + bpy.ops.object.camera_add() + camera = bpy.context.object + camera.name = "M11 Reference Camera" + camera.data.name = "M11 Reference Camera" + yaw = -math.pi / 4 + pitch = 0.55 + distance = 7 + three_position = Vector(( + distance * math.cos(pitch) * math.cos(yaw), + distance * math.cos(pitch) * math.sin(yaw), + distance * math.sin(pitch), + )) + camera.location = (three_position.x, -three_position.z, three_position.y) + camera.rotation_euler = (-camera.location).to_track_quat("-Z", "Y").to_euler() + camera.data.type = "PERSP" + camera.data.lens = 50 + camera.data.sensor_width = 36 + camera.data.sensor_fit = "HORIZONTAL" + camera.data.clip_start = 0.1 + camera.data.clip_end = 1000 + scene.camera = camera + return scene + + +def main() -> None: + if "--" not in sys.argv or len(sys.argv[sys.argv.index("--") + 1:]) not in (2, 3): + raise SystemExit("usage: blender --background --factory-startup --python generate-m11-render-reference.py -- FIXTURE OUTPUT [EXPECTED]") + arguments = sys.argv[sys.argv.index("--") + 1:] + fixture_arg, output_arg = arguments[:2] + fixture = pathlib.Path(fixture_arg).resolve() + output = pathlib.Path(output_arg).resolve() + fixture.parent.mkdir(parents=True, exist_ok=True) + output.parent.mkdir(parents=True, exist_ok=True) + scene = configure_scene() + bpy.ops.wm.save_as_mainfile(filepath=str(fixture), compress=True) + scene.render.filepath = str(output) + bpy.ops.render.render(write_still=True) + if len(arguments) == 3: + expected = pathlib.Path(arguments[2]).resolve() + actual_image = bpy.data.images.load(str(output), check_existing=False) + expected_image = bpy.data.images.load(str(expected), check_existing=False) + if actual_image.size[:] != expected_image.size[:]: + raise RuntimeError(f"reference dimensions differ: {actual_image.size[:]} != {expected_image.size[:]}") + actual_pixels = actual_image.pixels[:] + expected_pixels = expected_image.pixels[:] + maximum = max(abs(actual - reference) for actual, reference in zip(actual_pixels, expected_pixels)) + if maximum != 0: + raise RuntimeError(f"reference decoded pixels differ: max={maximum}") + print(f"m11-render-reference-pixels width={actual_image.size[0]} height={actual_image.size[1]} max=0") + print(f"m11-render-reference fixture={fixture} output={output}") + + +main() diff --git a/tools/web/generate-nla-evaluation-fixture.py b/tools/web/generate-nla-evaluation-fixture.py new file mode 100644 index 00000000..24c75f15 --- /dev/null +++ b/tools/web/generate-nla-evaluation-fixture.py @@ -0,0 +1,94 @@ +import sys + +import bpy + + +def mesh_object(name: str): + mesh = bpy.data.meshes.new(f"{name}Mesh") + mesh.from_pydata( + [(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (0.0, 1.0, 0.0)], + [], + [(0, 1, 2)], + ) + mesh.update() + obj = bpy.data.objects.new(name, mesh) + bpy.context.collection.objects.link(obj) + return obj + + +def location_action(name: str, axis: int): + action = bpy.data.actions.new(name) + slot = action.slots.new("OBJECT", "M10NlaTimeMapping") + layer = action.layers.new("M10 NLA Keys") + keyframe_strip = layer.strips.new(type="KEYFRAME") + channelbag = keyframe_strip.channelbags.new(slot) + curve = channelbag.fcurves.new(data_path="location", index=axis) + curve.keyframe_points.add(2) + curve.keyframe_points[0].co = (1.0, 0.0) + curve.keyframe_points[1].co = (11.0, 10.0) + for keyframe in curve.keyframe_points: + keyframe.interpolation = "LINEAR" + action.frame_start = 1.0 + action.frame_end = 11.0 + return action, slot + + +def configure_strip(strip, slot, *, frame_start, frame_end, scale, repeat, reverse): + strip.action_slot = slot + strip.action_frame_start = 1.0 + strip.action_frame_end = 11.0 + strip.frame_start = frame_start + strip.frame_end = frame_end + strip.scale = scale + strip.repeat = repeat + strip.blend_type = "REPLACE" + strip.extrapolation = "NOTHING" + strip.use_reverse = reverse + strip.influence = 1.0 + strip.blend_in = 0.0 + strip.blend_out = 0.0 + + +def main(output_path: str) -> None: + bpy.ops.wm.read_factory_settings(use_empty=True) + scene = bpy.context.scene + scene.frame_start = 1 + scene.frame_end = 70 + + obj = mesh_object("M10_NLA_TimeMapping") + animation_data = obj.animation_data_create() + track = animation_data.nla_tracks.new() + track.name = "M10 Time Mapping" + + scaled_action, scaled_slot = location_action("M10_NLA_Scaled_X", 0) + scaled_strip = track.strips.new("M10 Scaled Clip", 20, scaled_action) + configure_strip( + scaled_strip, + scaled_slot, + frame_start=20.0, + frame_end=40.0, + scale=2.0, + repeat=1.0, + reverse=False, + ) + + reverse_repeat_action, reverse_repeat_slot = location_action("M10_NLA_ReverseRepeat_Y", 1) + reverse_repeat_strip = track.strips.new("M10 Reverse Repeat Clip", 45, reverse_repeat_action) + configure_strip( + reverse_repeat_strip, + reverse_repeat_slot, + frame_start=45.0, + frame_end=65.0, + scale=1.0, + repeat=2.0, + reverse=True, + ) + + scene.frame_set(1) + bpy.ops.wm.save_as_mainfile(filepath=output_path, compress=True) + + +if __name__ == "__main__": + if "--" not in sys.argv or len(sys.argv) <= sys.argv.index("--") + 1: + raise SystemExit("usage: blender -b --python generate-nla-evaluation-fixture.py -- output.blend") + main(sys.argv[sys.argv.index("--") + 1]) diff --git a/tools/web/generate-nla-evaluation-golden.py b/tools/web/generate-nla-evaluation-golden.py new file mode 100644 index 00000000..06084a5f --- /dev/null +++ b/tools/web/generate-nla-evaluation-golden.py @@ -0,0 +1,77 @@ +import hashlib +import json +import pathlib +import sys + +import bpy + + +def strip_summary(strip): + return { + "id": strip.name, + "action": strip.action.name if strip.action is not None else None, + "frameStart": strip.frame_start, + "frameEnd": strip.frame_end, + "actionFrameStart": strip.action_frame_start, + "actionFrameEnd": strip.action_frame_end, + "scale": strip.scale, + "repeat": strip.repeat, + "blendIn": strip.blend_in, + "blendOut": strip.blend_out, + "influence": strip.influence, + "blendMode": strip.blend_type, + "extrapolation": strip.extrapolation, + "muted": strip.mute, + "reverse": strip.use_reverse, + } + + +def main(blend_path: str, output_path: str) -> None: + blend = pathlib.Path(blend_path).resolve() + bpy.ops.wm.open_mainfile(filepath=str(blend), load_ui=False) + + scene = bpy.context.scene + object_name = "M10_NLA_TimeMapping" + obj = bpy.data.objects.get(object_name) + if obj is None or obj.animation_data is None: + raise RuntimeError("M10 NLA fixture object or AnimData is missing") + + frames = [1, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70] + samples = [] + for frame in frames: + scene.frame_set(frame) + depsgraph = bpy.context.evaluated_depsgraph_get() + evaluated = obj.evaluated_get(depsgraph) + samples.append({ + "frame": frame, + "worldMatrix": [value for row in evaluated.matrix_world for value in row], + }) + + tracks = [] + for track in obj.animation_data.nla_tracks: + tracks.append({ + "name": track.name, + "muted": track.mute, + "solo": track.is_solo, + "strips": [strip_summary(strip) for strip in track.strips], + }) + + result = { + "schemaVersion": 1, + "fixture": f"tests/files/web/{blend.name}", + "fixtureSha256": hashlib.sha256(blend.read_bytes()).hexdigest(), + "blenderVersion": bpy.app.version_string, + "object": object_name, + "mesh": obj.data.name, + "tracks": tracks, + "frames": samples, + "tolerance": {"maxMatrixError": 1e-5}, + } + pathlib.Path(output_path).write_text(json.dumps(result, indent=2) + "\n", encoding="utf-8") + + +if __name__ == "__main__": + if "--" not in sys.argv or len(sys.argv) <= sys.argv.index("--") + 2: + raise SystemExit("usage: blender -b --python generate-nla-evaluation-golden.py -- input.blend output.json") + arguments = sys.argv[sys.argv.index("--") + 1:] + main(arguments[0], arguments[1]) diff --git a/tools/web/generate-sequencer-codec-fixture.sh b/tools/web/generate-sequencer-codec-fixture.sh new file mode 100755 index 00000000..c912238a --- /dev/null +++ b/tools/web/generate-sequencer-codec-fixture.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -ne 1 ]]; then + echo "usage: $0 OUTPUT.mp4" >&2 + exit 2 +fi + +output=$1 +ffmpeg_bin=${FFMPEG_BIN:-ffmpeg} +mkdir -p "$(dirname "$output")" + +"$ffmpeg_bin" -v error \ + -f lavfi -i "color=c=red:s=16x16:r=2:d=1" \ + -an -c:v libx264 -profile:v baseline -level 3.0 -pix_fmt yuv420p \ + -movflags +faststart -map_metadata -1 -fflags +bitexact -flags:v +bitexact \ + -metadata creation_time=1970-01-01T00:00:00Z -y "$output" + +printf 'sequencer-codec-fixture path=%s bytes=%s\n' "$output" "$(wc -c < "$output")" diff --git a/tools/web/generate-weight-paint-golden.py b/tools/web/generate-weight-paint-golden.py new file mode 100644 index 00000000..58a6e885 --- /dev/null +++ b/tools/web/generate-weight-paint-golden.py @@ -0,0 +1,110 @@ +import json +import pathlib +import sys + +import bpy + + +def group_weight(vertex, group_index): + return next((item.weight for item in vertex.groups if item.group == group_index), 0.0) + + +def set_weight(obj, vertex_index, group_index, value): + group = obj.vertex_groups[group_index] + if value == 0.0: + try: + group.remove([vertex_index]) + except RuntimeError: + pass + else: + group.add([vertex_index], value, "REPLACE") + + +def normalize_vertex(obj, vertex_index): + vertex = obj.data.vertices[vertex_index] + total = sum(item.weight for item in vertex.groups) + if total > 0.0: + for item in list(vertex.groups): + set_weight(obj, vertex_index, item.group, item.weight / total) + + +def limit_vertex(obj, vertex_index, limit): + vertex = obj.data.vertices[vertex_index] + while len(vertex.groups) > limit: + remove = min(vertex.groups, key=lambda item: (item.weight, -item.group)) + obj.vertex_groups[remove.group].remove([vertex_index]) + + +def mirror_map(obj, axis, tolerance): + result = {} + for source in obj.data.vertices: + reflected = source.co.copy() + reflected[axis] = -reflected[axis] + candidate = min(obj.data.vertices, key=lambda item: ((item.co - reflected).length, item.index)) + if (candidate.co - reflected).length > tolerance: + raise RuntimeError("mesh is not mirror symmetric") + result[source.index] = candidate.index + if any(result[result[index]] != index for index in result): + raise RuntimeError("mesh mirror map is not reciprocal") + return result + + +def snapshot(obj): + groups = [group.name for group in obj.vertex_groups] + return { + "groups": groups, + "vertices": [ + {"index": vertex.index, "weights": { + groups[item.group]: round(item.weight, 9) for item in vertex.groups + }} + for vertex in obj.data.vertices + ], + } + + +def main(blend_path, output_path): + bpy.ops.wm.open_mainfile(filepath=str(pathlib.Path(blend_path).resolve()), load_ui=False) + obj = bpy.data.objects.get("RiggedShapeObject") + if obj is None or obj.type != "MESH": + raise RuntimeError("weight paint fixture object is missing") + group = obj.vertex_groups.get("WebPaintGroup") or obj.vertex_groups.new(name="WebPaintGroup") + + set_weight(obj, 0, group.index, 0.75) + set_weight(obj, 1, group.index, 0.25) + steps = [{"name": "initial", **snapshot(obj)}] + + set_weight(obj, 2, group.index, 0.5) + normalize_vertex(obj, 2) + steps.append({"name": "normalize", **snapshot(obj)}) + + set_weight(obj, 3, group.index, 0.4) + limit_vertex(obj, 3, 2) + normalize_vertex(obj, 3) + steps.append({"name": "limit-normalize", **snapshot(obj)}) + + mapping = mirror_map(obj, 0, 1e-4) + for source in (0, mapping[0]): + set_weight(obj, source, group.index, 0.9) + steps.append({"name": "mirror", **snapshot(obj)}) + + result = { + "schemaVersion": 1, + "fixture": pathlib.Path(blend_path).name, + "blenderVersion": bpy.app.version_string, + "object": obj.name, + "mesh": obj.data.name, + "operations": [ + {"name": "normalize", "vertex": 2}, + {"name": "limit-normalize", "vertex": 3, "limit": 2}, + {"name": "mirror", "vertices": [0, 1], "axis": 0, "tolerance": 1e-4}, + ], + "steps": steps, + "tolerance": 1e-6, + } + pathlib.Path(output_path).write_text(json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +if __name__ == "__main__": + if "--" not in sys.argv or len(sys.argv) <= sys.argv.index("--") + 2: + raise SystemExit("usage: blender -b --python generate-weight-paint-golden.py -- input.blend output.json") + main(*sys.argv[sys.argv.index("--") + 1:sys.argv.index("--") + 3]) diff --git a/tools/web/render-server-job.py b/tools/web/render-server-job.py new file mode 100644 index 00000000..16844383 --- /dev/null +++ b/tools/web/render-server-job.py @@ -0,0 +1,45 @@ +import json +import pathlib +import sys + +import bpy + + +def main() -> None: + if "--" not in sys.argv or len(sys.argv[sys.argv.index("--") + 1:]) != 3: + raise SystemExit("usage: blender --background --python render-server-job.py -- SOURCE OUTPUT SETTINGS_JSON") + source_arg, output_arg, settings_arg = sys.argv[sys.argv.index("--") + 1:] + source = pathlib.Path(source_arg).resolve() + output = pathlib.Path(output_arg).resolve() + settings_path = pathlib.Path(settings_arg).resolve() + if not source.is_file() or source.stat().st_size < 1: + raise RuntimeError("server render source is missing") + bpy.ops.wm.open_mainfile(filepath=str(source), load_ui=False) + scene = bpy.context.scene + if scene is None: + raise RuntimeError("server render scene is missing") + settings = json.loads(settings_path.read_text(encoding="utf-8")) + engine = settings["renderEngine"] + scene.render.engine = "BLENDER_EEVEE" if engine == "BLENDER_EEVEE_NEXT" else engine + frame = int(settings["frameStart"]) + if frame != int(settings["frameEnd"]): + raise RuntimeError("server render schema 1 requires a still frame") + scene.render.resolution_x = int(settings["resolutionX"]) + scene.render.resolution_y = int(settings["resolutionY"]) + scene.render.resolution_percentage = int(settings["resolutionPercentage"]) + if scene.render.engine == "BLENDER_CYCLES": + scene.cycles.samples = int(settings["samples"]) + elif scene.render.engine == "BLENDER_EEVEE": + scene.eevee.taa_render_samples = int(settings["samples"]) + scene.render.image_settings.file_format = "PNG" if settings["outputMime"] == "image/png" else "OPEN_EXR" + scene.render.film_transparent = bool(settings["transparent"]) + scene.frame_set(frame) + scene.render.filepath = str(output) + scene.render.use_file_extension = True + bpy.ops.render.render(write_still=True) + if not output.is_file() or output.stat().st_size < 1: + raise RuntimeError("server render produced no output") + print(f"server-render-ok frame={frame} output={output} bytes={output.stat().st_size}") + + +main() diff --git a/web/app/public/engine-manifest.json b/web/app/public/engine-manifest.json index 67c8840d..3bc27f43 100644 --- a/web/app/public/engine-manifest.json +++ b/web/app/public/engine-manifest.json @@ -16,12 +16,12 @@ "js": { "fileName": "web_engine.js", "url": "/vendor/blender/single/web_engine.js", - "sha256": "eedb8cedeb2190fbece91d58cd5dd568356caf42191517aff2085911177157ca" + "sha256": "25aa51361aa8c95479873240c776d6213c1092dd480ee2a0036d2cbbd561dcad" }, "wasm": { "fileName": "web_engine.wasm", "url": "/vendor/blender/single/web_engine.wasm", - "sha256": "f079e2221b501eee7f0b8e3290c22f37210ba44ee7a392c7fa817fde16b9ee5e" + "sha256": "7f3a50f1d41b2ac0e6366e84caaabefc3cdfc47c055d5ea459abec654da6fcc0" } } }, @@ -36,17 +36,17 @@ "js": { "fileName": "web_engine.js", "url": "/vendor/blender/pthread/web_engine.js", - "sha256": "5a78f2e27969074f72902805c32453c980841c390db6c48eb9d43787d33a8837" + "sha256": "a64288eeb74fb0696d38d785348f0ba37e7ae716b5a341bf181f178f78e1324d" }, "wasm": { "fileName": "web_engine.wasm", "url": "/vendor/blender/pthread/web_engine.wasm", - "sha256": "a6bed7b410b15f0d5aca229d1e926bff861e3ff16f0d3d6abc1bc73bcaf5fc0b" + "sha256": "f2a26a9221b5d4d5e710463a89e2d93faf9b2d8a9076a455c5ce1a0e52d88b5f" }, "pthreadWorker": { "fileName": "web_engine.js", "url": "/vendor/blender/pthread/web_engine.js", - "sha256": "5a78f2e27969074f72902805c32453c980841c390db6c48eb9d43787d33a8837" + "sha256": "a64288eeb74fb0696d38d785348f0ba37e7ae716b5a341bf181f178f78e1324d" } } } diff --git a/web/app/public/vendor/blender/pthread/web_engine.js b/web/app/public/vendor/blender/pthread/web_engine.js index 17a821e0..fd7cbd4e 100644 --- a/web/app/public/vendor/blender/pthread/web_engine.js +++ b/web/app/public/vendor/blender/pthread/web_engine.js @@ -6,7 +6,7 @@ var Module = (() => { function(moduleArg = {}) { var moduleRtn; -function GROWABLE_HEAP_I8(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAP8}function GROWABLE_HEAP_U8(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAPU8}function GROWABLE_HEAP_I32(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAP32}function GROWABLE_HEAP_U32(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAPU32}function GROWABLE_HEAP_F64(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAPF64}var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string"&&process.type!="renderer";var ENVIRONMENT_IS_PTHREAD=ENVIRONMENT_IS_WORKER&&self.name?.startsWith("em-pthread");var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=url=>fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))})}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var wasmMemory;var wasmModule;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}if(ENVIRONMENT_IS_PTHREAD){var wasmPromiseResolve;var wasmPromiseReject;var initializedJS=false;function threadPrintErr(...args){var text=args.join(" ");console.error(text)}if(!Module["printErr"])err=threadPrintErr;function threadAlert(...args){var text=args.join(" ");postMessage({cmd:"alert",text,threadId:_pthread_self()})}self.alert=threadAlert;Module["instantiateWasm"]=(info,receiveInstance)=>new Promise((resolve,reject)=>{wasmPromiseResolve=module=>{var instance=new WebAssembly.Instance(module,getWasmImports());receiveInstance(instance);resolve()};wasmPromiseReject=reject});self.onunhandledrejection=e=>{throw e.reason||e};function handleMessage(e){try{var msgData=e["data"];var cmd=msgData.cmd;if(cmd==="load"){let messageQueue=[];self.onmessage=e=>messageQueue.push(e);self.startWorker=instance=>{postMessage({cmd:"loaded"});for(let msg of messageQueue){handleMessage(msg)}self.onmessage=handleMessage};for(const handler of msgData.handlers){if(!Module[handler]||Module[handler].proxy){Module[handler]=(...args)=>{postMessage({cmd:"callHandler",handler,args})};if(handler=="print")out=Module[handler];if(handler=="printErr")err=Module[handler]}}wasmMemory=msgData.wasmMemory;updateMemoryViews();wasmPromiseResolve(msgData.wasmModule)}else if(cmd==="run"){establishStackSpace(msgData.pthread_ptr);__emscripten_thread_init(msgData.pthread_ptr,0,0,1,0,0);PThread.receiveObjectTransfer(msgData);PThread.threadInitTLS();__emscripten_thread_mailbox_await(msgData.pthread_ptr);if(!initializedJS){initializedJS=true}try{invokeEntryPoint(msgData.start_routine,msgData.arg)}catch(ex){if(ex!="unwind"){throw ex}}}else if(msgData.target==="setimmediate"){}else if(cmd==="checkMailbox"){if(initializedJS){checkMailbox()}}else if(cmd){err(`worker: received unknown command ${cmd}`);err(msgData)}}catch(ex){__emscripten_thread_crashed();throw ex}}self.onmessage=handleMessage}if(!ENVIRONMENT_IS_PTHREAD){if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;wasmMemory=new WebAssembly.Memory({initial:INITIAL_MEMORY/65536,maximum:32768,shared:true})}updateMemoryViews()}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){var preRuns=Module["preRun"];if(preRuns){if(typeof preRuns=="function")preRuns=[preRuns];preRuns.forEach(addOnPreRun)}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(ENVIRONMENT_IS_PTHREAD)return;callRuntimeCallbacks(__ATINIT__)}function postRun(){if(ENVIRONMENT_IS_PTHREAD)return;var postRuns=Module["postRun"];if(postRuns){if(typeof postRuns=="function")postRuns=[postRuns];postRuns.forEach(addOnPostRun)}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);function findWasmBinary(){if(Module["locateFile"]){var f="web_engine.wasm";if(!isDataURI(f)){return locateFile(f)}return f}return new URL("web_engine.wasm",import.meta.url).href}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){assignWasmImports();return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;registerTLSInit(wasmExports["na"]);wasmTable=wasmExports["Z"];addOnInit(wasmExports["S"]);wasmModule=module;removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"],result["module"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}wasmBinaryFile??=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}function ExitStatus(status){this.name="ExitStatus";this.message=`Program terminated with exit(${status})`;this.status=status}var terminateWorker=worker=>{worker.terminate();worker.onmessage=e=>{}};var cleanupThread=pthread_ptr=>{var worker=PThread.pthreads[pthread_ptr];PThread.returnWorkerToPool(worker)};var spawnThread=threadParams=>{var worker=PThread.getNewWorker();if(!worker){return 6}PThread.runningWorkers.push(worker);PThread.pthreads[threadParams.pthread_ptr]=worker;worker.pthread_ptr=threadParams.pthread_ptr;var msg={cmd:"run",start_routine:threadParams.startRoutine,arg:threadParams.arg,pthread_ptr:threadParams.pthread_ptr};worker.postMessage(msg,threadParams.transferList);return 0};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var stackSave=()=>_emscripten_stack_get_current();var stackRestore=val=>__emscripten_stack_restore(val);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var proxyToMainThread=(funcIndex,emAsmAddr,sync,...callArgs)=>{var serializedNumCallArgs=callArgs.length;var sp=stackSave();var args=stackAlloc(serializedNumCallArgs*8);var b=args>>3;for(var i=0;i{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};function exitOnMainThread(returnCode){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(1,0,0,returnCode);_exit(returnCode)}var exitJS=(status,implicit)=>{EXITSTATUS=status;if(ENVIRONMENT_IS_PTHREAD){exitOnMainThread(status);throw"unwind"}_proc_exit(status)};var _exit=exitJS;var PThread={unusedWorkers:[],runningWorkers:[],tlsInitFunctions:[],pthreads:{},init(){if(!ENVIRONMENT_IS_PTHREAD){PThread.initMainThread()}},initMainThread(){var pthreadPoolSize=1;while(pthreadPoolSize--){PThread.allocateUnusedWorker()}addOnPreRun(()=>{addRunDependency("loading-workers");PThread.loadWasmModuleToAllWorkers(()=>removeRunDependency("loading-workers"))})},terminateAllThreads:()=>{for(var worker of PThread.runningWorkers){terminateWorker(worker)}for(var worker of PThread.unusedWorkers){terminateWorker(worker)}PThread.unusedWorkers=[];PThread.runningWorkers=[];PThread.pthreads=[]},returnWorkerToPool:worker=>{var pthread_ptr=worker.pthread_ptr;delete PThread.pthreads[pthread_ptr];PThread.unusedWorkers.push(worker);PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker),1);worker.pthread_ptr=0;__emscripten_thread_free_data(pthread_ptr)},receiveObjectTransfer(data){},threadInitTLS(){PThread.tlsInitFunctions.forEach(f=>f())},loadWasmModuleToWorker:worker=>new Promise(onFinishedLoading=>{worker.onmessage=e=>{var d=e["data"];var cmd=d.cmd;if(d.targetThread&&d.targetThread!=_pthread_self()){var targetWorker=PThread.pthreads[d.targetThread];if(targetWorker){targetWorker.postMessage(d,d.transferList)}else{err(`Internal error! Worker sent a message "${cmd}" to target pthread ${d.targetThread}, but that thread no longer exists!`)}return}if(cmd==="checkMailbox"){checkMailbox()}else if(cmd==="spawnThread"){spawnThread(d)}else if(cmd==="cleanupThread"){cleanupThread(d.thread)}else if(cmd==="loaded"){worker.loaded=true;onFinishedLoading(worker)}else if(cmd==="alert"){alert(`Thread ${d.threadId}: ${d.text}`)}else if(d.target==="setimmediate"){worker.postMessage(d)}else if(cmd==="callHandler"){Module[d.handler](...d.args)}else if(cmd){err(`worker sent an unknown command ${cmd}`)}};worker.onerror=e=>{var message="worker sent an error!";err(`${message} ${e.filename}:${e.lineno}: ${e.message}`);throw e};var handlers=[];var knownHandlers=["onExit","onAbort","print","printErr"];for(var handler of knownHandlers){if(Module.propertyIsEnumerable(handler)){handlers.push(handler)}}worker.postMessage({cmd:"load",handlers,wasmMemory,wasmModule})}),loadWasmModuleToAllWorkers(onMaybeReady){if(ENVIRONMENT_IS_PTHREAD){return onMaybeReady()}let pthreadPoolReady=Promise.all(PThread.unusedWorkers.map(PThread.loadWasmModuleToWorker));pthreadPoolReady.then(onMaybeReady)},allocateUnusedWorker(){var worker;var workerOptions={type:"module",name:"em-pthread"};worker=new Worker(new URL("web_engine.js",import.meta.url),workerOptions);PThread.unusedWorkers.push(worker)},getNewWorker(){if(PThread.unusedWorkers.length==0){PThread.allocateUnusedWorker();PThread.loadWasmModuleToWorker(PThread.unusedWorkers[0])}return PThread.unusedWorkers.pop()}};var callRuntimeCallbacks=callbacks=>{callbacks.forEach(f=>f(Module))};var establishStackSpace=pthread_ptr=>{updateMemoryViews();var stackHigh=GROWABLE_HEAP_U32()[pthread_ptr+52>>2];var stackSize=GROWABLE_HEAP_U32()[pthread_ptr+56>>2];var stackLow=stackHigh-stackSize;_emscripten_stack_set_limits(stackHigh,stackLow);stackRestore(stackHigh)};var wasmTableMirror=[];var wasmTable;var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var invokeEntryPoint=(ptr,arg)=>{runtimeKeepaliveCounter=0;noExitRuntime=0;var result=getWasmTableEntry(ptr)(arg);function finish(result){if(keepRuntimeAlive()){EXITSTATUS=result}else{__emscripten_thread_exit(result)}}finish(result)};var noExitRuntime=Module["noExitRuntime"]||true;var registerTLSInit=tlsInitFunc=>PThread.tlsInitFunctions.push(tlsInitFunc);var exceptionCaught=[];var uncaughtExceptionCount=0;var ___cxa_begin_catch=ptr=>{var info=new ExceptionInfo(ptr);if(!info.get_caught()){info.set_caught(true);uncaughtExceptionCount--}info.set_rethrown(false);exceptionCaught.push(info);___cxa_increment_exception_refcount(ptr);return ___cxa_get_exception_ptr(ptr)};var exceptionLast=0;var ___cxa_end_catch=()=>{_setThrew(0,0);var info=exceptionCaught.pop();___cxa_decrement_exception_refcount(info.excPtr);exceptionLast=0};class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){GROWABLE_HEAP_U32()[this.ptr+4>>2]=type}get_type(){return GROWABLE_HEAP_U32()[this.ptr+4>>2]}set_destructor(destructor){GROWABLE_HEAP_U32()[this.ptr+8>>2]=destructor}get_destructor(){return GROWABLE_HEAP_U32()[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;GROWABLE_HEAP_I8()[this.ptr+12]=caught}get_caught(){return GROWABLE_HEAP_I8()[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;GROWABLE_HEAP_I8()[this.ptr+13]=rethrown}get_rethrown(){return GROWABLE_HEAP_I8()[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){GROWABLE_HEAP_U32()[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return GROWABLE_HEAP_U32()[this.ptr+16>>2]}}var ___resumeException=ptr=>{if(!exceptionLast){exceptionLast=ptr}throw exceptionLast};var setTempRet0=val=>__emscripten_tempret_set(val);var findMatchingCatch=args=>{var thrown=exceptionLast;if(!thrown){setTempRet0(0);return 0}var info=new ExceptionInfo(thrown);info.set_adjusted_ptr(thrown);var thrownType=info.get_type();if(!thrownType){setTempRet0(0);return thrown}for(var caughtType of args){if(caughtType===0||caughtType===thrownType){break}var adjusted_ptr_addr=info.ptr+16;if(___cxa_can_catch(caughtType,thrownType,adjusted_ptr_addr)){setTempRet0(caughtType);return thrown}}setTempRet0(thrownType);return thrown};var ___cxa_find_matching_catch_2=()=>findMatchingCatch([]);var ___cxa_find_matching_catch_3=arg0=>findMatchingCatch([arg0]);var ___cxa_rethrow=()=>{var info=exceptionCaught.pop();if(!info){abort("no exception to throw")}var ptr=info.excPtr;if(!info.get_rethrown()){exceptionCaught.push(info);info.set_rethrown(true);info.set_caught(false);uncaughtExceptionCount++}exceptionLast=ptr;throw exceptionLast};var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var __abort_js=()=>{abort("")};var __emscripten_init_main_thread_js=tb=>{__emscripten_thread_init(tb,!ENVIRONMENT_IS_WORKER,1,!ENVIRONMENT_IS_WEB,65536,false);PThread.threadInitTLS()};var maybeExit=()=>{if(!keepRuntimeAlive()){try{if(ENVIRONMENT_IS_PTHREAD)__emscripten_thread_exit(EXITSTATUS);else _exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{func();maybeExit()}catch(e){handleException(e)}};var __emscripten_thread_mailbox_await=pthread_ptr=>{if(typeof Atomics.waitAsync==="function"){var wait=Atomics.waitAsync(GROWABLE_HEAP_I32(),pthread_ptr>>2,pthread_ptr);wait.value.then(checkMailbox);var waitingAsync=pthread_ptr+128;Atomics.store(GROWABLE_HEAP_I32(),waitingAsync>>2,1)}};var checkMailbox=()=>{var pthread_ptr=_pthread_self();if(pthread_ptr){__emscripten_thread_mailbox_await(pthread_ptr);callUserCallback(__emscripten_check_mailbox)}};var __emscripten_notify_mailbox_postmessage=(targetThread,currThreadId)=>{if(targetThread==currThreadId){setTimeout(checkMailbox)}else if(ENVIRONMENT_IS_PTHREAD){postMessage({targetThread,cmd:"checkMailbox"})}else{var worker=PThread.pthreads[targetThread];if(!worker){return}worker.postMessage({cmd:"checkMailbox"})}};var proxiedJSCallArgs=[];var __emscripten_receive_on_main_thread_js=(funcIndex,emAsmAddr,callingThread,numCallArgs,args)=>{proxiedJSCallArgs.length=numCallArgs;var b=args>>3;for(var i=0;i{if(!ENVIRONMENT_IS_PTHREAD)cleanupThread(thread);else postMessage({cmd:"cleanupThread",thread})};var __emscripten_thread_set_strongref=thread=>{};var warnOnce=text=>{warnOnce.shown||={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;err(text)}};var _emscripten_check_blocking_allowed=()=>{};var runtimeKeepalivePush=()=>{runtimeKeepaliveCounter+=1};var _emscripten_exit_with_live_runtime=()=>{runtimeKeepalivePush();throw"unwind"};var _emscripten_get_now=()=>performance.timeOrigin+performance.now();var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=GROWABLE_HEAP_U8().length;requestedSize>>>=0;if(requestedSize<=oldSize){return false}var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var _llvm_eh_typeid_for=type=>type;var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{GROWABLE_HEAP_I8().set(array,buffer)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,GROWABLE_HEAP_U8(),outPtr,maxBytesToWrite);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead=NaN)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.buffer instanceof SharedArrayBuffer?heapOrArray.slice(idx,endPtr):heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(GROWABLE_HEAP_U8(),ptr,maxBytesToRead):"";var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};PThread.init();var proxiedFunctionTable=[_proc_exit,exitOnMainThread];var wasmImports;function assignWasmImports(){wasmImports={s:___cxa_begin_catch,x:___cxa_end_catch,b:___cxa_find_matching_catch_2,c:___cxa_find_matching_catch_3,L:___cxa_rethrow,u:___cxa_throw,h:___resumeException,C:__abort_js,I:__emscripten_init_main_thread_js,E:__emscripten_notify_mailbox_postmessage,J:__emscripten_receive_on_main_thread_js,B:__emscripten_thread_cleanup,H:__emscripten_thread_mailbox_await,G:__emscripten_thread_set_strongref,K:_emscripten_check_blocking_allowed,F:_emscripten_exit_with_live_runtime,v:_emscripten_get_now,D:_emscripten_resize_heap,A:_exit,q:invoke_fiii,j:invoke_ii,e:invoke_iii,g:invoke_iiii,P:invoke_iiiiffifffffffi,k:invoke_iiiii,O:invoke_iiiiifi,m:invoke_iiiiii,r:invoke_iiiiiii,Q:invoke_iiiiiiifii,N:invoke_iiiiiiii,w:invoke_iiiiiiiii,R:invoke_iiiiiiiiii,z:invoke_jiii,p:invoke_v,n:invoke_vi,d:invoke_vii,i:invoke_viii,f:invoke_viiii,l:invoke_viiiii,o:invoke_viiiiii,M:invoke_viiiiiii,y:invoke_viiij,t:_llvm_eh_typeid_for,a:wasmMemory}}var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["S"])();var _web_engine_create=Module["_web_engine_create"]=()=>(_web_engine_create=Module["_web_engine_create"]=wasmExports["T"])();var _web_engine_get_memory_stats=Module["_web_engine_get_memory_stats"]=a0=>(_web_engine_get_memory_stats=Module["_web_engine_get_memory_stats"]=wasmExports["U"])(a0);var _web_engine_destroy=Module["_web_engine_destroy"]=a0=>(_web_engine_destroy=Module["_web_engine_destroy"]=wasmExports["V"])(a0);var _web_engine_get_live_handles=Module["_web_engine_get_live_handles"]=()=>(_web_engine_get_live_handles=Module["_web_engine_get_live_handles"]=wasmExports["W"])();var _web_engine_get_allocated_bytes=Module["_web_engine_get_allocated_bytes"]=()=>(_web_engine_get_allocated_bytes=Module["_web_engine_get_allocated_bytes"]=wasmExports["X"])();var _web_engine_open_blend=Module["_web_engine_open_blend"]=(a0,a1,a2)=>(_web_engine_open_blend=Module["_web_engine_open_blend"]=wasmExports["Y"])(a0,a1,a2);var _web_engine_apply_command=Module["_web_engine_apply_command"]=(a0,a1,a2)=>(_web_engine_apply_command=Module["_web_engine_apply_command"]=wasmExports["_"])(a0,a1,a2);var _web_engine_undo=Module["_web_engine_undo"]=a0=>(_web_engine_undo=Module["_web_engine_undo"]=wasmExports["$"])(a0);var _web_engine_redo=Module["_web_engine_redo"]=a0=>(_web_engine_redo=Module["_web_engine_redo"]=wasmExports["aa"])(a0);var _web_engine_get_scene_snapshot=Module["_web_engine_get_scene_snapshot"]=(a0,a1,a2)=>(_web_engine_get_scene_snapshot=Module["_web_engine_get_scene_snapshot"]=wasmExports["ba"])(a0,a1,a2);var _web_engine_get_scene_metadata=Module["_web_engine_get_scene_metadata"]=(a0,a1,a2)=>(_web_engine_get_scene_metadata=Module["_web_engine_get_scene_metadata"]=wasmExports["ca"])(a0,a1,a2);var _web_engine_get_scene_geometry=Module["_web_engine_get_scene_geometry"]=(a0,a1,a2)=>(_web_engine_get_scene_geometry=Module["_web_engine_get_scene_geometry"]=wasmExports["da"])(a0,a1,a2);var _web_engine_get_scene_delta=Module["_web_engine_get_scene_delta"]=(a0,a1,a2)=>(_web_engine_get_scene_delta=Module["_web_engine_get_scene_delta"]=wasmExports["ea"])(a0,a1,a2);var _web_engine_get_packed_asset=Module["_web_engine_get_packed_asset"]=(a0,a1,a2,a3,a4)=>(_web_engine_get_packed_asset=Module["_web_engine_get_packed_asset"]=wasmExports["fa"])(a0,a1,a2,a3,a4);var _web_engine_evaluate_depsgraph=Module["_web_engine_evaluate_depsgraph"]=(a0,a1,a2)=>(_web_engine_evaluate_depsgraph=Module["_web_engine_evaluate_depsgraph"]=wasmExports["ga"])(a0,a1,a2);var _web_engine_save_blend=Module["_web_engine_save_blend"]=(a0,a1,a2)=>(_web_engine_save_blend=Module["_web_engine_save_blend"]=wasmExports["ha"])(a0,a1,a2);var _malloc=Module["_malloc"]=a0=>(_malloc=Module["_malloc"]=wasmExports["ia"])(a0);var _web_engine_free_buffer=Module["_web_engine_free_buffer"]=a0=>(_web_engine_free_buffer=Module["_web_engine_free_buffer"]=wasmExports["ja"])(a0);var _free=Module["_free"]=a0=>(_free=Module["_free"]=wasmExports["ka"])(a0);var _web_engine_last_error_code=Module["_web_engine_last_error_code"]=()=>(_web_engine_last_error_code=Module["_web_engine_last_error_code"]=wasmExports["la"])();var _web_engine_last_error_message=Module["_web_engine_last_error_message"]=()=>(_web_engine_last_error_message=Module["_web_engine_last_error_message"]=wasmExports["ma"])();var __emscripten_tls_init=()=>(__emscripten_tls_init=wasmExports["na"])();var _pthread_self=()=>(_pthread_self=wasmExports["oa"])();var __emscripten_thread_init=(a0,a1,a2,a3,a4,a5)=>(__emscripten_thread_init=wasmExports["pa"])(a0,a1,a2,a3,a4,a5);var __emscripten_thread_crashed=()=>(__emscripten_thread_crashed=wasmExports["qa"])();var __emscripten_run_on_main_thread_js=(a0,a1,a2,a3,a4)=>(__emscripten_run_on_main_thread_js=wasmExports["ra"])(a0,a1,a2,a3,a4);var __emscripten_thread_free_data=a0=>(__emscripten_thread_free_data=wasmExports["sa"])(a0);var __emscripten_thread_exit=a0=>(__emscripten_thread_exit=wasmExports["ta"])(a0);var __emscripten_check_mailbox=()=>(__emscripten_check_mailbox=wasmExports["ua"])();var _setThrew=(a0,a1)=>(_setThrew=wasmExports["va"])(a0,a1);var __emscripten_tempret_set=a0=>(__emscripten_tempret_set=wasmExports["wa"])(a0);var _emscripten_stack_set_limits=(a0,a1)=>(_emscripten_stack_set_limits=wasmExports["xa"])(a0,a1);var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["ya"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["za"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["Aa"])();var ___cxa_increment_exception_refcount=a0=>(___cxa_increment_exception_refcount=wasmExports["Ba"])(a0);var ___cxa_decrement_exception_refcount=a0=>(___cxa_decrement_exception_refcount=wasmExports["Ca"])(a0);var ___cxa_can_catch=(a0,a1,a2)=>(___cxa_can_catch=wasmExports["Da"])(a0,a1,a2);var ___cxa_get_exception_ptr=a0=>(___cxa_get_exception_ptr=wasmExports["Ea"])(a0);var dynCall_jiii=Module["dynCall_jiii"]=(a0,a1,a2,a3)=>(dynCall_jiii=Module["dynCall_jiii"]=wasmExports["Fa"])(a0,a1,a2,a3);var dynCall_viiij=Module["dynCall_viiij"]=(a0,a1,a2,a3,a4,a5)=>(dynCall_viiij=Module["dynCall_viiij"]=wasmExports["Ga"])(a0,a1,a2,a3,a4,a5);function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_v(index){var sp=stackSave();try{getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vi(index,a1){var sp=stackSave();try{getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiifii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiffifffffffi(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiifi(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_jiii(index,a1,a2,a3){var sp=stackSave();try{return dynCall_jiii(index,a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiij(index,a1,a2,a3,a4,a5){var sp=stackSave();try{dynCall_viiij(index,a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["PThread"]=PThread;var calledRun;var calledPrerun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}if(ENVIRONMENT_IS_PTHREAD){readyPromiseResolve(Module);initRuntime();startWorker(Module);return}if(!calledPrerun){calledPrerun=1;preRun();if(runDependencies>0){return}}function doRun(){if(calledRun)return;calledRun=1;Module["calledRun"]=1;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise; +function GROWABLE_HEAP_I8(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAP8}function GROWABLE_HEAP_U8(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAPU8}function GROWABLE_HEAP_I32(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAP32}function GROWABLE_HEAP_U32(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAPU32}function GROWABLE_HEAP_F64(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAPF64}var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string"&&process.type!="renderer";var ENVIRONMENT_IS_PTHREAD=ENVIRONMENT_IS_WORKER&&self.name?.startsWith("em-pthread");var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=url=>fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))})}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var wasmMemory;var wasmModule;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}if(ENVIRONMENT_IS_PTHREAD){var wasmPromiseResolve;var wasmPromiseReject;var initializedJS=false;function threadPrintErr(...args){var text=args.join(" ");console.error(text)}if(!Module["printErr"])err=threadPrintErr;function threadAlert(...args){var text=args.join(" ");postMessage({cmd:"alert",text,threadId:_pthread_self()})}self.alert=threadAlert;Module["instantiateWasm"]=(info,receiveInstance)=>new Promise((resolve,reject)=>{wasmPromiseResolve=module=>{var instance=new WebAssembly.Instance(module,getWasmImports());receiveInstance(instance);resolve()};wasmPromiseReject=reject});self.onunhandledrejection=e=>{throw e.reason||e};function handleMessage(e){try{var msgData=e["data"];var cmd=msgData.cmd;if(cmd==="load"){let messageQueue=[];self.onmessage=e=>messageQueue.push(e);self.startWorker=instance=>{postMessage({cmd:"loaded"});for(let msg of messageQueue){handleMessage(msg)}self.onmessage=handleMessage};for(const handler of msgData.handlers){if(!Module[handler]||Module[handler].proxy){Module[handler]=(...args)=>{postMessage({cmd:"callHandler",handler,args})};if(handler=="print")out=Module[handler];if(handler=="printErr")err=Module[handler]}}wasmMemory=msgData.wasmMemory;updateMemoryViews();wasmPromiseResolve(msgData.wasmModule)}else if(cmd==="run"){establishStackSpace(msgData.pthread_ptr);__emscripten_thread_init(msgData.pthread_ptr,0,0,1,0,0);PThread.receiveObjectTransfer(msgData);PThread.threadInitTLS();__emscripten_thread_mailbox_await(msgData.pthread_ptr);if(!initializedJS){initializedJS=true}try{invokeEntryPoint(msgData.start_routine,msgData.arg)}catch(ex){if(ex!="unwind"){throw ex}}}else if(msgData.target==="setimmediate"){}else if(cmd==="checkMailbox"){if(initializedJS){checkMailbox()}}else if(cmd){err(`worker: received unknown command ${cmd}`);err(msgData)}}catch(ex){__emscripten_thread_crashed();throw ex}}self.onmessage=handleMessage}if(!ENVIRONMENT_IS_PTHREAD){if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;wasmMemory=new WebAssembly.Memory({initial:INITIAL_MEMORY/65536,maximum:32768,shared:true})}updateMemoryViews()}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){var preRuns=Module["preRun"];if(preRuns){if(typeof preRuns=="function")preRuns=[preRuns];preRuns.forEach(addOnPreRun)}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(ENVIRONMENT_IS_PTHREAD)return;callRuntimeCallbacks(__ATINIT__)}function postRun(){if(ENVIRONMENT_IS_PTHREAD)return;var postRuns=Module["postRun"];if(postRuns){if(typeof postRuns=="function")postRuns=[postRuns];postRuns.forEach(addOnPostRun)}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);function findWasmBinary(){if(Module["locateFile"]){var f="web_engine.wasm";if(!isDataURI(f)){return locateFile(f)}return f}return new URL("web_engine.wasm",import.meta.url).href}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){assignWasmImports();return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;registerTLSInit(wasmExports["oa"]);wasmTable=wasmExports["_"];addOnInit(wasmExports["T"]);wasmModule=module;removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"],result["module"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}wasmBinaryFile??=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}function ExitStatus(status){this.name="ExitStatus";this.message=`Program terminated with exit(${status})`;this.status=status}var terminateWorker=worker=>{worker.terminate();worker.onmessage=e=>{}};var cleanupThread=pthread_ptr=>{var worker=PThread.pthreads[pthread_ptr];PThread.returnWorkerToPool(worker)};var spawnThread=threadParams=>{var worker=PThread.getNewWorker();if(!worker){return 6}PThread.runningWorkers.push(worker);PThread.pthreads[threadParams.pthread_ptr]=worker;worker.pthread_ptr=threadParams.pthread_ptr;var msg={cmd:"run",start_routine:threadParams.startRoutine,arg:threadParams.arg,pthread_ptr:threadParams.pthread_ptr};worker.postMessage(msg,threadParams.transferList);return 0};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var stackSave=()=>_emscripten_stack_get_current();var stackRestore=val=>__emscripten_stack_restore(val);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var proxyToMainThread=(funcIndex,emAsmAddr,sync,...callArgs)=>{var serializedNumCallArgs=callArgs.length;var sp=stackSave();var args=stackAlloc(serializedNumCallArgs*8);var b=args>>3;for(var i=0;i{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};function exitOnMainThread(returnCode){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(1,0,0,returnCode);_exit(returnCode)}var exitJS=(status,implicit)=>{EXITSTATUS=status;if(ENVIRONMENT_IS_PTHREAD){exitOnMainThread(status);throw"unwind"}_proc_exit(status)};var _exit=exitJS;var PThread={unusedWorkers:[],runningWorkers:[],tlsInitFunctions:[],pthreads:{},init(){if(!ENVIRONMENT_IS_PTHREAD){PThread.initMainThread()}},initMainThread(){var pthreadPoolSize=1;while(pthreadPoolSize--){PThread.allocateUnusedWorker()}addOnPreRun(()=>{addRunDependency("loading-workers");PThread.loadWasmModuleToAllWorkers(()=>removeRunDependency("loading-workers"))})},terminateAllThreads:()=>{for(var worker of PThread.runningWorkers){terminateWorker(worker)}for(var worker of PThread.unusedWorkers){terminateWorker(worker)}PThread.unusedWorkers=[];PThread.runningWorkers=[];PThread.pthreads=[]},returnWorkerToPool:worker=>{var pthread_ptr=worker.pthread_ptr;delete PThread.pthreads[pthread_ptr];PThread.unusedWorkers.push(worker);PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker),1);worker.pthread_ptr=0;__emscripten_thread_free_data(pthread_ptr)},receiveObjectTransfer(data){},threadInitTLS(){PThread.tlsInitFunctions.forEach(f=>f())},loadWasmModuleToWorker:worker=>new Promise(onFinishedLoading=>{worker.onmessage=e=>{var d=e["data"];var cmd=d.cmd;if(d.targetThread&&d.targetThread!=_pthread_self()){var targetWorker=PThread.pthreads[d.targetThread];if(targetWorker){targetWorker.postMessage(d,d.transferList)}else{err(`Internal error! Worker sent a message "${cmd}" to target pthread ${d.targetThread}, but that thread no longer exists!`)}return}if(cmd==="checkMailbox"){checkMailbox()}else if(cmd==="spawnThread"){spawnThread(d)}else if(cmd==="cleanupThread"){cleanupThread(d.thread)}else if(cmd==="loaded"){worker.loaded=true;onFinishedLoading(worker)}else if(cmd==="alert"){alert(`Thread ${d.threadId}: ${d.text}`)}else if(d.target==="setimmediate"){worker.postMessage(d)}else if(cmd==="callHandler"){Module[d.handler](...d.args)}else if(cmd){err(`worker sent an unknown command ${cmd}`)}};worker.onerror=e=>{var message="worker sent an error!";err(`${message} ${e.filename}:${e.lineno}: ${e.message}`);throw e};var handlers=[];var knownHandlers=["onExit","onAbort","print","printErr"];for(var handler of knownHandlers){if(Module.propertyIsEnumerable(handler)){handlers.push(handler)}}worker.postMessage({cmd:"load",handlers,wasmMemory,wasmModule})}),loadWasmModuleToAllWorkers(onMaybeReady){if(ENVIRONMENT_IS_PTHREAD){return onMaybeReady()}let pthreadPoolReady=Promise.all(PThread.unusedWorkers.map(PThread.loadWasmModuleToWorker));pthreadPoolReady.then(onMaybeReady)},allocateUnusedWorker(){var worker;var workerOptions={type:"module",name:"em-pthread"};worker=new Worker(new URL("web_engine.js",import.meta.url),workerOptions);PThread.unusedWorkers.push(worker)},getNewWorker(){if(PThread.unusedWorkers.length==0){PThread.allocateUnusedWorker();PThread.loadWasmModuleToWorker(PThread.unusedWorkers[0])}return PThread.unusedWorkers.pop()}};var callRuntimeCallbacks=callbacks=>{callbacks.forEach(f=>f(Module))};var establishStackSpace=pthread_ptr=>{updateMemoryViews();var stackHigh=GROWABLE_HEAP_U32()[pthread_ptr+52>>2];var stackSize=GROWABLE_HEAP_U32()[pthread_ptr+56>>2];var stackLow=stackHigh-stackSize;_emscripten_stack_set_limits(stackHigh,stackLow);stackRestore(stackHigh)};var wasmTableMirror=[];var wasmTable;var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var invokeEntryPoint=(ptr,arg)=>{runtimeKeepaliveCounter=0;noExitRuntime=0;var result=getWasmTableEntry(ptr)(arg);function finish(result){if(keepRuntimeAlive()){EXITSTATUS=result}else{__emscripten_thread_exit(result)}}finish(result)};var noExitRuntime=Module["noExitRuntime"]||true;var registerTLSInit=tlsInitFunc=>PThread.tlsInitFunctions.push(tlsInitFunc);var exceptionCaught=[];var uncaughtExceptionCount=0;var ___cxa_begin_catch=ptr=>{var info=new ExceptionInfo(ptr);if(!info.get_caught()){info.set_caught(true);uncaughtExceptionCount--}info.set_rethrown(false);exceptionCaught.push(info);___cxa_increment_exception_refcount(ptr);return ___cxa_get_exception_ptr(ptr)};var exceptionLast=0;var ___cxa_end_catch=()=>{_setThrew(0,0);var info=exceptionCaught.pop();___cxa_decrement_exception_refcount(info.excPtr);exceptionLast=0};class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){GROWABLE_HEAP_U32()[this.ptr+4>>2]=type}get_type(){return GROWABLE_HEAP_U32()[this.ptr+4>>2]}set_destructor(destructor){GROWABLE_HEAP_U32()[this.ptr+8>>2]=destructor}get_destructor(){return GROWABLE_HEAP_U32()[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;GROWABLE_HEAP_I8()[this.ptr+12]=caught}get_caught(){return GROWABLE_HEAP_I8()[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;GROWABLE_HEAP_I8()[this.ptr+13]=rethrown}get_rethrown(){return GROWABLE_HEAP_I8()[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){GROWABLE_HEAP_U32()[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return GROWABLE_HEAP_U32()[this.ptr+16>>2]}}var ___resumeException=ptr=>{if(!exceptionLast){exceptionLast=ptr}throw exceptionLast};var setTempRet0=val=>__emscripten_tempret_set(val);var findMatchingCatch=args=>{var thrown=exceptionLast;if(!thrown){setTempRet0(0);return 0}var info=new ExceptionInfo(thrown);info.set_adjusted_ptr(thrown);var thrownType=info.get_type();if(!thrownType){setTempRet0(0);return thrown}for(var caughtType of args){if(caughtType===0||caughtType===thrownType){break}var adjusted_ptr_addr=info.ptr+16;if(___cxa_can_catch(caughtType,thrownType,adjusted_ptr_addr)){setTempRet0(caughtType);return thrown}}setTempRet0(thrownType);return thrown};var ___cxa_find_matching_catch_2=()=>findMatchingCatch([]);var ___cxa_find_matching_catch_3=arg0=>findMatchingCatch([arg0]);var ___cxa_rethrow=()=>{var info=exceptionCaught.pop();if(!info){abort("no exception to throw")}var ptr=info.excPtr;if(!info.get_rethrown()){exceptionCaught.push(info);info.set_rethrown(true);info.set_caught(false);uncaughtExceptionCount++}exceptionLast=ptr;throw exceptionLast};var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var __abort_js=()=>{abort("")};var __emscripten_init_main_thread_js=tb=>{__emscripten_thread_init(tb,!ENVIRONMENT_IS_WORKER,1,!ENVIRONMENT_IS_WEB,65536,false);PThread.threadInitTLS()};var maybeExit=()=>{if(!keepRuntimeAlive()){try{if(ENVIRONMENT_IS_PTHREAD)__emscripten_thread_exit(EXITSTATUS);else _exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{func();maybeExit()}catch(e){handleException(e)}};var __emscripten_thread_mailbox_await=pthread_ptr=>{if(typeof Atomics.waitAsync==="function"){var wait=Atomics.waitAsync(GROWABLE_HEAP_I32(),pthread_ptr>>2,pthread_ptr);wait.value.then(checkMailbox);var waitingAsync=pthread_ptr+128;Atomics.store(GROWABLE_HEAP_I32(),waitingAsync>>2,1)}};var checkMailbox=()=>{var pthread_ptr=_pthread_self();if(pthread_ptr){__emscripten_thread_mailbox_await(pthread_ptr);callUserCallback(__emscripten_check_mailbox)}};var __emscripten_notify_mailbox_postmessage=(targetThread,currThreadId)=>{if(targetThread==currThreadId){setTimeout(checkMailbox)}else if(ENVIRONMENT_IS_PTHREAD){postMessage({targetThread,cmd:"checkMailbox"})}else{var worker=PThread.pthreads[targetThread];if(!worker){return}worker.postMessage({cmd:"checkMailbox"})}};var proxiedJSCallArgs=[];var __emscripten_receive_on_main_thread_js=(funcIndex,emAsmAddr,callingThread,numCallArgs,args)=>{proxiedJSCallArgs.length=numCallArgs;var b=args>>3;for(var i=0;i{if(!ENVIRONMENT_IS_PTHREAD)cleanupThread(thread);else postMessage({cmd:"cleanupThread",thread})};var __emscripten_thread_set_strongref=thread=>{};var warnOnce=text=>{warnOnce.shown||={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;err(text)}};var _emscripten_check_blocking_allowed=()=>{};var runtimeKeepalivePush=()=>{runtimeKeepaliveCounter+=1};var _emscripten_exit_with_live_runtime=()=>{runtimeKeepalivePush();throw"unwind"};var _emscripten_get_now=()=>performance.timeOrigin+performance.now();var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=GROWABLE_HEAP_U8().length;requestedSize>>>=0;if(requestedSize<=oldSize){return false}var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var _llvm_eh_typeid_for=type=>type;var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{GROWABLE_HEAP_I8().set(array,buffer)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,GROWABLE_HEAP_U8(),outPtr,maxBytesToWrite);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead=NaN)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.buffer instanceof SharedArrayBuffer?heapOrArray.slice(idx,endPtr):heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(GROWABLE_HEAP_U8(),ptr,maxBytesToRead):"";var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};PThread.init();var proxiedFunctionTable=[_proc_exit,exitOnMainThread];var wasmImports;function assignWasmImports(){wasmImports={s:___cxa_begin_catch,w:___cxa_end_catch,b:___cxa_find_matching_catch_2,c:___cxa_find_matching_catch_3,M:___cxa_rethrow,u:___cxa_throw,h:___resumeException,D:__abort_js,J:__emscripten_init_main_thread_js,F:__emscripten_notify_mailbox_postmessage,K:__emscripten_receive_on_main_thread_js,C:__emscripten_thread_cleanup,I:__emscripten_thread_mailbox_await,H:__emscripten_thread_set_strongref,L:_emscripten_check_blocking_allowed,G:_emscripten_exit_with_live_runtime,v:_emscripten_get_now,E:_emscripten_resize_heap,B:_exit,o:invoke_fiii,j:invoke_ii,e:invoke_iii,g:invoke_iiii,Q:invoke_iiiiffifffffffi,k:invoke_iiiii,P:invoke_iiiiifi,m:invoke_iiiiii,r:invoke_iiiiiii,R:invoke_iiiiiiifii,y:invoke_iiiiiiii,x:invoke_iiiiiiiii,S:invoke_iiiiiiiiii,O:invoke_iiiiiiiiiifi,A:invoke_jiii,q:invoke_v,n:invoke_vi,d:invoke_vii,i:invoke_viii,f:invoke_viiii,l:invoke_viiiii,p:invoke_viiiiii,N:invoke_viiiiiii,z:invoke_viiij,t:_llvm_eh_typeid_for,a:wasmMemory}}var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["T"])();var _web_engine_create=Module["_web_engine_create"]=()=>(_web_engine_create=Module["_web_engine_create"]=wasmExports["U"])();var _web_engine_get_memory_stats=Module["_web_engine_get_memory_stats"]=a0=>(_web_engine_get_memory_stats=Module["_web_engine_get_memory_stats"]=wasmExports["V"])(a0);var _web_engine_destroy=Module["_web_engine_destroy"]=a0=>(_web_engine_destroy=Module["_web_engine_destroy"]=wasmExports["W"])(a0);var _web_engine_get_live_handles=Module["_web_engine_get_live_handles"]=()=>(_web_engine_get_live_handles=Module["_web_engine_get_live_handles"]=wasmExports["X"])();var _web_engine_get_allocated_bytes=Module["_web_engine_get_allocated_bytes"]=()=>(_web_engine_get_allocated_bytes=Module["_web_engine_get_allocated_bytes"]=wasmExports["Y"])();var _web_engine_open_blend=Module["_web_engine_open_blend"]=(a0,a1,a2)=>(_web_engine_open_blend=Module["_web_engine_open_blend"]=wasmExports["Z"])(a0,a1,a2);var _web_engine_apply_command=Module["_web_engine_apply_command"]=(a0,a1,a2)=>(_web_engine_apply_command=Module["_web_engine_apply_command"]=wasmExports["$"])(a0,a1,a2);var _web_engine_undo=Module["_web_engine_undo"]=a0=>(_web_engine_undo=Module["_web_engine_undo"]=wasmExports["aa"])(a0);var _web_engine_redo=Module["_web_engine_redo"]=a0=>(_web_engine_redo=Module["_web_engine_redo"]=wasmExports["ba"])(a0);var _web_engine_get_scene_snapshot=Module["_web_engine_get_scene_snapshot"]=(a0,a1,a2)=>(_web_engine_get_scene_snapshot=Module["_web_engine_get_scene_snapshot"]=wasmExports["ca"])(a0,a1,a2);var _web_engine_get_scene_metadata=Module["_web_engine_get_scene_metadata"]=(a0,a1,a2)=>(_web_engine_get_scene_metadata=Module["_web_engine_get_scene_metadata"]=wasmExports["da"])(a0,a1,a2);var _web_engine_get_scene_geometry=Module["_web_engine_get_scene_geometry"]=(a0,a1,a2)=>(_web_engine_get_scene_geometry=Module["_web_engine_get_scene_geometry"]=wasmExports["ea"])(a0,a1,a2);var _web_engine_get_scene_delta=Module["_web_engine_get_scene_delta"]=(a0,a1,a2)=>(_web_engine_get_scene_delta=Module["_web_engine_get_scene_delta"]=wasmExports["fa"])(a0,a1,a2);var _web_engine_get_packed_asset=Module["_web_engine_get_packed_asset"]=(a0,a1,a2,a3,a4)=>(_web_engine_get_packed_asset=Module["_web_engine_get_packed_asset"]=wasmExports["ga"])(a0,a1,a2,a3,a4);var _web_engine_evaluate_depsgraph=Module["_web_engine_evaluate_depsgraph"]=(a0,a1,a2)=>(_web_engine_evaluate_depsgraph=Module["_web_engine_evaluate_depsgraph"]=wasmExports["ha"])(a0,a1,a2);var _web_engine_save_blend=Module["_web_engine_save_blend"]=(a0,a1,a2)=>(_web_engine_save_blend=Module["_web_engine_save_blend"]=wasmExports["ia"])(a0,a1,a2);var _malloc=Module["_malloc"]=a0=>(_malloc=Module["_malloc"]=wasmExports["ja"])(a0);var _web_engine_free_buffer=Module["_web_engine_free_buffer"]=a0=>(_web_engine_free_buffer=Module["_web_engine_free_buffer"]=wasmExports["ka"])(a0);var _free=Module["_free"]=a0=>(_free=Module["_free"]=wasmExports["la"])(a0);var _web_engine_last_error_code=Module["_web_engine_last_error_code"]=()=>(_web_engine_last_error_code=Module["_web_engine_last_error_code"]=wasmExports["ma"])();var _web_engine_last_error_message=Module["_web_engine_last_error_message"]=()=>(_web_engine_last_error_message=Module["_web_engine_last_error_message"]=wasmExports["na"])();var __emscripten_tls_init=()=>(__emscripten_tls_init=wasmExports["oa"])();var _pthread_self=()=>(_pthread_self=wasmExports["pa"])();var __emscripten_thread_init=(a0,a1,a2,a3,a4,a5)=>(__emscripten_thread_init=wasmExports["qa"])(a0,a1,a2,a3,a4,a5);var __emscripten_thread_crashed=()=>(__emscripten_thread_crashed=wasmExports["ra"])();var __emscripten_run_on_main_thread_js=(a0,a1,a2,a3,a4)=>(__emscripten_run_on_main_thread_js=wasmExports["sa"])(a0,a1,a2,a3,a4);var __emscripten_thread_free_data=a0=>(__emscripten_thread_free_data=wasmExports["ta"])(a0);var __emscripten_thread_exit=a0=>(__emscripten_thread_exit=wasmExports["ua"])(a0);var __emscripten_check_mailbox=()=>(__emscripten_check_mailbox=wasmExports["va"])();var _setThrew=(a0,a1)=>(_setThrew=wasmExports["wa"])(a0,a1);var __emscripten_tempret_set=a0=>(__emscripten_tempret_set=wasmExports["xa"])(a0);var _emscripten_stack_set_limits=(a0,a1)=>(_emscripten_stack_set_limits=wasmExports["ya"])(a0,a1);var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["za"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["Aa"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["Ba"])();var ___cxa_increment_exception_refcount=a0=>(___cxa_increment_exception_refcount=wasmExports["Ca"])(a0);var ___cxa_decrement_exception_refcount=a0=>(___cxa_decrement_exception_refcount=wasmExports["Da"])(a0);var ___cxa_can_catch=(a0,a1,a2)=>(___cxa_can_catch=wasmExports["Ea"])(a0,a1,a2);var ___cxa_get_exception_ptr=a0=>(___cxa_get_exception_ptr=wasmExports["Fa"])(a0);var dynCall_jiii=Module["dynCall_jiii"]=(a0,a1,a2,a3)=>(dynCall_jiii=Module["dynCall_jiii"]=wasmExports["Ga"])(a0,a1,a2,a3);var dynCall_viiij=Module["dynCall_viiij"]=(a0,a1,a2,a3,a4,a5)=>(dynCall_viiij=Module["dynCall_viiij"]=wasmExports["Ha"])(a0,a1,a2,a3,a4,a5);function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_v(index){var sp=stackSave();try{getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vi(index,a1){var sp=stackSave();try{getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiifii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiffifffffffi(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiifi(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiiifi(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_jiii(index,a1,a2,a3){var sp=stackSave();try{return dynCall_jiii(index,a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiij(index,a1,a2,a3,a4,a5){var sp=stackSave();try{dynCall_viiij(index,a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["PThread"]=PThread;var calledRun;var calledPrerun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}if(ENVIRONMENT_IS_PTHREAD){readyPromiseResolve(Module);initRuntime();startWorker(Module);return}if(!calledPrerun){calledPrerun=1;preRun();if(runDependencies>0){return}}function doRun(){if(calledRun)return;calledRun=1;Module["calledRun"]=1;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise; return moduleRtn; diff --git a/web/app/public/vendor/blender/pthread/web_engine.wasm b/web/app/public/vendor/blender/pthread/web_engine.wasm index ab66ed0dc5ef422dcdfe0e7ab10c0fa20313a40a..4337c2f0454d66a21dbb9b1a356cbefde70f4548 100644 GIT binary patch delta 86734 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

Xa<+v0d7h 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

Uwp$aEIe7h&MD)_0{SnNDgw7$)tm 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 diff --git a/web/app/public/vendor/blender/single/web_engine.js b/web/app/public/vendor/blender/single/web_engine.js index dd482cba..283630ad 100644 --- a/web/app/public/vendor/blender/single/web_engine.js +++ b/web/app/public/vendor/blender/single/web_engine.js @@ -6,7 +6,7 @@ var Module = (() => { function(moduleArg = {}) { var moduleRtn; -var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string"&&process.type!="renderer";var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=url=>fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))})}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){var preRuns=Module["preRun"];if(preRuns){if(typeof preRuns=="function")preRuns=[preRuns];preRuns.forEach(addOnPreRun)}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function postRun(){var postRuns=Module["postRun"];if(postRuns){if(typeof postRuns=="function")postRuns=[postRuns];postRuns.forEach(addOnPostRun)}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);function findWasmBinary(){if(Module["locateFile"]){var f="web_engine.wasm";if(!isDataURI(f)){return locateFile(f)}return f}return new URL("web_engine.wasm",import.meta.url).href}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["I"];updateMemoryViews();wasmTable=wasmExports["Q"];addOnInit(wasmExports["J"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}wasmBinaryFile??=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var callRuntimeCallbacks=callbacks=>{callbacks.forEach(f=>f(Module))};var noExitRuntime=Module["noExitRuntime"]||true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var exceptionCaught=[];var uncaughtExceptionCount=0;var ___cxa_begin_catch=ptr=>{var info=new ExceptionInfo(ptr);if(!info.get_caught()){info.set_caught(true);uncaughtExceptionCount--}info.set_rethrown(false);exceptionCaught.push(info);___cxa_increment_exception_refcount(ptr);return ___cxa_get_exception_ptr(ptr)};var exceptionLast=0;var ___cxa_end_catch=()=>{_setThrew(0,0);var info=exceptionCaught.pop();___cxa_decrement_exception_refcount(info.excPtr);exceptionLast=0};class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var ___resumeException=ptr=>{if(!exceptionLast){exceptionLast=ptr}throw exceptionLast};var setTempRet0=val=>__emscripten_tempret_set(val);var findMatchingCatch=args=>{var thrown=exceptionLast;if(!thrown){setTempRet0(0);return 0}var info=new ExceptionInfo(thrown);info.set_adjusted_ptr(thrown);var thrownType=info.get_type();if(!thrownType){setTempRet0(0);return thrown}for(var caughtType of args){if(caughtType===0||caughtType===thrownType){break}var adjusted_ptr_addr=info.ptr+16;if(___cxa_can_catch(caughtType,thrownType,adjusted_ptr_addr)){setTempRet0(caughtType);return thrown}}setTempRet0(thrownType);return thrown};var ___cxa_find_matching_catch_2=()=>findMatchingCatch([]);var ___cxa_find_matching_catch_3=arg0=>findMatchingCatch([arg0]);var ___cxa_rethrow=()=>{var info=exceptionCaught.pop();if(!info){abort("no exception to throw")}var ptr=info.excPtr;if(!info.get_rethrown()){exceptionCaught.push(info);info.set_rethrown(true);info.set_caught(false);uncaughtExceptionCount++}exceptionLast=ptr;throw exceptionLast};var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var __abort_js=()=>{abort("")};var __emscripten_memcpy_js=(dest,src,num)=>HEAPU8.copyWithin(dest,src,src+num);var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var _llvm_eh_typeid_for=type=>type;var wasmTableMirror=[];var wasmTable;var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead=NaN)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var wasmImports={r:___cxa_begin_catch,v:___cxa_end_catch,a:___cxa_find_matching_catch_2,b:___cxa_find_matching_catch_3,B:___cxa_rethrow,t:___cxa_throw,g:___resumeException,y:__abort_js,A:__emscripten_memcpy_js,z:_emscripten_resize_heap,p:invoke_fiii,i:invoke_ii,d:invoke_iii,f:invoke_iiii,F:invoke_iiiiffifffffffi,j:invoke_iiiii,E:invoke_iiiiifi,l:invoke_iiiiii,q:invoke_iiiiiii,G:invoke_iiiiiiifii,D:invoke_iiiiiiii,u:invoke_iiiiiiiii,H:invoke_iiiiiiiiii,x:invoke_jiii,o:invoke_v,m:invoke_vi,c:invoke_vii,h:invoke_viii,e:invoke_viiii,k:invoke_viiiii,n:invoke_viiiiii,C:invoke_viiiiiii,w:invoke_viiij,s:_llvm_eh_typeid_for};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["J"])();var _web_engine_create=Module["_web_engine_create"]=()=>(_web_engine_create=Module["_web_engine_create"]=wasmExports["K"])();var _web_engine_get_memory_stats=Module["_web_engine_get_memory_stats"]=a0=>(_web_engine_get_memory_stats=Module["_web_engine_get_memory_stats"]=wasmExports["L"])(a0);var _web_engine_destroy=Module["_web_engine_destroy"]=a0=>(_web_engine_destroy=Module["_web_engine_destroy"]=wasmExports["M"])(a0);var _web_engine_get_live_handles=Module["_web_engine_get_live_handles"]=()=>(_web_engine_get_live_handles=Module["_web_engine_get_live_handles"]=wasmExports["N"])();var _web_engine_get_allocated_bytes=Module["_web_engine_get_allocated_bytes"]=()=>(_web_engine_get_allocated_bytes=Module["_web_engine_get_allocated_bytes"]=wasmExports["O"])();var _web_engine_open_blend=Module["_web_engine_open_blend"]=(a0,a1,a2)=>(_web_engine_open_blend=Module["_web_engine_open_blend"]=wasmExports["P"])(a0,a1,a2);var _web_engine_apply_command=Module["_web_engine_apply_command"]=(a0,a1,a2)=>(_web_engine_apply_command=Module["_web_engine_apply_command"]=wasmExports["R"])(a0,a1,a2);var _web_engine_undo=Module["_web_engine_undo"]=a0=>(_web_engine_undo=Module["_web_engine_undo"]=wasmExports["S"])(a0);var _web_engine_redo=Module["_web_engine_redo"]=a0=>(_web_engine_redo=Module["_web_engine_redo"]=wasmExports["T"])(a0);var _web_engine_get_scene_snapshot=Module["_web_engine_get_scene_snapshot"]=(a0,a1,a2)=>(_web_engine_get_scene_snapshot=Module["_web_engine_get_scene_snapshot"]=wasmExports["U"])(a0,a1,a2);var _web_engine_get_scene_metadata=Module["_web_engine_get_scene_metadata"]=(a0,a1,a2)=>(_web_engine_get_scene_metadata=Module["_web_engine_get_scene_metadata"]=wasmExports["V"])(a0,a1,a2);var _web_engine_get_scene_geometry=Module["_web_engine_get_scene_geometry"]=(a0,a1,a2)=>(_web_engine_get_scene_geometry=Module["_web_engine_get_scene_geometry"]=wasmExports["W"])(a0,a1,a2);var _web_engine_get_scene_delta=Module["_web_engine_get_scene_delta"]=(a0,a1,a2)=>(_web_engine_get_scene_delta=Module["_web_engine_get_scene_delta"]=wasmExports["X"])(a0,a1,a2);var _web_engine_get_packed_asset=Module["_web_engine_get_packed_asset"]=(a0,a1,a2,a3,a4)=>(_web_engine_get_packed_asset=Module["_web_engine_get_packed_asset"]=wasmExports["Y"])(a0,a1,a2,a3,a4);var _web_engine_evaluate_depsgraph=Module["_web_engine_evaluate_depsgraph"]=(a0,a1,a2)=>(_web_engine_evaluate_depsgraph=Module["_web_engine_evaluate_depsgraph"]=wasmExports["Z"])(a0,a1,a2);var _web_engine_save_blend=Module["_web_engine_save_blend"]=(a0,a1,a2)=>(_web_engine_save_blend=Module["_web_engine_save_blend"]=wasmExports["_"])(a0,a1,a2);var _malloc=Module["_malloc"]=a0=>(_malloc=Module["_malloc"]=wasmExports["$"])(a0);var _web_engine_free_buffer=Module["_web_engine_free_buffer"]=a0=>(_web_engine_free_buffer=Module["_web_engine_free_buffer"]=wasmExports["aa"])(a0);var _free=Module["_free"]=a0=>(_free=Module["_free"]=wasmExports["ba"])(a0);var _web_engine_last_error_code=Module["_web_engine_last_error_code"]=()=>(_web_engine_last_error_code=Module["_web_engine_last_error_code"]=wasmExports["ca"])();var _web_engine_last_error_message=Module["_web_engine_last_error_message"]=()=>(_web_engine_last_error_message=Module["_web_engine_last_error_message"]=wasmExports["da"])();var _setThrew=(a0,a1)=>(_setThrew=wasmExports["ea"])(a0,a1);var __emscripten_tempret_set=a0=>(__emscripten_tempret_set=wasmExports["fa"])(a0);var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["ga"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["ha"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["ia"])();var ___cxa_increment_exception_refcount=a0=>(___cxa_increment_exception_refcount=wasmExports["ja"])(a0);var ___cxa_decrement_exception_refcount=a0=>(___cxa_decrement_exception_refcount=wasmExports["ka"])(a0);var ___cxa_can_catch=(a0,a1,a2)=>(___cxa_can_catch=wasmExports["la"])(a0,a1,a2);var ___cxa_get_exception_ptr=a0=>(___cxa_get_exception_ptr=wasmExports["ma"])(a0);var dynCall_jiii=Module["dynCall_jiii"]=(a0,a1,a2,a3)=>(dynCall_jiii=Module["dynCall_jiii"]=wasmExports["na"])(a0,a1,a2,a3);var dynCall_viiij=Module["dynCall_viiij"]=(a0,a1,a2,a3,a4,a5)=>(dynCall_viiij=Module["dynCall_viiij"]=wasmExports["oa"])(a0,a1,a2,a3,a4,a5);function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_v(index){var sp=stackSave();try{getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vi(index,a1){var sp=stackSave();try{getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiifii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiffifffffffi(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiifi(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_jiii(index,a1,a2,a3){var sp=stackSave();try{return dynCall_jiii(index,a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiij(index,a1,a2,a3,a4,a5){var sp=stackSave();try{dynCall_viiij(index,a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}Module["ccall"]=ccall;Module["cwrap"]=cwrap;var calledRun;var calledPrerun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}if(!calledPrerun){calledPrerun=1;preRun();if(runDependencies>0){return}}function doRun(){if(calledRun)return;calledRun=1;Module["calledRun"]=1;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise; +var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string"&&process.type!="renderer";var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=url=>fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))})}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){var preRuns=Module["preRun"];if(preRuns){if(typeof preRuns=="function")preRuns=[preRuns];preRuns.forEach(addOnPreRun)}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function postRun(){var postRuns=Module["postRun"];if(postRuns){if(typeof postRuns=="function")postRuns=[postRuns];postRuns.forEach(addOnPostRun)}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);function findWasmBinary(){if(Module["locateFile"]){var f="web_engine.wasm";if(!isDataURI(f)){return locateFile(f)}return f}return new URL("web_engine.wasm",import.meta.url).href}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["J"];updateMemoryViews();wasmTable=wasmExports["R"];addOnInit(wasmExports["K"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}wasmBinaryFile??=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var callRuntimeCallbacks=callbacks=>{callbacks.forEach(f=>f(Module))};var noExitRuntime=Module["noExitRuntime"]||true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var exceptionCaught=[];var uncaughtExceptionCount=0;var ___cxa_begin_catch=ptr=>{var info=new ExceptionInfo(ptr);if(!info.get_caught()){info.set_caught(true);uncaughtExceptionCount--}info.set_rethrown(false);exceptionCaught.push(info);___cxa_increment_exception_refcount(ptr);return ___cxa_get_exception_ptr(ptr)};var exceptionLast=0;var ___cxa_end_catch=()=>{_setThrew(0,0);var info=exceptionCaught.pop();___cxa_decrement_exception_refcount(info.excPtr);exceptionLast=0};class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var ___resumeException=ptr=>{if(!exceptionLast){exceptionLast=ptr}throw exceptionLast};var setTempRet0=val=>__emscripten_tempret_set(val);var findMatchingCatch=args=>{var thrown=exceptionLast;if(!thrown){setTempRet0(0);return 0}var info=new ExceptionInfo(thrown);info.set_adjusted_ptr(thrown);var thrownType=info.get_type();if(!thrownType){setTempRet0(0);return thrown}for(var caughtType of args){if(caughtType===0||caughtType===thrownType){break}var adjusted_ptr_addr=info.ptr+16;if(___cxa_can_catch(caughtType,thrownType,adjusted_ptr_addr)){setTempRet0(caughtType);return thrown}}setTempRet0(thrownType);return thrown};var ___cxa_find_matching_catch_2=()=>findMatchingCatch([]);var ___cxa_find_matching_catch_3=arg0=>findMatchingCatch([arg0]);var ___cxa_rethrow=()=>{var info=exceptionCaught.pop();if(!info){abort("no exception to throw")}var ptr=info.excPtr;if(!info.get_rethrown()){exceptionCaught.push(info);info.set_rethrown(true);info.set_caught(false);uncaughtExceptionCount++}exceptionLast=ptr;throw exceptionLast};var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var __abort_js=()=>{abort("")};var __emscripten_memcpy_js=(dest,src,num)=>HEAPU8.copyWithin(dest,src,src+num);var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var _llvm_eh_typeid_for=type=>type;var wasmTableMirror=[];var wasmTable;var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead=NaN)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var wasmImports={r:___cxa_begin_catch,u:___cxa_end_catch,a:___cxa_find_matching_catch_2,b:___cxa_find_matching_catch_3,C:___cxa_rethrow,t:___cxa_throw,g:___resumeException,z:__abort_js,B:__emscripten_memcpy_js,A:_emscripten_resize_heap,n:invoke_fiii,i:invoke_ii,d:invoke_iii,f:invoke_iiii,G:invoke_iiiiffifffffffi,j:invoke_iiiii,F:invoke_iiiiifi,l:invoke_iiiiii,q:invoke_iiiiiii,H:invoke_iiiiiiifii,w:invoke_iiiiiiii,v:invoke_iiiiiiiii,I:invoke_iiiiiiiiii,E:invoke_iiiiiiiiiifi,y:invoke_jiii,p:invoke_v,m:invoke_vi,c:invoke_vii,h:invoke_viii,e:invoke_viiii,k:invoke_viiiii,o:invoke_viiiiii,D:invoke_viiiiiii,x:invoke_viiij,s:_llvm_eh_typeid_for};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["K"])();var _web_engine_create=Module["_web_engine_create"]=()=>(_web_engine_create=Module["_web_engine_create"]=wasmExports["L"])();var _web_engine_get_memory_stats=Module["_web_engine_get_memory_stats"]=a0=>(_web_engine_get_memory_stats=Module["_web_engine_get_memory_stats"]=wasmExports["M"])(a0);var _web_engine_destroy=Module["_web_engine_destroy"]=a0=>(_web_engine_destroy=Module["_web_engine_destroy"]=wasmExports["N"])(a0);var _web_engine_get_live_handles=Module["_web_engine_get_live_handles"]=()=>(_web_engine_get_live_handles=Module["_web_engine_get_live_handles"]=wasmExports["O"])();var _web_engine_get_allocated_bytes=Module["_web_engine_get_allocated_bytes"]=()=>(_web_engine_get_allocated_bytes=Module["_web_engine_get_allocated_bytes"]=wasmExports["P"])();var _web_engine_open_blend=Module["_web_engine_open_blend"]=(a0,a1,a2)=>(_web_engine_open_blend=Module["_web_engine_open_blend"]=wasmExports["Q"])(a0,a1,a2);var _web_engine_apply_command=Module["_web_engine_apply_command"]=(a0,a1,a2)=>(_web_engine_apply_command=Module["_web_engine_apply_command"]=wasmExports["S"])(a0,a1,a2);var _web_engine_undo=Module["_web_engine_undo"]=a0=>(_web_engine_undo=Module["_web_engine_undo"]=wasmExports["T"])(a0);var _web_engine_redo=Module["_web_engine_redo"]=a0=>(_web_engine_redo=Module["_web_engine_redo"]=wasmExports["U"])(a0);var _web_engine_get_scene_snapshot=Module["_web_engine_get_scene_snapshot"]=(a0,a1,a2)=>(_web_engine_get_scene_snapshot=Module["_web_engine_get_scene_snapshot"]=wasmExports["V"])(a0,a1,a2);var _web_engine_get_scene_metadata=Module["_web_engine_get_scene_metadata"]=(a0,a1,a2)=>(_web_engine_get_scene_metadata=Module["_web_engine_get_scene_metadata"]=wasmExports["W"])(a0,a1,a2);var _web_engine_get_scene_geometry=Module["_web_engine_get_scene_geometry"]=(a0,a1,a2)=>(_web_engine_get_scene_geometry=Module["_web_engine_get_scene_geometry"]=wasmExports["X"])(a0,a1,a2);var _web_engine_get_scene_delta=Module["_web_engine_get_scene_delta"]=(a0,a1,a2)=>(_web_engine_get_scene_delta=Module["_web_engine_get_scene_delta"]=wasmExports["Y"])(a0,a1,a2);var _web_engine_get_packed_asset=Module["_web_engine_get_packed_asset"]=(a0,a1,a2,a3,a4)=>(_web_engine_get_packed_asset=Module["_web_engine_get_packed_asset"]=wasmExports["Z"])(a0,a1,a2,a3,a4);var _web_engine_evaluate_depsgraph=Module["_web_engine_evaluate_depsgraph"]=(a0,a1,a2)=>(_web_engine_evaluate_depsgraph=Module["_web_engine_evaluate_depsgraph"]=wasmExports["_"])(a0,a1,a2);var _web_engine_save_blend=Module["_web_engine_save_blend"]=(a0,a1,a2)=>(_web_engine_save_blend=Module["_web_engine_save_blend"]=wasmExports["$"])(a0,a1,a2);var _malloc=Module["_malloc"]=a0=>(_malloc=Module["_malloc"]=wasmExports["aa"])(a0);var _web_engine_free_buffer=Module["_web_engine_free_buffer"]=a0=>(_web_engine_free_buffer=Module["_web_engine_free_buffer"]=wasmExports["ba"])(a0);var _free=Module["_free"]=a0=>(_free=Module["_free"]=wasmExports["ca"])(a0);var _web_engine_last_error_code=Module["_web_engine_last_error_code"]=()=>(_web_engine_last_error_code=Module["_web_engine_last_error_code"]=wasmExports["da"])();var _web_engine_last_error_message=Module["_web_engine_last_error_message"]=()=>(_web_engine_last_error_message=Module["_web_engine_last_error_message"]=wasmExports["ea"])();var _setThrew=(a0,a1)=>(_setThrew=wasmExports["fa"])(a0,a1);var __emscripten_tempret_set=a0=>(__emscripten_tempret_set=wasmExports["ga"])(a0);var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["ha"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["ia"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["ja"])();var ___cxa_increment_exception_refcount=a0=>(___cxa_increment_exception_refcount=wasmExports["ka"])(a0);var ___cxa_decrement_exception_refcount=a0=>(___cxa_decrement_exception_refcount=wasmExports["la"])(a0);var ___cxa_can_catch=(a0,a1,a2)=>(___cxa_can_catch=wasmExports["ma"])(a0,a1,a2);var ___cxa_get_exception_ptr=a0=>(___cxa_get_exception_ptr=wasmExports["na"])(a0);var dynCall_jiii=Module["dynCall_jiii"]=(a0,a1,a2,a3)=>(dynCall_jiii=Module["dynCall_jiii"]=wasmExports["oa"])(a0,a1,a2,a3);var dynCall_viiij=Module["dynCall_viiij"]=(a0,a1,a2,a3,a4,a5)=>(dynCall_viiij=Module["dynCall_viiij"]=wasmExports["pa"])(a0,a1,a2,a3,a4,a5);function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_v(index){var sp=stackSave();try{getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vi(index,a1){var sp=stackSave();try{getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiifii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiffifffffffi(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiifi(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiiifi(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_jiii(index,a1,a2,a3){var sp=stackSave();try{return dynCall_jiii(index,a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiij(index,a1,a2,a3,a4,a5){var sp=stackSave();try{dynCall_viiij(index,a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}Module["ccall"]=ccall;Module["cwrap"]=cwrap;var calledRun;var calledPrerun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}if(!calledPrerun){calledPrerun=1;preRun();if(runDependencies>0){return}}function doRun(){if(calledRun)return;calledRun=1;Module["calledRun"]=1;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise; return moduleRtn; diff --git a/web/app/public/vendor/blender/single/web_engine.wasm b/web/app/public/vendor/blender/single/web_engine.wasm index 3cf74860ef70facfc5389e3b55c914dcd393c0ab..a2b81752a32c4cbae57b81d3b8123f18cb528de3 100644 GIT binary patch literal 368764 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 &JBsnx^ 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 YR#jPgSjj$He2SoPp*_X?ii#>i@NYm)lI~ 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