Rename BlendState to ColorState, in order to match web idl
BUG=dawn:106
Change-Id: Id2cb1788becfacd09bd7f420d6525d22f96d1fe2
Reviewed-on: https://dawn-review.googlesource.com/c/4781
Commit-Queue: Yunchao He <yunchao.he@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 9ccee88..ded94749 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -998,8 +998,8 @@
"src/tests/DawnTest.h",
"src/tests/end2end/BasicTests.cpp",
"src/tests/end2end/BindGroupTests.cpp",
- "src/tests/end2end/BlendStateTests.cpp",
"src/tests/end2end/BufferTests.cpp",
+ "src/tests/end2end/ColorStateTests.cpp",
"src/tests/end2end/ComputeCopyStorageBufferTests.cpp",
"src/tests/end2end/CopyTests.cpp",
"src/tests/end2end/DepthStencilStateTests.cpp",
diff --git a/src/dawn_native/RenderPipeline.cpp b/src/dawn_native/RenderPipeline.cpp
index d738852..2b84042 100644
--- a/src/dawn_native/RenderPipeline.cpp
+++ b/src/dawn_native/RenderPipeline.cpp
@@ -151,13 +151,13 @@
mDepthStencilState->stencilFront.passOp != dawn::StencilOperation::Keep;
}
- bool BlendEnabled(const ColorStateDescriptor* mBlendState) {
- return mBlendState->alphaBlend.operation != dawn::BlendOperation::Add ||
- mBlendState->alphaBlend.srcFactor != dawn::BlendFactor::One ||
- mBlendState->alphaBlend.dstFactor != dawn::BlendFactor::Zero ||
- mBlendState->colorBlend.operation != dawn::BlendOperation::Add ||
- mBlendState->colorBlend.srcFactor != dawn::BlendFactor::One ||
- mBlendState->colorBlend.dstFactor != dawn::BlendFactor::Zero;
+ bool BlendEnabled(const ColorStateDescriptor* mColorState) {
+ return mColorState->alphaBlend.operation != dawn::BlendOperation::Add ||
+ mColorState->alphaBlend.srcFactor != dawn::BlendFactor::One ||
+ mColorState->alphaBlend.dstFactor != dawn::BlendFactor::Zero ||
+ mColorState->colorBlend.operation != dawn::BlendOperation::Add ||
+ mColorState->colorBlend.srcFactor != dawn::BlendFactor::One ||
+ mColorState->colorBlend.dstFactor != dawn::BlendFactor::Zero;
}
// RenderPipelineBase
diff --git a/src/dawn_native/RenderPipeline.h b/src/dawn_native/RenderPipeline.h
index 27064c0..0e95ced 100644
--- a/src/dawn_native/RenderPipeline.h
+++ b/src/dawn_native/RenderPipeline.h
@@ -30,7 +30,7 @@
MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device,
const RenderPipelineDescriptor* descriptor);
bool StencilTestEnabled(const DepthStencilStateDescriptor* mDepthStencilState);
- bool BlendEnabled(const ColorStateDescriptor* mBlendState);
+ bool BlendEnabled(const ColorStateDescriptor* mColorState);
class RenderPipelineBase : public PipelineBase {
public:
diff --git a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
index 83babde..4e4127d 100644
--- a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
+++ b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
@@ -127,7 +127,7 @@
return static_cast<uint8_t>(colorWriteMask);
}
- D3D12_RENDER_TARGET_BLEND_DESC ComputeBlendDesc(const ColorStateDescriptor* descriptor) {
+ D3D12_RENDER_TARGET_BLEND_DESC ComputeColorDesc(const ColorStateDescriptor* descriptor) {
D3D12_RENDER_TARGET_BLEND_DESC blendDesc;
blendDesc.BlendEnable = BlendEnabled(descriptor);
blendDesc.SrcBlend = D3D12Blend(descriptor->colorBlend.srcFactor);
@@ -287,7 +287,7 @@
for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) {
descriptorD3D12.RTVFormats[i] = D3D12TextureFormat(GetColorAttachmentFormat(i));
descriptorD3D12.BlendState.RenderTarget[i] =
- ComputeBlendDesc(GetColorStateDescriptor(i));
+ ComputeColorDesc(GetColorStateDescriptor(i));
}
descriptorD3D12.NumRenderTargets = static_cast<uint32_t>(GetColorAttachmentsMask().count());
diff --git a/src/dawn_native/opengl/RenderPipelineGL.cpp b/src/dawn_native/opengl/RenderPipelineGL.cpp
index 665f039..830fa40 100644
--- a/src/dawn_native/opengl/RenderPipelineGL.cpp
+++ b/src/dawn_native/opengl/RenderPipelineGL.cpp
@@ -90,7 +90,7 @@
}
}
- void ApplyBlendState(uint32_t attachment, const ColorStateDescriptor* descriptor) {
+ void ApplyColorState(uint32_t attachment, const ColorStateDescriptor* descriptor) {
if (BlendEnabled(descriptor)) {
glEnablei(GL_BLEND, attachment);
glBlendEquationSeparatei(attachment, GLBlendMode(descriptor->colorBlend.operation),
@@ -196,7 +196,7 @@
ApplyDepthStencilState(GetDepthStencilStateDescriptor(), &persistentPipelineState);
for (uint32_t attachmentSlot : IterateBitSet(GetColorAttachmentsMask())) {
- ApplyBlendState(attachmentSlot, GetColorStateDescriptor(attachmentSlot));
+ ApplyColorState(attachmentSlot, GetColorStateDescriptor(attachmentSlot));
}
}
diff --git a/src/dawn_native/vulkan/RenderPipelineVk.cpp b/src/dawn_native/vulkan/RenderPipelineVk.cpp
index 411765d..81b73b5 100644
--- a/src/dawn_native/vulkan/RenderPipelineVk.cpp
+++ b/src/dawn_native/vulkan/RenderPipelineVk.cpp
@@ -111,7 +111,7 @@
return static_cast<VkColorComponentFlagBits>(mask);
}
- VkPipelineColorBlendAttachmentState ComputeBlendDesc(
+ VkPipelineColorBlendAttachmentState ComputeColorDesc(
const ColorStateDescriptor* descriptor) {
VkPipelineColorBlendAttachmentState attachment;
attachment.blendEnable = BlendEnabled(descriptor) ? VK_TRUE : VK_FALSE;
@@ -199,10 +199,6 @@
RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor)
: RenderPipelineBase(device, descriptor) {
- // Eventually a bunch of the structures that need to be chained in the create info will be
- // held by objects such as the BlendState. They aren't implemented yet so we initialize
- // everything here.
-
VkPipelineShaderStageCreateInfo shaderStages[2];
{
shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
@@ -283,11 +279,11 @@
ComputeDepthStencilDesc(GetDepthStencilStateDescriptor());
// Initialize the "blend state info" that will be chained in the "create info" from the data
- // pre-computed in the BlendState
+ // pre-computed in the ColorState
std::array<VkPipelineColorBlendAttachmentState, kMaxColorAttachments> colorBlendAttachments;
for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) {
const ColorStateDescriptor* descriptor = GetColorStateDescriptor(i);
- colorBlendAttachments[i] = ComputeBlendDesc(descriptor);
+ colorBlendAttachments[i] = ComputeColorDesc(descriptor);
}
VkPipelineColorBlendStateCreateInfo colorBlend;
colorBlend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
diff --git a/src/tests/end2end/BlendStateTests.cpp b/src/tests/end2end/BlendStateTests.cpp
deleted file mode 100644
index 6b9ddd8..0000000
--- a/src/tests/end2end/BlendStateTests.cpp
+++ /dev/null
@@ -1,953 +0,0 @@
-// Copyright 2017 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <array>
-#include <cmath>
-
-#include "tests/DawnTest.h"
-
-#include "common/Assert.h"
-#include "common/Constants.h"
-#include "utils/ComboRenderPipelineDescriptor.h"
-#include "utils/DawnHelpers.h"
-
-constexpr static unsigned int kRTSize = 64;
-
-class BlendStateTest : public DawnTest {
- protected:
- void SetUp() override {
- DawnTest::SetUp();
-
- vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"(
- #version 450
- void main() {
- const vec2 pos[3] = vec2[3](vec2(-1.f, -1.f), vec2(3.f, -1.f), vec2(-1.f, 3.f));
- gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f);
- }
- )");
-
- bindGroupLayout = utils::MakeBindGroupLayout(
- device, {
- {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer},
- });
-
- pipelineLayout = utils::MakeBasicPipelineLayout(device, &bindGroupLayout);
-
- renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
- }
-
- struct TriangleSpec {
- RGBA8 color;
- std::array<float, 4> blendFactor = {};
- };
-
- // Set up basePipeline and testPipeline. testPipeline has the given blend state on the first attachment. basePipeline has no blending
- void SetupSingleSourcePipelines(const dawn::ColorStateDescriptor& colorStateDescriptor) {
- dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
- #version 450
- layout(set = 0, binding = 0) uniform myBlock {
- vec4 color;
- } myUbo;
-
- layout(location = 0) out vec4 fragColor;
-
- void main() {
- fragColor = myUbo.color;
- }
- )");
-
- utils::ComboRenderPipelineDescriptor baseDescriptor(device);
- baseDescriptor.layout = pipelineLayout;
- baseDescriptor.cVertexStage.module = vsModule;
- baseDescriptor.cFragmentStage.module = fsModule;
- baseDescriptor.cColorStates[0].format = renderPass.colorFormat;
-
- basePipeline = device.CreateRenderPipeline(&baseDescriptor);
-
- utils::ComboRenderPipelineDescriptor testDescriptor(device);
- testDescriptor.layout = pipelineLayout;
- testDescriptor.cVertexStage.module = vsModule;
- testDescriptor.cFragmentStage.module = fsModule;
- testDescriptor.cColorStates[0] = colorStateDescriptor;
- testDescriptor.cColorStates[0].format = renderPass.colorFormat;
-
- testPipeline = device.CreateRenderPipeline(&testDescriptor);
- }
-
- // Create a bind group to set the colors as a uniform buffer
- template <size_t N>
- dawn::BindGroup MakeBindGroupForColors(std::array<RGBA8, N> colors) {
- std::array<float, 4 * N> data;
- for (unsigned int i = 0; i < N; ++i) {
- data[4 * i + 0] = static_cast<float>(colors[i].r) / 255.f;
- data[4 * i + 1] = static_cast<float>(colors[i].g) / 255.f;
- data[4 * i + 2] = static_cast<float>(colors[i].b) / 255.f;
- data[4 * i + 3] = static_cast<float>(colors[i].a) / 255.f;
- }
-
- uint32_t bufferSize = static_cast<uint32_t>(4 * N * sizeof(float));
-
- dawn::Buffer buffer = utils::CreateBufferFromData(device, &data, bufferSize, dawn::BufferUsageBit::Uniform);
- return utils::MakeBindGroup(device, bindGroupLayout, {{0, buffer, 0, bufferSize}});
- }
-
- // Test that after drawing a triangle with the base color, and then the given triangle spec, the color is as expected
- void DoSingleSourceTest(RGBA8 base, const TriangleSpec& triangle, const RGBA8& expected) {
- dawn::Color blendColor{triangle.blendFactor[0], triangle.blendFactor[1], triangle.blendFactor[2], triangle.blendFactor[3]};
-
- dawn::CommandEncoder encoder = device.CreateCommandEncoder();
- {
- dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass.renderPassInfo);
- // First use the base pipeline to draw a triangle with no blending
- pass.SetPipeline(basePipeline);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { base } })));
- pass.Draw(3, 1, 0, 0);
-
- // Then use the test pipeline to draw the test triangle with blending
- pass.SetPipeline(testPipeline);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { triangle.color } })));
- pass.SetBlendColor(&blendColor);
- pass.Draw(3, 1, 0, 0);
- pass.EndPass();
- }
-
- dawn::CommandBuffer commands = encoder.Finish();
- queue.Submit(1, &commands);
-
- EXPECT_PIXEL_RGBA8_EQ(expected, renderPass.color, kRTSize / 2, kRTSize / 2);
- }
-
- // Given a vector of tests where each element is <testColor, expectedColor>, check that all expectations are true for the given blend operation
- void CheckBlendOperation(RGBA8 base, dawn::BlendOperation operation, std::vector<std::pair<RGBA8, RGBA8>> tests) {
- dawn::BlendDescriptor blend;
- blend.operation = operation;
- blend.srcFactor = dawn::BlendFactor::One;
- blend.dstFactor = dawn::BlendFactor::One;
-
- dawn::ColorStateDescriptor descriptor;
- descriptor.alphaBlend = blend;
- descriptor.colorBlend = blend;
- descriptor.colorWriteMask = dawn::ColorWriteMask::All;
-
- SetupSingleSourcePipelines(descriptor);
-
- for (const auto& test : tests) {
- DoSingleSourceTest(base, { test.first }, test.second);
- }
- }
-
- // Given a vector of tests where each element is <testSpec, expectedColor>, check that all expectations are true for the given blend factors
- void CheckBlendFactor(RGBA8 base, dawn::BlendFactor colorSrcFactor, dawn::BlendFactor colorDstFactor, dawn::BlendFactor alphaSrcFactor, dawn::BlendFactor alphaDstFactor, std::vector<std::pair<TriangleSpec, RGBA8>> tests) {
- dawn::BlendDescriptor colorBlend;
- colorBlend.operation = dawn::BlendOperation::Add;
- colorBlend.srcFactor = colorSrcFactor;
- colorBlend.dstFactor = colorDstFactor;
-
- dawn::BlendDescriptor alphaBlend;
- alphaBlend.operation = dawn::BlendOperation::Add;
- alphaBlend.srcFactor = alphaSrcFactor;
- alphaBlend.dstFactor = alphaDstFactor;
-
- dawn::ColorStateDescriptor descriptor;
- descriptor.colorBlend = colorBlend;
- descriptor.alphaBlend = alphaBlend;
- descriptor.colorWriteMask = dawn::ColorWriteMask::All;
-
- SetupSingleSourcePipelines(descriptor);
-
- for (const auto& test : tests) {
- DoSingleSourceTest(base, test.first, test.second);
- }
- }
-
- void CheckSrcBlendFactor(RGBA8 base, dawn::BlendFactor colorFactor, dawn::BlendFactor alphaFactor, std::vector<std::pair<TriangleSpec, RGBA8>> tests) {
- CheckBlendFactor(base, colorFactor, dawn::BlendFactor::One, alphaFactor, dawn::BlendFactor::One, tests);
- }
-
- void CheckDstBlendFactor(RGBA8 base, dawn::BlendFactor colorFactor, dawn::BlendFactor alphaFactor, std::vector<std::pair<TriangleSpec, RGBA8>> tests) {
- CheckBlendFactor(base, dawn::BlendFactor::One, colorFactor, dawn::BlendFactor::One, alphaFactor, tests);
- }
-
- utils::BasicRenderPass renderPass;
- dawn::RenderPipeline basePipeline;
- dawn::RenderPipeline testPipeline;
- dawn::ShaderModule vsModule;
- dawn::BindGroupLayout bindGroupLayout;
- dawn::PipelineLayout pipelineLayout;
-};
-
-namespace {
- // Add two colors and clamp
- constexpr RGBA8 operator+(const RGBA8& col1, const RGBA8& col2) {
- int r = static_cast<int>(col1.r) + static_cast<int>(col2.r);
- int g = static_cast<int>(col1.g) + static_cast<int>(col2.g);
- int b = static_cast<int>(col1.b) + static_cast<int>(col2.b);
- int a = static_cast<int>(col1.a) + static_cast<int>(col2.a);
- r = (r > 255 ? 255 : (r < 0 ? 0 : r));
- g = (g > 255 ? 255 : (g < 0 ? 0 : g));
- b = (b > 255 ? 255 : (b < 0 ? 0 : b));
- a = (a > 255 ? 255 : (a < 0 ? 0 : a));
-
- return RGBA8(static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b), static_cast<uint8_t>(a));
- }
-
- // Subtract two colors and clamp
- constexpr RGBA8 operator-(const RGBA8& col1, const RGBA8& col2) {
- int r = static_cast<int>(col1.r) - static_cast<int>(col2.r);
- int g = static_cast<int>(col1.g) - static_cast<int>(col2.g);
- int b = static_cast<int>(col1.b) - static_cast<int>(col2.b);
- int a = static_cast<int>(col1.a) - static_cast<int>(col2.a);
- r = (r > 255 ? 255 : (r < 0 ? 0 : r));
- g = (g > 255 ? 255 : (g < 0 ? 0 : g));
- b = (b > 255 ? 255 : (b < 0 ? 0 : b));
- a = (a > 255 ? 255 : (a < 0 ? 0 : a));
-
- return RGBA8(static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b), static_cast<uint8_t>(a));
- }
-
- // Get the component-wise minimum of two colors
- RGBA8 min(const RGBA8& col1, const RGBA8& col2) {
- return RGBA8(
- std::min(col1.r, col2.r),
- std::min(col1.g, col2.g),
- std::min(col1.b, col2.b),
- std::min(col1.a, col2.a)
- );
- }
-
- // Get the component-wise maximum of two colors
- RGBA8 max(const RGBA8& col1, const RGBA8& col2) {
- return RGBA8(
- std::max(col1.r, col2.r),
- std::max(col1.g, col2.g),
- std::max(col1.b, col2.b),
- std::max(col1.a, col2.a)
- );
- }
-
- // Blend two RGBA8 color values parameterized by the provided factors in the range [0.f, 1.f]
- RGBA8 mix(const RGBA8& col1, const RGBA8& col2, std::array<float, 4> fac) {
- float r = static_cast<float>(col1.r) * (1.f - fac[0]) + static_cast<float>(col2.r) * fac[0];
- float g = static_cast<float>(col1.g) * (1.f - fac[1]) + static_cast<float>(col2.g) * fac[1];
- float b = static_cast<float>(col1.b) * (1.f - fac[2]) + static_cast<float>(col2.b) * fac[2];
- float a = static_cast<float>(col1.a) * (1.f - fac[3]) + static_cast<float>(col2.a) * fac[3];
-
- return RGBA8({ static_cast<uint8_t>(std::round(r)), static_cast<uint8_t>(std::round(g)), static_cast<uint8_t>(std::round(b)), static_cast<uint8_t>(std::round(a)) });
- }
-
- // Blend two RGBA8 color values parameterized by the provided RGBA8 factor
- RGBA8 mix(const RGBA8& col1, const RGBA8& col2, const RGBA8& fac) {
- std::array<float, 4> f = { {
- static_cast<float>(fac.r) / 255.f,
- static_cast<float>(fac.g) / 255.f,
- static_cast<float>(fac.b) / 255.f,
- static_cast<float>(fac.a) / 255.f,
- } };
- return mix(col1, col2, f);
- }
-
- constexpr std::array<RGBA8, 8> kColors = { {
- // check operations over multiple channels
- RGBA8(64,0,0,0),
- RGBA8(0,64,0,0),
- RGBA8(64,0,32,0),
- RGBA8(0,64,32,0),
- RGBA8(128,0,128,128),
- RGBA8(0,128,128,128),
-
- // check cases that may cause overflow
- RGBA8(0,0,0,0),
- RGBA8(255,255,255,255),
- } };
-}
-
-// Test compilation and usage of the fixture
-TEST_P(BlendStateTest, Basic) {
- dawn::BlendDescriptor blend;
- blend.operation = dawn::BlendOperation::Add;
- blend.srcFactor = dawn::BlendFactor::One;
- blend.dstFactor = dawn::BlendFactor::Zero;
- dawn::ColorStateDescriptor descriptor;
- descriptor.alphaBlend = blend;
- descriptor.colorBlend = blend;
- descriptor.colorWriteMask = dawn::ColorWriteMask::All;
-
- SetupSingleSourcePipelines(descriptor);
-
- DoSingleSourceTest(RGBA8(0, 0, 0, 0), { RGBA8(255, 0, 0, 0) }, RGBA8(255, 0, 0, 0));
-}
-
-// The following tests check test that the blend operation works
-TEST_P(BlendStateTest, BlendOperationAdd) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<RGBA8, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- return std::make_pair(color, base + color);
- });
- CheckBlendOperation(base, dawn::BlendOperation::Add, tests);
-}
-
-TEST_P(BlendStateTest, BlendOperationSubtract) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<RGBA8, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- return std::make_pair(color, color - base);
- });
- CheckBlendOperation(base, dawn::BlendOperation::Subtract, tests);
-}
-
-TEST_P(BlendStateTest, BlendOperationReverseSubtract) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<RGBA8, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- return std::make_pair(color, base - color);
- });
- CheckBlendOperation(base, dawn::BlendOperation::ReverseSubtract, tests);
-}
-
-TEST_P(BlendStateTest, BlendOperationMin) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<RGBA8, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- return std::make_pair(color, min(base, color));
- });
- CheckBlendOperation(base, dawn::BlendOperation::Min, tests);
-}
-
-TEST_P(BlendStateTest, BlendOperationMax) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<RGBA8, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- return std::make_pair(color, max(base, color));
- });
- CheckBlendOperation(base, dawn::BlendOperation::Max, tests);
-}
-
-// The following tests check that the Source blend factor works
-TEST_P(BlendStateTest, SrcBlendFactorZero) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- return std::make_pair(TriangleSpec({ { color } }), base);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::Zero, dawn::BlendFactor::Zero, tests);
-}
-
-TEST_P(BlendStateTest, SrcBlendFactorOne) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- return std::make_pair(TriangleSpec({ { color } }), base + color);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::One, dawn::BlendFactor::One, tests);
-}
-
-TEST_P(BlendStateTest, SrcBlendFactorSrcColor) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac = color;
- fac.a = 0;
- RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::SrcColor, dawn::BlendFactor::Zero, tests);
-}
-
-TEST_P(BlendStateTest, SrcBlendFactorOneMinusSrcColor) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac = RGBA8(255, 255, 255, 255) - color;
- fac.a = 0;
- RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusSrcColor, dawn::BlendFactor::Zero, tests);
-}
-
-TEST_P(BlendStateTest, SrcBlendFactorSrcAlpha) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac(color.a, color.a, color.a, color.a);
- RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::SrcAlpha, dawn::BlendFactor::SrcAlpha, tests);
-}
-
-TEST_P(BlendStateTest, SrcBlendFactorOneMinusSrcAlpha) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(color.a, color.a, color.a, color.a);
- RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusSrcAlpha, dawn::BlendFactor::OneMinusSrcAlpha, tests);
-}
-
-TEST_P(BlendStateTest, SrcBlendFactorDstColor) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac = base;
- fac.a = 0;
- RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::DstColor, dawn::BlendFactor::Zero, tests);
-}
-
-TEST_P(BlendStateTest, SrcBlendFactorOneMinusDstColor) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac = RGBA8(255, 255, 255, 255) - base;
- fac.a = 0;
- RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusDstColor, dawn::BlendFactor::Zero, tests);
-}
-
-TEST_P(BlendStateTest, SrcBlendFactorDstAlpha) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac(base.a, base.a, base.a, base.a);
- RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::DstAlpha, dawn::BlendFactor::DstAlpha, tests);
-}
-
-TEST_P(BlendStateTest, SrcBlendFactorOneMinusDstAlpha) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(base.a, base.a, base.a, base.a);
- RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusDstAlpha, dawn::BlendFactor::OneMinusDstAlpha, tests);
-}
-
-TEST_P(BlendStateTest, SrcBlendFactorSrcAlphaSaturated) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- uint8_t f = std::min(color.a, static_cast<uint8_t>(255 - base.a));
- RGBA8 fac(f, f, f, 255);
- RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::SrcAlphaSaturated, dawn::BlendFactor::SrcAlphaSaturated, tests);
-}
-
-TEST_P(BlendStateTest, SrcBlendFactorBlendColor) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- auto triangleSpec = TriangleSpec({ { color }, {{ 0.2f, 0.4f, 0.6f, 0.8f }} });
- RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, triangleSpec.blendFactor);
- return std::make_pair(triangleSpec, expected);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::BlendColor, dawn::BlendFactor::BlendColor, tests);
-}
-
-TEST_P(BlendStateTest, SrcBlendFactorOneMinusBlendColor) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- auto triangleSpec = TriangleSpec({ { color }, {{ 0.2f, 0.4f, 0.6f, 0.8f }} });
- std::array<float, 4> f = { { 0.8f, 0.6f, 0.4f, 0.2f } };
- RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, f);
- return std::make_pair(triangleSpec, expected);
- });
- CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusBlendColor, dawn::BlendFactor::OneMinusBlendColor, tests);
-}
-
-// The following tests check that the Destination blend factor works
-TEST_P(BlendStateTest, DstBlendFactorZero) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- return std::make_pair(TriangleSpec({ { color } }), color);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::Zero, dawn::BlendFactor::Zero, tests);
-}
-
-TEST_P(BlendStateTest, DstBlendFactorOne) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- return std::make_pair(TriangleSpec({ { color } }), base + color);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::One, dawn::BlendFactor::One, tests);
-}
-
-TEST_P(BlendStateTest, DstBlendFactorSrcColor) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac = color;
- fac.a = 0;
- RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::SrcColor, dawn::BlendFactor::Zero, tests);
-}
-
-TEST_P(BlendStateTest, DstBlendFactorOneMinusSrcColor) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac = RGBA8(255, 255, 255, 255) - color;
- fac.a = 0;
- RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusSrcColor, dawn::BlendFactor::Zero, tests);
-}
-
-TEST_P(BlendStateTest, DstBlendFactorSrcAlpha) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac(color.a, color.a, color.a, color.a);
- RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::SrcAlpha, dawn::BlendFactor::SrcAlpha, tests);
-}
-
-TEST_P(BlendStateTest, DstBlendFactorOneMinusSrcAlpha) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(color.a, color.a, color.a, color.a);
- RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusSrcAlpha, dawn::BlendFactor::OneMinusSrcAlpha, tests);
-}
-
-TEST_P(BlendStateTest, DstBlendFactorDstColor) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac = base;
- fac.a = 0;
- RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::DstColor, dawn::BlendFactor::Zero, tests);
-}
-
-TEST_P(BlendStateTest, DstBlendFactorOneMinusDstColor) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac = RGBA8(255, 255, 255, 255) - base;
- fac.a = 0;
- RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusDstColor, dawn::BlendFactor::Zero, tests);
-}
-
-TEST_P(BlendStateTest, DstBlendFactorDstAlpha) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac(base.a, base.a, base.a, base.a);
- RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::DstAlpha, dawn::BlendFactor::DstAlpha, tests);
-}
-
-TEST_P(BlendStateTest, DstBlendFactorOneMinusDstAlpha) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(base.a, base.a, base.a, base.a);
- RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusDstAlpha, dawn::BlendFactor::OneMinusDstAlpha, tests);
-}
-
-TEST_P(BlendStateTest, DstBlendFactorSrcAlphaSaturated) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- uint8_t f = std::min(color.a, static_cast<uint8_t>(255 - base.a));
- RGBA8 fac(f, f, f, 255);
- RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
- return std::make_pair(TriangleSpec({ { color } }), expected);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::SrcAlphaSaturated, dawn::BlendFactor::SrcAlphaSaturated, tests);
-}
-
-TEST_P(BlendStateTest, DstBlendFactorBlendColor) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- auto triangleSpec = TriangleSpec({ { color }, {{ 0.2f, 0.4f, 0.6f, 0.8f }} });
- RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, triangleSpec.blendFactor);
- return std::make_pair(triangleSpec, expected);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::BlendColor, dawn::BlendFactor::BlendColor, tests);
-}
-
-TEST_P(BlendStateTest, DstBlendFactorOneMinusBlendColor) {
- RGBA8 base(32, 64, 128, 192);
- std::vector<std::pair<TriangleSpec, RGBA8>> tests;
- std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
- auto triangleSpec = TriangleSpec({ { color }, {{ 0.2f, 0.4f, 0.6f, 0.8f }} });
- std::array<float, 4> f = { { 0.8f, 0.6f, 0.4f, 0.2f } };
- RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, f);
- return std::make_pair(triangleSpec, expected);
- });
- CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusBlendColor, dawn::BlendFactor::OneMinusBlendColor, tests);
-}
-
-// Check that the color write mask works
-TEST_P(BlendStateTest, ColorWriteMask) {
- dawn::BlendDescriptor blend;
- blend.operation = dawn::BlendOperation::Add;
- blend.srcFactor = dawn::BlendFactor::One;
- blend.dstFactor = dawn::BlendFactor::One;
-
- dawn::ColorStateDescriptor descriptor;
- descriptor.colorBlend = blend;
- descriptor.alphaBlend = blend;
- {
- // Test single channel color write
- descriptor.colorWriteMask = dawn::ColorWriteMask::Red;
- SetupSingleSourcePipelines(descriptor);
-
- RGBA8 base(32, 64, 128, 192);
- for (auto& color : kColors) {
- RGBA8 expected = base + RGBA8(color.r, 0, 0, 0);
- DoSingleSourceTest(base, { color }, expected);
- }
- }
-
- {
- // Test multi channel color write
- descriptor.colorWriteMask = dawn::ColorWriteMask::Green | dawn::ColorWriteMask::Alpha;
- SetupSingleSourcePipelines(descriptor);
-
- RGBA8 base(32, 64, 128, 192);
- for (auto& color : kColors) {
- RGBA8 expected = base + RGBA8(0, color.g, 0, color.a);
- DoSingleSourceTest(base, { color }, expected);
- }
- }
-
- {
- // Test no channel color write
- descriptor.colorWriteMask = dawn::ColorWriteMask::None;
- SetupSingleSourcePipelines(descriptor);
-
- RGBA8 base(32, 64, 128, 192);
- for (auto& color : kColors) {
- DoSingleSourceTest(base, { color }, base);
- }
- }
-}
-
-// Check that the color write mask works when blending is disabled
-TEST_P(BlendStateTest, ColorWriteMaskBlendingDisabled) {
- {
- dawn::BlendDescriptor blend;
- blend.operation = dawn::BlendOperation::Add;
- blend.srcFactor = dawn::BlendFactor::One;
- blend.dstFactor = dawn::BlendFactor::Zero;
- dawn::ColorStateDescriptor descriptor;
- descriptor.alphaBlend = blend;
- descriptor.colorBlend = blend;
-
- descriptor.colorWriteMask = dawn::ColorWriteMask::Red;
- SetupSingleSourcePipelines(descriptor);
-
- RGBA8 base(32, 64, 128, 192);
- RGBA8 expected(32, 0, 0, 0);
-
- dawn::CommandEncoder encoder = device.CreateCommandEncoder();
- {
- dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass.renderPassInfo);
- pass.SetPipeline(testPipeline);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { base } })));
- pass.Draw(3, 1, 0, 0);
- pass.EndPass();
- }
-
- dawn::CommandBuffer commands = encoder.Finish();
- queue.Submit(1, &commands);
- EXPECT_PIXEL_RGBA8_EQ(expected, renderPass.color, kRTSize / 2, kRTSize / 2);
- }
-}
-
-// Test that independent blend states on render targets works
-TEST_P(BlendStateTest, IndependentBlendState) {
- DAWN_SKIP_TEST_IF(IsWindows() && IsVulkan() && IsIntel());
-
- std::array<dawn::Texture, 4> renderTargets;
- std::array<dawn::TextureView, 4> renderTargetViews;
-
- dawn::TextureDescriptor descriptor;
- descriptor.dimension = dawn::TextureDimension::e2D;
- descriptor.size.width = kRTSize;
- descriptor.size.height = kRTSize;
- descriptor.size.depth = 1;
- descriptor.arraySize = 1;
- descriptor.sampleCount = 1;
- descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm;
- descriptor.levelCount = 1;
- descriptor.usage = dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc;
-
- for (uint32_t i = 0; i < 4; ++i) {
- renderTargets[i] = device.CreateTexture(&descriptor);
- renderTargetViews[i] = renderTargets[i].CreateDefaultTextureView();
- }
-
- dawn::RenderPassColorAttachmentDescriptor colorAttachments[4];
- for (uint32_t i = 0; i < 4; ++i) {
- colorAttachments[i].attachment = renderTargetViews[i];
- colorAttachments[i].resolveTarget = nullptr;
- colorAttachments[i].clearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
- colorAttachments[i].loadOp = dawn::LoadOp::Clear;
- colorAttachments[i].storeOp = dawn::StoreOp::Store;
- }
-
- dawn::RenderPassDescriptor renderpass = device.CreateRenderPassDescriptorBuilder()
- .SetColorAttachments(4, colorAttachments)
- .GetResult();
-
- dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
- #version 450
- layout(set = 0, binding = 0) uniform myBlock {
- vec4 color0;
- vec4 color1;
- vec4 color2;
- vec4 color3;
- } myUbo;
-
- layout(location = 0) out vec4 fragColor0;
- layout(location = 1) out vec4 fragColor1;
- layout(location = 2) out vec4 fragColor2;
- layout(location = 3) out vec4 fragColor3;
-
- void main() {
- fragColor0 = myUbo.color0;
- fragColor1 = myUbo.color1;
- fragColor2 = myUbo.color2;
- fragColor3 = myUbo.color3;
- }
- )");
-
- utils::ComboRenderPipelineDescriptor baseDescriptor(device);
- baseDescriptor.layout = pipelineLayout;
- baseDescriptor.cVertexStage.module = vsModule;
- baseDescriptor.cFragmentStage.module = fsModule;
- baseDescriptor.numColorStates = 4;
-
- basePipeline = device.CreateRenderPipeline(&baseDescriptor);
-
- utils::ComboRenderPipelineDescriptor testDescriptor(device);
- testDescriptor.layout = pipelineLayout;
- testDescriptor.cVertexStage.module = vsModule;
- testDescriptor.cFragmentStage.module = fsModule;
- testDescriptor.numColorStates = 4;
-
- // set blend states
- dawn::BlendDescriptor blend1;
- blend1.operation = dawn::BlendOperation::Add;
- blend1.srcFactor = dawn::BlendFactor::One;
- blend1.dstFactor = dawn::BlendFactor::One;
-
- dawn::BlendDescriptor blend2;
- blend2.operation = dawn::BlendOperation::Subtract;
- blend2.srcFactor = dawn::BlendFactor::One;
- blend2.dstFactor = dawn::BlendFactor::One;
-
- dawn::BlendDescriptor blend3;
- blend3.operation = dawn::BlendOperation::Min;
- blend3.srcFactor = dawn::BlendFactor::One;
- blend3.dstFactor = dawn::BlendFactor::One;
-
- testDescriptor.cColorStates[0].colorBlend = blend1;
- testDescriptor.cColorStates[0].alphaBlend = blend1;
-
- testDescriptor.cColorStates[1].colorBlend = blend2;
- testDescriptor.cColorStates[1].alphaBlend = blend2;
-
- testDescriptor.cColorStates[3].colorBlend = blend3;
- testDescriptor.cColorStates[3].alphaBlend = blend3;
-
- testPipeline = device.CreateRenderPipeline(&testDescriptor);
-
- for (unsigned int c = 0; c < kColors.size(); ++c) {
- RGBA8 base = kColors[((c + 31) * 29) % kColors.size()];
- RGBA8 color0 = kColors[((c + 19) * 13) % kColors.size()];
- RGBA8 color1 = kColors[((c + 11) * 43) % kColors.size()];
- RGBA8 color2 = kColors[((c + 7) * 3) % kColors.size()];
- RGBA8 color3 = kColors[((c + 13) * 71) % kColors.size()];
-
- RGBA8 expected0 = color0 + base;
- RGBA8 expected1 = color1 - base;
- RGBA8 expected2 = color2;
- RGBA8 expected3 = min(color3, base);
-
- dawn::CommandEncoder encoder = device.CreateCommandEncoder();
- {
- dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderpass);
- pass.SetPipeline(basePipeline);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 4>({ { base, base, base, base } })));
- pass.Draw(3, 1, 0, 0);
-
- pass.SetPipeline(testPipeline);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 4>({ { color0, color1, color2, color3 } })));
- pass.Draw(3, 1, 0, 0);
- pass.EndPass();
- }
-
- dawn::CommandBuffer commands = encoder.Finish();
- queue.Submit(1, &commands);
-
- EXPECT_PIXEL_RGBA8_EQ(expected0, renderTargets[0], kRTSize / 2, kRTSize / 2) << "Attachment slot 0 should have been " << color0 << " + " << base << " = " << expected0;
- EXPECT_PIXEL_RGBA8_EQ(expected1, renderTargets[1], kRTSize / 2, kRTSize / 2) << "Attachment slot 1 should have been " << color1 << " - " << base << " = " << expected1;
- EXPECT_PIXEL_RGBA8_EQ(expected2, renderTargets[2], kRTSize / 2, kRTSize / 2) << "Attachment slot 2 should have been " << color2 << " = " << expected2 << "(no blending)";
- EXPECT_PIXEL_RGBA8_EQ(expected3, renderTargets[3], kRTSize / 2, kRTSize / 2) << "Attachment slot 3 should have been min(" << color3 << ", " << base << ") = " << expected3;
- }
-}
-
-// Test that the default blend color is correctly set at the beginning of every subpass
-TEST_P(BlendStateTest, DefaultBlendColor) {
- dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
- #version 450
- layout(set = 0, binding = 0) uniform myBlock {
- vec4 color;
- } myUbo;
-
- layout(location = 0) out vec4 fragColor;
-
- void main() {
- fragColor = myUbo.color;
- }
- )");
-
- utils::ComboRenderPipelineDescriptor baseDescriptor(device);
- baseDescriptor.layout = pipelineLayout;
- baseDescriptor.cVertexStage.module = vsModule;
- baseDescriptor.cFragmentStage.module = fsModule;
- baseDescriptor.cColorStates[0].format = renderPass.colorFormat;
-
- basePipeline = device.CreateRenderPipeline(&baseDescriptor);
-
- utils::ComboRenderPipelineDescriptor testDescriptor(device);
- testDescriptor.layout = pipelineLayout;
- testDescriptor.cVertexStage.module = vsModule;
- testDescriptor.cFragmentStage.module = fsModule;
- testDescriptor.cColorStates[0].format = renderPass.colorFormat;
-
- dawn::BlendDescriptor blend;
- blend.operation = dawn::BlendOperation::Add;
- blend.srcFactor = dawn::BlendFactor::BlendColor;
- blend.dstFactor = dawn::BlendFactor::One;
- testDescriptor.cColorStates[0].colorBlend = blend;
- testDescriptor.cColorStates[0].alphaBlend = blend;
-
- testPipeline = device.CreateRenderPipeline(&testDescriptor);
- constexpr dawn::Color kWhite{1.0f, 1.0f, 1.0f, 1.0f};
-
- // Check that the initial blend color is (0,0,0,0)
- {
- dawn::CommandEncoder encoder = device.CreateCommandEncoder();
- {
- dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass.renderPassInfo);
- pass.SetPipeline(basePipeline);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { RGBA8(0, 0, 0, 0) } })));
- pass.Draw(3, 1, 0, 0);
- pass.SetPipeline(testPipeline);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { RGBA8(255, 255, 255, 255) } })));
- pass.Draw(3, 1, 0, 0);
- pass.EndPass();
- }
-
- dawn::CommandBuffer commands = encoder.Finish();
- queue.Submit(1, &commands);
-
- EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, kRTSize / 2, kRTSize / 2);
- }
-
- // Check that setting the blend color works
- {
- dawn::CommandEncoder encoder = device.CreateCommandEncoder();
- {
- dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass.renderPassInfo);
- pass.SetPipeline(basePipeline);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { RGBA8(0, 0, 0, 0) } })));
- pass.Draw(3, 1, 0, 0);
- pass.SetPipeline(testPipeline);
- pass.SetBlendColor(&kWhite);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { RGBA8(255, 255, 255, 255) } })));
- pass.Draw(3, 1, 0, 0);
- pass.EndPass();
- }
-
- dawn::CommandBuffer commands = encoder.Finish();
- queue.Submit(1, &commands);
-
- EXPECT_PIXEL_RGBA8_EQ(RGBA8(255, 255, 255, 255), renderPass.color, kRTSize / 2, kRTSize / 2);
- }
-
- // Check that the blend color is not inherited between render passes
- {
- dawn::CommandEncoder encoder = device.CreateCommandEncoder();
- {
- dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass.renderPassInfo);
- pass.SetPipeline(basePipeline);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { RGBA8(0, 0, 0, 0) } })));
- pass.Draw(3, 1, 0, 0);
- pass.SetPipeline(testPipeline);
- pass.SetBlendColor(&kWhite);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { RGBA8(255, 255, 255, 255) } })));
- pass.Draw(3, 1, 0, 0);
- pass.EndPass();
- }
- {
- dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass.renderPassInfo);
- pass.SetPipeline(basePipeline);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { RGBA8(0, 0, 0, 0) } })));
- pass.Draw(3, 1, 0, 0);
- pass.SetPipeline(testPipeline);
- pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { RGBA8(255, 255, 255, 255) } })));
- pass.Draw(3, 1, 0, 0);
- pass.EndPass();
- }
-
- dawn::CommandBuffer commands = encoder.Finish();
- queue.Submit(1, &commands);
-
- EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, kRTSize / 2, kRTSize / 2);
- }
-}
-
-DAWN_INSTANTIATE_TEST(BlendStateTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend)
diff --git a/src/tests/end2end/ColorStateTests.cpp b/src/tests/end2end/ColorStateTests.cpp
new file mode 100644
index 0000000..ea099f4
--- /dev/null
+++ b/src/tests/end2end/ColorStateTests.cpp
@@ -0,0 +1,1014 @@
+// Copyright 2017 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <array>
+#include <cmath>
+
+#include "tests/DawnTest.h"
+
+#include "common/Assert.h"
+#include "common/Constants.h"
+#include "utils/ComboRenderPipelineDescriptor.h"
+#include "utils/DawnHelpers.h"
+
+constexpr static unsigned int kRTSize = 64;
+
+class ColorStateTest : public DawnTest {
+ protected:
+ void SetUp() override {
+ DawnTest::SetUp();
+
+ vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"(
+ #version 450
+ void main() {
+ const vec2 pos[3] = vec2[3](vec2(-1.f, -1.f), vec2(3.f, -1.f), vec2(-1.f, 3.f));
+ gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f);
+ }
+ )");
+
+ bindGroupLayout = utils::MakeBindGroupLayout(
+ device, {
+ {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer},
+ });
+
+ pipelineLayout = utils::MakeBasicPipelineLayout(device, &bindGroupLayout);
+
+ renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
+ }
+
+ struct TriangleSpec {
+ RGBA8 color;
+ std::array<float, 4> blendFactor = {};
+ };
+
+ // Set up basePipeline and testPipeline. testPipeline has the given blend state on the first
+ // attachment. basePipeline has no blending
+ void SetupSingleSourcePipelines(const dawn::ColorStateDescriptor& colorStateDescriptor) {
+ dawn::ShaderModule fsModule =
+ utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
+ #version 450
+ layout(set = 0, binding = 0) uniform myBlock {
+ vec4 color;
+ } myUbo;
+
+ layout(location = 0) out vec4 fragColor;
+
+ void main() {
+ fragColor = myUbo.color;
+ }
+ )");
+
+ utils::ComboRenderPipelineDescriptor baseDescriptor(device);
+ baseDescriptor.layout = pipelineLayout;
+ baseDescriptor.cVertexStage.module = vsModule;
+ baseDescriptor.cFragmentStage.module = fsModule;
+ baseDescriptor.cColorStates[0].format = renderPass.colorFormat;
+
+ basePipeline = device.CreateRenderPipeline(&baseDescriptor);
+
+ utils::ComboRenderPipelineDescriptor testDescriptor(device);
+ testDescriptor.layout = pipelineLayout;
+ testDescriptor.cVertexStage.module = vsModule;
+ testDescriptor.cFragmentStage.module = fsModule;
+ testDescriptor.cColorStates[0] = colorStateDescriptor;
+ testDescriptor.cColorStates[0].format = renderPass.colorFormat;
+
+ testPipeline = device.CreateRenderPipeline(&testDescriptor);
+ }
+
+ // Create a bind group to set the colors as a uniform buffer
+ template <size_t N>
+ dawn::BindGroup MakeBindGroupForColors(std::array<RGBA8, N> colors) {
+ std::array<float, 4 * N> data;
+ for (unsigned int i = 0; i < N; ++i) {
+ data[4 * i + 0] = static_cast<float>(colors[i].r) / 255.f;
+ data[4 * i + 1] = static_cast<float>(colors[i].g) / 255.f;
+ data[4 * i + 2] = static_cast<float>(colors[i].b) / 255.f;
+ data[4 * i + 3] = static_cast<float>(colors[i].a) / 255.f;
+ }
+
+ uint32_t bufferSize = static_cast<uint32_t>(4 * N * sizeof(float));
+
+ dawn::Buffer buffer =
+ utils::CreateBufferFromData(device, &data, bufferSize, dawn::BufferUsageBit::Uniform);
+ return utils::MakeBindGroup(device, bindGroupLayout, {{0, buffer, 0, bufferSize}});
+ }
+
+ // Test that after drawing a triangle with the base color, and then the given triangle spec, the
+ // color is as expected
+ void DoSingleSourceTest(RGBA8 base, const TriangleSpec& triangle, const RGBA8& expected) {
+ dawn::Color blendColor{triangle.blendFactor[0], triangle.blendFactor[1],
+ triangle.blendFactor[2], triangle.blendFactor[3]};
+
+ dawn::CommandEncoder encoder = device.CreateCommandEncoder();
+ {
+ dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass.renderPassInfo);
+ // First use the base pipeline to draw a triangle with no blending
+ pass.SetPipeline(basePipeline);
+ pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({{base}})));
+ pass.Draw(3, 1, 0, 0);
+
+ // Then use the test pipeline to draw the test triangle with blending
+ pass.SetPipeline(testPipeline);
+ pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({{triangle.color}})));
+ pass.SetBlendColor(&blendColor);
+ pass.Draw(3, 1, 0, 0);
+ pass.EndPass();
+ }
+
+ dawn::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ EXPECT_PIXEL_RGBA8_EQ(expected, renderPass.color, kRTSize / 2, kRTSize / 2);
+ }
+
+ // Given a vector of tests where each element is <testColor, expectedColor>, check that all
+ // expectations are true for the given blend operation
+ void CheckBlendOperation(RGBA8 base,
+ dawn::BlendOperation operation,
+ std::vector<std::pair<RGBA8, RGBA8>> tests) {
+ dawn::BlendDescriptor blend;
+ blend.operation = operation;
+ blend.srcFactor = dawn::BlendFactor::One;
+ blend.dstFactor = dawn::BlendFactor::One;
+
+ dawn::ColorStateDescriptor descriptor;
+ descriptor.alphaBlend = blend;
+ descriptor.colorBlend = blend;
+ descriptor.colorWriteMask = dawn::ColorWriteMask::All;
+
+ SetupSingleSourcePipelines(descriptor);
+
+ for (const auto& test : tests) {
+ DoSingleSourceTest(base, {test.first}, test.second);
+ }
+ }
+
+ // Given a vector of tests where each element is <testSpec, expectedColor>, check that all
+ // expectations are true for the given blend factors
+ void CheckBlendFactor(RGBA8 base,
+ dawn::BlendFactor colorSrcFactor,
+ dawn::BlendFactor colorDstFactor,
+ dawn::BlendFactor alphaSrcFactor,
+ dawn::BlendFactor alphaDstFactor,
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests) {
+ dawn::BlendDescriptor colorBlend;
+ colorBlend.operation = dawn::BlendOperation::Add;
+ colorBlend.srcFactor = colorSrcFactor;
+ colorBlend.dstFactor = colorDstFactor;
+
+ dawn::BlendDescriptor alphaBlend;
+ alphaBlend.operation = dawn::BlendOperation::Add;
+ alphaBlend.srcFactor = alphaSrcFactor;
+ alphaBlend.dstFactor = alphaDstFactor;
+
+ dawn::ColorStateDescriptor descriptor;
+ descriptor.colorBlend = colorBlend;
+ descriptor.alphaBlend = alphaBlend;
+ descriptor.colorWriteMask = dawn::ColorWriteMask::All;
+
+ SetupSingleSourcePipelines(descriptor);
+
+ for (const auto& test : tests) {
+ DoSingleSourceTest(base, test.first, test.second);
+ }
+ }
+
+ void CheckSrcBlendFactor(RGBA8 base,
+ dawn::BlendFactor colorFactor,
+ dawn::BlendFactor alphaFactor,
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests) {
+ CheckBlendFactor(base, colorFactor, dawn::BlendFactor::One, alphaFactor,
+ dawn::BlendFactor::One, tests);
+ }
+
+ void CheckDstBlendFactor(RGBA8 base,
+ dawn::BlendFactor colorFactor,
+ dawn::BlendFactor alphaFactor,
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests) {
+ CheckBlendFactor(base, dawn::BlendFactor::One, colorFactor, dawn::BlendFactor::One,
+ alphaFactor, tests);
+ }
+
+ utils::BasicRenderPass renderPass;
+ dawn::RenderPipeline basePipeline;
+ dawn::RenderPipeline testPipeline;
+ dawn::ShaderModule vsModule;
+ dawn::BindGroupLayout bindGroupLayout;
+ dawn::PipelineLayout pipelineLayout;
+};
+
+namespace {
+ // Add two colors and clamp
+ constexpr RGBA8 operator+(const RGBA8& col1, const RGBA8& col2) {
+ int r = static_cast<int>(col1.r) + static_cast<int>(col2.r);
+ int g = static_cast<int>(col1.g) + static_cast<int>(col2.g);
+ int b = static_cast<int>(col1.b) + static_cast<int>(col2.b);
+ int a = static_cast<int>(col1.a) + static_cast<int>(col2.a);
+ r = (r > 255 ? 255 : (r < 0 ? 0 : r));
+ g = (g > 255 ? 255 : (g < 0 ? 0 : g));
+ b = (b > 255 ? 255 : (b < 0 ? 0 : b));
+ a = (a > 255 ? 255 : (a < 0 ? 0 : a));
+
+ return RGBA8(static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b),
+ static_cast<uint8_t>(a));
+ }
+
+ // Subtract two colors and clamp
+ constexpr RGBA8 operator-(const RGBA8& col1, const RGBA8& col2) {
+ int r = static_cast<int>(col1.r) - static_cast<int>(col2.r);
+ int g = static_cast<int>(col1.g) - static_cast<int>(col2.g);
+ int b = static_cast<int>(col1.b) - static_cast<int>(col2.b);
+ int a = static_cast<int>(col1.a) - static_cast<int>(col2.a);
+ r = (r > 255 ? 255 : (r < 0 ? 0 : r));
+ g = (g > 255 ? 255 : (g < 0 ? 0 : g));
+ b = (b > 255 ? 255 : (b < 0 ? 0 : b));
+ a = (a > 255 ? 255 : (a < 0 ? 0 : a));
+
+ return RGBA8(static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b),
+ static_cast<uint8_t>(a));
+ }
+
+ // Get the component-wise minimum of two colors
+ RGBA8 min(const RGBA8& col1, const RGBA8& col2) {
+ return RGBA8(std::min(col1.r, col2.r), std::min(col1.g, col2.g), std::min(col1.b, col2.b),
+ std::min(col1.a, col2.a));
+ }
+
+ // Get the component-wise maximum of two colors
+ RGBA8 max(const RGBA8& col1, const RGBA8& col2) {
+ return RGBA8(std::max(col1.r, col2.r), std::max(col1.g, col2.g), std::max(col1.b, col2.b),
+ std::max(col1.a, col2.a));
+ }
+
+ // Blend two RGBA8 color values parameterized by the provided factors in the range [0.f, 1.f]
+ RGBA8 mix(const RGBA8& col1, const RGBA8& col2, std::array<float, 4> fac) {
+ float r = static_cast<float>(col1.r) * (1.f - fac[0]) + static_cast<float>(col2.r) * fac[0];
+ float g = static_cast<float>(col1.g) * (1.f - fac[1]) + static_cast<float>(col2.g) * fac[1];
+ float b = static_cast<float>(col1.b) * (1.f - fac[2]) + static_cast<float>(col2.b) * fac[2];
+ float a = static_cast<float>(col1.a) * (1.f - fac[3]) + static_cast<float>(col2.a) * fac[3];
+
+ return RGBA8({static_cast<uint8_t>(std::round(r)), static_cast<uint8_t>(std::round(g)),
+ static_cast<uint8_t>(std::round(b)), static_cast<uint8_t>(std::round(a))});
+ }
+
+ // Blend two RGBA8 color values parameterized by the provided RGBA8 factor
+ RGBA8 mix(const RGBA8& col1, const RGBA8& col2, const RGBA8& fac) {
+ std::array<float, 4> f = {{
+ static_cast<float>(fac.r) / 255.f,
+ static_cast<float>(fac.g) / 255.f,
+ static_cast<float>(fac.b) / 255.f,
+ static_cast<float>(fac.a) / 255.f,
+ }};
+ return mix(col1, col2, f);
+ }
+
+ constexpr std::array<RGBA8, 8> kColors = {{
+ // check operations over multiple channels
+ RGBA8(64, 0, 0, 0),
+ RGBA8(0, 64, 0, 0),
+ RGBA8(64, 0, 32, 0),
+ RGBA8(0, 64, 32, 0),
+ RGBA8(128, 0, 128, 128),
+ RGBA8(0, 128, 128, 128),
+
+ // check cases that may cause overflow
+ RGBA8(0, 0, 0, 0),
+ RGBA8(255, 255, 255, 255),
+ }};
+} // namespace
+
+// Test compilation and usage of the fixture
+TEST_P(ColorStateTest, Basic) {
+ dawn::BlendDescriptor blend;
+ blend.operation = dawn::BlendOperation::Add;
+ blend.srcFactor = dawn::BlendFactor::One;
+ blend.dstFactor = dawn::BlendFactor::Zero;
+ dawn::ColorStateDescriptor descriptor;
+ descriptor.alphaBlend = blend;
+ descriptor.colorBlend = blend;
+ descriptor.colorWriteMask = dawn::ColorWriteMask::All;
+
+ SetupSingleSourcePipelines(descriptor);
+
+ DoSingleSourceTest(RGBA8(0, 0, 0, 0), {RGBA8(255, 0, 0, 0)}, RGBA8(255, 0, 0, 0));
+}
+
+// The following tests check test that the blend operation works
+TEST_P(ColorStateTest, BlendOperationAdd) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<RGBA8, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) { return std::make_pair(color, base + color); });
+ CheckBlendOperation(base, dawn::BlendOperation::Add, tests);
+}
+
+TEST_P(ColorStateTest, BlendOperationSubtract) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<RGBA8, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) { return std::make_pair(color, color - base); });
+ CheckBlendOperation(base, dawn::BlendOperation::Subtract, tests);
+}
+
+TEST_P(ColorStateTest, BlendOperationReverseSubtract) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<RGBA8, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) { return std::make_pair(color, base - color); });
+ CheckBlendOperation(base, dawn::BlendOperation::ReverseSubtract, tests);
+}
+
+TEST_P(ColorStateTest, BlendOperationMin) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<RGBA8, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) { return std::make_pair(color, min(base, color)); });
+ CheckBlendOperation(base, dawn::BlendOperation::Min, tests);
+}
+
+TEST_P(ColorStateTest, BlendOperationMax) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<RGBA8, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) { return std::make_pair(color, max(base, color)); });
+ CheckBlendOperation(base, dawn::BlendOperation::Max, tests);
+}
+
+// The following tests check that the Source blend factor works
+TEST_P(ColorStateTest, SrcBlendFactorZero) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(
+ kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) { return std::make_pair(TriangleSpec({{color}}), base); });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::Zero, dawn::BlendFactor::Zero, tests);
+}
+
+TEST_P(ColorStateTest, SrcBlendFactorOne) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(
+ kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) { return std::make_pair(TriangleSpec({{color}}), base + color); });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::One, dawn::BlendFactor::One, tests);
+}
+
+TEST_P(ColorStateTest, SrcBlendFactorSrcColor) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ RGBA8 fac = color;
+ fac.a = 0;
+ RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::SrcColor, dawn::BlendFactor::Zero, tests);
+}
+
+TEST_P(ColorStateTest, SrcBlendFactorOneMinusSrcColor) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ RGBA8 fac = RGBA8(255, 255, 255, 255) - color;
+ fac.a = 0;
+ RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusSrcColor, dawn::BlendFactor::Zero, tests);
+}
+
+TEST_P(ColorStateTest, SrcBlendFactorSrcAlpha) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ RGBA8 fac(color.a, color.a, color.a, color.a);
+ RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::SrcAlpha, dawn::BlendFactor::SrcAlpha, tests);
+}
+
+TEST_P(ColorStateTest, SrcBlendFactorOneMinusSrcAlpha) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(
+ kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
+ RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(color.a, color.a, color.a, color.a);
+ RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusSrcAlpha,
+ dawn::BlendFactor::OneMinusSrcAlpha, tests);
+}
+
+TEST_P(ColorStateTest, SrcBlendFactorDstColor) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ RGBA8 fac = base;
+ fac.a = 0;
+ RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::DstColor, dawn::BlendFactor::Zero, tests);
+}
+
+TEST_P(ColorStateTest, SrcBlendFactorOneMinusDstColor) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ RGBA8 fac = RGBA8(255, 255, 255, 255) - base;
+ fac.a = 0;
+ RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusDstColor, dawn::BlendFactor::Zero, tests);
+}
+
+TEST_P(ColorStateTest, SrcBlendFactorDstAlpha) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ RGBA8 fac(base.a, base.a, base.a, base.a);
+ RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::DstAlpha, dawn::BlendFactor::DstAlpha, tests);
+}
+
+TEST_P(ColorStateTest, SrcBlendFactorOneMinusDstAlpha) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(
+ kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
+ RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(base.a, base.a, base.a, base.a);
+ RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusDstAlpha,
+ dawn::BlendFactor::OneMinusDstAlpha, tests);
+}
+
+TEST_P(ColorStateTest, SrcBlendFactorSrcAlphaSaturated) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ uint8_t f = std::min(color.a, static_cast<uint8_t>(255 - base.a));
+ RGBA8 fac(f, f, f, 255);
+ RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::SrcAlphaSaturated,
+ dawn::BlendFactor::SrcAlphaSaturated, tests);
+}
+
+TEST_P(ColorStateTest, SrcBlendFactorBlendColor) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(
+ kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
+ auto triangleSpec = TriangleSpec({{color}, {{0.2f, 0.4f, 0.6f, 0.8f}}});
+ RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, triangleSpec.blendFactor);
+ return std::make_pair(triangleSpec, expected);
+ });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::BlendColor, dawn::BlendFactor::BlendColor, tests);
+}
+
+TEST_P(ColorStateTest, SrcBlendFactorOneMinusBlendColor) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ auto triangleSpec = TriangleSpec({{color}, {{0.2f, 0.4f, 0.6f, 0.8f}}});
+ std::array<float, 4> f = {{0.8f, 0.6f, 0.4f, 0.2f}};
+ RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, f);
+ return std::make_pair(triangleSpec, expected);
+ });
+ CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusBlendColor,
+ dawn::BlendFactor::OneMinusBlendColor, tests);
+}
+
+// The following tests check that the Destination blend factor works
+TEST_P(ColorStateTest, DstBlendFactorZero) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(
+ kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) { return std::make_pair(TriangleSpec({{color}}), color); });
+ CheckDstBlendFactor(base, dawn::BlendFactor::Zero, dawn::BlendFactor::Zero, tests);
+}
+
+TEST_P(ColorStateTest, DstBlendFactorOne) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(
+ kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) { return std::make_pair(TriangleSpec({{color}}), base + color); });
+ CheckDstBlendFactor(base, dawn::BlendFactor::One, dawn::BlendFactor::One, tests);
+}
+
+TEST_P(ColorStateTest, DstBlendFactorSrcColor) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ RGBA8 fac = color;
+ fac.a = 0;
+ RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckDstBlendFactor(base, dawn::BlendFactor::SrcColor, dawn::BlendFactor::Zero, tests);
+}
+
+TEST_P(ColorStateTest, DstBlendFactorOneMinusSrcColor) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ RGBA8 fac = RGBA8(255, 255, 255, 255) - color;
+ fac.a = 0;
+ RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusSrcColor, dawn::BlendFactor::Zero, tests);
+}
+
+TEST_P(ColorStateTest, DstBlendFactorSrcAlpha) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ RGBA8 fac(color.a, color.a, color.a, color.a);
+ RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckDstBlendFactor(base, dawn::BlendFactor::SrcAlpha, dawn::BlendFactor::SrcAlpha, tests);
+}
+
+TEST_P(ColorStateTest, DstBlendFactorOneMinusSrcAlpha) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(
+ kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
+ RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(color.a, color.a, color.a, color.a);
+ RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusSrcAlpha,
+ dawn::BlendFactor::OneMinusSrcAlpha, tests);
+}
+
+TEST_P(ColorStateTest, DstBlendFactorDstColor) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ RGBA8 fac = base;
+ fac.a = 0;
+ RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckDstBlendFactor(base, dawn::BlendFactor::DstColor, dawn::BlendFactor::Zero, tests);
+}
+
+TEST_P(ColorStateTest, DstBlendFactorOneMinusDstColor) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ RGBA8 fac = RGBA8(255, 255, 255, 255) - base;
+ fac.a = 0;
+ RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusDstColor, dawn::BlendFactor::Zero, tests);
+}
+
+TEST_P(ColorStateTest, DstBlendFactorDstAlpha) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ RGBA8 fac(base.a, base.a, base.a, base.a);
+ RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckDstBlendFactor(base, dawn::BlendFactor::DstAlpha, dawn::BlendFactor::DstAlpha, tests);
+}
+
+TEST_P(ColorStateTest, DstBlendFactorOneMinusDstAlpha) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(
+ kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
+ RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(base.a, base.a, base.a, base.a);
+ RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusDstAlpha,
+ dawn::BlendFactor::OneMinusDstAlpha, tests);
+}
+
+TEST_P(ColorStateTest, DstBlendFactorSrcAlphaSaturated) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ uint8_t f = std::min(color.a, static_cast<uint8_t>(255 - base.a));
+ RGBA8 fac(f, f, f, 255);
+ RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
+ return std::make_pair(TriangleSpec({{color}}), expected);
+ });
+ CheckDstBlendFactor(base, dawn::BlendFactor::SrcAlphaSaturated,
+ dawn::BlendFactor::SrcAlphaSaturated, tests);
+}
+
+TEST_P(ColorStateTest, DstBlendFactorBlendColor) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(
+ kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
+ auto triangleSpec = TriangleSpec({{color}, {{0.2f, 0.4f, 0.6f, 0.8f}}});
+ RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, triangleSpec.blendFactor);
+ return std::make_pair(triangleSpec, expected);
+ });
+ CheckDstBlendFactor(base, dawn::BlendFactor::BlendColor, dawn::BlendFactor::BlendColor, tests);
+}
+
+TEST_P(ColorStateTest, DstBlendFactorOneMinusBlendColor) {
+ RGBA8 base(32, 64, 128, 192);
+ std::vector<std::pair<TriangleSpec, RGBA8>> tests;
+ std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
+ [&](const RGBA8& color) {
+ auto triangleSpec = TriangleSpec({{color}, {{0.2f, 0.4f, 0.6f, 0.8f}}});
+ std::array<float, 4> f = {{0.8f, 0.6f, 0.4f, 0.2f}};
+ RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, f);
+ return std::make_pair(triangleSpec, expected);
+ });
+ CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusBlendColor,
+ dawn::BlendFactor::OneMinusBlendColor, tests);
+}
+
+// Check that the color write mask works
+TEST_P(ColorStateTest, ColorWriteMask) {
+ dawn::BlendDescriptor blend;
+ blend.operation = dawn::BlendOperation::Add;
+ blend.srcFactor = dawn::BlendFactor::One;
+ blend.dstFactor = dawn::BlendFactor::One;
+
+ dawn::ColorStateDescriptor descriptor;
+ descriptor.colorBlend = blend;
+ descriptor.alphaBlend = blend;
+ {
+ // Test single channel color write
+ descriptor.colorWriteMask = dawn::ColorWriteMask::Red;
+ SetupSingleSourcePipelines(descriptor);
+
+ RGBA8 base(32, 64, 128, 192);
+ for (auto& color : kColors) {
+ RGBA8 expected = base + RGBA8(color.r, 0, 0, 0);
+ DoSingleSourceTest(base, {color}, expected);
+ }
+ }
+
+ {
+ // Test multi channel color write
+ descriptor.colorWriteMask = dawn::ColorWriteMask::Green | dawn::ColorWriteMask::Alpha;
+ SetupSingleSourcePipelines(descriptor);
+
+ RGBA8 base(32, 64, 128, 192);
+ for (auto& color : kColors) {
+ RGBA8 expected = base + RGBA8(0, color.g, 0, color.a);
+ DoSingleSourceTest(base, {color}, expected);
+ }
+ }
+
+ {
+ // Test no channel color write
+ descriptor.colorWriteMask = dawn::ColorWriteMask::None;
+ SetupSingleSourcePipelines(descriptor);
+
+ RGBA8 base(32, 64, 128, 192);
+ for (auto& color : kColors) {
+ DoSingleSourceTest(base, {color}, base);
+ }
+ }
+}
+
+// Check that the color write mask works when blending is disabled
+TEST_P(ColorStateTest, ColorWriteMaskBlendingDisabled) {
+ {
+ dawn::BlendDescriptor blend;
+ blend.operation = dawn::BlendOperation::Add;
+ blend.srcFactor = dawn::BlendFactor::One;
+ blend.dstFactor = dawn::BlendFactor::Zero;
+ dawn::ColorStateDescriptor descriptor;
+ descriptor.alphaBlend = blend;
+ descriptor.colorBlend = blend;
+
+ descriptor.colorWriteMask = dawn::ColorWriteMask::Red;
+ SetupSingleSourcePipelines(descriptor);
+
+ RGBA8 base(32, 64, 128, 192);
+ RGBA8 expected(32, 0, 0, 0);
+
+ dawn::CommandEncoder encoder = device.CreateCommandEncoder();
+ {
+ dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass.renderPassInfo);
+ pass.SetPipeline(testPipeline);
+ pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({{base}})));
+ pass.Draw(3, 1, 0, 0);
+ pass.EndPass();
+ }
+
+ dawn::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+ EXPECT_PIXEL_RGBA8_EQ(expected, renderPass.color, kRTSize / 2, kRTSize / 2);
+ }
+}
+
+// Test that independent color states on render targets works
+TEST_P(ColorStateTest, IndependentColorState) {
+ DAWN_SKIP_TEST_IF(IsWindows() && IsVulkan() && IsIntel());
+
+ std::array<dawn::Texture, 4> renderTargets;
+ std::array<dawn::TextureView, 4> renderTargetViews;
+
+ dawn::TextureDescriptor descriptor;
+ descriptor.dimension = dawn::TextureDimension::e2D;
+ descriptor.size.width = kRTSize;
+ descriptor.size.height = kRTSize;
+ descriptor.size.depth = 1;
+ descriptor.arraySize = 1;
+ descriptor.sampleCount = 1;
+ descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm;
+ descriptor.levelCount = 1;
+ descriptor.usage = dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc;
+
+ for (uint32_t i = 0; i < 4; ++i) {
+ renderTargets[i] = device.CreateTexture(&descriptor);
+ renderTargetViews[i] = renderTargets[i].CreateDefaultTextureView();
+ }
+
+ dawn::RenderPassColorAttachmentDescriptor colorAttachments[4];
+ for (uint32_t i = 0; i < 4; ++i) {
+ colorAttachments[i].attachment = renderTargetViews[i];
+ colorAttachments[i].resolveTarget = nullptr;
+ colorAttachments[i].clearColor = {0.0f, 0.0f, 0.0f, 0.0f};
+ colorAttachments[i].loadOp = dawn::LoadOp::Clear;
+ colorAttachments[i].storeOp = dawn::StoreOp::Store;
+ }
+
+ dawn::RenderPassDescriptor renderpass = device.CreateRenderPassDescriptorBuilder()
+ .SetColorAttachments(4, colorAttachments)
+ .GetResult();
+
+ dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
+ #version 450
+ layout(set = 0, binding = 0) uniform myBlock {
+ vec4 color0;
+ vec4 color1;
+ vec4 color2;
+ vec4 color3;
+ } myUbo;
+
+ layout(location = 0) out vec4 fragColor0;
+ layout(location = 1) out vec4 fragColor1;
+ layout(location = 2) out vec4 fragColor2;
+ layout(location = 3) out vec4 fragColor3;
+
+ void main() {
+ fragColor0 = myUbo.color0;
+ fragColor1 = myUbo.color1;
+ fragColor2 = myUbo.color2;
+ fragColor3 = myUbo.color3;
+ }
+ )");
+
+ utils::ComboRenderPipelineDescriptor baseDescriptor(device);
+ baseDescriptor.layout = pipelineLayout;
+ baseDescriptor.cVertexStage.module = vsModule;
+ baseDescriptor.cFragmentStage.module = fsModule;
+ baseDescriptor.numColorStates = 4;
+
+ basePipeline = device.CreateRenderPipeline(&baseDescriptor);
+
+ utils::ComboRenderPipelineDescriptor testDescriptor(device);
+ testDescriptor.layout = pipelineLayout;
+ testDescriptor.cVertexStage.module = vsModule;
+ testDescriptor.cFragmentStage.module = fsModule;
+ testDescriptor.numColorStates = 4;
+
+ // set color states
+ dawn::BlendDescriptor blend1;
+ blend1.operation = dawn::BlendOperation::Add;
+ blend1.srcFactor = dawn::BlendFactor::One;
+ blend1.dstFactor = dawn::BlendFactor::One;
+
+ dawn::BlendDescriptor blend2;
+ blend2.operation = dawn::BlendOperation::Subtract;
+ blend2.srcFactor = dawn::BlendFactor::One;
+ blend2.dstFactor = dawn::BlendFactor::One;
+
+ dawn::BlendDescriptor blend3;
+ blend3.operation = dawn::BlendOperation::Min;
+ blend3.srcFactor = dawn::BlendFactor::One;
+ blend3.dstFactor = dawn::BlendFactor::One;
+
+ testDescriptor.cColorStates[0].colorBlend = blend1;
+ testDescriptor.cColorStates[0].alphaBlend = blend1;
+
+ testDescriptor.cColorStates[1].colorBlend = blend2;
+ testDescriptor.cColorStates[1].alphaBlend = blend2;
+
+ testDescriptor.cColorStates[3].colorBlend = blend3;
+ testDescriptor.cColorStates[3].alphaBlend = blend3;
+
+ testPipeline = device.CreateRenderPipeline(&testDescriptor);
+
+ for (unsigned int c = 0; c < kColors.size(); ++c) {
+ RGBA8 base = kColors[((c + 31) * 29) % kColors.size()];
+ RGBA8 color0 = kColors[((c + 19) * 13) % kColors.size()];
+ RGBA8 color1 = kColors[((c + 11) * 43) % kColors.size()];
+ RGBA8 color2 = kColors[((c + 7) * 3) % kColors.size()];
+ RGBA8 color3 = kColors[((c + 13) * 71) % kColors.size()];
+
+ RGBA8 expected0 = color0 + base;
+ RGBA8 expected1 = color1 - base;
+ RGBA8 expected2 = color2;
+ RGBA8 expected3 = min(color3, base);
+
+ dawn::CommandEncoder encoder = device.CreateCommandEncoder();
+ {
+ dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderpass);
+ pass.SetPipeline(basePipeline);
+ pass.SetBindGroup(
+ 0, MakeBindGroupForColors(std::array<RGBA8, 4>({{base, base, base, base}})));
+ pass.Draw(3, 1, 0, 0);
+
+ pass.SetPipeline(testPipeline);
+ pass.SetBindGroup(0, MakeBindGroupForColors(
+ std::array<RGBA8, 4>({{color0, color1, color2, color3}})));
+ pass.Draw(3, 1, 0, 0);
+ pass.EndPass();
+ }
+
+ dawn::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ EXPECT_PIXEL_RGBA8_EQ(expected0, renderTargets[0], kRTSize / 2, kRTSize / 2)
+ << "Attachment slot 0 should have been " << color0 << " + " << base << " = "
+ << expected0;
+ EXPECT_PIXEL_RGBA8_EQ(expected1, renderTargets[1], kRTSize / 2, kRTSize / 2)
+ << "Attachment slot 1 should have been " << color1 << " - " << base << " = "
+ << expected1;
+ EXPECT_PIXEL_RGBA8_EQ(expected2, renderTargets[2], kRTSize / 2, kRTSize / 2)
+ << "Attachment slot 2 should have been " << color2 << " = " << expected2
+ << "(no blending)";
+ EXPECT_PIXEL_RGBA8_EQ(expected3, renderTargets[3], kRTSize / 2, kRTSize / 2)
+ << "Attachment slot 3 should have been min(" << color3 << ", " << base
+ << ") = " << expected3;
+ }
+}
+
+// Test that the default blend color is correctly set at the beginning of every subpass
+TEST_P(ColorStateTest, DefaultBlendColor) {
+ dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
+ #version 450
+ layout(set = 0, binding = 0) uniform myBlock {
+ vec4 color;
+ } myUbo;
+
+ layout(location = 0) out vec4 fragColor;
+
+ void main() {
+ fragColor = myUbo.color;
+ }
+ )");
+
+ utils::ComboRenderPipelineDescriptor baseDescriptor(device);
+ baseDescriptor.layout = pipelineLayout;
+ baseDescriptor.cVertexStage.module = vsModule;
+ baseDescriptor.cFragmentStage.module = fsModule;
+ baseDescriptor.cColorStates[0].format = renderPass.colorFormat;
+
+ basePipeline = device.CreateRenderPipeline(&baseDescriptor);
+
+ utils::ComboRenderPipelineDescriptor testDescriptor(device);
+ testDescriptor.layout = pipelineLayout;
+ testDescriptor.cVertexStage.module = vsModule;
+ testDescriptor.cFragmentStage.module = fsModule;
+ testDescriptor.cColorStates[0].format = renderPass.colorFormat;
+
+ dawn::BlendDescriptor blend;
+ blend.operation = dawn::BlendOperation::Add;
+ blend.srcFactor = dawn::BlendFactor::BlendColor;
+ blend.dstFactor = dawn::BlendFactor::One;
+ testDescriptor.cColorStates[0].colorBlend = blend;
+ testDescriptor.cColorStates[0].alphaBlend = blend;
+
+ testPipeline = device.CreateRenderPipeline(&testDescriptor);
+ constexpr dawn::Color kWhite{1.0f, 1.0f, 1.0f, 1.0f};
+
+ // Check that the initial blend color is (0,0,0,0)
+ {
+ dawn::CommandEncoder encoder = device.CreateCommandEncoder();
+ {
+ dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass.renderPassInfo);
+ pass.SetPipeline(basePipeline);
+ pass.SetBindGroup(0,
+ MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(0, 0, 0, 0)}})));
+ pass.Draw(3, 1, 0, 0);
+ pass.SetPipeline(testPipeline);
+ pass.SetBindGroup(
+ 0, MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(255, 255, 255, 255)}})));
+ pass.Draw(3, 1, 0, 0);
+ pass.EndPass();
+ }
+
+ dawn::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, kRTSize / 2, kRTSize / 2);
+ }
+
+ // Check that setting the blend color works
+ {
+ dawn::CommandEncoder encoder = device.CreateCommandEncoder();
+ {
+ dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass.renderPassInfo);
+ pass.SetPipeline(basePipeline);
+ pass.SetBindGroup(0,
+ MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(0, 0, 0, 0)}})));
+ pass.Draw(3, 1, 0, 0);
+ pass.SetPipeline(testPipeline);
+ pass.SetBlendColor(&kWhite);
+ pass.SetBindGroup(
+ 0, MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(255, 255, 255, 255)}})));
+ pass.Draw(3, 1, 0, 0);
+ pass.EndPass();
+ }
+
+ dawn::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ EXPECT_PIXEL_RGBA8_EQ(RGBA8(255, 255, 255, 255), renderPass.color, kRTSize / 2,
+ kRTSize / 2);
+ }
+
+ // Check that the blend color is not inherited between render passes
+ {
+ dawn::CommandEncoder encoder = device.CreateCommandEncoder();
+ {
+ dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass.renderPassInfo);
+ pass.SetPipeline(basePipeline);
+ pass.SetBindGroup(0,
+ MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(0, 0, 0, 0)}})));
+ pass.Draw(3, 1, 0, 0);
+ pass.SetPipeline(testPipeline);
+ pass.SetBlendColor(&kWhite);
+ pass.SetBindGroup(
+ 0, MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(255, 255, 255, 255)}})));
+ pass.Draw(3, 1, 0, 0);
+ pass.EndPass();
+ }
+ {
+ dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass.renderPassInfo);
+ pass.SetPipeline(basePipeline);
+ pass.SetBindGroup(0,
+ MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(0, 0, 0, 0)}})));
+ pass.Draw(3, 1, 0, 0);
+ pass.SetPipeline(testPipeline);
+ pass.SetBindGroup(
+ 0, MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(255, 255, 255, 255)}})));
+ pass.Draw(3, 1, 0, 0);
+ pass.EndPass();
+ }
+
+ dawn::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, kRTSize / 2, kRTSize / 2);
+ }
+}
+
+DAWN_INSTANTIATE_TEST(ColorStateTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend)
diff --git a/src/tests/unittests/validation/RenderPipelineValidationTests.cpp b/src/tests/unittests/validation/RenderPipelineValidationTests.cpp
index 550b014..da66698 100644
--- a/src/tests/unittests/validation/RenderPipelineValidationTests.cpp
+++ b/src/tests/unittests/validation/RenderPipelineValidationTests.cpp
@@ -51,8 +51,7 @@
device.CreateRenderPipeline(&descriptor);
}
-TEST_F(RenderPipelineValidationTest, BlendState) {
-
+TEST_F(RenderPipelineValidationTest, ColorState) {
{
// This one succeeds because attachment 0 is the color attachment
utils::ComboRenderPipelineDescriptor descriptor(device);
@@ -72,4 +71,3 @@
ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
}
}
-