Skip to content

[draft] Combined weekend playground polyfills (CI smoke)#1702

Draft
bkaradzic-microsoft wants to merge 22 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:weekend/all-combined
Draft

[draft] Combined weekend playground polyfills (CI smoke)#1702
bkaradzic-microsoft wants to merge 22 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:weekend/all-combined

Conversation

@bkaradzic-microsoft
Copy link
Copy Markdown
Contributor

@bkaradzic-microsoft bkaradzic-microsoft commented May 18, 2026

Note

This draft PR is kept open as a reference for the proven CI-green combined state. Code review and landing happen on the 7 individual split PRs below. CI verified: run 26044922430 - all 28 checks pass.

Note: the proven combined branch includes an 8th change (ES2020+ -> ES2019 syntax-repair polyfill for Chakra). That split (#1709) was closed in favour of investigating @babel/standalone properly (#1711), so it is not in the active landing plan below.

Split PRs (recommended landing order)

Tier 1 - parallel-reviewable, no source conflicts:

  1. Fix ExternalTexture_OpenGL throw-stubs to avoid MSVC C4702 under /WX #1703 - ExternalTexture C4702 build fix
  2. Document accurate root cause for post-#1695 pixel-diff fallouts #1704 - config.json reason rewrites (5 entries)
  3. Document accurate root cause for 17 subtle pixel-diff tests #1705 - config.json reason rewrites (17 entries)

Tier 2 - sequential, each touches Apps/Playground/CMakeLists.txt SCRIPTS list + Apps/Playground/Shared/AppContext.cpp LoadScript order; rebase the next branch after the previous merges:

  1. Add File/Blob/FileReader polyfill for Playground (re-enables 19 GLTF tests) #1706 - File/Blob/FileReader polyfill (largest test impact: 19 re-enables)
  2. Add fetch() polyfill over XMLHttpRequest for Playground #1707 - fetch polyfill
  3. Add DOM globals polyfill + native AbortController for Playground #1708 - DOM globals + native AbortController + Android CMake link
  4. Add cubemap auto-expand polyfill for Playground (re-enables 7 PBR tests) #1710 - Cubemap auto-expand polyfill (loaded after babylon.max.js)

Reference PNGs across all 7 PRs come from Babylon.js (never re-baked by BN). Combined diff: 0 PNGs.


Combined preview of weekend Playground polyfill + triage work. See the 7 split PRs above for individual review. This draft is kept open as a single CI smoke-test anchor; closes when the splits land.

bkaradzic-microsoft and others added 12 commits May 15, 2026 20:58
BabylonNative's native JsRuntimeHost ships a Blob C++ ObjectWrap but no
File constructor or FileReader. Several Babylon.js serializer round-trip
tests (GLTF/OBJ export then re-load) use new File([blob], 'scene.glb') and
rely on the loader's FilesInputStore path that internally invokes
FileReader.readAsArrayBuffer. Without these the tests fail with
ReferenceError: 'File' is not defined / 'FileReader' is not defined.

Adds a self-detecting JS polyfill that:
 - no-ops on runtimes that already expose File/FileReader (V8, JSC may)
 - no-ops when the native Blob polyfill is missing
 - decorates a native Blob with name/lastModified to produce a File
   (deliberately without setPrototypeOf - pivoting the napi ObjectWrap
   prototype strips its bound instance methods on Chakra)
 - implements FileReader on top of Blob.arrayBuffer() with readAsArrayBuffer,
   readAsText, readAsDataURL, readAsBinaryString and the standard event
   surface (onload/onerror/onloadend, addEventListener/removeEventListener)

Hooked into Playground via SCRIPTS in Apps/Playground/CMakeLists.txt and
loaded from AppContext.cpp before ammo.js / babylon.max.js.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Re-enables tests at indices 208-216, 219-226, 275, 276 in
Apps/Playground/Scripts/config.json (GLTF Serializer round-trip variants
and OBJ Stanford Bunny round-trip in both handednesses), which were
previously excluded with "Pixel comparison fails (more than 20% pixels
differ)" or "File API not available" reasons. The actual root cause was
the missing File/FileReader globals, now addressed in the previous commit.

All 19 tests verified passing via headless Playground runner without
--include-excluded.

