Repository navigation
Add optional NativeDawn plugin for WebGPU on Windows - #1781
CedricGuillemet wants to merge 56 commits into
Conversation
|
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 :) |
|
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. |
Babylon-Lite bundles on NativeDawn/WebGPURan 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, ~92 don't work, grouped by reason:
Physics/nav/gaussian-splatting fail as expected (no web workers + feature How to test a lite bundle
How to test the glTF texture fix |
|
@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. |
|
@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. |
|
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! |
NativeDawn follow-up: workarounds and shims to replaceThis 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. Updated 2026-10-05 against cbb2f4ee. Checked items are complete; partially addressed items remain unchecked. Runtime/bootstrap
JavaScript-host / Web platform gaps
Dawn/platform workaround
Test and CI accommodations
Validation statusLocal Windows validation passed: standalone runtime 49 checks, Playground host 18 checks, Babylon-Lite 80/240000 differing pixels (0.033%), focused Babylon.js WebGPU 13/13, broader indices 0-175 161/161 with 15 existing catalog skips, and shared native/JavaScript coverage 6 native tests / 128 JS checks. The full-suite GUI Slate stall remains and reproduces with the original PR bindings; selected-scope success is not a full-suite pass. The rebase preserved the exact tested file tree. CI for the rebased head is still pending, not yet confirmed green. Not included in this debt listThe WebGPU constant globals, |
|
Good list. Four of these are already root-caused and fixed on my Already fixed in JsRuntimeHost (not yet PR'd — on
|
Integrate PR BabylonJS#1781 with current master, remove superseded runtime shims, and use typed Dawn frame/resize APIs. Move Babylon engine bootstrap into Playground and cover standalone Lite, runtime, and host behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve upstream per-test validation workaround metadata and scene cleanup while retaining Dawn bootstrap and canvas behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Resolve CI and catalog conflicts while preserving separate Dawn paths and including Dawn jobs in the aggregate CI gate. Record complete Windows Lite and WebGPU results without hiding existing dependency, packaging or capability failures. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Integrate Babylon.js 9.31.3, validation updates and CI caching while retaining the optional Dawn build routes. Resolve Canvas gradients using the upstream implementation and preserve WebGPU-specific catalog restrictions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep upstream straight RGBA interpolation while restoring nearest-byte packing for both linear and radial ramps. Update the merged exact-byte unit assertions to retain the existing Chrome-backed Dawn quantization contract. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep runtime regressions available locally, expose the configured JavaScript engine through Dawn TestUtils, and record complete pinned-consumer Windows validation results. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…9075) > Supersedes #18562, which GitHub cannot reopen because its head branch moved after it was closed. Same change, brought up to date with current master; the measurements below are new. ## Summary This PR adds an internal render-command batching layer for WebGPU draw submission, bringing the WebGPU engine into congruence with a pattern Babylon.js core already ships: the bgfx-based NativeEngine batches its engine commands through a `CommandBufferEncoder` (`thinNativeEngine.pure.ts`) and flushes them across the JS/native bridge in bulk, rather than paying one Node-API crossing per call. The WebGPU engine has no equivalent layer, so on hosts that run it over a JS/native boundary every `setPipeline`, `setBindGroup`, `setVertexBuffer`, `setIndexBuffer` and `draw*` crosses that boundary individually. That per-draw bridge traffic is the CPU cost profiled in [this forum post](https://forum.babylonjs.com/t/solving-cpu-bottleneck-in-nativexr-hill-valley-ar-portal-scene/63558) (iPhone XS / iPhone 12, Hill Valley AR portal scene). This is also the architecture browsers use one layer down for the identical boundary problem: Chromium's Dawn wire protocol serializes WebGPU commands into batched buffers across the renderer-to-GPU-process boundary, and Unreal's `FRHICommandList` records on the render thread and translates on the RHI thread. The boundary here is N-API or JSI rather than IPC or a thread hop; the remedy is the same. **Browsers are unaffected, and this is now measured rather than asserted** (see *Browser cost* below): the no-provider path costs about 5 ns per draw in isolation, and a 3,000-draw-per-frame scene in Chrome 152 shows no measurable frame-time difference between this branch and master. **Scope note:** this is transport-level, order-preserving submission batching, not draw merging. It does not reorder, sort or combine draws, so it composes with instancing rather than competing with it, and the stream format reserves multi-draw fields for future multi-draw-indirect lowering. ## Why the engine is the right layer Every native WebGPU host for JavaScript binds the render-pass encoder one call at a time, because none of them owns the draw loop: - BabylonNative's Dawn backend ([BabylonJS/BabylonNative#1781](BabylonJS/BabylonNative#1781)) binds each `GPURenderPassEncoder` method as its own N-API callback; `setBindGroup` reads its dynamic offsets into a fresh `std::vector` on every call ([NativeDawn.cpp#L2295-L2340](https://github.com/BabylonJS/BabylonNative/blob/da15f7ae1a6f5c59a4d6f1fd322145a3d308f70f/Plugins/NativeDawn/Source/NativeDawn.cpp#L2295-L2340)). - react-native-webgpu binds the same methods as per-call JSI host functions ([GPURenderPassEncoder.h#L103-L112](https://github.com/wcandillon/react-native-webgpu/blob/24cf75ba084cb23fb477053fc310ecbfceae6bb2/packages/webgpu/cpp/rnwgpu/api/GPURenderPassEncoder.h#L103-L112)). Its optimizations (prototype caches, import-once native buffers, async work pumped on the owning instance) target per-object and per-frame costs; per-draw crossings are out of its reach for the same reason. - The wgpu-native backend this PR was developed against is the same shape. The Babylon Lite native plan treats this as a first-class metric: every prototype must report "JS↔C crossing count and cost per frame", and option S0d is a purpose-built JS↔C wire that crosses once per frame ([BABYLON_LITE_PLAN.md](https://github.com/bghgary/BabylonNative/blob/bghgary/babylon-lite-plan/BABYLON_LITE_PLAN.md)). This PR is that wire for the existing engine: it lives in the transport layer, behind the standard WebGPU API, and a host opts in by installing a provider. The benefit scales with draws per frame, which is the honest way to read the native numbers below: a frame with ten draws gains nothing; the full Hill Valley scene at 81 draws per frame gains about 8% on iPhone 12; the stress scene at thousands of draws per second cuts draw-level bridge crossings by about 95%. ## Architecture - `RenderCommandBatcher`: a small generic lower-or-replay adapter. It tries the optional lowering path; for a draw that cannot lower, it flushes pending lowered work first, then replays the original command exactly once. Ordering is preserved unconditionally. - `WebGPURenderPassCommandStream`: the WebGPU-specific lowerer. It encodes compatibility-mode draws as opcode words into a reused `Uint32Array`, state-diffing pipeline, index/vertex buffers and bind groups so redundant state is elided before it is recorded, then submits the whole batch through a single `_recordCommands` call on the render pass. - Flush boundaries: batched draws are recorded before every direct render-pass mutation (viewport, scissor, stencil reference, blend constant, full-quad clears, occlusion query begin/end, debug marker push/pop/insert, and render-pass end) through a `ThinWebGPUEngine._flushRenderPassCommands()` seam that is a no-op for engines that do not batch. Without these boundaries a draw batched inside an occlusion-query bracket would be recorded after the bracket closes (the query reports zero samples and the mesh wrongly disappears as occluded), and GPU captures would attribute draws to the wrong debug group. The occlusion-query flush sits inside the `_occlusionQueryActive` guard introduced by #18918. - The draw hot path is allocation-free: the engine reuses two scratch draw-command objects (both the lowering and replay paths consume commands synchronously), and the stream reuses scratch id arrays. `_draw` now has one apply site, `ApplyWebGPURenderPassDrawCommand`, so indirect and multi-draw lowering plug in at one place rather than at each encoder call. - Failure policy: a protocol-version mismatch or a backend recording failure logs one error (enriched with the provider's diagnostics) and permanently falls back to direct encoder calls for that engine; nothing throws from the render loop. Resource ids returned by the provider are validated as positive safe integers at the trust boundary; an out-of-range id makes the draw replay directly. - Typed indexed/non-indexed draw command variants instead of a magic numeric draw type. ## Host contract A host opts in by installing a provider on the engine: ```js engine.renderPassCommandRecorderProvider = { protocolVersion: 1, // must equal WebGPURenderPassCommandStreamProtocolVersion in the decoder build isEnabled: () => true, // host-controlled toggle getResourceId: (resource) => /* host handle id for a pipeline/buffer/bind group, or 0 */, getLastError: () => /* optional backend diagnostics for failure logs */, }; ``` and exposing a `_recordCommands(words, drawCount, multiDrawCallCount, multiDrawDrawCount, wordCount)` hook on its render-pass encoders. The recorder must consume the words synchronously; the buffer is reused after the call returns. The `protocolVersion` handshake mirrors the `PROTOCOL_VERSION` guard the bgfx-based NativeEngine uses to keep the JS encoder and native decoder from drifting, soft-failing here because this transport is an optimization rather than the engine's only path. ## Browser cost With no provider installed (always the case in browsers) a compatibility-mode draw fills the scratch command, calls `submit`, which returns at the provider null check, and replays through `ApplyWebGPURenderPassDrawCommand`, which issues the same encoder calls the old inline code did. - Isolated microbenchmark of exactly that path against the inline encoder calls, on a stub render pass (V8 / Node 24, Apple M4 Max, 2,000,000 draws per round, three rounds): inline 8.0–8.3 ns per draw, no-provider path 13.1–13.4 ns per draw. The added cost is about 5 ns per draw, or 15 µs for a 3,000-draw frame. - Chrome 152 (WebGPU on Metal, Apple M4 Max), UMD bundles built from this branch and from its master base, a scene of 3,000 meshes with 64 materials (2,996 draws per frame), compatibility mode, 600 timed frames after 150 warm-up frames, measuring main-thread time from `beginFrame()` through `scene.render()`: | Bundle | mean | p50 | p90 | p99 | |---|---:|---:|---:|---:| | master | 38.9 ms | 44.5 ms | 50.9 ms | 69.9 ms | | this branch | 38.1 ms | 43.4 ms | 51.9 ms | 59.6 ms | The two are indistinguishable within run-to-run noise; Chrome's own cost in that scene is about 13 µs per draw, three orders of magnitude above the added path. ## Test coverage - Contract-level unit coverage of the batcher and stream: lowering absent/present/disabled, fallback replay with flush-before-replay ordering, state-diff behavior per state kind, indexed/non-indexed/indirect draws, late and out-of-range resource ids, command storage growth, render-pass boundary flushing, no-provider behavior, protocol-version mismatch, and permanent fallback with diagnostics when backend recording fails. - Flush-boundary scenario tests (`webgpuRenderPassFlushBoundaries.test.ts`) drive the real registered engine prototype methods against a semantic model of the native render pass: a GPU timeline that executes direct calls and decoded command-stream batches in submission order, attributing each executed draw to the occlusion query and debug groups open at execution time. Assertions are on user-visible outcomes ("which draws did query N observe", "which draws are inside group X", "did the draws stay on the batched path"), not on encoder-call ordinality. ## Validation - `npx vitest run --project=unit` over `packages/dev/core/test/unit` with this branch merged with current master: 289 files, 4,793 tests pass plus the one pre-existing expected failure, including 39 focused tests across `renderCommandBatcher.test.ts`, `webgpuRenderPassCommandStream.test.ts`, `webgpuRenderPassFlushBoundaries.test.ts` and `engine.query.test.ts`. - `npx eslint --quiet` over all changed source and test files: clean. The `pure.ts` barrel entry is in the generator's order. - `nx build babylonjs`: passes; the resulting bundle is the one measured above. Native integration validation (BabylonNative, WebGPU backed by wgpu-native): - macOS Release/LTO Playground, non-AR Hill Valley stress scene, material parity checks passing (`issues=0`), SSAO2 enabled in `mode=prepass`. Command stream enabled, steady windows: about 13,920 draws per 2 s grouped into about 720 command-stream bridge calls, about 94.8% fewer draw-level bridge traversals, at 59.5–60.5 native fps. Command stream disabled: same draw volume, `cmdStreams=0`, about 59–60 native fps. The M4 path is vsync-bound, so this run demonstrates the bridge-call reduction rather than an fps gain. - iPhone 12 (A14), full Hill Valley scene forced non-XR (112 active meshes, 81 draws per frame with SSAO2): 18 fps → 19.5 fps with the command stream enabled; no change in the pre-placement phase, which draws about 10 meshes per frame. - iPhone XS (A12): about 40 fps → 45 fps with improved 1% lows, as documented in the forum post. ## Related work This continues the Hill Valley NativeXR AR Portal optimization series already merged from this validation effort: - #18539 — Support SSAO2 with world-space normals - #18521 — Move custom frame requester to AbstractEngine to enable 120fps support in BabylonNative - #18538 — Fix KTX noMipmap flip for compressed sRGB mipmaps ## Future work - Multi-draw-indirect lowering through the reserved `multiDrawCallCount`/`multiDrawDrawCount` stream fields (wgpu-native exposes MDI; the WebGPU spec does not yet). - Lower debug markers (`pushDebugGroup`/`popDebugGroup`/`insertDebugMarker`) into stream opcodes so that enabling GPU profiler markers does not fragment batches with boundary flushes; today markers stay correct (flushed) but each marker costs a bridge crossing and splits the batch, which is acceptable for profiling sessions only. - The command-stream types already admit `GPURenderBundleEncoder`, so the same lowering could batch bundle construction under `compatibilityMode = false` if profiling justifies it.
Applies BabylonJS/UrlLib#39 transport fixes to Dependencies/UrlLib, updates NativeDawn to use vendored dependencies, and refreshes validation docs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…iew fixes - Route data: URLs through fetch/UrlLib instead of a plugin-owned base64 decoder. - Coerce URL constructor/canParse arguments with ToString() in the URL polyfill and drop the global URL wrapper. - Fire Image.onerror when decoding fails. - Implement cancelAnimationFrame with monotonic ids. - Build JsRuntimeHost/UrlLib unit tests in the Linux NativeDawn job. - Remove a duplicate Spotlight shadows catalog entry; clarify CI visualization docs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… waits Atmosphere scenes never become ready on the CI WARP adapter; each waited out the 20-minute default sized for the ASAN job, pushing the Win32 NativeDawn job past its timeout. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… route readback failures to error callback createScene's promise wait is now capped by the readiness timeout instead of a fixed 10 minutes, so --readiness-timeout bounds scenes that never resolve. NativeDawn framebuffer readback failures (e.g. device lost) are now delivered to the error callback passed to TestUtils.getFrameBufferData, mirroring TestUtils, instead of throwing uncaught from Tick. The runner treats "lost" like "cancel", so a lost device aborts the run cleanly with a summary. A diagnostic-only device-lost callback logs the reason and message. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…s on WebGPU - ContextDawn stores shadowColor/Blur/OffsetX/OffsetY (saved/restored) and warns once instead of throwing, matching the bgfx canvas. - config.json: exclude Material Plugin (GLSL-only plugin), Node material PBR 1 (intermittent createScene stall), and the Atmosphere/OpenPBR scenes that lose the device on the CI WARP adapter, on WebGPU only, with reasons. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…master references; move Dawn docs - Resolve device.lost from the Dawn device-lost callback - Isolate requestAnimationFrame callbacks, validate handle type tags - Size readback from the presented texture; define event stubs only if missing - Restore 32 shared reference PNGs to master versions - Move Dawn parity/validation docs into Plugins/NativeDawn and update scripts Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Remove the obsolete _LoadScriptModuleAsync exclusions from 14 scenes on all graphics APIs, relax errorRatio for two Compound scenes, and correct the reason for the 5 IBL shadow scenes that stay excluded. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The SPZ/Manifold WASM module never finishes loading on Linux JSC, hanging the validation run (same as the already-opted-in SPZ/CSG scenes). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical defects can render to obsolete windows, display stale image textures, and falsely report failed or incomplete parity runs as passing.
7 open issues
Failed invocations can produce a passing parity report · New Retrying cases overwrites the cumulative schedule · New Reattach does not rebind the surface to the new window · New Reloaded canvas images reuse stale cached textures · New Scene path replacement corrupts absolute URLs · New Dawn context defaults disagree with NanoVG state · New Save and Restore omit wrapper-owned drawing state · New
What changed in this PR
Adds an optional Dawn-backed WebGPU plugin so Babylon.js WebGPUEngine can run on Babylon Native, with Windows rendering support and bgfx remaining the default.
Changes:
- Integrates NativeDawn with Embedding, shared Canvas helpers and Playground initialization.
- Adds shared data-URL decoding, byte-preserving POST handling and regression coverage.
- Adds WebGPU build jobs, Windows rendering validation and Babylon-Lite parity tooling.
| File | Description |
|---|---|
| Polyfills/Window/Source/Window.cpp | Supplies a pixel-ratio fallback without bgfx. |
| Polyfills/Canvas/Source/nanovg/nanovg_dawn.h | Declares the Dawn NanoVG backend interface. |
| Polyfills/Canvas/Source/nanovg/nanovg_dawn_shader.h | Adds WGSL shaders for Canvas rendering. |
| Polyfills/Canvas/Source/MeasureText.h | Declares backend-independent text measurement. |
| Polyfills/Canvas/Source/MeasureText.cpp | Shares measurement through NanoVG contexts. |
| Polyfills/Canvas/Source/Image.h | Exposes decoded image pixels. |
| Polyfills/Canvas/Source/Image.cpp | Implements decoded-pixel access. |
| Polyfills/Canvas/Source/Gradient.cpp | Rounds and clamps gradient color bytes. |
| Polyfills/Canvas/Source/Colors.h | Updates HSL conversion and quantization. |
| Polyfills/Canvas/Source/CanvasEncoding.h | Declares shared PNG encoding. |
| Polyfills/Canvas/Source/CanvasEncoding.cpp | Encodes RGBA pixels as PNG data URLs. |
| Polyfills/Canvas/Source/Canvas.cpp | Uses the shared PNG encoder. |
| Polyfills/Canvas/CMakeLists.txt | Builds encoding helpers and optional Dawn rendering. |
| Plugins/TestUtils/Source/TestUtils.cpp | Exposes exit-callback notification. |
| Plugins/TestUtils/Include/Babylon/Plugins/TestUtils.h | Declares backend-independent exit notification. |
| Plugins/TestUtils/CMakeLists.txt | Publishes the GraphicsDevice dependency. |
| Plugins/NativeDawn/Source/ImageDecode.cpp | Decodes images into RGBA pixels. |
| Plugins/NativeDawn/Source/ImageDataDawn.h | Declares persistent Canvas image data. |
| Plugins/NativeDawn/Source/ImageDataDawn.cpp | Implements clamped pixel buffers and readback. |
| Plugins/NativeDawn/Source/ContextDawn.h | Declares Dawn Canvas2D operations and state. |
| Plugins/NativeDawn/Source/CanvasDawn.h | Declares Dawn canvas rendering and readback. |
| Plugins/NativeDawn/Include/Babylon/Plugins/NativeDawn.h | Exposes plugin lifecycle and surface APIs. |
| Plugins/NativeDawn/CMakeLists.txt | Defines plugin dependencies and runtime integration. |
| Plugins/CMakeLists.txt | Registers the optional NativeDawn plugin. |
| Embedding/Source/View.cpp | Routes frames, resizing and input through Dawn. |
| Embedding/Source/RuntimeImpl.h | Adds Dawn lifecycle and frame-dispatch state. |
| Embedding/Source/Runtime.cpp | Initializes and tears down Dawn on the JS thread. |
| Embedding/CMakeLists.txt | Selects Dawn and links Compression support. |
| Dependencies/UrlLib/Tests/UrlRequestErrorReporting.cpp | Adds HTTP body, header and lifecycle tests. |
| Dependencies/UrlLib/Tests/SchemeResolver.cpp | Tests cancellation before resolver invocation. |
| Dependencies/UrlLib/Tests/Main.cpp | Initializes WinRT for transport tests. |
| Dependencies/UrlLib/Tests/DataUrl.cpp | Tests data-URL decoding and lifecycle behavior. |
| Dependencies/UrlLib/Tests/CMakeLists.txt | Registers new transport tests and entry point. |
| Dependencies/UrlLib/Source/UrlRequest_Windows_Shared.h | Preserves POST bytes and handles optional headers. |
| Dependencies/UrlLib/Source/UrlRequest_Shared.h | Routes shared schemes through cancellable resolution. |
| Dependencies/UrlLib/Source/UrlRequest_Base.h | Integrates built-in data-URL resolution. |
| Dependencies/UrlLib/Source/UrlRequest_Apple.mm | Preserves raw POST body bytes. |
| Dependencies/UrlLib/Source/UrlRequest_Android.cpp | Writes POST bodies as byte-array chunks. |
| Dependencies/UrlLib/Source/DataUrl.h | Implements data-URL parsing and decoding. |
| Dependencies/UrlLib/README.md | Documents data URLs and transport behavior. |
| Dependencies/UrlLib/Include/UrlLib/UrlLib.h | Documents shared scheme-resolution contracts. |
| Dependencies/UrlLib/CMakeLists.txt | Adds the base-n decoding dependency. |
| Dependencies/JsRuntimeHost/Polyfills/URL/Source/URL.cpp | Coerces URL arguments to strings. |
| CMakeLists.txt | Configures optional Dawn dependencies and backends. |
| BUILDING.md | Documents WebGPU builds, validation and limitations. |
| Apps/UnitTests/Source/Tests.Canvas.Readback.cpp | Tests gradient rounding and HSL colors. |
| Apps/Playground/Win32/App.cpp | Resolves script paths and selects bootstrap behavior. |
| Apps/Playground/Shared/PlaygroundScripts.h | Documents Dawn bootstrap loading. |
| Apps/Playground/Shared/PlaygroundScripts.cpp | Loads Dawn bootstrap and forwards readiness options. |
| Apps/Playground/Shared/Diagnostics.cpp | Adds symbolized Windows crash stacks. |
| Apps/Playground/Shared/CommandLine.h | Adds a readiness-timeout option. |
| Apps/Playground/Shared/CommandLine.cpp | Parses and validates readiness timeouts. |
| Apps/Playground/Scripts/run_snippet.js | Runs individual Playground snippets. |
| Apps/Playground/Scripts/playground_runner.js | Waits for WebGPU engine initialization. |
| Apps/Playground/Scripts/lite_parity_native.js | Bridges native evaluation and screenshots to parity tests. |
| Apps/Playground/Scripts/lite_native.js | Adds a Lite rendering and pixel-comparison harness. |
| Apps/Playground/Scripts/dawn_playground.js | Initializes the Playground WebGPU engine. |
| Apps/Playground/Scripts/dawn_playground_native.js | Tests bootstrap, resizing and framebuffer readback. |
| Apps/Playground/CMakeLists.txt | Packages harnesses and Windows runtime support. |
| Apps/Playground/Android/BabylonNative/build.gradle | Enables plugin-only Dawn builds. |
| .github/workflows/ci.yml | Adds NativeDawn platform jobs. |
| .github/workflows/build-win32.yml | Builds Dawn and runs Lite/WebGPU validation. |
| .github/workflows/build-macos.yml | Adds Dawn plugin build coverage. |
| .github/workflows/build-linux.yml | Adds Dawn build dependencies and coverage. |
| .github/workflows/build-ios.yml | Adds Dawn plugin build coverage. |
| .github/workflows/build-android.yml | Adds plugin-only Dawn build coverage. |
| .github/scripts/prepare-lite-parity.mjs | Generates isolated native parity suites. |
| .github/scripts/lite-parity-tools.test.mjs | Tests serialization and reporting helpers. |
| .github/scripts/lite-parity-reporter.mjs | Records schedules, attempts and runner outcomes. |
| .github/scripts/lite-parity-records.mjs | Summarizes cases and associates attempt metrics. |
| .github/scripts/lite-parity-fixture.mjs | Adapts native and browser parity fixtures. |
| .github/scripts/diagnose-lite-parity.mjs | Replays failed native scenes in browsers. |
| .github/scripts/complete-lite-parity.mjs | Exercises remaining catalog scenes. |
| .github/scripts/capture-dawn-provenance.mjs | Records source and executable fingerprints. |
| .github/scripts/bundle-lite-scene.mjs | Bundles one Lite scene for native execution. |
| .github/scripts/bundle-lite-parity.mjs | Bundles the Lite parity catalog. |
| .github/instructions/babylon-native-debugging.instructions.md | Documents the readiness-timeout flag. |
Review effort: Balanced
| const status = !invocationStatus || pending > 0 ? "IN PROGRESS / incomplete" | ||
| : cases.some(record => record.status !== "passed") || | ||
| (!cases.length && invocationStatus !== "passed") ? "failed" : "passed"; |
| writeFileSync(join(this.output, "scheduled-tests.json"), JSON.stringify( | ||
| suite.allTests().map(test => ({ | ||
| id: test.id, title: test.titlePath(), location: test.location, | ||
| })), null, 2)); |
| m_runtime.m_appRuntime->Dispatch([lw, lh](Napi::Env env) { | ||
| Babylon::Plugins::NativeDawn::ResizeSurface(env, lw, lh); |
| std::shared_ptr<arcana::cancellation_source> m_cancellationSource{}; | ||
| JsRuntimeScheduler m_runtimeScheduler; | ||
|
|
||
| std::unordered_map<const NativeCanvasImage*, int> m_nvgImageIndices; |
| .replace(/'\/textures\//g, "'" + pgRoot + "/textures/") | ||
| .replace(/"textures\//g, '"' + pgRoot + "/textures/") | ||
| .replace(/'textures\//g, "'" + pgRoot + "/textures/") | ||
| .replace(/\/scenes\//g, pgRoot + "/scenes/") |
| std::string m_lineCap{}; // 'butt', 'round', 'square' | ||
| std::string m_lineJoin{}; // 'round', 'bevel', 'miter' | ||
|
|
||
| // Dash pattern from setLineDash. Retained only so getLineDash() round-trips; | ||
| // strokes are always drawn solid (nanovg has no dashed stroke). | ||
| std::vector<double> m_lineDash{}; | ||
| std::string m_filter{}; | ||
| std::string m_direction{"ltr"}; // 'ltr', 'rtl' | ||
| float m_miterLimit{0.f}; | ||
| float m_lineWidth{0.f}; |
| struct SavedStyle | ||
| { | ||
| std::variant<std::string, GradientStyle> fillStyle; | ||
| std::variant<std::string, GradientStyle> strokeStyle; | ||
| Font font; | ||
| int currentFontId; | ||
| ShadowState shadow; | ||
| }; |


Babylon Native renders WebGPU through bgfx, which cannot expose the full WebGPU API that Babylon.js's
WebGPUEngineexpects. This adds an optional NativeDawn plugin that implements the JavaScript WebGPU API directly on top of Dawn, soWebGPUEnginecan run on Babylon Native on Windows. The bgfx path is unchanged and remains the default.The goal is for Dawn to behave as an ordinary plugin: it registers its own WebGPU surface and uses the shared polyfills (canvas, URL, XMLHttpRequest, window) rather than patching the runtime. Playground gains a WebGPU configuration that runs the existing validation scenes against it.
Only Windows has a surface and is exercised end to end. The other platform jobs compile the plugin but do not render with it.
Worth a look:
Apps/Playground/Scripts/config.jsoneach carry areason. The main groups are 17 Atmosphere and OpenPBR scenes, which stall and then lose the device on the CI software adapter (no shader error; they pass on a real GPU) and so have no CI coverage until a GPU runner is available, 5 IBL-shadow scenes (bgfx throwsUnsupported alpha mode: 19; Dawn hits a 2D/3D texture binding mismatch), and 3 SPZ scenes that needReadableStreamin JsRuntimeHost.es-module-importworkaround (excluded on JavaScriptCore, where the SPZ/Manifold WASM never loads).--generate-references, so it gates on loading and rendering failures, not pixel comparison. Pixel comparison against references has only been run locally.Context2Dshadows are stored but not rendered, matching the bgfx canvas.Plugins/NativeDawn. Their results are a point-in-time record, and some links point at local build artifacts.