[M113] Add alphaToCoverage validation aligning with WebGPU V1
Add alphaToCoverage validation regards to targets[0] has alpha channel.
This change reflecting WebGPU V1 spec update is aimed to ship together
with WebGPU in Chromium.
Bug: dawn:1759, chromium:1434828
No-Try: true
Change-Id: I0aef60cf8c4dc828e05d6027644ffed35b33f652
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/128061
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Auto-Submit: Shrek Shao <shrekshao@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Loko Kung <lokokung@google.com>
(cherry picked from commit a965f520f94724f4c3b89a10ff68492f809e7f4b)
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/128640
Commit-Queue: Shrek Shao <shrekshao@google.com>
Kokoro: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn/native/Format.cpp b/src/dawn/native/Format.cpp
index f7f368e..c8abe42 100644
--- a/src/dawn/native/Format.cpp
+++ b/src/dawn/native/Format.cpp
@@ -80,6 +80,11 @@
return aspects & (Aspect::Depth | Aspect::Stencil);
}
+bool Format::HasAlphaChannel() const {
+ // This is true for current formats. May need revisit if new formats and extensions are added.
+ return componentCount == 4 && IsColor();
+}
+
bool Format::IsMultiPlanar() const {
return aspects & (Aspect::Plane0 | Aspect::Plane1);
}
diff --git a/src/dawn/native/Format.h b/src/dawn/native/Format.h
index 8a895c7..0f9f171 100644
--- a/src/dawn/native/Format.h
+++ b/src/dawn/native/Format.h
@@ -106,6 +106,7 @@
bool HasDepth() const;
bool HasStencil() const;
bool HasDepthOrStencil() const;
+ bool HasAlphaChannel() const;
// IsMultiPlanar() returns true if the format allows selecting a plane index. This is only
// allowed by multi-planar formats (ex. NV12).
diff --git a/src/dawn/native/RenderPipeline.cpp b/src/dawn/native/RenderPipeline.cpp
index 6f0915a..f3d26be 100644
--- a/src/dawn/native/RenderPipeline.cpp
+++ b/src/dawn/native/RenderPipeline.cpp
@@ -427,10 +427,23 @@
}
DAWN_TRY(ValidateColorAttachmentBytesPerSample(device, colorAttachmentFormats));
- DAWN_INVALID_IF(fragmentMetadata.usesSampleMaskOutput && alphaToCoverageEnabled,
- "alphaToCoverageEnabled is true when the sample_mask builtin is a "
- "pipeline output of fragment stage of %s.",
- descriptor->module);
+ if (alphaToCoverageEnabled) {
+ DAWN_INVALID_IF(fragmentMetadata.usesSampleMaskOutput,
+ "alphaToCoverageEnabled is true when the sample_mask builtin is a "
+ "pipeline output of fragment stage of %s.",
+ descriptor->module);
+
+ DAWN_INVALID_IF(descriptor->targetCount == 0 ||
+ descriptor->targets[0].format == wgpu::TextureFormat::Undefined,
+ "alphaToCoverageEnabled is true when color target[0] is not present.");
+
+ const Format* format;
+ DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor->targets[0].format));
+ DAWN_INVALID_IF(
+ !format->HasAlphaChannel(),
+ "alphaToCoverageEnabled is true when target[0].format (%s) has no alpha channel.",
+ format->format);
+ }
return {};
}
@@ -533,6 +546,10 @@
DAWN_TRY_CONTEXT(ValidateMultisampleState(&descriptor->multisample),
"validating multisample state.");
+ DAWN_INVALID_IF(
+ descriptor->multisample.alphaToCoverageEnabled && descriptor->fragment == nullptr,
+ "alphaToCoverageEnabled is true when fragment state is not present.");
+
if (descriptor->fragment != nullptr) {
DAWN_TRY_CONTEXT(ValidateFragmentState(device, descriptor->fragment, descriptor->layout,
descriptor->depthStencil,
diff --git a/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp b/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
index 8468b06..05fecd9 100644
--- a/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
@@ -848,7 +848,7 @@
}
// Tests if the sample_mask builtin is a pipeline output of fragment shader,
-// then alphaToCoverageEnabled must be false
+// then alphaToCoverageEnabled must be false.
TEST_F(RenderPipelineValidationTest, AlphaToCoverageAndSampleMaskOutput) {
wgpu::ShaderModule fsModuleSampleMaskOutput = utils::CreateShaderModule(device, R"(
struct Output {
@@ -897,6 +897,84 @@
}
}
+// Tests when alphaToCoverageEnabled is true, targets[0] must exist and have alpha channel.
+TEST_F(RenderPipelineValidationTest, AlphaToCoverageAndColorTargetAlpha) {
+ {
+ // Control case
+ utils::ComboRenderPipelineDescriptor descriptor;
+ descriptor.vertex.module = vsModule;
+ descriptor.cFragment.module = fsModule;
+ descriptor.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm;
+ descriptor.multisample.count = 4;
+ descriptor.multisample.alphaToCoverageEnabled = true;
+
+ device.CreateRenderPipeline(&descriptor);
+ }
+
+ {
+ // Fragment state must exist
+ utils::ComboRenderPipelineDescriptor descriptor;
+ descriptor.vertex.module = vsModule;
+ descriptor.fragment = nullptr;
+ descriptor.multisample.count = 4;
+ descriptor.multisample.alphaToCoverageEnabled = true;
+
+ ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
+ }
+
+ {
+ // Fragment targets[0] must exist
+ utils::ComboRenderPipelineDescriptor descriptor;
+ descriptor.vertex.module = vsModule;
+ descriptor.cFragment.module = fsModule;
+ descriptor.cFragment.targetCount = 0;
+ descriptor.cFragment.targets = nullptr;
+ descriptor.multisample.count = 4;
+ descriptor.multisample.alphaToCoverageEnabled = true;
+ descriptor.EnableDepthStencil(wgpu::TextureFormat::Depth32Float);
+
+ ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
+ }
+
+ {
+ // Fragment targets[0].format must have alpha channel (only 1 target)
+ utils::ComboRenderPipelineDescriptor descriptor;
+ descriptor.vertex.module = vsModule;
+ descriptor.cFragment.module = fsModule;
+ descriptor.cTargets[0].format = wgpu::TextureFormat::R8Unorm;
+ descriptor.multisample.count = 4;
+ descriptor.multisample.alphaToCoverageEnabled = true;
+
+ ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
+ }
+
+ wgpu::ShaderModule fsModule2 = utils::CreateShaderModule(device, R"(
+ struct FragmentOut {
+ @location(0) target0 : vec4f,
+ @location(1) target1 : vec4f,
+ }
+ @fragment fn main() -> FragmentOut {
+ var out: FragmentOut;
+ out.target0 = vec4f(0, 0, 0, 1);
+ out.target1 = vec4f(1, 0, 0, 0);
+ return out;
+ })");
+
+ {
+ // Fragment targets[0].format must have alpha channel (2 targets)
+ utils::ComboRenderPipelineDescriptor descriptor;
+ descriptor.vertex.module = vsModule;
+ descriptor.cFragment.module = fsModule2;
+ descriptor.cFragment.targetCount = 2;
+ descriptor.cTargets[0].format = wgpu::TextureFormat::R8Unorm;
+ descriptor.cTargets[1].format = wgpu::TextureFormat::RGBA8Unorm;
+ descriptor.multisample.count = 4;
+ descriptor.multisample.alphaToCoverageEnabled = true;
+
+ ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
+ }
+}
+
// Tests that the texture component type in shader must match the bind group layout.
TEST_F(RenderPipelineValidationTest, TextureComponentTypeCompatibility) {
constexpr uint32_t kNumTextureComponentType = 3u;