fix(editor): time out a wedged play-realm prepare instead of hanging … #596
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build | |
| # Push-gated CI: every master push must keep the architecture invariants green — | |
| # boundary guards, full-component ABI static_asserts (compiled by the web build), | |
| # the C++ doctest harnesses, and both TS suites. workflow_dispatch stays for | |
| # re-runs and branch experiments. | |
| on: | |
| push: | |
| branches: [master] | |
| workflow_dispatch: | |
| concurrency: | |
| group: build-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build-emscripten: | |
| name: Web (Emscripten) + C++ tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| cache: 'pnpm' | |
| - name: Setup Emscripten | |
| uses: mymindstorm/setup-emsdk@v14 | |
| with: | |
| version: 5.0.0 | |
| - name: Setup ccache | |
| uses: hendrikmuhs/ccache-action@v1.2.20 | |
| with: | |
| key: ${{ runner.os }}-emscripten | |
| max-size: 500M | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Build Web target | |
| run: node build-tools/cli.js build -t web --no-sync | |
| - name: Build Playable target | |
| run: node build-tools/cli.js build -t playable --no-sync | |
| - name: Build WeChat target | |
| run: node build-tools/cli.js build -t wechat --no-sync | |
| - name: Verify build outputs | |
| run: | | |
| test -f build/wasm/web/esengine.js && echo "✓ Web JS" | |
| test -f build/wasm/web/esengine.wasm && echo "✓ Web WASM" | |
| test -f build/wasm/playable/esengine.single.js && echo "✓ Playable JS" | |
| test -f build/wasm/wechat/esengine.wxgame.js && echo "✓ WeChat JS" | |
| test -f build/wasm/wechat/esengine.wxgame.wasm && echo "✓ WeChat WASM" | |
| - name: Build C++ test harnesses | |
| run: | | |
| emcmake cmake -S . -B build/wasm/web-tests -DCMAKE_BUILD_TYPE=Release -DES_BUILD_WEB=ON -DES_BUILD_TESTS=ON | |
| cmake --build build/wasm/web-tests -j "$(nproc)" --target \ | |
| test_ecs test_events test_particle test_tilemap test_sparse_set \ | |
| test_resource_pool test_shader_variant test_log_format test_registry_safety \ | |
| test_shader_device test_draw_params test_texture_fb test_buffer_device \ | |
| test_compressed_format test_batch_builder test_boundary_span test_webgpu_device \ | |
| webgpu_engine_bringup | |
| # The root package.json is ESM, so the emscripten-emitted .js must run as .cjs. | |
| - name: Run C++ test harnesses | |
| run: | | |
| for t in test_ecs test_events test_particle test_tilemap test_sparse_set \ | |
| test_resource_pool test_shader_variant test_log_format test_registry_safety \ | |
| test_shader_device test_draw_params test_texture_fb test_buffer_device \ | |
| test_compressed_format test_batch_builder test_boundary_span test_webgpu_device; do | |
| cp "build/wasm/web-tests/bin/$t.js" "build/wasm/web-tests/bin/$t.cjs" | |
| echo "=== $t" | |
| node "build/wasm/web-tests/bin/$t.cjs" | |
| done | |
| # No --continue-on-error: a side-module compile break must FAIL the gate. | |
| # It used to be swallowed here, so a physics-module error lived on master | |
| # for days and only surfaced when a release ran `build -t all` for real. | |
| - name: Build remaining targets (spine, physics) | |
| run: node build-tools/cli.js build -t all --no-sync | |
| - name: Upload web wasm artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wasm-web | |
| path: build/wasm/web/ | |
| retention-days: 3 | |
| test: | |
| name: Tests (SDK + Desktop) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| cache: 'pnpm' | |
| - name: Check GL boundary (no gl* outside GLDevice) | |
| run: node tools/check-gl-boundary.mjs | |
| - name: Check render-command boundary (DrawCommands built only by BatchBuilder) | |
| run: node tools/check-draw-command-boundary.mjs | |
| - name: Install dependencies | |
| run: pnpm install | |
| # Filtered so the SDK is checked by ITS OWN pinned TypeScript (the workspace | |
| # carries two majors — desktop is on 6.x); a bare root `pnpm exec tsc` | |
| # resolves an arbitrary compiler. Mirrors the repo's `verify` gate exactly. | |
| - name: Type-check SDK | |
| run: pnpm --filter ./sdk exec tsc --noEmit | |
| - name: Check API surface (snapshot + release-tag policy) | |
| run: node tools/api-surface.mjs --check | |
| - name: Build SDK | |
| run: pnpm --filter ./sdk build | |
| - name: Type-check Examples | |
| run: node build-tools/cli.js check-examples | |
| - name: Run SDK tests | |
| run: pnpm --filter ./sdk exec vitest run | |
| - name: Type-check Desktop | |
| run: pnpm --filter @estella/editor exec tsc --noEmit | |
| - name: Run Desktop tests | |
| run: pnpm --filter @estella/editor exec vitest run | |
| # Headless pixel verification: real scenes rendered by the editor's electron | |
| # host under xvfb (SwiftShader WebGL), asserted pixel-by-pixel. | |
| verify-render: | |
| name: Render verify (headless) | |
| runs-on: ubuntu-latest | |
| needs: build-emscripten | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| cache: 'pnpm' | |
| - name: Download web wasm artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wasm-web | |
| path: build/wasm/web/ | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Build SDK | |
| run: pnpm --filter ./sdk build | |
| - name: Stage wasm for the editor host | |
| run: | | |
| mkdir -p desktop/public/wasm | |
| cp build/wasm/web/* desktop/public/wasm/ | |
| - name: Build editor renderer bundle | |
| working-directory: desktop | |
| run: pnpm exec vite build | |
| # Twin coverage guard: every fixture .esshader must carry its WGSL twin | |
| # (hand-written, generated, or switch-skipped) — a twin-less shader is | |
| # WebGPU-dead by design and gen-shader-twins --check exits 1 on it. | |
| - name: Check shader twins | |
| run: node tools/gen-shader-twins.mjs --check $(find desktop/public/scenes -name '*.esshader') | |
| # Pipeline smoke: strip a fixture's twins into a temp copy and require the | |
| # vendored converters (glslang wasm + naga WASI wasm) to produce one — | |
| # exercises the whole GLSL→SPIR-V→WGSL chain on every CI run. | |
| - name: Shader-twin pipeline smoke | |
| run: | | |
| tmp=$(mktemp -d) | |
| sed '/#pragma vertex wgsl/,$d' desktop/public/scenes/mat-tint/tint.esshader > "$tmp/smoke.esshader" | |
| if node tools/gen-shader-twins.mjs --check "$tmp/smoke.esshader"; then | |
| echo "expected would-generate (exit 1) for a twin-less shader"; exit 1 | |
| fi | |
| - name: Run headless verify scenes | |
| working-directory: desktop | |
| env: | |
| # CI runners can't use the SUID sandbox helper (not root-owned in | |
| # node_modules); rendering verification doesn't need it. | |
| ELECTRON_DISABLE_SANDBOX: '1' | |
| run: | | |
| run_scene() { | |
| echo "=== $1" | |
| shift | |
| env "$@" xvfb-run -a pnpm exec electron scripts/headless-verify.mjs | |
| } | |
| run_scene sprite-default | |
| # Editor grid on/off pixel diff — guards the custom-draw reflected | |
| # material path (drawMeshWithMaterial + MaterialConstants UBO). | |
| run_scene editor-grid ESTELLA_VERIFY_GRID=64 ESTELLA_VERIFY_STEPS=3 | |
| run_scene mesh2d \ | |
| ESTELLA_VERIFY_SCENE=/scenes/mesh2d.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/mesh2d.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=4 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.30,"y":0.556,"rgb":[255,0,0],"tol":40},{"x":0.70,"y":0.556,"rgb":[0,255,0],"tol":40},{"x":0.30,"y":0.40,"rgb":[255,0,0],"tol":40}]' | |
| run_scene parallax-shape \ | |
| ESTELLA_VERIFY_SCENE=/scenes/parallax-shape.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/parallax-shape.textures.json \ | |
| ESTELLA_VERIFY_STEPS=4 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[0,255,0],"tol":40},{"x":0.25,"y":0.5,"rgb":[255,0,0],"tol":40}]' | |
| run_scene tilemap-flip \ | |
| ESTELLA_VERIFY_SCENE=/scenes/tilemap-flip.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/tilemap-flip.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=2 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.40,"y":0.36,"rgb":[255,0,0]},{"x":0.60,"y":0.36,"rgb":[0,255,0]},{"x":0.40,"y":0.58,"rgb":[0,0,255]},{"x":0.60,"y":0.58,"rgb":[255,255,0]}]' | |
| # Tiled tile (GID) object: an object-layer object with a gid renders as a | |
| # positioned Sprite (bottom-left anchor, tileset UV) — the green tile centered. | |
| run_scene tilemap-gidobj \ | |
| ESTELLA_VERIFY_SCENE=/scenes/tilemap-gidobj.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/tilemap-gidobj.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=10 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[0,255,0],"tol":40},{"x":0.5,"y":0.12,"rgb":[0,0,0],"tol":30}]' | |
| run_scene ui-mask \ | |
| ESTELLA_VERIFY_SCENE=/scenes/ui-list-mask.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/ui-list-mask.textures.json \ | |
| ESTELLA_VERIFY_W=800 ESTELLA_VERIFY_H=600 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[255,0,0],"tol":60},{"x":0.9,"y":0.5,"rgb":[0,0,255],"tol":60},{"x":0.5,"y":0.85,"rgb":[0,0,255],"tol":60},{"x":0.5,"y":0.15,"rgb":[0,0,255],"tol":60}]' | |
| # Y-sort: same scene twice — mask on flips the overlap to the lower | |
| # (higher-Y-key) entity; mask off preserves submission order. Both | |
| # asserted so neither path can silently regress. | |
| run_scene ysort-on \ | |
| ESTELLA_VERIFY_YSORT=1 \ | |
| ESTELLA_VERIFY_SCENE=/scenes/ysort.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/ysort.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=2 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[0,255,0],"tol":30},{"x":0.5,"y":0.25,"rgb":[255,0,0],"tol":30},{"x":0.5,"y":0.75,"rgb":[0,255,0],"tol":30}]' | |
| run_scene ysort-off \ | |
| ESTELLA_VERIFY_SCENE=/scenes/ysort.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/ysort.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=2 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[255,0,0],"tol":30}]' | |
| # Motion trail: move a TrailRenderer entity across the frame in play mode, | |
| # asserting the ribbon rasterizes as a green band (head/mid/tail) with black | |
| # above it — the CPU-ribbon-through-the-batch-face path. | |
| run_scene trail \ | |
| ESTELLA_VERIFY_PLAY=1 \ | |
| ESTELLA_VERIFY_SCENE=/scenes/trail.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/trail.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 \ | |
| 'ESTELLA_VERIFY_TRAIL={"from":[-200,0],"to":[200,0],"steps":20}' \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.80,"y":0.5,"rgb":[0,255,0],"tol":50},{"x":0.5,"y":0.5,"rgb":[0,255,0],"tol":50},{"x":0.25,"y":0.5,"rgb":[0,255,0],"tol":60},{"x":0.5,"y":0.15,"rgb":[0,0,0],"tol":30}]' | |
| # Linear-light pipeline (colorSpace: 'linear'). The lit falloff moves 52 | |
| # to 125 (perceptually-uniform light); the mid-gray texture must ROUND-TRIP | |
| # exactly (sRGB store → hw decode → blit encode = identity) — a missing | |
| # sRGB upload variant would read ~187, so tol 6 is a hard discriminator. | |
| run_scene linear-lit \ | |
| ESTELLA_VERIFY_COLORSPACE=linear \ | |
| ESTELLA_VERIFY_SCENE=/scenes/mat-lit-point.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/mat-lit-point.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=2 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[0,254,0],"tol":25},{"x":0.75,"y":0.5,"rgb":[0,125,0],"tol":25}]' | |
| run_scene linear-midtone \ | |
| ESTELLA_VERIFY_COLORSPACE=linear \ | |
| ESTELLA_VERIFY_SCENE=/scenes/srgb-midtone.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/srgb-midtone.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=2 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[128,128,128],"tol":6}]' | |
| # Post-process chain guards — bloom's first CI pixel coverage. The gamma | |
| # scene pins the LDR multi-pass chain; the linear scene is the HDR | |
| # discriminator: threshold 1.5 sits above anything an 8-bit chain can | |
| # store, so glow OUTSIDE the sprite (x=0.64) proves over-range light | |
| # survived the float intermediates — the same scene blooms NOTHING in | |
| # gamma mode. | |
| run_scene bloom-ldr \ | |
| ESTELLA_VERIFY_PLAY=1 \ | |
| ESTELLA_VERIFY_SCENE=/scenes/bloom-ldr.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/bloom-ldr.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=10 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.61,"y":0.5,"rgb":[0,130,0],"tol":35},{"x":0.70,"y":0.5,"rgb":[0,32,0],"tol":25},{"x":0.85,"y":0.5,"rgb":[0,0,0],"tol":12}]' | |
| run_scene hdr-bloom \ | |
| ESTELLA_VERIFY_PLAY=1 ESTELLA_VERIFY_COLORSPACE=linear \ | |
| ESTELLA_VERIFY_SCENE=/scenes/hdr-bloom.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/hdr-bloom.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=10 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[0,255,0],"tol":25},{"x":0.64,"y":0.5,"rgb":[0,255,0],"tol":35},{"x":0.85,"y":0.5,"rgb":[0,0,0],"tol":12}]' | |
| # The SAME pixel assertions on the WebGPU backend — Dawn renders through the | |
| # bundled SwiftShader Vulkan (--use-webgpu-adapter=swiftshader), so a GPU-less | |
| # runner verifies the second backend for real: WGSL twins, the reflected | |
| # material path, the editor grid, and the async readback (material preview). | |
| - name: Run headless verify scenes (WebGPU / SwiftShader) | |
| working-directory: desktop | |
| env: | |
| ELECTRON_DISABLE_SANDBOX: '1' | |
| ESTELLA_VERIFY_BACKEND: webgpu | |
| ESTELLA_VERIFY_WEBGPU_ADAPTER: swiftshader | |
| run: | | |
| run_scene() { | |
| echo "=== $1" | |
| shift | |
| env "$@" xvfb-run -a pnpm exec electron scripts/headless-verify.mjs | |
| } | |
| run_scene webgpu-sprite-grid ESTELLA_VERIFY_GRID=64 ESTELLA_VERIFY_STEPS=3 | |
| run_scene webgpu-parallax-shape \ | |
| ESTELLA_VERIFY_SCENE=/scenes/parallax-shape.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/parallax-shape.textures.json \ | |
| ESTELLA_VERIFY_STEPS=4 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[0,255,0],"tol":40},{"x":0.25,"y":0.5,"rgb":[255,0,0],"tol":40}]' | |
| # Tolerances tightened after the readback fix (force-color-profile=srgb): | |
| # capturePage was display-referred, so wide-gamut machines read a fake | |
| # "R/B bleed" here. Deterministic now — GL and WebGPU read identical. | |
| run_scene webgpu-mat-lit-point \ | |
| ESTELLA_VERIFY_SCENE=/scenes/mat-lit-point.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/mat-lit-point.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=2 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[0,253,0],"tol":25},{"x":0.75,"y":0.5,"rgb":[0,52,0],"tol":25}]' | |
| run_scene webgpu-mat-tint-preview \ | |
| ESTELLA_VERIFY_SCENE=/scenes/mat-tint.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/mat-tint.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=2 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[255,0,0],"tol":40}]' \ | |
| 'ESTELLA_VERIFY_PREVIEW={"w":64,"h":64,"rgb":[255,0,0],"tol":45}' | |
| run_scene webgpu-ysort \ | |
| ESTELLA_VERIFY_YSORT=1 \ | |
| ESTELLA_VERIFY_SCENE=/scenes/ysort.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/ysort.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=2 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[0,255,0],"tol":30},{"x":0.5,"y":0.25,"rgb":[255,0,0],"tol":30},{"x":0.5,"y":0.75,"rgb":[0,255,0],"tol":30}]' | |
| run_scene webgpu-trail \ | |
| ESTELLA_VERIFY_PLAY=1 \ | |
| ESTELLA_VERIFY_SCENE=/scenes/trail.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/trail.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 \ | |
| 'ESTELLA_VERIFY_TRAIL={"from":[-200,0],"to":[200,0],"steps":20}' \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.80,"y":0.5,"rgb":[0,255,0],"tol":50},{"x":0.5,"y":0.5,"rgb":[0,255,0],"tol":50},{"x":0.25,"y":0.5,"rgb":[0,255,0],"tol":60},{"x":0.5,"y":0.15,"rgb":[0,0,0],"tol":30}]' | |
| # Linear pipeline on the second backend — same expectations as the GL | |
| # linear scenes (backends are each other's gold). The mid-gray round-trip | |
| # additionally proves the ENGINE-SIDE sRGB upload promotion: on WebGPU | |
| # every texture uploads through rm_createTexture, so a missing SRGB8A8 | |
| # promotion would read ~187 here while GL (raw-GL upload path) stayed 128. | |
| run_scene webgpu-linear-lit \ | |
| ESTELLA_VERIFY_COLORSPACE=linear \ | |
| ESTELLA_VERIFY_SCENE=/scenes/mat-lit-point.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/mat-lit-point.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=2 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[0,254,0],"tol":25},{"x":0.75,"y":0.5,"rgb":[0,125,0],"tol":25}]' | |
| run_scene webgpu-linear-midtone \ | |
| ESTELLA_VERIFY_COLORSPACE=linear \ | |
| ESTELLA_VERIFY_SCENE=/scenes/srgb-midtone.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/srgb-midtone.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=2 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[128,128,128],"tol":6}]' | |
| # Bloom on the second backend — measured byte-identical to GL after the | |
| # bind-group eviction fix (stale-id aliasing silently zeroed the WGSL | |
| # chain's params); these scenes keep that regression impossible to miss. | |
| run_scene webgpu-bloom-ldr \ | |
| ESTELLA_VERIFY_PLAY=1 \ | |
| ESTELLA_VERIFY_SCENE=/scenes/bloom-ldr.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/bloom-ldr.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=10 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.61,"y":0.5,"rgb":[0,130,0],"tol":35},{"x":0.70,"y":0.5,"rgb":[0,32,0],"tol":25},{"x":0.85,"y":0.5,"rgb":[0,0,0],"tol":12}]' | |
| run_scene webgpu-hdr-bloom \ | |
| ESTELLA_VERIFY_PLAY=1 ESTELLA_VERIFY_COLORSPACE=linear \ | |
| ESTELLA_VERIFY_SCENE=/scenes/hdr-bloom.esscene \ | |
| ESTELLA_VERIFY_MANIFEST=/scenes/hdr-bloom.textures.json \ | |
| ESTELLA_VERIFY_W=256 ESTELLA_VERIFY_H=256 ESTELLA_VERIFY_STEPS=10 \ | |
| 'ESTELLA_VERIFY_EXPECT=[{"x":0.5,"y":0.5,"rgb":[0,255,0],"tol":25},{"x":0.64,"y":0.5,"rgb":[0,255,0],"tol":35},{"x":0.85,"y":0.5,"rgb":[0,0,0],"tol":12}]' |