Updates reasons for tests that still fail with the polyfill present:
 - idx 217 (Draco mesh compression): Draco loader uses 'utf8' encoding name
   which Babylon Native's TextDecoder polyfill rejects (only 'utf-8' OK)
 - idx 277, 278 (glTF to OBJ round trip): hangs on Win32 Chakra D3D11

Remaining still-excluded entries (218, 227-230, 420, 421) keep their
existing pixel-diff reasons; they are unrelated to the File polyfill.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nt.createEvent)


Playground tests that use modern browser APIs hit ReferenceErrors on
the Chakra-based BN runtime because TextEncoder and PointerEvent are
not part of older Chakra's built-ins. Several serializer tests also
exercise document.createEvent + element.dispatchEvent shapes that the
existing minimal document polyfill in validation_native.js did not
cover.

- Apps/Playground/Scripts/dom_polyfill.js: new self-detecting JS
  polyfills for TextEncoder (UTF-8 encode + encodeInto) and PointerEvent
  (constructor with the MouseEvent + PointerEvent surface).
- Apps/Playground/Shared/AppContext.cpp + CMakeLists.txt: wire in the
  native AbortController polyfill from JsRuntimeHost and load
  dom_polyfill.js before ammo.js.
- Apps/Playground/Scripts/validation_native.js: extend the existing
  document shim with createEvent, dispatchEvent, addEventListener and
  a generic-element fallback for createElement so click-event-based
  serializer tests can run.
- Apps/Playground/Scripts/config.json: re-enable "Serialize scene
  without materials" (idx 342), which now validates with 248 px diff.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tubs


The OpenGL backend's ExternalTexture::Impl methods (GetInfo, Set, Get)
unconditionally threw std::runtime_error{"not implemented"}, but these
methods are called unconditionally from the dispatch code in
ExternalTexture_Shared.h (included after the class definition).
MSVC's flow analysis inlined the throw and flagged the post-call code
as unreachable, producing C4702 errors under /WX on the
OpenGLWindowsDevOnly build configuration.

A previous change (BabylonJS#1695) masked the warning with a
`target_compile_options(ExternalTexture PRIVATE /wd4702)` gated on
GRAPHICS_API=OpenGL+MSVC. This silenced the diagnostic but hid any
other legitimate C4702 introduced in the same translation unit going
forward.

Attempts to fix this via `[[noreturn]]` on the stub methods made things
worse - MSVC propagated the "never returns" attribute through the
dispatch code, flagging MORE post-call statements as unreachable.

This change instead replaces the throw-stubs with inert no-op stubs
that return default-constructed values. The dispatch code in
ExternalTexture_Shared.h now sees the calls as returning normally,
which eliminates the unreachable-code paths entirely. The /wd4702
workaround in CMakeLists.txt is removed.

Functional impact: Calling an ExternalTexture API on the OpenGL backend
(which is not shipped, only used for the OpenGLWindowsDevOnly developer
build) now yields an inert/null texture rather than a thrown exception.
The OpenGL backend has never supported ExternalTexture, no tests
exercise it on OpenGL, and the Playground does not use it - so this
behavioural change has no test impact.

Verified clean build under Debug + Release + RelWithDebInfo of
ExternalTexture, full Playground build under OpenGL and D3D11
backends. Smoke test passes on D3D11 Release.
Implements a minimal `fetch(input, init)` polyfill in Apps/Playground that
wraps XMLHttpRequest. Provides a Response-like result with
`.ok`, `.status`, `.statusText`, `.url`, `.text()`, `.arrayBuffer()`,
`.json()`, `.blob()`, and a `headers` stub. Self-detecting: no-op if a
global `fetch` already exists; bails if XMLHttpRequest is unavailable.

Wired into the Playground SCRIPTS list and loaded by AppContext before
`ammo.js` / `babylon.max.js` so playground snippets that use the modern
`fetch` API can run on Babylon Native's host environments (Chakra and V8),
which do not provide `fetch` by default.

Babylon Native's `XMLHttpRequest` only dispatches events through
`addEventListener` (no `onload`/`onerror` properties) and fires
`loadend` rather than `load`; the polyfill listens on `loadend` and
inspects `status` to decide resolve/reject. When `responseType` is
`arraybuffer`, BN's `XHR.responseText` is empty, so `.text()` decodes
the array buffer directly rather than reading `responseText`.
Babylon Native's NativeEngine.createCubeTexture override only handles
.env single-file cubemaps and 6-face arrays; .dds / .ktx / .ktx2 URLs
fall through to a throw ("Cannot load cubemap because 6 files were not
defined"). The loader-aware path used by the WebGL engine (texture
loader registry lookup) is bypassed entirely.

Add a JS-side polyfill that detects .dds / .ktx / .ktx2 single-URL
cubemap loads and retries with the .env extension. Babylon's CI
generates both .dds and .env from the same source HDR and uploads
them to the same CDN path, so the swap is transparent for the
Babylon-hosted environments these tests reference. On 404 (no .env
counterpart exists) the polyfill re-invokes the original code path,
preserving the existing throw semantics.

This unblocks 7 tests that were excluded because the throw aborted
the scene before any rendering could happen:

  idx 141 NMEGLTF
  idx 172 Anisotropic
  idx 173 Clear Coat
  idx 246 PBRMetallicRoughnessMaterial
  idx 247 PBRSpecularGlossinessMaterial
  idx 248 PBR
  idx 290 Prepass SSAO + depth of field

Strip excludeFromAutomaticTesting + reason from those 7 entries in
config.json. All 7 validate sub-threshold on Win32 V8 D3D11 Release
without --include-excluded after the strip (pixel diff range
308..2638, well under the 2.5% threshold).
After the SPIRV-Cross HLSL opcode bump, 5 tests that previously crashed at
shader compile time now reach the renderer but produce pixel diffs above
the 2.5% threshold. Per-test investigation:

  idx 331 Baked Vertex Animation   76931 px (31% off) — animation evaluates
       to a different sub-frame than the WebGL reference. Output is
       deterministic across runs; the rendering itself is correct, just
       at a different point in the animation cycle. Re-bake reference.

  idx 363 Screen Space Reflections 2   36839 px (15% off) — real SSR
       regression. The wet/reflective floor surface that the reference
       shows is missing in BN's output. Document, keep excluded.

  idx 369 Sprites Pixel Perfect   18269 px (7.6% off) — real sprite
       alpha-blending regression. Transparent pixels around the sprite
       quad show as opaque black instead of the underlying scene color.
       Document, keep excluded.

  idx 395 soft-transparent-shadows   8452 px (3.4% off) — real shadow
       filter precision regression. The soft shadow blur produces a
       grainy / aliased pattern instead of the smooth filtered output
       in the reference. Document, keep excluded.

  idx 399 apply-all-post-processes   7126 px (2.9% off) — minor noise
       from chromatic aberration / film-grain post-process. Per-channel
       mean diff is under 3; only stochastic noise exceeds the per-pixel
       threshold. Output is deterministic. Re-bake reference.

Re-bake refs for 331 + 399 (PNG-optimized) and strip
excludeFromAutomaticTesting. Update the three remaining entries'
"reason" field to name the actual regression instead of the stale
"Test crashes or hangs on Babylon Native" text.
Triaged 17 tests previously documented as `Pixel comparison fails` /
`Newly added test crashes` / `Framebuffer creation fails` /
`Test fails locally on Win32 D3D11 sweep`. None of these descriptions match
current behaviour. All 17 reach the renderer and produce pixel diffs (no
crash, no FB-creation failure), but with consistent visible regressions:

- 9 tests (160, 174, 175, 196, 197, 370, 402, 566, 587 partial) show
  GUI controls rendering with red/orange backgrounds where reference
  expects green/white -- a recurring BN GUI color regression.
- 4 tests (580, 584, 587, 592) show OpenPBR analytic-lights spheres
  rendering saturated red where reference shows pink/white subsurface
  scattering or anisotropy -- spirv-cross HLSL emit gap.
- 1 test (169) shows instanced billboard foliage rendering red instead
  of green -- vertex/instance color routing.
- 1 test (179) emits extra red LineEdgesRenderer lines not in reference.
- 1 test (602) Background material blur produces red splotches.
- 1 test (182) has GUI slider handles red instead of green plus minor
  skull edge AA differences.
- 1 test (256) Instanced Bones has deterministic ~3.5% px sub-pixel
  animation/edge-AA delta with structurally-correct render.

These updated reasons help future debuggers triage rather than send them
chasing the misleading `crashes` / `framebuffer creation fails` claims.
Babylon Native's Chakra-based builds parse only up to ES2019; modern
Babylon.js playground snippets routinely use optional chaining (?.),
nullish coalescing (??), numeric separators (1_000_000), and logical
assignment (||= &&= ??=), causing 26 visual tests to fail with
SyntaxError at the eval() site in validation_native.js.

Add a lightweight regex-based syntax-repair polyfill loaded before
babylon.max.js. The polyfill exposes __bnTranspileES2019(code) as a
top-level global var. validation_native.js's new evalWithFallback
helper catches SyntaxError on the first eval and retries once with
the repaired source. Engines that already accept the source (V8,
JSC) never hit the retry path.

The polyfill is syntax-only (parse-time fix), not semantic - ?. is
rewritten to . so a null target throws TypeError at runtime instead
of short-circuiting to undefined. This is a deliberate trade-off:
parse failures are 100% blocking; runtime TypeErrors are diagnosable
and at worst surface a different bug that was previously hidden.

Strip excludeFromAutomaticTesting from idx 403
(custom-handling-of-materials-for-render-target-pass) which now
passes end-to-end. Updated reasons for the other 25 entries to
describe their post-polyfill state: 9 hit runtime errors after
parse repair, 12 hit asset/module gaps, 4 hit ES2022 syntax not
covered by the polyfill (e.g. private class fields).
Reference images must come from Babylon.js, never from Babylon Native's
own renderer (the renderer might be broken; re-baking from BN would mask
real regressions).

Restore upstream/master versions of:
- Apps/Playground/ReferenceImages/apply-all-post-processes.png
- Apps/Playground/ReferenceImages/bakedVertexAnimation.png

Revert the two re-enables (idx 'Baked Vertex Animation' and
'apply-all-post-processes') in config.json. Both stay excluded with
descriptive reason text naming the post-SPIRV-Cross-bump pixel-diff
regression and explicitly noting that BN-side re-baking is not the fix
per project policy.

The three reason rewrites for Screen Space Reflections 2 / Sprites
Pixel Perfect / soft-transparent-shadows (legitimately excluded BN
rendering regressions) are kept from the original commit.
AppContext.cpp includes <Babylon/Polyfills/AbortController.h> and calls
Babylon::Polyfills::AbortController::Initialize(env), but the Android-
specific CMakeLists at Apps/Playground/Android/BabylonNative did not
add AbortController to BabylonNativeJNI's PRIVATE link libraries. The
header is published only by the AbortController target, so Android CI
failed with 'fatal error: Babylon/Polyfills/AbortController.h file not
found'. Linux/macOS/Windows builds were fine because they use the main
Apps/Playground/CMakeLists.txt which already links AbortController.

Mirror the change there so Android picks up the include directory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Linux JSC and GCC CI runners (OpenGL backend) hit a 3.4% pixel diff
(8097/240000 px) on this test that does not appear on the D3D11 backend.
The test was re-enabled by the file-api branch because it passes on
Win32 D3D11, but the OpenGL backend has a separate rendering issue
that is out of scope for the File API polyfill work. Mark the test
excludedGraphicsApis=[OpenGL] so it still runs on D3D11/D3D12/Metal/
Vulkan and stays skipped on OpenGL.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Same pattern as KHR gpu instancing: the Camera serializer roundtrip
test diverges on the OpenGL backend (Linux JSC and GCC CI runners,
~21% pixel diff with the rendered clear color showing through where
the geometry should be) but passes on D3D11. Exclude on OpenGL while
we figure out the backend-specific issue.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bkaradzic-microsoft and others added 9 commits May 18, 2026 08:58
CI continues to surface the same OpenGL-only rendering issue on every
test using playgroundId #O0M0J9#25 (the Camera serializer roundtrip):
- left-handed (already excluded last round)
- right-handed (failed this round, ~50K px diff)
- left-handed, round trip twice
- right-handed, round trip twice

All share the same scene and codepath. Mark the remaining 3 with
excludedGraphicsApis=[OpenGL] preemptively so we stop iterating one
test at a time on this same failure mode. Tests still run on D3D11/
D3D12/Metal/Vulkan.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…l-combined

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ombined

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ined

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant