Skip to content

Integrate native canvas texture updates and font metrics - #19041

Merged
bkaradzic-microsoft merged 3 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:shotgun-landing
Oct 26, 2026
Merged

bkaradzic-microsoft merged 3 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:shotgun-landing

Conversation

@bkaradzic-microsoft

Copy link
Copy Markdown
Member

Babylon Native GUI text renders differently from the browser in two ways. This PR fixes both in ThinNativeEngine.

Font metrics

getFontOffset returned all zeros (it was a TODO). As a result, GUI controls computed zero line heights, and text was mis-positioned or clipped.

It now measures the font with the native Canvas, using font bounding-box metrics for line layout as browsers do. On older runtimes it falls back to glyph bounds, and then to an estimate from the CSS px size. The measuring canvas is created once, reused, and disposed with the engine.

Dynamic texture flags

createDynamicTexture ignored generateMipMaps, and updateDynamicTexture ignored premulAlpha. Minified GUI and text textures therefore aliased, and alpha blending differed from the browser.

Both flags are now passed to Canvas.getCanvasTexture. This is gated on the runtime advertising supportsDynamicTextureMipMaps, so older Babylon Native runtimes keep the previous behavior.

Tests

  • Added nativeEngine.fontMetrics.test.ts.
  • packages/dev/core/test/unit/Engines/: 30 files, 261 passed, 1 expected fail.
  • tsc and eslint are clean.

Babylon Native GUI text rendered differently from the browser in two
ways:

- ThinNativeEngine.getFontOffset returned all zeros (a TODO), so GUI
  controls computed zero line heights and mis-positioned or clipped
  text. It now measures the font with the native Canvas, using font
  bounding-box metrics as browsers do for line layout, and falls back
  to glyph bounds on older runtimes, then to an estimate from the CSS
  px size. The measuring canvas is created once, reused, and disposed
  with the engine.
- createDynamicTexture ignored generateMipMaps, and updateDynamicTexture
  ignored premulAlpha. Minified GUI and text textures therefore
  aliased, and alpha blending differed from the browser. The flags are
  now passed to Canvas.getCanvasTexture, gated on the runtime
  advertising supportsDynamicTextureMipMaps, so older runtimes keep the
  previous behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Copilot AI balanced review requested due to automatic review settings October 22, 2026 23:47
@bjsplat

bjsplat commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Texture updates bypass the promised compatibility gate, and failed context initialization can replace canvases without disposing them.

2 open issues
What changed in this PR

This PR improves Babylon Native GUI text layout and dynamic texture rendering through ThinNativeEngine.

Changes:

  • Adds native font measurement with fallback estimates and a reusable measuring canvas.
  • Forwards dynamic texture alpha and mipmap options, with capability-gated mipmap creation.
  • Adds unit tests for font bounds, canvas reuse, and texture options.
File Description
packages/​dev/​core/​test/​unit/​Engines/​nativeEngine.fontMetrics.test.ts Tests font metrics and dynamic texture behavior.
packages/​dev/​core/​src/​Engines/​thinNativeEngine.pure.ts Implements font measurement, canvas cleanup, and texture option handling.
packages/​dev/​core/​src/​Engines/​Native/​nativeInterfaces.ts Declares the optional native runtime capability.

Review effort: Balanced

Comment thread packages/dev/core/src/Engines/thinNativeEngine.pure.ts Outdated
Comment thread packages/dev/core/src/Engines/thinNativeEngine.pure.ts Outdated
Address review feedback:

- updateDynamicTexture now passes the premultiplied-alpha and mip
  options to Canvas.getCanvasTexture, and records _premulAlpha, only
  when the runtime advertises supportsDynamicTextureMipMaps. Older
  runtimes keep the argument-less legacy call.
- getFontOffset reuses the existing measuring canvas when its 2D
  context was unavailable, so a retry never orphans a canvas that engine
  disposal would not release.

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

bjsplat commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

@bghgary bghgary left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Reviewed by Copilot on behalf of @bghgary]

LGTM

Comment thread packages/dev/core/src/Engines/thinNativeEngine.pure.ts Outdated
@bjsplat

bjsplat commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Snapshot stored with reference name:
refs/pull/19041/merge

Test environment:
https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/19041/merge/index.html

To test a playground add it to the URL, for example:

https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/19041/merge/index.html#WGZLGJ#4600

Links to test your changes to core in the published versions of the Babylon tools (does not contain changes you made to the tools themselves):

https://playground.babylonjs.com/?snapshot=refs/pull/19041/merge
https://sandbox.babylonjs.com/?snapshot=refs/pull/19041/merge
https://gui.babylonjs.com/?snapshot=refs/pull/19041/merge
https://nme.babylonjs.com/?snapshot=refs/pull/19041/merge

To test the snapshot in the playground with a playground ID add it after the snapshot query string:

https://playground.babylonjs.com/?snapshot=refs/pull/19041/merge#BCU1XR#0

If you made changes to the sandbox or playground in this PR, additional comments will be generated soon containing links to the dev versions of those tools.

Add INativeCanvas (ICanvas plus a required dispose()) for native-only
disposal, and use it for INativeCanvasConstructor, createCanvas and
_fontMeasureCanvas. This removes the cast and the optional call.

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

bjsplat commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

@bkaradzic-microsoft
bkaradzic-microsoft enabled auto-merge (squash) October 23, 2026 00:39

@Popov72 Popov72 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve. The complete current-head review found no actionable issues in native font measurement, canvas lifetime, or capability-gated dynamic texture updates.

@bjsplat

bjsplat commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

🟢 Memory Leak Test Results

4 passed, 0 leaked out of 4 scenarios

🟢 All memory leak tests passed — no leaks detected.

Passed Scenarios (4)
Scenario Package
Core Playground #2FDQT5#1508 @babylonjs/core
Core Playground #T90MQ4#14 @babylonjs/core
Core Playground #8EDB5N#2 @babylonjs/core
Core Playground #LL5BIQ#636 @babylonjs/core

@bjsplat

bjsplat commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

⚡ Performance Test Results

🟢 All performance tests passed — no regressions detected.

@bjsplat

bjsplat commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

@bjsplat

bjsplat commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

@bjsplat

bjsplat commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

@bkaradzic-microsoft

Copy link
Copy Markdown
Member Author

CI note: the only failure in Native tests (experimental) is GUI Gradient Linear with transparency (641/642 passed; it fails all 3 retries with a 60% diff). The KHR_interactivity failure is a browser crash or timeout on Overview.glb and isn't related to this PR.

The cause is a cross-repo pairing. Chrome interpolates canvas gradient stops in straight RGBA, and the reference expects (191, 8, 11) at (50,50). BN master interpolates premultiplied, then hands out the raw premultiplied texture, so legacy getCanvasTexture() applies alpha twice and matches Chrome by coincidence. With this PR, getCanvasTexture(premulAlpha=false) correctly unpremultiplies, exposing the premultiplied interpolation (224).

BN \ Babylon.js master this PR
master 191 ✅ 224 ❌
BabylonJS/BabylonNative#1889 (straight-RGBA interpolation) 165 ❌ 191 ✅

I reproduced this locally with this PR's CDN snapshot in a BN Playground. With BabylonJS/BabylonNative#1889 it shows no pixel difference.

Plan: BabylonJS/BabylonNative#1889 now temporarily excludes this test, so it can land first. Once the BN nightly includes it, re-running this PR's Native tests should pass. The test is re-enabled in BN after the Babylon.js npm update.

bghgary pushed a commit to BabylonJS/BabylonNative that referenced this pull request Oct 26, 2026
## Problem

The BabylonNative Nightly CI pipeline hasn't published a build since
#1889 landed:

| Run | BN commit | Nightly job | Packaged VisualizationTests |
|---|---|---|---|
| 61289 (last success) | bac047b | 59 min (just under the limit) | 34
min |
| 61325 (manual) | c181462 | cancelled at 60 min, during UnitTests | 39
min |
| 61329 (scheduled) | c181462 | cancelled at 60 min, during Upload
Playground | 38 min |

The `Nightly` job has no `timeoutInMinutes`, so Azure applies its
60-minute default. As more validation tests are enabled, the packaged
visualization run has grown, and the job now overruns before publishing
`BabylonNativeNightlyPlayground`.

Babylon.js CI only uses fully succeeded nightly runs, so it is still
testing against bac047b. That is why BabylonJS/Babylon.js#19041 keeps
failing on "GUI Gradient Linear with transparency", which #1889
addresses. Each failure re-runs the whole native suite 3 times, which
then hits B.js's own 90-minute limit.

## Fix

Set `timeoutInMinutes: 180` on the `Nightly` job.

Run 61329 also had an unrelated agent-lost failure on Android macOS
JavaScriptCore during the ccache restore.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
@deltakosh deltakosh self-assigned this Oct 26, 2026

@deltakosh deltakosh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @bkaradzic-microsoft,

Approved from my side; merging still depends on the required CI gate passing.

Thanks!

@bkaradzic-microsoft
bkaradzic-microsoft merged commit 87e6355 into BabylonJS:master Oct 26, 2026
24 of 25 checks passed
bkaradzic-microsoft added a commit to BabylonJS/BabylonNative that referenced this pull request Oct 27, 2026
Bumps all `babylonjs*` packages from 9.31.3 to 9.32.1. 9.32.1 includes
BabylonJS/Babylon.js#19041 and #19043.

It also enables 14 tests that now pass:
- Gaussian Splatting modify
- Gaussian Splatting Bake Transforms
- GUI Transform StackPanel
- GLTF Box with bad Draco normalized flag
- GLTF Buggy with Meshopt Compression
- Order independent transparency
- GUI Force Resize Width
- GUI Gradient Linear with transparency
- Vertex buffers with non aligned strides
- Font size inheritance
- skybox-with-boombox
- OpenPBR Thin Walled Subsurface Transmission Blending - IBL
- FrameGraph nrge clustered lights
- Clustered lights - debug markers

**Depends on #1975 (Draco full bitstream).** Without it, the GLTF Draco
tests (202–206) fail with Babylon.js 9.32.1.

Verification: I ran Win32 D3D11 V8 locally with this change plus #1975,
#1976 and #1977, as a single process like CI. Result: `ran=689
passed=689 failed=0`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants