Wire alphaToCoverage into the WebGPU render pipeline state - #9208
Conversation
Build size reportThis PR changes the size of the minified bundles.
|
mvaligursky
left a comment
There was a problem hiding this comment.
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.
Material#alphaToCoveragewas silently ignored on WebGPU. The backend had an emptysetAlphaToCoveragestub and never setGPUMultisampleState.alphaToCoverageEnabled, so theflag reached the device and was dropped. The shader side already worked — the WGSL
outputAlphachunk honoursLIT_ALPHA_TO_COVERAGE— only the pipeline state was missing.Changes:
WebgpuGraphicsDevice#setAlphaToCoveragenow stores the state and passes it to renderpipeline creation, which sets
multisample.alphaToCoverageEnabled.lookupHashesgrows from15 to 16 entries, as all previous slots were in use.
getAlphaToCoveragedrops the flag where the spec disallows it instead of failingpipeline 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_COVERAGEon a single-sampled framebuffer is a no-op rather than an error.This is not hypothetical:
CameraFrame's default HDR format isPIXELFORMAT_111110F(
rg11b10ufloat), which has no alpha channel, so the unguarded version fails pipelinecreation and renders nothing. A debug warning is logged once when the attachment format is
the only thing preventing it.
alphaToCoverageis initialised in the sharedGraphicsDevice#initializeRenderState,alongside
cullModeandfrontFace.alphaToCoverage = trueassignment fromWideLineRenderer. ItsShaderMaterialwrites alpha
1.0unconditionally and never went through the lit shader options, so the flagcould 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#_sortKeyForwardfor 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 tocoverage 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:
Material#alphaToCoveragenow takes effect on WebGPU. Its docs nolonger 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:
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.