Skip to content

NativeDawn plugin - #1781

Draft
CedricGuillemet wants to merge 41 commits into
BabylonJS:masterfrom
CedricGuillemet:nativeDawn
Draft

NativeDawn plugin#1781
CedricGuillemet wants to merge 41 commits into
BabylonJS:masterfrom
CedricGuillemet:nativeDawn

Conversation

@CedricGuillemet

Copy link
Copy Markdown
Collaborator

No description provided.

@matthargett

Copy link
Copy Markdown

can we coordinate a little more on these kinds of changes? I spent months on the wgpu-native backing for this integration, and you all said you wouldn't be switching off of bgfx. I feel like I'm being as transparent and inclusive as I can, but I'm getting a bit of whiplash with this sudden change in policy (even if it favors my goals).

@CedricGuillemet

Copy link
Copy Markdown
Collaborator Author

can we coordinate a little more on these kinds of changes? I spent months on the wgpu-native backing for this integration, and you all said you wouldn't be switching off of bgfx. I feel like I'm being as transparent and inclusive as I can, but I'm getting a bit of whiplash with this sudden change in policy (even if it favors my goals).

This is only an experiment. Taking a closer look at #1605 is also on my todolist. We are also investing time on CI testing and harness. No need to worry , we won't forget you :)

@bghgary

bghgary commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

We are still in the middle of planning with some prototyping/experiments. See here for some idea of what we are up to: BabylonJS/Babylon-Lite#10.

@CedricGuillemet

CedricGuillemet commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

Babylon-Lite bundles on NativeDawn/WebGPU

Ran all 216 Babylon-Lite scene bundles through the Playground on the NativeDawn/WebGPU backend. Tested with the V8 JS engine (the WebGPU bootstrap is V8-only; ChakraCore fails).

~124 scenes render correctly — PBR/glTF, IBL/HDR, shadows (incl. CSM), skinning/morph/VAT, most NME materials, frame-graph post-processes (DoF/TAA/bloom), gizmos, geospatial camera, .babylon (Sponza), grid material, floating-origin, etc.

~92 don't work, grouped by reason:

Reason Scenes
WASM not fetchable — Havok physics 40-42,44-49,100-106,209
WASM — Recast navigation 170,172,173
WASM — manifold CSG 91
WASM — basis/KTX2, meshopt, text-shaper (silent black) 36,112,211,180,181
Web Worker (Blob URL) — Gaussian Splatting, gizmos 120-129,222
Blob.stream/ReadableStream missing 66,72,140
Canvas 2D API incomplete (sprites, DynamicTexture) 18,24,25,50-57,90,92-98,117,118,145,146,179,205,206
Multi-canvas (getContext null) 227,228
Missing local lab assets (fetch) — fixable, just copy assets 58,59,73,81,83,160,171,174,175,210,254,255,257,258,259,260,266
Silent black, not yet isolated (still black post texture-fix — unrelated cause) 12,30,63,149,153,263,264

Physics/nav/gaussian-splatting fail as expected (no web workers + feature .wasm can't be fetched). The biggest fixable buckets are the Canvas-2D API and the missing-local-assets.

How to test a lite bundle

  1. In the Babylon-Lite repo, build the lib and bundle a scene as a single self-contained IIFE classic script (the default build:bundle-scenes output is ESM+code-split, which the Playground's classic LoadScript can't load):
    pnpm build:lib
    # esbuild: bundle:true, splitting:false, format:"iife",
    #   alias babylon-lite -> packages/babylon-lite/build/lib
    # -> lab/public/bundle/sceneN.playground.js
    
  2. Copy sceneN.playground.js into the Playground Scripts/ dir.
  3. Run it with the V8 build:
    Playground.exe --headless app:///Scripts/sceneN.playground.js
    
    (drop --headless to see the window). Any root-relative asset (/foo.png) is redirected to app:///Scripts/foo.png, so place sibling assets there.

How to test the glTF texture fix

# the exact bug case (renders textured, not black):
Playground.exe --headless --test "BoomBox with Unlit" app:///Scripts/validation_native.js
# -> "No pixel difference!"  /  "GLTF BoomBox with Unlit Material validated"

# full validation suite:
Playground.exe --headless app:///Scripts/validation_native.js
# -> Run complete. ran=259 passed=259 failed=0

@laurentdiazfr

laurentdiazfr commented Jul 21, 2026

Copy link
Copy Markdown

@CedricGuillemet What are your thoughts on https://github.com/wcandillon/react-native-webgpu as a Dawn backend? Would being tied to React Native be a limitation for what you're trying to achieve? React native and Expo excosystem is huge nowadays, it will increase the adoption of babylon native.

@CedricGuillemet

Copy link
Copy Markdown
Collaborator Author

@laurentdiazfr https://github.com/wcandillon/react-native-webgpu looks great and being able to use Lite directly is awesome. BabylonNative corresponds to different needs (same for BRN). For example, usage of XR or tight integration of native libraries with Babylon. The more possibilities, the better, I think. For example, fills the gap when a user wants to integrate 3D in a native android app.

@matthargett

Copy link
Copy Markdown

FYI, expanded my wgpu branch suite based on the report above. A few fixes is that branch, but there's several more WHATWG/W3C API compliance issues that need fixing to expand it to the fuller screenshot suite on browsers.

I submitted those PRs into JsRuntimeHost, and validated them locally on macOS 26, iOS 18, and Android 12 (Pico XR). I tested across JSC and QuickJS in that matrix, but ran out of time to go further today (I spot check a lot of the screenshots etc manually so I can dig deeper into pixel differences.)

This has been a great driver to make BabylonNative competitive as a 3D-forward app runtime that stands shoulder to shoulder with other peers like deno, bun, NodeJS, and electron! Very exciting!

CedricGuillemet and others added 11 commits July 23, 2026 17:41
# Conflicts:
#	Apps/Playground/Scripts/config.json
- ci.yml: temporarily disable all non-NativeDawn jobs (kept commented for
  easy re-enable); only the 5 NativeDawn jobs run for now.
- build-win32.yml (NativeDawn path): before the visualization tests, clone
  Babylon-Lite, pnpm install + build:lib, bundle scene1 (BoomBox PBR) into a
  single IIFE via .github/scripts/bundle-lite-scene.mjs, then run it through
  the Playground on WebGPU/Dawn and pixel-compare against a committed reference.
- Add Scripts/lite_native.js harness (loads the lite bundle, waits for
  canvas.dataset.ready, captures + compares vs ReferenceImages/lite-scene1.png).
- Add ReferenceImages/lite-scene1.png (WARP-rendered reference).
- CMakeLists: copy lite_native.js to the Playground output.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1db7d22c-605b-47b4-ac32-8337efb8df48
CedricGuillemet and others added 18 commits July 28, 2026 16:25
CI's software adapter is 'Microsoft Basic Render Driver' (vendorID 0x1414,
adapterType=Unknown, NOT CPU), so the previous adapterType==CPU check never
enabled the MSAA-resolve lazy-clear workaround on CI -> 'FrameGraph image
processing' still rendered black. Detect the software adapter more broadly
(AdapterType::CPU OR Microsoft vendor id 0x1414 OR WARP/'Basic Render Driver'
device/description strings) and always log the adapter info for diagnosis. Real
GPUs (NVIDIA/AMD/Intel vendor ids, hardware adapter types) are unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1db7d22c-605b-47b4-ac32-8337efb8df48
… bug)

The MSAA-resolve lazy-clear workaround fixes this test on some WARP versions
(local repro) but not the CI runner's 'Microsoft Basic Render Driver' WARP, so
the test still renders 62% black there. Exclude it on WebGPU (renders correctly
on real GPUs) so the single-process CI run proceeds past index 460 and surfaces
any remaining failures. The broadened software-adapter workaround + adapter
logging stay in the plugin.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1db7d22c-605b-47b4-ac32-8337efb8df48
Surfaces every failing test index/title (INPROC_FAIL_IDX token) in a
single CI run so all CI-WARP-black tests can be excluded together.
Restored to fail-fast in a follow-up commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1db7d22c-605b-47b4-ac32-8337efb8df48
NativeDawn exposes every WebGPU object (buffers, textures, pipelines,
command encoders/passes, bind groups, views...) as an N-API External
wrapper whose finalizer releases the underlying wgpu handle. On the V8
engine, jsruntimehost's N-API enqueues GC finalizers into
napi_env__::pending_finalizers but -- unlike real Node's event-loop
env -- never drains that queue at runtime. Because the render loop
creates many short-lived wrappers per frame, their finalizers (and the
Dawn allocations they hold) accumulated unbounded: ~70MB/test, OOMing a
single-process validation run around test ~130 (~9GB), and degrading
rendering to black on CI WARP well before that.

