Skip to content

Add independent blending for multiple render targets - #9140

Merged
mvaligursky merged 1 commit into
mainfrom
mv-independent-blending
Jul 30, 2026
Merged

Add independent blending for multiple render targets#9140
mvaligursky merged 1 commit into
mainfrom
mv-independent-blending

Conversation

@mvaligursky

Copy link
Copy Markdown
Contributor

Adds support for giving each color attachment of a render target its own blend state and color write mask, exposing the full capability of WebGPU's per-target blend state and WebGL2's OES_draw_buffers_indexed extension. Previously a single blend state was applied to all attachments on both backends.

Changes:

  • BlendState gains per-attachment overrides. An attachment which has not been assigned its own state follows attachment 0, so existing behaviour is unchanged.
  • WebGPU builds a blend state and write mask per color target, rather than reusing attachment 0's for all of them.
  • WebGL2 detects OES_draw_buffers_indexed and uses its indexed entry points. When the extension is unavailable, the state of attachment 0 is used for all attachments and a warning is logged, so features relying on this must check the capability.
  • The single-attachment path is unchanged on both backends - no additional state is tracked and the same GL calls are issued.

API Changes:

  • BlendState#setAttachment(index, src) - assigns an independent blend state to a color attachment, in 1 to 7 range. The blend state of the supplied source is copied.
  • BlendState#clearAttachment(index) - makes the attachment follow attachment 0 again.
  • BlendState#getAttachment(index, dst) - stores the blend state of an attachment in the supplied blend state, avoiding allocations by allowing a single instance to be reused.
  • BlendState#hasAttachmentOverrides - true when any attachment has been given an independent blend state.
  • GraphicsDevice#supportsIndependentBlending - true on WebGPU, and on WebGL2 when OES_draw_buffers_indexed is available.

Note that the blend constant used by BLENDMODE_CONSTANT is a single value on both APIs and so cannot vary per attachment, and dual-source blending remains limited to a single color target.

Examples:

  • New hidden test/independent-blending example, rendering into a two attachment MRT with a different blend equation and write mask per attachment, and displaying both attachments side by side.

@github-actions

Copy link
Copy Markdown

Public API report

This PR changes the public API surface (+6 / −0), per the docs' rules (@ignore / @Private / undocumented are excluded).

Show API diff
+BlendState.clearAttachment(index: number): void
+BlendState.get hasAttachmentOverrides(): boolean
+BlendState.getAttachment(index: number, dst: BlendState): BlendState
+BlendState.setAttachment(index: number, src: BlendState | null): void
+GraphicsDevice.readonly supportsIndependentBlending: boolean
+WebglGraphicsDevice.readonly supportsIndependentBlending: boolean

Informational only — this never fails the build.

@github-actions

Copy link
Copy Markdown

Build size report

This PR changes the size of the minified bundles.

Bundle Minified Gzip Brotli
playcanvas.min.js 2354.4 KB (+2.5 KB, +0.11%) 604.6 KB (+0.7 KB, +0.12%) 469.5 KB (+0.1 KB, +0.03%)
playcanvas.min.mjs 2351.8 KB (+2.5 KB, +0.11%) 603.6 KB (+0.8 KB, +0.13%) 468.9 KB (+0.5 KB, +0.10%)

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

Adds support for independent blending in MRT rendering by extending BlendState with per-attachment overrides and wiring that through both WebGPU and WebGL2 (via OES_draw_buffers_indexed), while exposing a capability flag on GraphicsDevice.

Changes:

  • Extend BlendState with per-attachment blend/write-mask overrides (with attachment inheritance semantics) and update hashing/equality accordingly.
  • WebGPU: build per-target blend + writeMask entries for render pipeline color targets.
  • WebGL2: when OES_draw_buffers_indexed is available, apply indexed blend state per draw buffer; otherwise fall back to attachment 0 with a warning and expose supportsIndependentBlending.

Reviewed changes

Copilot reviewed 7 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/platform/graphics/blend-state.test.mjs Adds unit tests for per-attachment overrides, key stability, copy/clone behavior, and dual-source detection.
src/platform/graphics/blend-state.js Implements per-attachment blend state storage, override detection, and keying/equality updates.
src/platform/graphics/webgpu/webgpu-render-pipeline.js Emits per-color-target blend state and write mask in pipeline descriptors.
src/platform/graphics/webgpu/webgpu-graphics-device.js Sets supportsIndependentBlending = true for WebGPU devices.
src/platform/graphics/webgl/webgl-graphics-device.js Detects OES_draw_buffers_indexed, exposes capability, and applies blend state per attachment when supported.
src/platform/graphics/graphics-device.js Documents and exposes the new supportsIndependentBlending capability flag.
examples/src/examples/test/independent-blending.example.mjs Adds a hidden example demonstrating different blend/write-mask behavior per MRT attachment.
Comments suppressed due to low confidence (1)

src/platform/graphics/blend-state.js:342

  • If per-attachment states are stored with a marker bit (bit 31) to make a packed value of 0 representable, getAttachment must mask that bit out before assigning to dst.attachment0. Otherwise dst would appear to have attachment overrides (negative attachment0) and its key / flags become inconsistent.
        const state = this.hasAttachmentOverrides ? this._attachments[index] : 0;
        dst.attachment0 = state !== 0 ? state : (this.attachment0 & stateMask);
        return dst;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/platform/graphics/blend-state.js
@mvaligursky
mvaligursky merged commit 4d15fc1 into main Jul 30, 2026
11 checks passed
@mvaligursky
mvaligursky deleted the mv-independent-blending branch July 30, 2026 13:25
@mvaligursky mvaligursky mentioned this pull request Jul 30, 2026
5 tasks
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.

2 participants