Skip to content

Wire alphaToCoverage into the WebGPU render pipeline state - #9208

Merged
mvaligursky merged 2 commits into
mainfrom
mv-webgpu-alpha-to-coverage
Aug 21, 2026
Merged

Wire alphaToCoverage into the WebGPU render pipeline state#9208
mvaligursky merged 2 commits into
mainfrom
mv-webgpu-alpha-to-coverage

Conversation

@mvaligursky

@mvaligursky mvaligursky commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Material#alphaToCoverage was silently ignored on WebGPU. The backend had an empty
setAlphaToCoverage stub and never set GPUMultisampleState.alphaToCoverageEnabled, so the
flag reached the device and was dropped. The shader side already worked — the WGSL
outputAlpha chunk honours LIT_ALPHA_TO_COVERAGE — only the pipeline state was missing.

Changes:

  • WebgpuGraphicsDevice#setAlphaToCoverage now stores the state and passes it to render
    pipeline creation, which sets multisample.alphaToCoverageEnabled.
  • The effective state participates in the render pipeline cache key. lookupHashes grows from
    15 to 16 entries, as all previous slots were in use.
  • New getAlphaToCoverage drops the flag where the spec disallows it instead of failing
    pipeline creation. The spec requires a multi-sampled target whose first color attachment uses
    a blendable format with an alpha channel. A material is not bound to a single render target —
    the same one can be drawn into a multi-sampled forward pass, a single-sampled pass, or a
    depth-only shadow pass with no color attachment — so a hard failure would make the flag
    unusable in any app with more than one pass. This also matches WebGL, where enabling
    SAMPLE_ALPHA_TO_COVERAGE on a single-sampled framebuffer is a no-op rather than an error.
    This is not hypothetical: CameraFrame's default HDR format is PIXELFORMAT_111110F
    (rg11b10ufloat), which has no alpha channel, so the unguarded version fails pipeline
    creation and renders nothing. A debug warning is logged once when the attachment format is
    the only thing preventing it.
  • alphaToCoverage is initialised in the shared GraphicsDevice#initializeRenderState,
    alongside cullMode and frontFace.
  • Removed the alphaToCoverage = true assignment from WideLineRenderer. Its ShaderMaterial
    writes alpha 1.0 unconditionally and never went through the lit shader options, so the flag
    could not affect rasterization on either backend - but once WebGPU honours it, it would have
    become a pipeline validation failure whenever wide lines were drawn into a multi-sampled HDR
    target without an alpha channel. Note this also clears bit 22 of MeshInstance#_sortKeyForward
    for wide lines, moving them out of the "render after opaque" bucket.

Note that WebGL and WebGPU now differ on PIXELFORMAT_111110F: WebGL still applies alpha to
coverage there, as it uses the shader's alpha output regardless of whether the target stores
alpha, while WebGPU drops it. That asymmetry comes from the two specs rather than from this
change, and the example exposes it deliberately.

API Changes:

  • No signature changes. Material#alphaToCoverage now takes effect on WebGPU. Its docs no
    longer say "WebGL2 only" and now state the multi-sampling requirement, and the additional
    WebGPU requirement that the first color attachment have a blendable format with an alpha
    channel.

Examples:

  • New hidden internal test test/alpha-to-coverage. Three labelled rows — alpha to coverage,
    alpha blending, opaque — over a shared opacity ramp, so a working coverage row is visibly
    quantized against the smooth blended row. Controls toggle MSAA (on the back buffer or the
    CameraFrame target, whichever is active), CameraFrame, and the CameraFrame render format, so
    both silent-fallback paths can be exercised.

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

Build size report

This PR changes the size of the minified bundles.

Bundle Minified Gzip Brotli
playcanvas.min.js 2372.6 KB (+0.4 KB, +0.02%) 609.7 KB (+0.1 KB, +0.02%) 473.5 KB (+0.0 KB, +0.01%)
playcanvas.min.mjs 2370.0 KB (+0.4 KB, +0.02%) 608.6 KB (+0.1 KB, +0.02%) 473.2 KB (+0.5 KB, +0.10%)

@mvaligursky mvaligursky left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Automated PR review by Codex (GPT-5).

No blocking findings. I reviewed the WebGPU state propagation, effective-state pipeline cache key, WebGPU alpha-to-coverage validation rules, single-sample/depth-only fallbacks, render-state initialization and reset behavior, the WideLineRenderer cleanup, public documentation, example coverage, performance impact, and compatibility with the current base. The implementation correctly keys the immutable pipeline state only when alpha-to-coverage can actually be enabled, avoiding both invalid WebGPU pipelines and unnecessary cache variants.

Validation performed: focused material/WideLineRenderer unit tests (44 passing), ESLint on all changed engine source files, and git diff --check. The current GitHub build, unit, lint, types, docs, API, examples, and deployment checks are also green.

Residual test risk: the backend pipeline descriptor behavior is exercised visually by the new hidden example rather than by a focused automated WebGPU pipeline unit test, but I did not find a correctness issue in the implementation.

@mvaligursky
mvaligursky merged commit 2fe26dc into main Aug 21, 2026
10 checks passed
@mvaligursky
mvaligursky deleted the mv-webgpu-alpha-to-coverage branch August 21, 2026 10:44
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.

1 participant