Fix: when built against V8 (NAPI_JAVASCRIPT_ENGINE == V8), reach into the
internal env to force a GC (enqueuing dead wrappers' finalizers) and drain
the finalizer queue, gated by wrapper-creation count so steady-state
rendering isn't penalised, plus an unconditional drain after each
validation-harness readback. Live per-frame wrapper counts drop from
unbounded (200->900+ over 5 tests) to bounded (~60-250), and process
memory plateaus and reclaims instead of climbing to OOM. No-op on other
engines, which finalize through their own mechanisms.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1db7d22c-605b-47b4-ac32-8337efb8df48
…osis

Runs indices 479/504/533/606/670/761 each in a fresh Playground process to
determine whether the in-suite failures are cross-test state accumulation or
genuine per-test failures. Reverted after diagnosis.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1db7d22c-605b-47b4-ac32-8337efb8df48
* Downlevel the Babylon.js core bundle to ES5 in the nightly script fetch (#1789)

[Created by Copilot on behalf of @bghgary]

## Context
The
[nightly](https://dev.azure.com/babylonjs/ContinousIntegration/_build/latest?definitionId=36&branchName=master)
has failed every run since 2026-07-07. `Glow layer and LODs` never
satisfies `Scene.executeWhenReady`, burns the 600s budget and aborts the
run at test 24; a green run executes 294.

Babylon.js emits its UMD bundles at an ES2015 target since
[#18647](https://github.com/BabylonJS/Babylon.js/pull/18647) — the TC39
decorator migration needs the `accessor` keyword. Babylon Native runs
them on Chakra, which consumes ES5. Babylon.js added a downlevel step
for this in [#18630](https://github.com/BabylonJS/Babylon.js/pull/18630)
and applies it to `babylon.max.js` before launching our `Playground.exe`
/ `UnitTests.exe` in its monorepo pipeline, which is why the Babylon.js
side of this same test stays green. `getNightly` never got the
equivalent.

Nothing errors: the bundle loads, and meshes, textures, the glow render
target and all four blur post-processes all report ready.
`GlowLayer.isLayerReady()` just stays `false`. Any scene with a
`GlowLayer` is affected — the LOD in the failing test is incidental, and
it surfaces through this test alone because every other glow scene is
already `excludeFromAutomaticTesting`.

## Change
`getNightly` downlevels `node_modules/babylonjs/babylon.max.js` after
fetching. Playground and UnitTests both copy that file, so one transform
covers both. Only the core bundle needs it: Chakra parses ES2015 classes
and arrows fine, and it is the Stage 3 decorator emit that breaks it —
the `accessor` keyword appears twice in preview core and not at all in
preview addons. Babylon.js downlevels the same single file.

`getNightly` also now fetches `babylonjs-addons`, which it had been
skipping. The Playground loads addons immediately after the core bundle,
so a preview core was running against an npm-pinned addons build.

`downlevelNativeScripts.mjs` is a verbatim copy of the Babylon.js script
apart from a provenance header, so the two stay diffable.

Downloads are now awaited and status-checked — they were
fire-and-forget, so the downlevel could race a partial write.

## Verification
Win32 x64 D3D11 against the current preview: the full suite goes from
aborting at test 24 to `ran=296 passed=296 failed=0`, both with addons
pinned and with addons fetched from preview.

Swapping only `babylon.max.js` in one unchanged build:

| babylonjs | Result |
|---|---|
| 9.15.0 | passes |
| 9.16.0 – 9.18.0 | never ready |
| 9.16.0, downleveled | passes |

Unaffected in Chromium on all of those versions, so this is the script
target rather than a Babylon.js regression.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 714b4495-258e-4645-abf7-26c17bc29d5b

* Updated bgfx.cmake to latest. (#1795)

Co-authored-by: Branimir Karadzic <branimirkaradzic@gmail.com>

* ShaderCompiler: preserve original sampler names colliding with HLSL/MSL keywords (#1796)

### Problem

SPIRV-Cross's HLSL and MSL backends rename any shader resource whose
name collides with a reserved keyword of the target language, prepending
an underscore. Babylon.js generates GLSL samplers named `Texture2D` and
`Texture2DArray`, which are HLSL built-in object types, so they are
emitted into the bgfx uniform table as `_Texture2D` / `_Texture2DArray`.

Both bgfx and Babylon.js look samplers up by their **original GLSL
name**, so the renamed uniform never binds. There is no error — the
stage just silently samples nothing and renders transparent.

### Fix

`AppendSamplers` recovers the pre-transpile name from the parser's
`ParsedIR` via `get_name(id)`. The `Compiler` transpiles a *private
copy* of the IR, so the parser still holds the original names. That name
is emitted into the bgfx uniform table and the stage map.

- The texture register (`DecorationBinding`) is untouched — only the
app-facing name changes.
- Non-colliding samplers resolve to the identical string, so they are
bit-for-bit unaffected.
- The existing OpenGL stage-assignment guard is now keyed on the
recovered name so the vertex and fragment passes agree on the same
identifier.

### Validation

Built `x64 / Release / D3D11` against this branch and ran the full
Playground validation suite:

```
Run complete. ran=300 passed=300 failed=0 missingRef=0 skipped=420
```

No regressions. Because every non-colliding sampler keeps its current
name, the change is a no-op for all 300 currently-enabled tests.

### Scope

This is intentionally code-only — no `config.json` changes. The test
this unblocks (`MultiRenderTarget with different texture types`,
`#XSNYAU#22`) also needs a matching Babylon.js-side fix for the
mixed-type MRT write path, so its un-exclusion is deliberately left for
a later change once both halves are in. Verified: with only this fix
applied, that test still fails, so enabling it here would red the build.

---------

Co-authored-by: Branimir Karadzic <branimirkaradzic@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae

* Bump @babel/core from 7.29.0 to 7.29.6 in /Apps (#1793)

Bumps
[@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core)
from 7.29.0 to 7.29.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/babel/babel/releases">@​babel/core's
releases</a>.</em></p>
<blockquote>
<h2>v7.29.6 (2026-05-25)</h2>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>babel-generator</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/18014">#18014</a>
Catchup source map position in preserveFormat (<a
href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-core</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/18001">#18001</a>
[7.x packport]Improve input source map handling (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-core</code>, <code>babel-generator</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17998">#17998</a>
Preserve original identifier names from input sourcemaps (<a
href="https://github.com/babel/babel/tree/HEAD/packages/babel-core/issues/17992">#17992</a>)
(<a href="https://github.com/Andarist"><code>@​Andarist</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 3</h4>
<ul>
<li>Huáng Jùnliàng (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
<li>Mateusz Burzyński (<a
href="https://github.com/Andarist"><code>@​Andarist</code></a>)</li>
<li>Nicolò Ribaudo (<a
href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
<h2>v7.29.5 (2026-05-05)</h2>
<h4>:house:  Internal</h4>
<ul>
<li><code>babel-preset-env</code>
<ul>
<li>Update <code>@babel/*</code> dependencies</li>
</ul>
</li>
</ul>
<h2>v7.29.4 (2026-05-05)</h2>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>babel-plugin-transform-modules-systemjs</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17974">#17974</a>
[7.x backport]fix(systemjs): improve module string name support (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 1</h4>
<ul>
<li>Huáng Jùnliàng (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
<h2>v7.29.3 (2026-04-30)</h2>
<h4>:eyeglasses: Spec Compliance</h4>
<ul>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17923">#17923</a>
Support flow extends bound (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>babel-helper-create-class-features-plugin</code>,
<code>babel-plugin-proposal-decorators</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17931">#17931</a>
fix(decorators): replace super within all removed static elements (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-register</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17915">#17915</a> Fix
thread synchronization issues in <code>@babel/register</code> (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-compat-data</code>,
<code>babel-plugin-bugfix-safari-rest-destructuring-rhs-array</code>,
<code>babel-preset-env</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17788">#17788</a> Add
bugfix plugin for Safari array rest destructuring bug (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>:nail_care: Polish</h4>
<ul>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17782">#17782</a>
Improve trailing comma comment handling (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>:memo: Documentation</h4>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17847">#17847</a>
Replace npmjs.com links with npmx.dev (<a
href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/babel/babel/commit/04ea6b27fdac8f40c3481aec2080ac9678779509"><code>04ea6b2</code></a>
v7.29.6</li>
<li><a
href="https://github.com/babel/babel/commit/99f498a9b9fa0b900d603fbe8f6601bb3b9e42bb"><code>99f498a</code></a>
[7.x packport]Improve input source map handling (<a
href="https://github.com/babel/babel/tree/HEAD/packages/babel-core/issues/18001">#18001</a>)</li>
<li><a
href="https://github.com/babel/babel/commit/feba0a3654c596bd369d1ef1231f5d56666d56dc"><code>feba0a3</code></a>
Preserve original identifier names from input sourcemaps (<a
href="https://github.com/babel/babel/tree/HEAD/packages/babel-core/issues/17992">#17992</a>)
(<a
href="https://github.com/babel/babel/tree/HEAD/packages/babel-core/issues/17998">#17998</a>)</li>
<li>See full diff in <a
href="https://github.com/babel/babel/commits/v7.29.6/packages/babel-core">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@babel/core&package-manager=npm_and_yarn&previous-version=7.29.0&new-version=7.29.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/BabylonJS/BabylonNative/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump fast-uri from 3.1.0 to 3.1.4 in /Apps (#1790)

Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.0 to
3.1.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/fastify/fast-uri/releases">fast-uri's
releases</a>.</em></p>
<blockquote>
<h2>v3.1.4</h2>
<h2>⚠️ Security Release</h2>
<p>Fix for <a
href="https://github.com/fastify/fast-uri/security/advisories/GHSA-v2hh-gcrm-f6hx">https://github.com/fastify/fast-uri/security/advisories/GHSA-v2hh-gcrm-f6hx</a></p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/fastify/fast-uri/compare/v3.1.3...v3.1.4">https://github.com/fastify/fast-uri/compare/v3.1.3...v3.1.4</a></p>
<h2>v3.1.3</h2>
<h2>⚠️ Security Release</h2>
<ul>
<li>Fixes: <a
href="https://github.com/fastify/fast-uri/security/advisories/GHSA-4c8g-83qw-93j6">https://github.com/fastify/fast-uri/security/advisories/GHSA-4c8g-83qw-93j6</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/fastify/fast-uri/compare/v3.1.2...v3.1.3">https://github.com/fastify/fast-uri/compare/v3.1.2...v3.1.3</a></p>
<h2>v3.1.2</h2>
<h2>⚠️ Security Release</h2>
<ul>
<li>Fix for <a
href="https://github.com/fastify/fast-uri/security/advisories/GHSA-v39h-62p7-jpjc">https://github.com/fastify/fast-uri/security/advisories/GHSA-v39h-62p7-jpjc</a></li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>Handle malformed fragment decoding as a parse error by <a
href="https://github.com/mcollina"><code>@​mcollina</code></a> in <a
href="https://redirect.github.com/fastify/fast-uri/pull/171">fastify/fast-uri#171</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/fastify/fast-uri/compare/v3.1.1...v3.1.2">https://github.com/fastify/fast-uri/compare/v3.1.1...v3.1.2</a></p>
<h2>v3.1.1</h2>
<h2>⚠️ Security Release</h2>
<ul>
<li>Fix for <a
href="https://github.com/fastify/fast-uri/security/advisories/GHSA-q3j6-qgpj-74h6">https://github.com/fastify/fast-uri/security/advisories/GHSA-q3j6-qgpj-74h6</a></li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>build(deps-dev): bump tsd from 0.32.0 to 0.33.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fastify/fast-uri/pull/148">fastify/fast-uri#148</a></li>
<li>build(deps): bump actions/checkout from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fastify/fast-uri/pull/149">fastify/fast-uri#149</a></li>
<li>chore(.npmrc): ignore scripts by <a
href="https://github.com/Fdawgs"><code>@​Fdawgs</code></a> in <a
href="https://redirect.github.com/fastify/fast-uri/pull/150">fastify/fast-uri#150</a></li>
<li>build(deps-dev): remove <code>@​fastify/pre-commit</code> by <a
href="https://github.com/Fdawgs"><code>@​Fdawgs</code></a> in <a
href="https://redirect.github.com/fastify/fast-uri/pull/151">fastify/fast-uri#151</a></li>
<li>build(deps): bump actions/setup-node from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fastify/fast-uri/pull/152">fastify/fast-uri#152</a></li>
<li>ci(ci): add concurrency config by <a
href="https://github.com/Fdawgs"><code>@​Fdawgs</code></a> in <a
href="https://redirect.github.com/fastify/fast-uri/pull/153">fastify/fast-uri#153</a></li>
<li>build(deps): bump actions/setup-node from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fastify/fast-uri/pull/154">fastify/fast-uri#154</a></li>
<li>build(deps): bump actions/checkout from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fastify/fast-uri/pull/156">fastify/fast-uri#156</a></li>
<li>chore(license): standardise license notice by <a
href="https://github.com/Fdawgs"><code>@​Fdawgs</code></a> in <a
href="https://redirect.github.com/fastify/fast-uri/pull/159">fastify/fast-uri#159</a></li>
<li>style: remove trailing whitespace by <a
href="https://github.com/Fdawgs"><code>@​Fdawgs</code></a> in <a
href="https://redirect.github.com/fastify/fast-uri/pull/161">fastify/fast-uri#161</a></li>
<li>ci: remove unused github files by <a
href="https://github.com/Tony133"><code>@​Tony133</code></a> in <a
href="https://redirect.github.com/fastify/fast-uri/pull/162">fastify/fast-uri#162</a></li>
<li>chore: update readme by <a
href="https://github.com/Tony133"><code>@​Tony133</code></a> in <a
href="https://redirect.github.com/fastify/fast-uri/pull/164">fastify/fast-uri#164</a></li>
<li>build(deps): bump
fastify/workflows/.github/workflows/plugins-ci-package-manager.yml from
5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fastify/fast-uri/pull/165">fastify/fast-uri#165</a></li>
<li>build(deps): bump fastify/workflows/.github/workflows/plugins-ci.yml
from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fastify/fast-uri/pull/166">fastify/fast-uri#166</a></li>
<li>build(deps-dev): bump neostandard from 0.12.2 to 0.13.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/fastify/fast-uri/pull/167">fastify/fast-uri#167</a></li>
<li>ci: add lock-threads workflow by <a
href="https://github.com/Fdawgs"><code>@​Fdawgs</code></a> in <a
href="https://redirect.github.com/fastify/fast-uri/pull/169">fastify/fast-uri#169</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/Tony133"><code>@​Tony133</code></a> made
their first contribution in <a
href="https://redirect.github.com/fastify/fast-uri/pull/162">fastify/fast-uri#162</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/fastify/fast-uri/commit/6aeece669e4166b2446a89f17c07a3b15dfb7ed4"><code>6aeece6</code></a>
Bumped v3.1.4</li>
<li><a
href="https://github.com/fastify/fast-uri/commit/2d50fbabc80e4d0884fe0f6a98fe118ce6faa353"><code>2d50fba</code></a>
fix: reject literal backslash in URI authority</li>
<li><a
href="https://github.com/fastify/fast-uri/commit/0549fe35b0d482233f3be2816439f3ec803603fa"><code>0549fe3</code></a>
Bumped v3.1.3</li>
<li><a
href="https://github.com/fastify/fast-uri/commit/2a6d357a18a68e6d812824379fd3388a1ae50d05"><code>2a6d357</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/fastify/fast-uri/commit/919dd8ea7689fcc220d0d9b71307f5095e723ef9"><code>919dd8e</code></a>
Bumped v3.1.2</li>
<li><a
href="https://github.com/fastify/fast-uri/commit/c65ba573714af6b8e19e481d9444c27bc4355d07"><code>c65ba57</code></a>
fixup: linting</li>
<li><a
href="https://github.com/fastify/fast-uri/commit/6c86c17c3d76fb93aa3700ec6c0fa00faeb97293"><code>6c86c17</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/fastify/fast-uri/commit/a95158ad308df4d92bbde4eba699ce5165e9f796"><code>a95158a</code></a>
Handle malformed fragment decoding without throwing (<a
href="https://redirect.github.com/fastify/fast-uri/issues/171">#171</a>)</li>
<li><a
href="https://github.com/fastify/fast-uri/commit/cea547c91c6aae610041b17b75792ca4aa035a6d"><code>cea547c</code></a>
Bumped v3.1.1</li>
<li><a
href="https://github.com/fastify/fast-uri/commit/876ce79b662c3e5015e4e7dffe6f37752ad34f35"><code>876ce79</code></a>
Merge commit from fork</li>
<li>Additional commits viewable in <a
href="https://github.com/fastify/fast-uri/compare/v3.1.0...v3.1.4">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=fast-uri&package-manager=npm_and_yarn&previous-version=3.1.0&new-version=3.1.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/BabylonJS/BabylonNative/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump @babel/plugin-transform-modules-systemjs from 7.29.0 to 7.29.7 in /Apps/PrecompiledShaderTest/JavaScript (#1794)

Bumps
[@babel/plugin-transform-modules-systemjs](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-modules-systemjs)
from 7.29.0 to 7.29.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/babel/babel/releases">@​babel/plugin-transform-modules-systemjs's
releases</a>.</em></p>
<blockquote>
<h2>v7.29.7 (2026-05-25)</h2>
<p>Re-release all packages with npm provenance attestations</p>
<h2>v7.29.6 (2026-05-25)</h2>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>babel-generator</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/18014">#18014</a>
Catchup source map position in preserveFormat (<a
href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-core</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/18001">#18001</a>
[7.x packport]Improve input source map handling (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-core</code>, <code>babel-generator</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17998">#17998</a>
Preserve original identifier names from input sourcemaps (<a
href="https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-modules-systemjs/issues/17992">#17992</a>)
(<a href="https://github.com/Andarist"><code>@​Andarist</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 3</h4>
<ul>
<li>Huáng Jùnliàng (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
<li>Mateusz Burzyński (<a
href="https://github.com/Andarist"><code>@​Andarist</code></a>)</li>
<li>Nicolò Ribaudo (<a
href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
<h2>v7.29.5 (2026-05-05)</h2>
<h4>:house:  Internal</h4>
<ul>
<li><code>babel-preset-env</code>
<ul>
<li>Update <code>@babel/*</code> dependencies</li>
</ul>
</li>
</ul>
<h2>v7.29.4 (2026-05-05)</h2>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>babel-plugin-transform-modules-systemjs</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17974">#17974</a>
[7.x backport]fix(systemjs): improve module string name support (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 1</h4>
<ul>
<li>Huáng Jùnliàng (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
<h2>v7.29.3 (2026-04-30)</h2>
<h4>:eyeglasses: Spec Compliance</h4>
<ul>
<li><code>babel-parser</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17923">#17923</a>
Support flow extends bound (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>:bug: Bug Fix</h4>
<ul>
<li><code>babel-helper-create-class-features-plugin</code>,
<code>babel-plugin-proposal-decorators</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17931">#17931</a>
fix(decorators): replace super within all removed static elements (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-register</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17915">#17915</a> Fix
thread synchronization issues in <code>@babel/register</code> (<a
href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-compat-data</code>,
<code>babel-plugin-bugfix-safari-rest-destructuring-rhs-array</code>,
<code>babel-preset-env</code>
<ul>
<li><a
href="https://redirect.github.com/babel/babel/pull/17788">#17788</a> Add
bugfix plugin for Safari array rest destructuring bug (<a
href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
</ul>
<h4>:nail_care: Polish</h4>
<ul>
<li><code>babel-parser</code></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/babel/babel/commit/4fba7541180bf5f58256d8e358b544e3831ad090"><code>4fba754</code></a>
v7.29.7</li>
<li><a
href="https://github.com/babel/babel/commit/a458f66074b97d54773db8159af673d23b26079b"><code>a458f66</code></a>
v7.29.4</li>
<li><a
href="https://github.com/babel/babel/commit/32ebd5aaf2526ddd176fd6a3d1e3dc594abdc8d9"><code>32ebd5a</code></a>
[7.x backport]fix(systemjs): improve module string name support (<a
href="https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-modules-systemjs/issues/17974">#17974</a>)</li>
<li>See full diff in <a
href="https://github.com/babel/babel/commits/v7.29.7/packages/babel-plugin-transform-modules-systemjs">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@babel/plugin-transform-modules-systemjs&package-manager=npm_and_yarn&previous-version=7.29.0&new-version=7.29.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/BabylonJS/BabylonNative/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* up spirvcross (#1802)

* Canvas: implement getImageData and fix drawImage crash on plain ImageBitmap (#1800)

Two Canvas 2D fixes that together make the `drawImage` → `getImageData`
round trip work, plus input validation for the new entry points.

## 1. `getImageData` returned all zeros

`ImageData::GetData` was a stub:

```cpp
// return a well size array with 0
// TODO: Get datas from context/canvas
```

It allocated a buffer and `memset` it to 0, so every `getImageData()`
call read back a fully transparent image no matter what had been drawn.
`Context::GetImageData` also discarded the source x/y arguments (`//
TODO: support source x and y`).

BN's canvas is GPU-backed (nanovg), and a framebuffer readback is a poor
fit for the data-texture callers that rely on `getImageData`: the
surface is premultiplied and may be resampled, so the bytes that come
back are not the bytes that went in. Instead this keeps a **CPU-side
RGBA8 mirror** — `drawImage` copies the decoded image's exact pixels
(`Image::GetPixels` → bimg `m_data`) into `Context::m_cpuPixels`, and
`getImageData` reads that region back out. `BlitPixelsToCpu` covers all
three `drawImage` arities with nearest-neighbour sampling, which is
exact for the 1:1 draws these callers use. The mirror is reallocated and
cleared on canvas resize.

Also fixes a pre-existing `ImageData` constructor bug that read the
height from `info[1]` instead of `info[2]`, so a non-square `ImageData`
reported its width as its height.

## 2. `drawImage` access violation on a plain `ImageBitmap`

Babylon Native sets `forceBitmapOverHTMLImageElement`, which routes
`LoadImage` through `NativeEngine::CreateImageBitmap`. That returns a
plain JS object `{data, width, height, format}` — **not** a wrapped
`NativeCanvasImage`. `Context::DrawImage` called
`NativeCanvasImage::Unwrap` on whatever it was handed, so `napi_unwrap`
dereferenced a never-wrapped object and crashed with an access violation
(0xC0000005).

`DrawImage` now detects the plain `ImageBitmap` by its own `data` typed
array, converts its pixels to RGBA8 via `bimg::imageConvert` (memcpy
fast path when the source is already RGBA8), creates a transient nanovg
image, and shares the arity-3/5/9 draw plus CPU-mirror blit with the
existing path through a new `DrawImageCommon` helper. The
`NativeCanvasImage` path is behaviour-identical.

## 3. Input validation

Both new paths take sizes straight from JS and hand them to bimg and to
raw pointer arithmetic, so the third commit validates them at the
boundary:

- `drawImage` rejects an out-of-range bimg texture format, dimensions
whose pixel count would overflow the destination allocation, and a
`data` array shorter than `width*height` for its format.
**`bimg::imageConvert` is given neither buffer's real length**, so
without these it would read past the end of the source.
- `getImageData` requires its four arguments and rejects a region whose
byte size would overflow `size_t` before `ImageData` tries to allocate
it.
- `BlitPixelsToCpu` clamps its iteration range to the destination rect's
intersection with the canvas up front. The destination size is
caller-controlled and arrives unsigned, so a negative width wraps to
~4e9; the previous per-pixel bounds test still walked the whole rect and
could stall the JS thread for billions of no-op iterations.

Range checks are computed in `uint64_t` so they stay meaningful where
`size_t` is 32-bit (Android armeabi-v7a), and the new arithmetic uses
fixed-width types rather than `long`, whose width differs between MSVC
and Apple clang.

## Validation

- **"Displacement map" (idx 200)** — `Mesh.applyDisplacementMap` is
exactly this `drawImage` → `getImageData` round trip. It crashes on
master; with this change it renders at **0.155% pixel difference**
against a 2.5% limit. It is still `excludeFromAutomaticTesting` in
`config.json`, so reproduce with `--include-excluded --test-index=200`.
Un-excluding it is deliberately left to a separate test-enablement PR.
- **Full validation suite: `ran=295 passed=295 failed=0`** — no
regressions.

<details>
<summary>Why 295 and not 300</summary>

The 5 tests at indices 53–57 (`Scissor test`, `Scissor test with 0.9
hardware scaling`, `… with 1.5 hardware scaling`, `… with negative x and
y`, `… with out of bounds width and height`) are excluded from that run
because they **crash on unmodified master**, not because of anything
here:

```
--- BN: CRASH ---
Unknown signal? Exception Code 87a
  7: bgfx/src/renderer_d3d11.cpp  6173  bgfx::d3d11::RendererContextD3D11::submit
  8: bgfx/src/bgfx.cpp            2917  bgfx::Context::renderFrame
 13: Core/Graphics/Source/DeviceImpl.cpp  539  Babylon::Graphics::DeviceImpl::Frame
```

Exit code 3, Win32 x64 D3D11 Debug. Verified pre-existing by reverting
`Polyfills/Canvas` to master and rebuilding — identical crash at the
same test. (`0x87a` is the DXGI `0x887A…` device-removed family.) The
duplicate `Scissor test` at idx 311 passes. Worth noting because it
truncates any full-suite run at 52 tests; happy to file it separately.
</details>

## Not included

My working branch also un-excluded idx 106 "Gaussian Splatting Part
Test" alongside this change. That does **not** hold on stock upstream,
so it is left out: Babylon's SOG loader unpacks the `.sog` container and
materialises its WebP images through `URL.createObjectURL`, and upstream
JsRuntimeHost has no blob-URL support, so the load fails before any
canvas work happens. That un-exclusion needs a JsRuntimeHost change
first.

There are **no `config.json` changes in this PR** — it is native-only
and enables no tests.

---------

Co-authored-by: bkaradzic-microsoft <bkaradzic-microsoft@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Branimir Karadzic <branimirkaradzic@gmail.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae

* Updated bgfx.cmake. (#1803)

Co-authored-by: Branimir Karadzic <branimirkaradzic@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: af4ea82e-5fb0-4e56-b71a-f8ff3932d033

* Add native Draco and meshopt codec plugins (#1797)

Adds native C++ implementations of the Draco and meshopt mesh decoders
as two
new optional plugins, `NativeDraco` and `NativeMeshopt`.

Today Babylon Native runs these codecs through the same WASM modules the
web
uses. That pulls a WASM runtime into a native app purely to decompress
meshes,
costs a module instantiation per session, and copies buffers across the
JS/WASM
boundary. These plugins let the native host do the work directly.

### What's here

| Plugin | Exposes | Replaces |
| --- | --- | --- |
| `NativeDraco` | `_native.DracoCodec.Decode(data, attributeIds?)` |
`draco_decoder_gltf.wasm` |
| `NativeDraco` | `_native.DracoCodec.Version` | — |
| `NativeMeshopt` | `_native.MeshoptCodec.Decode(source, count, stride,
mode, filter?)` | `meshopt_decoder.wasm` |
| `NativeMeshopt` | `_native.MeshoptCodec.Version` | — |

Each codec is a single object rather than a set of free functions, so
the
JavaScript side needs one feature probe per codec instead of one per
entry
point, and so the object can publish the codec version compiled into the
binary. Version matters because meshoptimizer records its codec version
in the
first header byte and a decoder refuses streams newer than it
understands; a
bare function name gives a caller no way to find that out except by
failing.

`DracoCodec.Decode` supports both call shapes the Babylon loaders use:
the glTF
path, where the caller passes the `EXT/KHR_draco_mesh_compression` map
of
Babylon vertex-buffer kind to Draco unique id, and the standalone `.drc`
path,
where no map is supplied and the plugin probes the standard named
attributes.
Attribute data is de-interleaved and tightly packed per point, mirroring
emscripten's `GetAttributeDataArrayForAllPoints` that the WASM decoder
relies
on, so the returned buffers are drop-in equivalents.

### Dependencies

Two new `FetchContent` dependencies, both pinned to release tags:

- `google/draco` @ `1.5.7`
- `zeux/meshoptimizer` @ `v0.22`

Both are declared `EXCLUDE_FROM_ALL` and built with tests, install,
executables
and JS glue disabled. draco adds its include directories as `PRIVATE`,
so
`Dependencies/CMakeLists.txt` re-exports them as `PUBLIC` on the
combined
target; the target is named `draco` under MSVC and `draco_static`
elsewhere and
both spellings are handled.

draco is additionally built with `DRACO_GLTF_BITSTREAM=ON`, which
compiles only
mesh compression, normal encoding and the standard edgebreaker. That is
the
same subset `draco_decoder_gltf.wasm` is built with, and that module is
what
Babylon.js's default configuration loads, so this costs no capability
the
JavaScript path has. It drops point cloud compression, the predictive
edgebreaker and pre-glTF backwards compatibility; point cloud streams
return a
Draco error instead of decoding, matching Babylon.js.

### Footprint, and why both plugins default to `OFF`

Both options default to `OFF`. Nothing routes to these entry points
until the
Babylon.js side lands, so until then no consumer should be paying for
them.
Measured on Playground, Win32 x64, RelWithDebInfo, same tree, only the
two
options changed:

| configuration | `Playground.exe` | delta |
| --- | ---: | ---: |
| both `OFF` (default) | 11,100,672 B | baseline |
| `NativeMeshopt` only | 11,132,928 B | +31.5 KB |
| both `ON` | 11,734,528 B | +619 KB (+5.7%) |

meshoptimizer is nearly free; draco is +587 KB.

Draco started at +1.64 MB. Two things brought it down, both of which
track
Babylon.js more closely rather than less:

- **No encoder.** Babylon.js does not bundle one either — `DracoDecoder`
loads
  `draco_decoder_gltf.wasm` and `DracoEncoder` fetches a separate
`draco_encoder.wasm` on demand. Linking the encoder here would cost
every
binary ~1.2 MB to serve an authoring path Babylon Native does not
exercise.
- **`DRACO_GLTF_BITSTREAM`**, as described above.

Together that is a 65% reduction in draco's contribution, which is close
enough
to what a minidraco fork would buy that staying on the upstream
bitstream looks
like the better trade.

### Trust boundary

Worth stating plainly, because it cuts both ways:

- On **V8** and **JavaScriptCore**, this moves decoding of untrusted
mesh data
  out of the WASM sandbox and into the host process.
- On **QuickJS** and **Hermes** there is no WebAssembly at all, so this
is
  net-new capability rather than a relocation of existing capability.
- It removes a runtime fetch of executable code from a CDN, since no
`.wasm`
  ships in this tree.

That is the reason the input-validation and malformed/truncated-input
tests
below exist, and why the codecs validate every caller-supplied extent
rather
than trusting the calling loader.

### Scope

The change is **additive**. Nothing existing is modified except the
CMake
wiring that registers the new plugins, four lines in
`Embedding/Source/Runtime.cpp` that initialize them, the `UnitTests`
wiring for
the new coverage, and the three CI workflows that enable the plugins for
the
jobs which run `UnitTests`. No existing plugin, engine path, or test
configuration is touched, and `Apps/Playground/Scripts/config.json` is
deliberately left alone.

### On validation

These entry points are not reachable from the currently pinned
`babylonjs`
npm package (9.15.0), so the plugins are inert until the corresponding
Babylon.js side lands and the loaders start feature-detecting them. The
validation suite therefore cannot exercise this code, but
`Apps/UnitTests`
can, and now does.

Both plugins are linked and initialized in `Apps/UnitTests` behind their
options, and `tests.javaScript.all.ts` carries a describe block for each
— 16
cases covering decoding and malformed, truncated and out-of-range input
for
every entry point. The blocks report as **skipped** when the build did
not opt
in, rather than passing vacuously; CI enables both options for the
Linux, macOS
and Win32 jobs that run `UnitTests`, including the sanitizer
configurations.

Neither codec's positive test is self-referential. Since neither plugin
exposes
an encoder, both fixtures come from the reference JavaScript encoders,
which
pins these decoders to the upstream bitstreams rather than to
themselves:

- **Draco** — two fixtures from `draco3dgltf` **1.5.7**, the same
package
Babylon.js takes its decoder from. One is a two-triangle mesh; the other
is a
  63-vertex sphere carrying POSITION, NORMAL and TEX_COORD, which covers
multi-attribute decoding and a non-degenerate edgebreaker traversal —
the
  parts `DRACO_GLTF_BITSTREAM` actually restricts.
- **meshopt** — a stream from the reference meshoptimizer **0.22.0**
encoder,
with the test asserting the native decoder reproduces the original bytes
  exactly.

The sphere fixture is worth a note. It decodes to **62** vertices and 91
triangles, not the 63 and 96 that were encoded: Draco merges points
whose
attributes all match and drops the degenerate triangles at the poles.
Rather
than adjust the expected numbers to whatever this decoder produced, they
were
taken from the reference `draco3dgltf` decoder run over the same buffer,
which
reports 62 points and 91 faces. This decoder reproduces that exactly.

meshoptimizer's vertex codec is versioned in the first header byte.
Encoders
from 0.23.0 onward emit format version 1 (`0xa1`), which a v0.22 decoder
correctly rejects with `-1`, so the encoder used to produce the fixture
was
version-matched deliberately. That version is now also pinned at compile
time
with a `static_assert`, because bgfx vendors its own copy of
meshoptimizer and
the dependency guard skips our `FetchContent` when a target of that name
already exists — a mismatch there would surface as silent data
corruption
rather than a build break.

- **No regression.** Full validation suite on this branch:
`ran=295 passed=295 failed=0 missingRef=0 skipped=420`, identical to the
upstream `master` baseline. (Indices 53–57 are excluded from the run:
they
  crash in `RendererContextD3D11::submit` on pristine `master` too.)
- **Unit tests.** `JavaScript.All` — 42 passing, 0 failing.

### Note for the Babylon.js side

`dracoEncoder.ts` on the Babylon.js side feature-detects
`_native.encodeDracoMesh`.
With no native encoder that probe is simply false and it falls back
exactly as
it does today, so nothing breaks — but the paired Babylon.js change
should drop
that hunk rather than ship a probe that can never succeed. If the
capability is
wanted, the encoder can come back behind an off-by-default option.

---------

Co-authored-by: bkaradzic-microsoft <bkaradzic@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Branimir Karadzic <branimirkaradzic@gmail.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae

* NativeEngine: route instanced generic attributes landing on TexCoord3..TexCoord15 (#1801)

Fixes a silent data-loss bug in the divisor-driven instancing reroute in
`NativeEngine::Draw`.

## Problem

When Babylon.js declares a *generic* (consumer-named) vertex attribute
with `divisor == 1`, the attribute is compiled to an ordinary per-vertex
`bgfx::Attrib` slot, but bgfx can only feed per-instance data through
the `i_data` slots (the top TEXCOORD semantics). `NativeEngine`
therefore reroutes such attributes to the correct `i_data` slot via a
lazily-compiled program variant.

That reroute was guarded by `attrib < bgfx::Attrib::TexCoord3`, so any
generic instanced attribute that landed on **TexCoord3 or above was
silently skipped**. `BuildInstanceDataBuffer` still packed its data into
the instance data buffer, but the shader kept reading the attribute as a
per-vertex input — i.e. unrelated vertex bytes.

Sprite instancing assigns `cellInfo -> TexCoord3`, so instanced sprites
read garbage cell info and rendered collapsed / mis-UVed.

## Fix

Widen the guard to every real bgfx attribute (`attrib <
bgfx::Attrib::Count`).

The built-in instanced attributes (`world0-3`, `splatIndex0-3`,
`instanceColor`) are assigned *synthetic* locations at/above
`INSTANCE_DATA_FIRST_LOCATION - 4`, which is `>= bgfx::Attrib::Count`,
so they still compare false and remain correctly skipped — they already
arrive as instance data.

## Evidence

Measured on Win32 x64 D3D11 (Debug) with Babylon.js
`_features.supportSpriteInstancing` forced to `true`, so the instanced
sprite path is actually exercised. Negative control = this commit
reverted, everything else identical:

| test | without fix | with fix |
|---|---|---|
| 155 `Sprites` | 4.242% **FAIL** | **1.405% PASS** |
| 293 `Prepass SSAO + sprites` | 1.617% pass | **0.001% pass** |
| 369 `Sprites Pixel Perfect` | 25.532% FAIL | 7.612% FAIL (much closer,
still over threshold) |

Full validation suite is unchanged: `ran=295 passed=295 failed=0`
(`--test-index=0-52,58-719`; 53-57 are the pre-existing scissor crash on
master).

## Notes

- **No `config.json` changes.** Un-excluding the sprite/MSDF tests
additionally requires the Babylon.js-side `supportSpriteInstancing`
flag; this PR is the Native prerequisite for that work and is a strict
improvement on its own.
- **No shader cache version bump needed.** Instanced program variants
bypass the shader cache entirely (`ShaderProvider::Get` consults the
cache only when `instancedAttributes` is empty), and the non-instanced
compilation path is untouched.

---------

Co-authored-by: Branimir Karadzic <branimirkaradzic@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Copilot-Session: af4ea82e-5fb0-4e56-b71a-f8ff3932d033

* NativeEngine: MultiRenderTarget framebuffers + OIT alpha blend modes (#1754)

Paired engine PR: BabylonJS/Babylon.js#18568

## What

Foundational native support for `MultiRenderTarget` (MRT) and the
order-independent-transparency blend
modes, removing several "`engine._gl` is null" crash classes. It does
**not** by itself turn the
MRT/OIT/FrameGraph validation tests green — those need further work (see
Follow-ups).

## Changes

- **`Plugins/NativeEngine/Source/NativeEngine.cpp` / `.h`**
  - `CreateFrameBuffer` and `CreateMultiFrameBuffer` now share a private
`CreateFrameBufferImpl(env, colorTextures, …)` helper that builds one
bgfx framebuffer with N color
attachments (+ an optional depth/stencil attachment), so a
`MultiRenderTarget` renders to all
targets at once (bgfx writes every attachment of the bound framebuffer —
no `drawBuffers` needed).
The two N-API methods are kept for protocol compatibility and only
differ in how they parse
`info[0]` (single nullable texture vs. array); the single-RT path is
just the 0-or-1-color case.
The shared helper validates the attachment count against
`caps->limits.maxFBAttachments` and keeps
the `"Stencil without depth…"` warning and the `isTextureValid` assert.
- Add alpha blend modes `ALPHA_ONEONE_ONEONE` (11) and
`ALPHA_LAYER_ACCUMULATE` (17) used by the
    depth-peeling OIT renderer.

## Paired engine PR

Pairs with the Babylon.js change (`createMultipleRenderTarget` + MRT
helper overrides, `applyStates`
override, reverse-Z `clear`, alpha-mode mapping). That PR
feature-detects both `createMultiFrameBuffer`
and the two alpha constants, so it degrades gracefully on native
binaries that predate this PR.

## CI

The validation suite uses the published `babylonjs` npm, which doesn't
yet contain the paired JS, so
no new tests are enabled here and CI stays in the usual "pending dep
bump" state. Verified locally
against a `babylon.max.js` built from the paired branch, and the plugin
compiles clean
(`cmake --build build/win32 --target NativeEngine --config
RelWithDebInfo`).

## Follow-ups (separate work)

- OIT depth-peeling still faults inside the D3D11 driver on `submit`
(multi-output / SRV↔RTV
ping-pong); needs interactive GPU debugging (PIX / VS Graphics
Debugger).
- Native does not yet apply the blend **equation** (`MAX`) —
`setAlphaEquation` is a no-op — so OIT
  blending would still be incorrect after the crash is fixed.
- 2D-array / cube color attachments for MRT are approximated as 2D (test
"MRT with different texture
  types").

---

## Related PRs & landing order

- **Babylon.js (engine / TS):**
https://github.com/BabylonJS/Babylon.js/pull/18568
- **BabylonNative (C++ engine):**
https://github.com/BabylonJS/BabylonNative/pull/1754

These two are co-dependent and land in this order:
1. **Babylon.js #18568 first** — it only adds native-engine TS
overrides, changes no WebGL behavior,
re-enables no validation tests on its own, and feature-detects this PR's
additions, so it can merge
   independently.
2. A **`babylonjs` npm release** ships that TS change.
3. **BabylonNative #1754 last** — after bumping the bundled `babylonjs`
to that release. (This
foundational pair does not yet turn the MRT/OIT/FrameGraph tests green —
that is separate follow-up
   work.)

---------

Co-authored-by: Branimir Karadzic <branimirkaradzic@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Bump esbuild and tsx in /Apps/PrecompiledShaderTest/JavaScript (#1791)

Bumps [esbuild](https://github.com/evanw/esbuild) to 0.28.1 and updates
ancestor dependency [tsx](https://github.com/privatenumber/tsx). These
dependencies need to be updated together.

Updates `esbuild` from 0.27.3 to 0.28.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/evanw/esbuild/releases">esbuild's
releases</a>.</em></p>
<blockquote>
<h2>v0.28.1</h2>
<ul>
<li>
<p>Disallow <code>\</code> in local development server HTTP requests (<a
href="https://github.com/evanw/esbuild/security/advisories/GHSA-g7r4-m6w7-qqqr">GHSA-g7r4-m6w7-qqqr</a>)</p>
<p>This release fixes a security issue where HTTP requests to esbuild's
local development server could traverse outside of the serve directory
on Windows using a <code>\</code> backslash character. It happened due
to the use of Go's <code>path.Clean()</code> function, which only
handles Unix-style <code>/</code> characters. HTTP requests with paths
containing <code>\</code> are no longer allowed.</p>
<p>Thanks to <a
href="https://github.com/dellalibera"><code>@​dellalibera</code></a> for
reporting this issue.</p>
</li>
<li>
<p>Add integrity checks to the Deno API (<a
href="https://github.com/evanw/esbuild/security/advisories/GHSA-gv7w-rqvm-qjhr">GHSA-gv7w-rqvm-qjhr</a>)</p>
<p>The previous release of esbuild added integrity checks to esbuild's
npm install script. This release also adds integrity checks to esbuild's
Deno install script. Now esbuild's Deno API will also fail with an error
if the downloaded esbuild binary contains something other than the
expected content.</p>
<p>Note that esbuild's Deno API installs from
<code>registry.npmjs.org</code> by default, but allows the
<code>NPM_CONFIG_REGISTRY</code> environment variable to override this
with a custom package registry. This change means that the esbuild
executable served by <code>NPM_CONFIG_REGISTRY</code> must now match the
expected content.</p>
<p>Thanks to <a
href="https://github.com/sondt99"><code>@​sondt99</code></a> for
reporting this issue.</p>
</li>
<li>
<p>Avoid inlining <code>using</code> and <code>await using</code>
declarations (<a
href="https://redirect.github.com/evanw/esbuild/issues/4482">#4482</a>)</p>
<p>Previously esbuild's minifier sometimes incorrectly inlined
<code>using</code> and <code>await using</code> declarations into
subsequent uses of that declaration, which then fails to dispose of the
resource correctly. This bug happened because inlining was done for
<code>let</code> and <code>const</code> declarations by avoiding doing
it for <code>var</code> declarations, which no longer worked when more
declaration types were added. Here's an example:</p>
<pre lang="js"><code>// Original code
{
  using x = new Resource()
  x.activate()
}
<p>// Old output (with --minify)<br />
new Resource().activate();</p>
<p>// New output (with --minify)<br />
{using e=new Resource;e.activate()}<br />
</code></pre></p>
</li>
<li>
<p>Fix module evaluation when an error is thrown (<a
href="https://redirect.github.com/evanw/esbuild/issues/4461">#4461</a>,
<a
href="https://redirect.github.com/evanw/esbuild/pull/4467">#4467</a>)</p>
<p>If an error is thrown during module evaluation, esbuild previously
didn't preserve the state of the module for subsequent module
references. This was observable if <code>import()</code> or
<code>require()</code> is used to import a module multiple times. The
thrown error is supposed to be thrown by every call to
<code>import()</code> or <code>require()</code>, not just the first.
With this release, esbuild will now throw the same error every time you
call <code>import()</code> or <code>require()</code> on a module that
throws during its evaluation.</p>
</li>
<li>
<p>Fix some edge cases around the <code>new</code> operator (<a
href="https://redirect.github.com/evanw/esbuild/issues/4477">#4477</a>)</p>
<p>Previously esbuild incorrectly printed certain edge cases involving
complex expressions inside the target of a <code>new</code> expression
(specifically an optional chain and/or a tagged template literal). The
generated code for the <code>new</code> target was not correctly wrapped
with parentheses, and either contained a syntax error or had different
semantics. These edge cases have been fixed so that they now correctly
wrap the <code>new</code> target in parentheses. Here is an example of
some affected code:</p>
<pre lang="js"><code>// Original code
new (foo()`bar`)()
new (foo()?.bar)()
<p>// Old output<br />
new foo()<code>bar</code>();<br />
new (foo())?.bar();</p>
<p></code></pre></p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/evanw/esbuild/blob/main/CHANGELOG.md">esbuild's
changelog</a>.</em></p>
<blockquote>
<h2>0.28.1</h2>
<ul>
<li>
<p>Disallow <code>\</code> in local development server HTTP requests (<a
href="https://github.com/evanw/esbuild/security/advisories/GHSA-g7r4-m6w7-qqqr">GHSA-g7r4-m6w7-qqqr</a>)</p>
<p>This release fixes a security issue where HTTP requests to esbuild's
local development server could traverse outside of the serve directory
on Windows using a <code>\</code> backslash character. It happened due
to the use of Go's <code>path.Clean()</code> function, which only
handles Unix-style <code>/</code> characters. HTTP requests with paths
containing <code>\</code> are no longer allowed.</p>
<p>Thanks to <a
href="https://github.com/dellalibera"><code>@​dellalibera</code></a> for
reporting this issue.</p>
</li>
<li>
<p>Add integrity checks to the Deno API (<a
href="https://github.com/evanw/esbuild/security/advisories/GHSA-gv7w-rqvm-qjhr">GHSA-gv7w-rqvm-qjhr</a>)</p>
<p>The previous release of esbuild added integrity checks to esbuild's
npm install script. This release also adds integrity checks to esbuild's
Deno install script. Now esbuild's Deno API will also fail with an error
if the downloaded esbuild binary contains something other than the
expected content.</p>
<p>Note that esbuild's Deno API installs from
<code>registry.npmjs.org</code> by default, but allows the
<code>NPM_CONFIG_REGISTRY</code> environment variable to override this
with a custom package registry. This change means that the esbuild
executable served by <code>NPM_CONFIG_REGISTRY</code> must now match the
expected content.</p>
<p>Thanks to <a
href="https://github.com/sondt99"><code>@​sondt99</code></a> for
reporting this issue.</p>
</li>
<li>
<p>Avoid inlining <code>using</code> and <code>await using</code>
declarations (<a
href="https://redirect.github.com/evanw/esbuild/issues/4482">#4482</a>)</p>
<p>Previously esbuild's minifier sometimes incorrectly inlined
<code>using</code> and <code>await using</code> declarations into
subsequent uses of that declaration, which then fails to dispose of the
resource correctly. This bug happened because inlining was done for
<code>let</code> and <code>const</code> declarations by avoiding doing
it for <code>var</code> declarations, which no longer worked when more
declaration types were added. Here's an example:</p>
<pre lang="js"><code>// Original code
{
  using x = new Resource()
  x.activate()
}
<p>// Old output (with --minify)<br />
new Resource().activate();</p>
<p>// New output (with --minify)<br />
{using e=new Resource;e.activate()}<br />
</code></pre></p>
</li>
<li>
<p>Fix module evaluation when an error is thrown (<a
href="https://redirect.github.com/evanw/esbuild/issues/4461">#4461</a>,
<a
href="https://redirect.github.com/evanw/esbuild/pull/4467">#4467</a>)</p>
<p>If an error is thrown during module evaluation, esbuild previously
didn't preserve the state of the module for subsequent module
references. This was observable if <code>import()</code> or
<code>require()</code> is used to import a module multiple times. The
thrown error is supposed to be thrown by every call to
<code>import()</code> or <code>require()</code>, not just the first.
With this release, esbuild will now throw the same error every time you
call <code>import()</code> or <code>require()</code> on a module that
throws during its evaluation.</p>
</li>
<li>
<p>Fix some edge cases around the <code>new</code> operator (<a
href="https://redirect.github.com/evanw/esbuild/issues/4477">#4477</a>)</p>
<p>Previously esbuild incorrectly printed certain edge cases involving
complex expressions inside the target of a <code>new</code> expression
(specifically an optional chain and/or a tagged template literal). The
generated code for the <code>new</code> target was not correctly wrapped
with parentheses, and either contained a syntax error or had different
semantics. These edge cases have been fixed so that they now correctly
wrap the <code>new</code> target in parentheses. Here is an example of
some affected code:</p>
<pre lang="js"><code>// Original code
new (foo()`bar`)()
new (foo()?.bar)()
<p>// Old output<br />
new foo()<code>bar</code>();<br />
new (foo())?.bar();<br />
</code></pre></p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/evanw/esbuild/commit/bb9db84c02433fbe37b3509f53f9f3e3cc48725e"><code>bb9db84</code></a>
publish 0.28.1 to npm</li>
<li><a
href="https://github.com/evanw/esbuild/commit/9ff053e53b8eeb990f59355dbea365277ac45ee2"><code>9ff053e</code></a>
security: add integrity checks to the Deno API</li>
<li><a
href="https://github.com/evanw/esbuild/commit/0a9bf2135b67c7e28989a5ba19f0f000805a5ab5"><code>0a9bf21</code></a>
enforce non-negative size in gzip parser</li>
<li><a
href="https://github.com/evanw/esbuild/commit/e2a1a7132058ee067fe736eac15f695861b8654e"><code>e2a1a71</code></a>
security: forbid <code>\\</code> in local dev server requests</li>
<li><a
href="https://github.com/evanw/esbuild/commit/83a2cbfc35809f4fd5152da59572d7bed7739d78"><code>83a2cbf</code></a>
fix <a
href="https://redirect.github.com/evanw/esbuild/issues/4482">#4482</a>:
don't inline <code>using</code> declarations</li>
<li><a
href="https://github.com/evanw/esbuild/commit/308ad745d824c77bc607603451b257d0f2fd9a38"><code>308ad74</code></a>
fix <a
href="https://redirect.github.com/evanw/esbuild/issues/4471">#4471</a>:
renaming of nested <code>var</code> declarations</li>
<li><a
href="https://github.com/evanw/esbuild/commit/f013f5f99a015bce92ec48d49181d4ad3177b29b"><code>f013f5f</code></a>
fix some typos</li>
<li><a
href="https://github.com/evanw/esbuild/commit/aafd6e48b1088336a5f5a17e930be7e840d43d8c"><code>aafd6e4</code></a>
chore: fix some minor issues in comments (<a
href="https://redirect.github.com/evanw/esbuild/issues/4462">#4462</a>)</li>
<li><a
href="https://github.com/evanw/esbuild/commit/15300c30b5e22f7cfcbed850c246d35095658386"><code>15300c3</code></a>
follow up: cjs evaluation fixes</li>
<li><a
href="https://github.com/evanw/esbuild/commit/1bda0c31d7697c0af44b3ab39b81e599e559a395"><code>1bda0c3</code></a>
fix <a
href="https://redirect.github.com/evanw/esbuild/issues/4461">#4461</a>,
fix <a
href="https://redirect.github.com/evanw/esbuild/issues/4467">#4467</a>:
esm evaluation fixes</li>
<li>Additional commits viewable in <a
href="https://github.com/evanw/esbuild/compare/v0.27.3...v0.28.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `tsx` from 4.21.0 to 4.23.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/privatenumber/tsx/releases">tsx's
releases</a>.</em></p>
<blockquote>
<h2>v4.23.1</h2>
<h2><a
href="https://github.com/privatenumber/tsx/compare/v4.23.0...v4.23.1">4.23.1</a>
(2026-07-13)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>support tsImport after global preload (<a
href="https://github.com/privatenumber/tsx/commit/8d4ffc24f37b396ca2fe3f251aa92c4919f2c1a4">8d4ffc2</a>)</li>
<li><strong>watch:</strong> avoid clearing piped output (<a
href="https://github.com/privatenumber/tsx/commit/95d0672e0247a829ae4469daa493212967ea768e">95d0672</a>)</li>
<li><strong>watch:</strong> treat script and dependency paths literally
(<a
href="https://github.com/privatenumber/tsx/commit/79fddde523d3bb7d0af66682ce1265f95113a073">79fddde</a>)</li>
</ul>
<h3>Performance Improvements</h3>
<ul>
<li>index transform cache lazily (<a
href="https://github.com/privatenumber/tsx/commit/e818ad608159a6fb36fb8a0bd59327fec313323d">e818ad6</a>)</li>
<li>load esbuild lazily in CLI (<a
href="https://github.com/privatenumber/tsx/commit/d0679381b60a55a9b5863603a4022a81db5d13c8">d067938</a>)</li>
<li>map Node TypeScript formats directly (<a
href="https://github.com/privatenumber/tsx/commit/cdcc6232a3277fb3028b226958b66c49a6d86c17">cdcc623</a>)</li>
<li>use sync module hooks on Node v22.22.3+ (<a
href="https://github.com/privatenumber/tsx/commit/f8992f1a50213e11b7ef8ab5121c78e0d2f29384">f8992f1</a>)</li>
</ul>
<hr />
<p>This release is also available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/tsx/v/4.23.1"><code>npm
package (@​latest dist-tag)</code></a></li>
</ul>
<h2>v4.23.0</h2>
<h1><a
href="https://github.com/privatenumber/tsx/compare/v4.22.5...v4.23.0">4.23.0</a>
(2026-07-03)</h1>
<h3>Bug Fixes</h3>
<ul>
<li>avoid redundant filesystem probes during module resolution (<a
href="https://github.com/privatenumber/tsx/commit/257bbbb7eb2784cad6a3bb7a2d9c9747d28d96ec">257bbbb</a>),
closes <a
href="https://redirect.github.com/privatenumber/tsx/issues/809">privatenumber/tsx#809</a></li>
</ul>
<h3>Features</h3>
<ul>
<li>add multi-scenario startup benchmark suite (<a
href="https://github.com/privatenumber/tsx/commit/c178197b104d055fd3431f7448982f3156394d12">c178197</a>),
closes <a
href="https://redirect.github.com/privatenumber/tsx/issues/809">privatenumber/tsx#809</a>
<a
href="https://redirect.github.com/privatenumber/tsx/issues/809">#809</a>
<a href="https://github.com/hi/issues/signal">hi#signal</a> <a
href="https://redirect.github.com/privatenumber/tsx/issues/145">privatenumber/tsx#145</a>
<a
href="https://redirect.github.com/privatenumber/tsx/issues/809">#809</a></li>
</ul>
<hr />
<p>This release is also available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/tsx/v/4.23.0"><code>npm
package (@​latest dist-tag)</code></a></li>
</ul>
<h2>v4.22.5</h2>
<h2><a
href="https://github.com/privatenumber/tsx/compare/v4.22.4...v4.22.5">4.22.5</a>
(2026-07-02)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>isolate hook state per async module.register() registration (<a
href="https://github.com/privatenumber/tsx/commit/a305f365f0cbcc31a44549dcbb0e63dc2883e96d">a305f36</a>)</li>
</ul>
<hr />
<p>This release is also available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/tsx/v/4.22.5"><code>npm
package (@​latest dist-tag)</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/privatenumber/tsx/commit/79fddde523d3bb7d0af66682ce1265f95113a073"><code>79fddde</code></a>
fix(watch): treat script and dependency paths literally</li>
<li><a
href="https://github.com/privatenumber/tsx/commit/e818ad608159a6fb36fb8a0bd59327fec313323d"><code>e818ad6</code></a>
perf: index transform cache lazily</li>
<li><a
href="https://github.com/privatenumber/tsx/commit/cdcc6232a3277fb3028b226958b66c49a6d86c17"><code>cdcc623</code></a>
perf: map Node TypeScript formats directly</li>
<li><a
href="https://github.com/privatenumber/tsx/commit/d0679381b60a55a9b5863603a4022a81db5d13c8"><code>d067938</code></a>
perf: load esbuild lazily in CLI</li>
<li><a
href="https://github.com/privatenumber/tsx/commit/95d0672e0247a829ae4469daa493212967ea768e"><code>95d0672</code></a>
fix(watch): avoid clearing piped output</li>
<li><a
href="https://github.com/privatenumber/tsx/commit/6fd4607e8a99d1efe27f185f749c659138f00ece"><code>6fd4607</code></a>
docs: add per-page metadata</li>
<li><a
href="https://github.com/privatenumber/tsx/commit/f4176d8c6329a12205ed9b8c582e559cafc45018"><code>f4176d8</code></a>
docs: generate sitemap</li>
<li><a
href="https://github.com/privatenumber/tsx/commit/8d4ffc24f37b396ca2fe3f251aa92c4919f2c1a4"><code…
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Restore the JsRuntimeHost pin lost in the master merge

Merging master into nativeDawn resolved the CMakeLists.txt conflict in
favour of master's pin, reverting the branch from cb988ba (JsRuntimeHost
master) back to 9271f13 from four weeks earlier. That silently dropped
three fixes:

  BabylonJS#223 keep escaped handles alive when their escapable scope closes
  BabylonJS#225 QuickJS: report success from the napi_throw family
  BabylonJS#227 Fix V8 N-API leaking every promise ever created

Losing BabylonJS#227 reintroduced the promise leak, and the full validation sweep
went from completing to aborting partway through with peak private bytes
climbing from 5.4 GB to 12.1 GB.

  before: ran=632 passed=628, peak 5397 MB
  merged: crash at 'Sponza Clustered Lighting (node material pbr)',
          exit 3, peak 12134 MB
  after:  ran=632 passed=627, peak 5797 MB, 0 validation errors

The remaining delta is Iridescence NME, a known flake that passes in
isolation (0.250% against a 2.5% tolerance); the other four are the
pre-existing Draco WASM LinkErrors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 88569c10-a7ff-4373-9a58-afa9c68b8c09

* Playground: run five bgfx-only exclusions on WebGPU, and correct three stale reasons

Five tests were excluded on every graphics API because bgfx/D3D11 fails
them, even though their own recorded reason said Dawn passes. Dropping
WebGPU from excludedGraphicsApis keeps bgfx quarantined while restoring
the coverage on Dawn:

  SOGS with SH no texture lookup                    0.019% (allowed 5%)
  Gaussian Splatting Debug Part Test                1.530% (allowed 5%)
  Gaussian Splatting Debug Part Auto Tracking Test  2.190% (allowed 5%)
  Gaussian Splatting Part Serialize Test            0.748% (allowed 5%)
  needDepthPrePass extended material families       exact match

All five were re-measured in isolation and then in a full in-order sweep,
so none of them depends on running alone.

Three more carried the same "Dawn passes this test" claim but do not.
Re-measured and recorded what they actually do:

  Gaussian Splatting GLTF  36.513% off (allowed 2.5%)
  Sprite maps              96.170% off (allowed 2.5%)
  GUI                       4.066% off (allowed 4%)

Sweep: ran=637 passed=633 failed=4, zero validation errors. The four
failures are the pre-existing Draco WASM LinkErrors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 88569c10-a7ff-4373-9a58-afa9c68b8c09

* NativeDawn: request the adapter's standard WebGPU features

The device descriptor requested no optional features at all. Features are
opt-in exactly like limits, and requestDevice() hands the JS layer this one
pre-created device, so every optional capability the hardware had was
permanently invisible to Babylon.js.

The most visible casualty was float32-filterable. Without it r32float and
rgba32float textures are UnfilterableFloat, so pairing one with a filtering
sampler fails validation with "None of the supported sample types
(UnfilterableFloat) ... match the expected sample types (Float)", taking the
whole render pass and command buffer down with it. Across the OpenPBR
anisotropy tests that was 264 validation errors; it is now 0, and 18 of those
23 tests render correctly (several pixel-exact) where all 23 failed before.

Dawn's own extensions start at 0x00050000 and are excluded: several change
device semantics or need matching toggles.

Full sweep: ran=636 passed=632, the 4 failures being the pre-existing Draco
WASM LinkError tests. No measurable runtime cost (7m23s vs 7m48s before).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 88569c10-a7ff-4373-9a58-afa9c68b8c09

* Playground: run 83 previously-excluded tests on WebGPU

Requesting the adapter's standard features unblocked a large batch of tests
that were globally excluded. Re-probing all 180 globally-excluded tests with
features enabled found 85 now rendering correctly, against 21 on the same
probe before that change -- the new passes are almost entirely float-texture
work (IBL/CDF prefiltering, OpenPBR, voxel and screen-space shadowing, IES
profiles), several of whose exclusion reasons literally read "CDF renderer /
IBL prefiltering fails with 'FLOAT' undefined".

These stay excluded on bgfx, where they still crash, hang or mis-render, so
each entry swaps excludeFromAutomaticTesting for the non-WebGPU API list and
keeps its original reason.

Two of the 85 are deliberately left quarantined: "SOGS with SH" and "Draco
Mesh Compression (fallback)" pass in an excluded-only chunk but their own
reasons already document them aborting or drifting after earlier tests in a
full run, which the sweep confirmed.

Full in-order sweep: ran=719 passed=715, up from ran=636 passed=632. The 4
failures are the same pre-existing Draco WASM ones, unchanged by this commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 88569c10-a7ff-4373-9a58-afa9c68b8c09

---------

Co-authored-by: Branimir Karadzic <branimirkaradzic@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 88569c10-a7ff-4373-9a58-afa9c68b8c09
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: fdde4f8a-dd76-4407-bd1d-8e0b7d8b38b1
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: fdde4f8a-dd76-4407-bd1d-8e0b7d8b38b1
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: fdde4f8a-dd76-4407-bd1d-8e0b7d8b38b1
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: fdde4f8a-dd76-4407-bd1d-8e0b7d8b38b1
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: fdde4f8a-dd76-4407-bd1d-8e0b7d8b38b1
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: fdde4f8a-dd76-4407-bd1d-8e0b7d8b38b1
@CedricGuillemet

Copy link
Copy Markdown
Collaborator Author

NativeDawn follow-up: workarounds and shims to replace

This is a cleanup checklist, not a list of merge blockers. The current implementations are useful for proving and testing the backend, but these should be replaced with explicit APIs or fixes in the appropriate layer.

Runtime/bootstrap

  • Replace the BABYLON.Tools.LoadScript monkey patch with explicit native-host integration (Plugins/NativeDawn/Source/NativeDawn.cpp:4242-4265). Babylon.js chooses _LoadScriptWeb because _native is absent, then waits forever for a synthetic <script>.onload. NativeDawn currently overrides Tools.LoadScript with LoadFile + Function(...). The proper fix is for Babylon.js/runtime bootstrap to select or inject the native script loader through a supported capability/API rather than detect native execution from the presence of _native. This likely needs a Babylon.js change.

  • Replace the BABYLON.NativeEngine alias and private bootstrap globals (NativeDawn.cpp:4226-4347). NativeDawn pre-creates a WebGPUEngine, stores it in __dawnPendingEngine/__dawnEngine, and replaces BABYLON.NativeEngine with a function returning that singleton. A proper backend/bootstrap contract should expose the selected engine and its async readiness without overriding an unrelated constructor.

  • Expose a supported backend identifier instead of detecting NativeDawn through _nativeDawnClear (validation_native.js:116-130, run_snippet.js:26-30). _nativeDawnClear is an implementation/milestone hook, not a capability API. The runtime or TestUtils should expose the active graphics backend explicitly.

  • Replace magic JS globals used by the host frame/resize bridge (Embedding/Source/View.cpp:219-234,278-289; NativeDawn.cpp:4904+). frame and __dawnResize are private string-based contracts. Prefer typed NativeDawn entry points or one documented runtime bridge object.

JavaScript-host / Web platform gaps

  • Remove the synchronous WebAssembly.instantiate override (NativeDawn.cpp:4426-4443). It exists because the V8 foreground task runner is not pumped, so async compilation promises never settle. The proper fix is to integrate/pump V8 foreground tasks; globally forcing all WASM compilation onto the JS thread changes semantics and can stall it.

  • Move UTF-16 support into the shared TextDecoder polyfill (NativeDawn.cpp:4394-4423). The JS wrapper exists only because the host decoder supports UTF-8 while glslang/twgsl requests utf-16le. JsRuntimeHost/shared polyfills should implement the required encoding labels directly.

  • Consolidate the DOM/canvas shims into shared polyfills (NativeDawn.cpp:4446-4649; validation fallback at validation_native.js:772-802). NativeDawn creates or monkey-patches document, getElementById, querySelector, window, canvas aliases, and cached __dawnCanvas behavior. These should live in one tested Window/DOM/Canvas compatibility layer rather than backend-specific patches layered over another document implementation.

  • Fix URL/Blob/object-URL behavior in the shared host polyfills (NativeDawn.cpp:4675-4714,4736-4781). NativeDawn wraps Blob to stash bytes, converts object URLs to data URLs, and wraps URL to coerce URL-object arguments because the current host APIs are not browser-compatible. Those fixes belong in JsRuntimeHost/common URL, Blob, XHR and image-loading implementations.

  • Replace the global root-relative fetch rewrite with an application asset resolver (NativeDawn.cpp:4717-4733). Rewriting /asset to app:///Scripts/asset is specific to bundled Babylon-Lite lab scenes and should not alter fetch semantics for every NativeDawn application.

  • Move Image / createImageBitmap compatibility into a shared image polyfill (NativeDawn.cpp:4696-4902). Native image decoding is required functionality, but implementing the browser API surface inside the graphics backend makes it unavailable or divergent for other backends.

Dawn/platform workaround

  • Remove the WARP MSAA lazy-clear workaround after the Dawn issue is fixed (NativeDawn.cpp:359-422). The code enables nonzero_clear_resources_on_creation_for_testing and detects WARP using adapter type, Microsoft vendor ID and device-name substrings. This should be tracked upstream in Dawn; string/vendor sniffing and a testing-only toggle should not be permanent.

Test and CI accommodations

  • Restore actual NativeDawn pixel validation in CI (.github/workflows/build-win32.yml:173-184). The full suite currently runs with --generate-references because WARP output differs from hardware-generated references, so it gates load/render/crash behavior but not image regressions. Use WARP-specific references or a stable hardware runner; keep Babylon-Lite's WARP pixel test as the current narrow guard until then.

  • Pin the Babylon-Lite revision used by CI (build-win32.yml:147-160). Cloning the default branch tip makes this PR's CI depend on unrelated future Babylon-Lite changes. Pin a commit/tag and update it deliberately.

  • Fix shader-transpiler readiness upstream instead of pre-warming it (validation_native.js:907-948). The first GLSL effect has order-dependent isReady() behavior until glslang/twgsl is loaded. The proper Babylon.js fix should make readiness deterministic; then remove the harness pre-warm.

  • Replace the growing per-test global reset list with real test isolation (validation_native.js:658-727). The harness manually restores Math.random, loader observers, reverse depth, snapshot rendering and every ForceGLSL static because playgrounds leak process-global state. Prefer a fresh engine/context per test or a supported Babylon.js reset API.

  • Turn WebGPU exclusions and widened tolerances into tracked issues (Apps/Playground/Scripts/config.json). The reasons document real gaps well (unsupported LINE_LOOP/TRIANGLE_FAN conversion, decoder/runtime issues, platform raster differences), but issue links/owners are needed so exclusions do not become permanent silently.

Not included in this debt list

The WebGPU constant globals, OffscreenCanvas, Image, createImageBitmap, requestAnimationFrame, backend-specific CMake selection, and platform guards are not inherently hacks: a native WebGPU host needs those capabilities. The debt is where they are implemented today (backend-local patches/private globals), incomplete standards behavior, or temporary CI bypasses—not the existence of the APIs themselves.

@bkaradzic-microsoft

Copy link
Copy Markdown
Member

Good list. Four of these are already root-caused and fixed on my shotgun branch — the fixes are in JsRuntimeHost, which is where you're asking for them to live, so they turn into deletions here rather than rewrites.

Already fixed in JsRuntimeHost (not yet PR'd — on bkaradzic-microsoft/JsRuntimeHost@shotgun)

Remove the synchronous WebAssembly.instantiate override (NativeDawn.cpp:4426-4443) — your diagnosis is exactly right. AppRuntime_V8 creates a default v8::Platform but nothing ever called PumpMessageLoop, so V8 finished the compile on a background thread, posted a foreground task to resolve the promise, and that task was never run. (Sync new WebAssembly.Module was unaffected, which made it look like a hang rather than a failure. AppRuntime_JSI already did the equivalent via TaskRunnerAdapter; the direct-V8 path was simply missing it.) DrainMicrotasks now pumps the queue, and the platform is wrapped in a DispatchingPlatform whose foreground task runner nudges the app dispatcher so a pump also happens when the app is otherwise idle. The override can just be deleted.

Move UTF-16 support into the shared TextDecoder polyfill (NativeDawn.cpp:4394-4423) — done in Polyfills/TextDecoder: utf-16le/utf-16be including the leading-BOM strip. The JS wrapper can go.

Same commit also fixes a third one you'd have hit next: XMLHttpRequest.responseText/response truncated at the first NUL byte. ResponseString() returns a string_view and only data() was handed to Napi, so it was measured with strlen — Emscripten inlines the .wasm payload as a JS string literal, so spz.js arrived as 1955 of 805507 characters.

Together those three are what let me re-enable validation tests 100, 104, 157 and 188 (Gaussian Splatting SPZ, CSG2/manifold), which were excluded precisely because loading a script as an ES module didn't work on Native.

Already fixed on master's current JsRuntimeHost pin

The Blob stash / object-URL → data: URL conversion (NativeDawn.cpp:4675-4714) is redundant as of JsRuntimeHost #207, which is in cb988ba. The URL polyfill now registers a blob: scheme resolver with UrlLib, so fetch, XMLHttpRequest and any other UrlLib consumer resolve blob: URLs directly — including the forceBitmapOverHTMLImageElement XHR path that motivated the hack. createObjectURL/revokeObjectURL are backed by a real store, and the shim here is already guarded by if (!url.Get("createObjectURL").IsFunction()).

The URL coercion wrapper (NativeDawn.cpp:4736-4781) is still needed though — URL::URL does info[0].As<Napi::String>() with no USVString conversion, so a URL object as url or base throws. That's a genuine small JsRuntimeHost fix.

Agreed, not fixed

The DOM/canvas/Image/createImageBitmap consolidation — my Batches 77/78 add Image, PointerEvent, screen, DOM event dispatch and KeyboardEvent to validation_native.js, which is the same debt you're flagging at validation_native.js:772-802, not a resolution. Worth doing once in a shared Window/DOM/Canvas layer rather than twice badly.

Happy to split the JsRuntimeHost commit into PRs (V8 pumping / TextDecoder / XHR NUL are independent) so this PR can bump the pin and drop the three shims.

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.

5 participants