Skip to content

NativeEngine: MultiRenderTarget framebuffers + OIT alpha blend modes#1754

Open
bkaradzic-microsoft wants to merge 3 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:native-multi-render-target
Open

NativeEngine: MultiRenderTarget framebuffers + OIT alpha blend modes#1754
bkaradzic-microsoft wants to merge 3 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:native-multi-render-target

Conversation

@bkaradzic-microsoft

@bkaradzic-microsoft bkaradzic-microsoft commented Jun 11, 2026

Copy link
Copy Markdown
Member

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

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 NativeEngine: MultiRenderTarget framebuffers + OIT alpha blend modes #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.)

@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]

One refactor comment inline.

Comment thread Plugins/NativeEngine/Source/NativeEngine.cpp

@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. The description is now stale, though: since #18568 feature-detects the native surface, the "co-dependent / landing order" framing no longer applies — the two can land separately, and this can come out of draft.

@bkaradzic-microsoft bkaradzic-microsoft marked this pull request as ready for review July 8, 2026 22:59
Copilot AI review requested due to automatic review settings July 8, 2026 22:59

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.

Pull request overview

This PR adds foundational NativeEngine support for MultiRenderTarget framebuffers by creating a single bgfx framebuffer with N color attachments (plus optional depth/stencil), and exposes two additional alpha blend modes needed by OIT depth-peeling workflows. It also updates the bundled Babylon.js npm dependencies used by the Apps workspace.

Changes:

  • Add createMultiFrameBuffer and refactor framebuffer creation into a shared CreateFrameBufferImpl that supports multiple color attachments.
  • Expose additional alpha blend mode constants (ALPHA_ONEONE_ONEONE, ALPHA_LAYER_ACCUMULATE) for native-side blend state mapping.
  • Bump Babylon.js-related npm dependencies in Apps/ to ^9.15.0 (and update lockfile).

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.

File Description
Plugins/NativeEngine/Source/NativeEngine.h Declares new MRT framebuffer entrypoint and shared framebuffer helper.
Plugins/NativeEngine/Source/NativeEngine.cpp Implements MRT framebuffer creation via a shared helper; adds OIT blend mode constants and JS binding for createMultiFrameBuffer.
Apps/package.json Updates Babylon.js package dependency versions to ^9.15.0.
Apps/package-lock.json Locks Babylon.js packages to 9.15.0 and updates related integrity metadata.
Files not reviewed (1)
  • Apps/package-lock.json: Generated file

Comment on lines +1899 to +1902
if (colorCount + 1 > caps->limits.maxFBAttachments)
{
throw Napi::Error::New(env, "Invalid number of color attachments for frame buffer");
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 05862e2. The guard now reserves the depth/stencil slot only when one is generated: const uint32_t depthStencilCount = (generateStencilBuffer || generateDepth) ? 1u : 0u; then checks colorCount + depthStencilCount > caps->limits.maxFBAttachments. This matches the attachment-creation path below (which only appends the depth/stencil attachment inside the same generateStencilBuffer || generateDepth branch), so a max-color-count MRT with no depth/stencil is no longer wrongly rejected.

bkaradzic and others added 2 commits July 8, 2026 16:07
Foundational native-engine support for MultiRenderTarget (MRT) and the order-
independent-transparency blend modes, removing several "engine._gl is null"
crash classes. It does not by itself land the MRT/OIT/FrameGraph validation
tests, which need further work (see follow-ups below).

- Add NativeEngine::CreateMultiFrameBuffer: build one bgfx framebuffer with N
  color attachments (+ optional depth) so a MultiRenderTarget renders to all
  targets at once (bgfx writes every attachment of the bound framebuffer, so
  no drawBuffers is needed). JS-controlled attachment count is validated
  against caps->limits.maxFBAttachments.
- Add alpha blend modes ALPHA_ONEONE_ONEONE (11) and ALPHA_LAYER_ACCUMULATE
  (17) used by the depth-peeling OIT renderer.

Pairs with the Babylon.js change (createMultipleRenderTarget + MRT helper
overrides, applyStates, reverse-Z clear). Known follow-ups: the OIT depth-
peeling path still faults inside the D3D11 driver on submit (needs interactive
GPU debugging), and the blend equation (MAX) is not yet applied natively.

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

Address review feedback (discussion_r3546780088): the single- and multi-RT
framebuffer paths were ~95% identical. Extract the shared color/depth
attachment setup, framebuffer creation and cleanup into a private
CreateFrameBufferImpl helper. Both N-API methods are kept so the protocol is
unchanged; they now only differ in how they parse info[0] (single nullable
texture vs. array) before calling the shared helper.

This restores the "Stencil without depth..." warning and the isTextureValid
assert that the multi path had dropped, and lets the single-RT path be the
0-or-1-color case (the shared helper no longer rejects colorCount == 0). The
maxFBAttachments upper-bound guard is preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bkaradzic-microsoft bkaradzic-microsoft force-pushed the native-multi-render-target branch from 04f709e to 779fc34 Compare July 8, 2026 23:07
The attachment-count guard in CreateFrameBufferImpl unconditionally added
1 for a depth/stencil attachment (colorCount + 1), which incorrectly
rejected a valid MRT that uses the maximum number of color attachments
with no depth/stencil (generateDepth=false, generateStencilBuffer=false).
Reserve the depth/stencil slot only when one is actually generated,
matching the attachment-creation logic below.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants