1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <algorithm>
16 #include <array>
17 #include <cmath>
18
19 #include "tests/DawnTest.h"
20
21 #include "common/Assert.h"
22 #include "common/Constants.h"
23 #include "utils/ComboRenderPipelineDescriptor.h"
24 #include "utils/WGPUHelpers.h"
25
26 constexpr static unsigned int kRTSize = 64;
27
28 class ColorStateTest : public DawnTest {
29 protected:
SetUp()30 void SetUp() override {
31 DawnTest::SetUp();
32
33 wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
34 device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform}});
35 pipelineLayout = utils::MakePipelineLayout(device, {bindGroupLayout});
36
37 // TODO(crbug.com/dawn/489): D3D12_Microsoft_Basic_Render_Driver_CPU
38 // produces invalid results for these tests.
39 DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsWARP());
40
41 vsModule = utils::CreateShaderModule(device, R"(
42 [[stage(vertex)]]
43 fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
44 var pos = array<vec2<f32>, 3>(
45 vec2<f32>(-1.0, -1.0),
46 vec2<f32>(3.0, -1.0),
47 vec2<f32>(-1.0, 3.0));
48 return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
49 }
50 )");
51
52 renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
53 }
54
55 struct TriangleSpec {
56 RGBA8 color;
57 std::array<float, 4> blendFactor = {};
58 };
59
60 // Set up basePipeline and testPipeline. testPipeline has the given blend state on the first
61 // attachment. basePipeline has no blending
SetupSingleSourcePipelines(wgpu::ColorTargetState colorTargetState)62 void SetupSingleSourcePipelines(wgpu::ColorTargetState colorTargetState) {
63 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
64 [[block]] struct MyBlock {
65 color : vec4<f32>;
66 };
67
68 [[group(0), binding(0)]] var<uniform> myUbo : MyBlock;
69
70 [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
71 return myUbo.color;
72 }
73 )");
74
75 utils::ComboRenderPipelineDescriptor baseDescriptor;
76 baseDescriptor.layout = pipelineLayout;
77 baseDescriptor.vertex.module = vsModule;
78 baseDescriptor.cFragment.module = fsModule;
79 baseDescriptor.cTargets[0].format = renderPass.colorFormat;
80
81 basePipeline = device.CreateRenderPipeline(&baseDescriptor);
82
83 utils::ComboRenderPipelineDescriptor testDescriptor;
84 testDescriptor.layout = pipelineLayout;
85 testDescriptor.vertex.module = vsModule;
86 testDescriptor.cFragment.module = fsModule;
87 testDescriptor.cTargets[0] = colorTargetState;
88 testDescriptor.cTargets[0].format = renderPass.colorFormat;
89
90 testPipeline = device.CreateRenderPipeline(&testDescriptor);
91 }
92
93 // Create a bind group to set the colors as a uniform buffer
94 template <size_t N>
MakeBindGroupForColors(std::array<RGBA8,N> colors)95 wgpu::BindGroup MakeBindGroupForColors(std::array<RGBA8, N> colors) {
96 std::array<float, 4 * N> data;
97 for (unsigned int i = 0; i < N; ++i) {
98 data[4 * i + 0] = static_cast<float>(colors[i].r) / 255.f;
99 data[4 * i + 1] = static_cast<float>(colors[i].g) / 255.f;
100 data[4 * i + 2] = static_cast<float>(colors[i].b) / 255.f;
101 data[4 * i + 3] = static_cast<float>(colors[i].a) / 255.f;
102 }
103
104 uint32_t bufferSize = static_cast<uint32_t>(4 * N * sizeof(float));
105
106 wgpu::Buffer buffer =
107 utils::CreateBufferFromData(device, &data, bufferSize, wgpu::BufferUsage::Uniform);
108 return utils::MakeBindGroup(device, testPipeline.GetBindGroupLayout(0),
109 {{0, buffer, 0, bufferSize}});
110 }
111
112 // Test that after drawing a triangle with the base color, and then the given triangle spec, the
113 // color is as expected
DoSingleSourceTest(RGBA8 base,const TriangleSpec & triangle,const RGBA8 & expected)114 void DoSingleSourceTest(RGBA8 base, const TriangleSpec& triangle, const RGBA8& expected) {
115 wgpu::Color blendConstant{triangle.blendFactor[0], triangle.blendFactor[1],
116 triangle.blendFactor[2], triangle.blendFactor[3]};
117
118 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
119 {
120 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
121 // First use the base pipeline to draw a triangle with no blending
122 pass.SetPipeline(basePipeline);
123 pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({{base}})));
124 pass.Draw(3);
125
126 // Then use the test pipeline to draw the test triangle with blending
127 pass.SetPipeline(testPipeline);
128 pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({{triangle.color}})));
129 pass.SetBlendConstant(&blendConstant);
130 pass.Draw(3);
131 pass.EndPass();
132 }
133
134 wgpu::CommandBuffer commands = encoder.Finish();
135 queue.Submit(1, &commands);
136
137 EXPECT_PIXEL_RGBA8_EQ(expected, renderPass.color, kRTSize / 2, kRTSize / 2);
138 }
139
140 // Given a vector of tests where each element is <testColor, expectedColor>, check that all
141 // expectations are true for the given blend operation
CheckBlendOperation(RGBA8 base,wgpu::BlendOperation operation,std::vector<std::pair<RGBA8,RGBA8>> tests)142 void CheckBlendOperation(RGBA8 base,
143 wgpu::BlendOperation operation,
144 std::vector<std::pair<RGBA8, RGBA8>> tests) {
145 wgpu::BlendComponent blendComponent;
146 blendComponent.operation = operation;
147 blendComponent.srcFactor = wgpu::BlendFactor::One;
148 blendComponent.dstFactor = wgpu::BlendFactor::One;
149
150 wgpu::BlendState blend;
151 blend.color = blendComponent;
152 blend.alpha = blendComponent;
153
154 wgpu::ColorTargetState descriptor;
155 descriptor.blend = &blend;
156 descriptor.writeMask = wgpu::ColorWriteMask::All;
157
158 SetupSingleSourcePipelines(descriptor);
159
160 for (const auto& test : tests) {
161 DoSingleSourceTest(base, {test.first}, test.second);
162 }
163 }
164
165 // Given a vector of tests where each element is <testSpec, expectedColor>, check that all
166 // expectations are true for the given blend factors
CheckBlendFactor(RGBA8 base,wgpu::BlendFactor colorSrcFactor,wgpu::BlendFactor colorDstFactor,wgpu::BlendFactor alphaSrcFactor,wgpu::BlendFactor alphaDstFactor,std::vector<std::pair<TriangleSpec,RGBA8>> tests)167 void CheckBlendFactor(RGBA8 base,
168 wgpu::BlendFactor colorSrcFactor,
169 wgpu::BlendFactor colorDstFactor,
170 wgpu::BlendFactor alphaSrcFactor,
171 wgpu::BlendFactor alphaDstFactor,
172 std::vector<std::pair<TriangleSpec, RGBA8>> tests) {
173 wgpu::BlendComponent colorBlend;
174 colorBlend.operation = wgpu::BlendOperation::Add;
175 colorBlend.srcFactor = colorSrcFactor;
176 colorBlend.dstFactor = colorDstFactor;
177
178 wgpu::BlendComponent alphaBlend;
179 alphaBlend.operation = wgpu::BlendOperation::Add;
180 alphaBlend.srcFactor = alphaSrcFactor;
181 alphaBlend.dstFactor = alphaDstFactor;
182
183 wgpu::BlendState blend;
184 blend.color = colorBlend;
185 blend.alpha = alphaBlend;
186
187 wgpu::ColorTargetState descriptor;
188 descriptor.blend = &blend;
189 descriptor.writeMask = wgpu::ColorWriteMask::All;
190
191 SetupSingleSourcePipelines(descriptor);
192
193 for (const auto& test : tests) {
194 DoSingleSourceTest(base, test.first, test.second);
195 }
196 }
197
CheckSrcBlendFactor(RGBA8 base,wgpu::BlendFactor colorFactor,wgpu::BlendFactor alphaFactor,std::vector<std::pair<TriangleSpec,RGBA8>> tests)198 void CheckSrcBlendFactor(RGBA8 base,
199 wgpu::BlendFactor colorFactor,
200 wgpu::BlendFactor alphaFactor,
201 std::vector<std::pair<TriangleSpec, RGBA8>> tests) {
202 CheckBlendFactor(base, colorFactor, wgpu::BlendFactor::One, alphaFactor,
203 wgpu::BlendFactor::One, tests);
204 }
205
CheckDstBlendFactor(RGBA8 base,wgpu::BlendFactor colorFactor,wgpu::BlendFactor alphaFactor,std::vector<std::pair<TriangleSpec,RGBA8>> tests)206 void CheckDstBlendFactor(RGBA8 base,
207 wgpu::BlendFactor colorFactor,
208 wgpu::BlendFactor alphaFactor,
209 std::vector<std::pair<TriangleSpec, RGBA8>> tests) {
210 CheckBlendFactor(base, wgpu::BlendFactor::One, colorFactor, wgpu::BlendFactor::One,
211 alphaFactor, tests);
212 }
213
214 wgpu::PipelineLayout pipelineLayout;
215 utils::BasicRenderPass renderPass;
216 wgpu::RenderPipeline basePipeline;
217 wgpu::RenderPipeline testPipeline;
218 wgpu::ShaderModule vsModule;
219 };
220
221 namespace {
222 // Add two colors and clamp
operator +(const RGBA8 & col1,const RGBA8 & col2)223 constexpr RGBA8 operator+(const RGBA8& col1, const RGBA8& col2) {
224 int r = static_cast<int>(col1.r) + static_cast<int>(col2.r);
225 int g = static_cast<int>(col1.g) + static_cast<int>(col2.g);
226 int b = static_cast<int>(col1.b) + static_cast<int>(col2.b);
227 int a = static_cast<int>(col1.a) + static_cast<int>(col2.a);
228 r = (r > 255 ? 255 : (r < 0 ? 0 : r));
229 g = (g > 255 ? 255 : (g < 0 ? 0 : g));
230 b = (b > 255 ? 255 : (b < 0 ? 0 : b));
231 a = (a > 255 ? 255 : (a < 0 ? 0 : a));
232
233 return RGBA8(static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b),
234 static_cast<uint8_t>(a));
235 }
236
237 // Subtract two colors and clamp
operator -(const RGBA8 & col1,const RGBA8 & col2)238 constexpr RGBA8 operator-(const RGBA8& col1, const RGBA8& col2) {
239 int r = static_cast<int>(col1.r) - static_cast<int>(col2.r);
240 int g = static_cast<int>(col1.g) - static_cast<int>(col2.g);
241 int b = static_cast<int>(col1.b) - static_cast<int>(col2.b);
242 int a = static_cast<int>(col1.a) - static_cast<int>(col2.a);
243 r = (r > 255 ? 255 : (r < 0 ? 0 : r));
244 g = (g > 255 ? 255 : (g < 0 ? 0 : g));
245 b = (b > 255 ? 255 : (b < 0 ? 0 : b));
246 a = (a > 255 ? 255 : (a < 0 ? 0 : a));
247
248 return RGBA8(static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b),
249 static_cast<uint8_t>(a));
250 }
251
252 // Get the component-wise minimum of two colors
min(const RGBA8 & col1,const RGBA8 & col2)253 RGBA8 min(const RGBA8& col1, const RGBA8& col2) {
254 return RGBA8(std::min(col1.r, col2.r), std::min(col1.g, col2.g), std::min(col1.b, col2.b),
255 std::min(col1.a, col2.a));
256 }
257
258 // Get the component-wise maximum of two colors
max(const RGBA8 & col1,const RGBA8 & col2)259 RGBA8 max(const RGBA8& col1, const RGBA8& col2) {
260 return RGBA8(std::max(col1.r, col2.r), std::max(col1.g, col2.g), std::max(col1.b, col2.b),
261 std::max(col1.a, col2.a));
262 }
263
264 // Blend two RGBA8 color values parameterized by the provided factors in the range [0.f, 1.f]
mix(const RGBA8 & col1,const RGBA8 & col2,std::array<float,4> fac)265 RGBA8 mix(const RGBA8& col1, const RGBA8& col2, std::array<float, 4> fac) {
266 float r = static_cast<float>(col1.r) * (1.f - fac[0]) + static_cast<float>(col2.r) * fac[0];
267 float g = static_cast<float>(col1.g) * (1.f - fac[1]) + static_cast<float>(col2.g) * fac[1];
268 float b = static_cast<float>(col1.b) * (1.f - fac[2]) + static_cast<float>(col2.b) * fac[2];
269 float a = static_cast<float>(col1.a) * (1.f - fac[3]) + static_cast<float>(col2.a) * fac[3];
270
271 return RGBA8({static_cast<uint8_t>(std::round(r)), static_cast<uint8_t>(std::round(g)),
272 static_cast<uint8_t>(std::round(b)), static_cast<uint8_t>(std::round(a))});
273 }
274
275 // Blend two RGBA8 color values parameterized by the provided RGBA8 factor
mix(const RGBA8 & col1,const RGBA8 & col2,const RGBA8 & fac)276 RGBA8 mix(const RGBA8& col1, const RGBA8& col2, const RGBA8& fac) {
277 std::array<float, 4> f = {{
278 static_cast<float>(fac.r) / 255.f,
279 static_cast<float>(fac.g) / 255.f,
280 static_cast<float>(fac.b) / 255.f,
281 static_cast<float>(fac.a) / 255.f,
282 }};
283 return mix(col1, col2, f);
284 }
285
286 constexpr std::array<RGBA8, 8> kColors = {{
287 // check operations over multiple channels
288 RGBA8(64, 0, 0, 0),
289 RGBA8(0, 64, 0, 0),
290 RGBA8(64, 0, 32, 0),
291 RGBA8(0, 64, 32, 0),
292 RGBA8(128, 0, 128, 128),
293 RGBA8(0, 128, 128, 128),
294
295 // check cases that may cause overflow
296 RGBA8(0, 0, 0, 0),
297 RGBA8(255, 255, 255, 255),
298 }};
299 } // namespace
300
301 // Test compilation and usage of the fixture
TEST_P(ColorStateTest,Basic)302 TEST_P(ColorStateTest, Basic) {
303 wgpu::BlendComponent blendComponent;
304 blendComponent.operation = wgpu::BlendOperation::Add;
305 blendComponent.srcFactor = wgpu::BlendFactor::One;
306 blendComponent.dstFactor = wgpu::BlendFactor::Zero;
307
308 wgpu::BlendState blend;
309 blend.color = blendComponent;
310 blend.alpha = blendComponent;
311
312 wgpu::ColorTargetState descriptor;
313 descriptor.blend = &blend;
314 descriptor.writeMask = wgpu::ColorWriteMask::All;
315
316 SetupSingleSourcePipelines(descriptor);
317
318 DoSingleSourceTest(RGBA8(0, 0, 0, 0), {RGBA8(255, 0, 0, 0)}, RGBA8(255, 0, 0, 0));
319 }
320
321 // The following tests check test that the blend operation works
TEST_P(ColorStateTest,BlendOperationAdd)322 TEST_P(ColorStateTest, BlendOperationAdd) {
323 RGBA8 base(32, 64, 128, 192);
324 std::vector<std::pair<RGBA8, RGBA8>> tests;
325 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
326 [&](const RGBA8& color) { return std::make_pair(color, base + color); });
327 CheckBlendOperation(base, wgpu::BlendOperation::Add, tests);
328 }
329
TEST_P(ColorStateTest,BlendOperationSubtract)330 TEST_P(ColorStateTest, BlendOperationSubtract) {
331 RGBA8 base(32, 64, 128, 192);
332 std::vector<std::pair<RGBA8, RGBA8>> tests;
333 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
334 [&](const RGBA8& color) { return std::make_pair(color, color - base); });
335 CheckBlendOperation(base, wgpu::BlendOperation::Subtract, tests);
336 }
337
TEST_P(ColorStateTest,BlendOperationReverseSubtract)338 TEST_P(ColorStateTest, BlendOperationReverseSubtract) {
339 RGBA8 base(32, 64, 128, 192);
340 std::vector<std::pair<RGBA8, RGBA8>> tests;
341 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
342 [&](const RGBA8& color) { return std::make_pair(color, base - color); });
343 CheckBlendOperation(base, wgpu::BlendOperation::ReverseSubtract, tests);
344 }
345
TEST_P(ColorStateTest,BlendOperationMin)346 TEST_P(ColorStateTest, BlendOperationMin) {
347 RGBA8 base(32, 64, 128, 192);
348 std::vector<std::pair<RGBA8, RGBA8>> tests;
349 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
350 [&](const RGBA8& color) { return std::make_pair(color, min(base, color)); });
351 CheckBlendOperation(base, wgpu::BlendOperation::Min, tests);
352 }
353
TEST_P(ColorStateTest,BlendOperationMax)354 TEST_P(ColorStateTest, BlendOperationMax) {
355 RGBA8 base(32, 64, 128, 192);
356 std::vector<std::pair<RGBA8, RGBA8>> tests;
357 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
358 [&](const RGBA8& color) { return std::make_pair(color, max(base, color)); });
359 CheckBlendOperation(base, wgpu::BlendOperation::Max, tests);
360 }
361
362 // The following tests check that the Source blend factor works
TEST_P(ColorStateTest,SrcBlendFactorZero)363 TEST_P(ColorStateTest, SrcBlendFactorZero) {
364 RGBA8 base(32, 64, 128, 192);
365 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
366 std::transform(
367 kColors.begin(), kColors.end(), std::back_inserter(tests),
368 [&](const RGBA8& color) { return std::make_pair(TriangleSpec({{color}}), base); });
369 CheckSrcBlendFactor(base, wgpu::BlendFactor::Zero, wgpu::BlendFactor::Zero, tests);
370 }
371
TEST_P(ColorStateTest,SrcBlendFactorOne)372 TEST_P(ColorStateTest, SrcBlendFactorOne) {
373 RGBA8 base(32, 64, 128, 192);
374 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
375 std::transform(
376 kColors.begin(), kColors.end(), std::back_inserter(tests),
377 [&](const RGBA8& color) { return std::make_pair(TriangleSpec({{color}}), base + color); });
378 CheckSrcBlendFactor(base, wgpu::BlendFactor::One, wgpu::BlendFactor::One, tests);
379 }
380
TEST_P(ColorStateTest,SrcBlendFactorSrc)381 TEST_P(ColorStateTest, SrcBlendFactorSrc) {
382 RGBA8 base(32, 64, 128, 192);
383 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
384 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
385 [&](const RGBA8& color) {
386 RGBA8 fac = color;
387 fac.a = 0;
388 RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
389 return std::make_pair(TriangleSpec({{color}}), expected);
390 });
391 CheckSrcBlendFactor(base, wgpu::BlendFactor::Src, wgpu::BlendFactor::Zero, tests);
392 }
393
TEST_P(ColorStateTest,SrcBlendFactorOneMinusSrc)394 TEST_P(ColorStateTest, SrcBlendFactorOneMinusSrc) {
395 RGBA8 base(32, 64, 128, 192);
396 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
397 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
398 [&](const RGBA8& color) {
399 RGBA8 fac = RGBA8(255, 255, 255, 255) - color;
400 fac.a = 0;
401 RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
402 return std::make_pair(TriangleSpec({{color}}), expected);
403 });
404 CheckSrcBlendFactor(base, wgpu::BlendFactor::OneMinusSrc, wgpu::BlendFactor::Zero, tests);
405 }
406
TEST_P(ColorStateTest,SrcBlendFactorSrcAlpha)407 TEST_P(ColorStateTest, SrcBlendFactorSrcAlpha) {
408 RGBA8 base(32, 64, 128, 192);
409 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
410 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
411 [&](const RGBA8& color) {
412 RGBA8 fac(color.a, color.a, color.a, color.a);
413 RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
414 return std::make_pair(TriangleSpec({{color}}), expected);
415 });
416 CheckSrcBlendFactor(base, wgpu::BlendFactor::SrcAlpha, wgpu::BlendFactor::SrcAlpha, tests);
417 }
418
TEST_P(ColorStateTest,SrcBlendFactorOneMinusSrcAlpha)419 TEST_P(ColorStateTest, SrcBlendFactorOneMinusSrcAlpha) {
420 RGBA8 base(32, 64, 128, 192);
421 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
422 std::transform(
423 kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
424 RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(color.a, color.a, color.a, color.a);
425 RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
426 return std::make_pair(TriangleSpec({{color}}), expected);
427 });
428 CheckSrcBlendFactor(base, wgpu::BlendFactor::OneMinusSrcAlpha,
429 wgpu::BlendFactor::OneMinusSrcAlpha, tests);
430 }
431
TEST_P(ColorStateTest,SrcBlendFactorDst)432 TEST_P(ColorStateTest, SrcBlendFactorDst) {
433 RGBA8 base(32, 64, 128, 192);
434 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
435 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
436 [&](const RGBA8& color) {
437 RGBA8 fac = base;
438 fac.a = 0;
439 RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
440 return std::make_pair(TriangleSpec({{color}}), expected);
441 });
442 CheckSrcBlendFactor(base, wgpu::BlendFactor::Dst, wgpu::BlendFactor::Zero, tests);
443 }
444
TEST_P(ColorStateTest,SrcBlendFactorOneMinusDst)445 TEST_P(ColorStateTest, SrcBlendFactorOneMinusDst) {
446 RGBA8 base(32, 64, 128, 192);
447 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
448 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
449 [&](const RGBA8& color) {
450 RGBA8 fac = RGBA8(255, 255, 255, 255) - base;
451 fac.a = 0;
452 RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
453 return std::make_pair(TriangleSpec({{color}}), expected);
454 });
455 CheckSrcBlendFactor(base, wgpu::BlendFactor::OneMinusDst, wgpu::BlendFactor::Zero, tests);
456 }
457
TEST_P(ColorStateTest,SrcBlendFactorDstAlpha)458 TEST_P(ColorStateTest, SrcBlendFactorDstAlpha) {
459 RGBA8 base(32, 64, 128, 192);
460 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
461 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
462 [&](const RGBA8& color) {
463 RGBA8 fac(base.a, base.a, base.a, base.a);
464 RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
465 return std::make_pair(TriangleSpec({{color}}), expected);
466 });
467 CheckSrcBlendFactor(base, wgpu::BlendFactor::DstAlpha, wgpu::BlendFactor::DstAlpha, tests);
468 }
469
TEST_P(ColorStateTest,SrcBlendFactorOneMinusDstAlpha)470 TEST_P(ColorStateTest, SrcBlendFactorOneMinusDstAlpha) {
471 RGBA8 base(32, 64, 128, 192);
472 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
473 std::transform(
474 kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
475 RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(base.a, base.a, base.a, base.a);
476 RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
477 return std::make_pair(TriangleSpec({{color}}), expected);
478 });
479 CheckSrcBlendFactor(base, wgpu::BlendFactor::OneMinusDstAlpha,
480 wgpu::BlendFactor::OneMinusDstAlpha, tests);
481 }
482
TEST_P(ColorStateTest,SrcBlendFactorSrcAlphaSaturated)483 TEST_P(ColorStateTest, SrcBlendFactorSrcAlphaSaturated) {
484 RGBA8 base(32, 64, 128, 192);
485 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
486 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
487 [&](const RGBA8& color) {
488 uint8_t f = std::min(color.a, static_cast<uint8_t>(255 - base.a));
489 RGBA8 fac(f, f, f, 255);
490 RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);
491 return std::make_pair(TriangleSpec({{color}}), expected);
492 });
493 CheckSrcBlendFactor(base, wgpu::BlendFactor::SrcAlphaSaturated,
494 wgpu::BlendFactor::SrcAlphaSaturated, tests);
495 }
496
TEST_P(ColorStateTest,SrcBlendFactorConstant)497 TEST_P(ColorStateTest, SrcBlendFactorConstant) {
498 RGBA8 base(32, 64, 128, 192);
499 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
500 std::transform(
501 kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
502 auto triangleSpec = TriangleSpec({{color}, {{0.2f, 0.4f, 0.6f, 0.8f}}});
503 RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, triangleSpec.blendFactor);
504 return std::make_pair(triangleSpec, expected);
505 });
506 CheckSrcBlendFactor(base, wgpu::BlendFactor::Constant, wgpu::BlendFactor::Constant, tests);
507 }
508
TEST_P(ColorStateTest,SrcBlendFactorOneMinusConstant)509 TEST_P(ColorStateTest, SrcBlendFactorOneMinusConstant) {
510 RGBA8 base(32, 64, 128, 192);
511 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
512 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
513 [&](const RGBA8& color) {
514 auto triangleSpec = TriangleSpec({{color}, {{0.2f, 0.4f, 0.6f, 0.8f}}});
515 std::array<float, 4> f = {{0.8f, 0.6f, 0.4f, 0.2f}};
516 RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, f);
517 return std::make_pair(triangleSpec, expected);
518 });
519 CheckSrcBlendFactor(base, wgpu::BlendFactor::OneMinusConstant,
520 wgpu::BlendFactor::OneMinusConstant, tests);
521 }
522
523 // The following tests check that the Destination blend factor works
TEST_P(ColorStateTest,DstBlendFactorZero)524 TEST_P(ColorStateTest, DstBlendFactorZero) {
525 RGBA8 base(32, 64, 128, 192);
526 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
527 std::transform(
528 kColors.begin(), kColors.end(), std::back_inserter(tests),
529 [&](const RGBA8& color) { return std::make_pair(TriangleSpec({{color}}), color); });
530 CheckDstBlendFactor(base, wgpu::BlendFactor::Zero, wgpu::BlendFactor::Zero, tests);
531 }
532
TEST_P(ColorStateTest,DstBlendFactorOne)533 TEST_P(ColorStateTest, DstBlendFactorOne) {
534 RGBA8 base(32, 64, 128, 192);
535 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
536 std::transform(
537 kColors.begin(), kColors.end(), std::back_inserter(tests),
538 [&](const RGBA8& color) { return std::make_pair(TriangleSpec({{color}}), base + color); });
539 CheckDstBlendFactor(base, wgpu::BlendFactor::One, wgpu::BlendFactor::One, tests);
540 }
541
TEST_P(ColorStateTest,DstBlendFactorSrc)542 TEST_P(ColorStateTest, DstBlendFactorSrc) {
543 RGBA8 base(32, 64, 128, 192);
544 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
545 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
546 [&](const RGBA8& color) {
547 RGBA8 fac = color;
548 fac.a = 0;
549 RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
550 return std::make_pair(TriangleSpec({{color}}), expected);
551 });
552 CheckDstBlendFactor(base, wgpu::BlendFactor::Src, wgpu::BlendFactor::Zero, tests);
553 }
554
TEST_P(ColorStateTest,DstBlendFactorOneMinusSrc)555 TEST_P(ColorStateTest, DstBlendFactorOneMinusSrc) {
556 RGBA8 base(32, 64, 128, 192);
557 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
558 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
559 [&](const RGBA8& color) {
560 RGBA8 fac = RGBA8(255, 255, 255, 255) - color;
561 fac.a = 0;
562 RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
563 return std::make_pair(TriangleSpec({{color}}), expected);
564 });
565 CheckDstBlendFactor(base, wgpu::BlendFactor::OneMinusSrc, wgpu::BlendFactor::Zero, tests);
566 }
567
TEST_P(ColorStateTest,DstBlendFactorSrcAlpha)568 TEST_P(ColorStateTest, DstBlendFactorSrcAlpha) {
569 RGBA8 base(32, 64, 128, 192);
570 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
571 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
572 [&](const RGBA8& color) {
573 RGBA8 fac(color.a, color.a, color.a, color.a);
574 RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
575 return std::make_pair(TriangleSpec({{color}}), expected);
576 });
577 CheckDstBlendFactor(base, wgpu::BlendFactor::SrcAlpha, wgpu::BlendFactor::SrcAlpha, tests);
578 }
579
TEST_P(ColorStateTest,DstBlendFactorOneMinusSrcAlpha)580 TEST_P(ColorStateTest, DstBlendFactorOneMinusSrcAlpha) {
581 RGBA8 base(32, 64, 128, 192);
582 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
583 std::transform(
584 kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
585 RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(color.a, color.a, color.a, color.a);
586 RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
587 return std::make_pair(TriangleSpec({{color}}), expected);
588 });
589 CheckDstBlendFactor(base, wgpu::BlendFactor::OneMinusSrcAlpha,
590 wgpu::BlendFactor::OneMinusSrcAlpha, tests);
591 }
592
TEST_P(ColorStateTest,DstBlendFactorDst)593 TEST_P(ColorStateTest, DstBlendFactorDst) {
594 RGBA8 base(32, 64, 128, 192);
595 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
596 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
597 [&](const RGBA8& color) {
598 RGBA8 fac = base;
599 fac.a = 0;
600 RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
601 return std::make_pair(TriangleSpec({{color}}), expected);
602 });
603 CheckDstBlendFactor(base, wgpu::BlendFactor::Dst, wgpu::BlendFactor::Zero, tests);
604 }
605
TEST_P(ColorStateTest,DstBlendFactorOneMinusDst)606 TEST_P(ColorStateTest, DstBlendFactorOneMinusDst) {
607 RGBA8 base(32, 64, 128, 192);
608 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
609 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
610 [&](const RGBA8& color) {
611 RGBA8 fac = RGBA8(255, 255, 255, 255) - base;
612 fac.a = 0;
613 RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
614 return std::make_pair(TriangleSpec({{color}}), expected);
615 });
616 CheckDstBlendFactor(base, wgpu::BlendFactor::OneMinusDst, wgpu::BlendFactor::Zero, tests);
617 }
618
TEST_P(ColorStateTest,DstBlendFactorDstAlpha)619 TEST_P(ColorStateTest, DstBlendFactorDstAlpha) {
620 RGBA8 base(32, 64, 128, 192);
621 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
622 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
623 [&](const RGBA8& color) {
624 RGBA8 fac(base.a, base.a, base.a, base.a);
625 RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
626 return std::make_pair(TriangleSpec({{color}}), expected);
627 });
628 CheckDstBlendFactor(base, wgpu::BlendFactor::DstAlpha, wgpu::BlendFactor::DstAlpha, tests);
629 }
630
TEST_P(ColorStateTest,DstBlendFactorOneMinusDstAlpha)631 TEST_P(ColorStateTest, DstBlendFactorOneMinusDstAlpha) {
632 RGBA8 base(32, 64, 128, 192);
633 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
634 std::transform(
635 kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
636 RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(base.a, base.a, base.a, base.a);
637 RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
638 return std::make_pair(TriangleSpec({{color}}), expected);
639 });
640 CheckDstBlendFactor(base, wgpu::BlendFactor::OneMinusDstAlpha,
641 wgpu::BlendFactor::OneMinusDstAlpha, tests);
642 }
643
TEST_P(ColorStateTest,DstBlendFactorSrcAlphaSaturated)644 TEST_P(ColorStateTest, DstBlendFactorSrcAlphaSaturated) {
645 RGBA8 base(32, 64, 128, 192);
646 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
647 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
648 [&](const RGBA8& color) {
649 uint8_t f = std::min(color.a, static_cast<uint8_t>(255 - base.a));
650 RGBA8 fac(f, f, f, 255);
651 RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);
652 return std::make_pair(TriangleSpec({{color}}), expected);
653 });
654 CheckDstBlendFactor(base, wgpu::BlendFactor::SrcAlphaSaturated,
655 wgpu::BlendFactor::SrcAlphaSaturated, tests);
656 }
657
TEST_P(ColorStateTest,DstBlendFactorConstant)658 TEST_P(ColorStateTest, DstBlendFactorConstant) {
659 RGBA8 base(32, 64, 128, 192);
660 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
661 std::transform(
662 kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {
663 auto triangleSpec = TriangleSpec({{color}, {{0.2f, 0.4f, 0.6f, 0.8f}}});
664 RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, triangleSpec.blendFactor);
665 return std::make_pair(triangleSpec, expected);
666 });
667 CheckDstBlendFactor(base, wgpu::BlendFactor::Constant, wgpu::BlendFactor::Constant, tests);
668 }
669
TEST_P(ColorStateTest,DstBlendFactorOneMinusConstant)670 TEST_P(ColorStateTest, DstBlendFactorOneMinusConstant) {
671 RGBA8 base(32, 64, 128, 192);
672 std::vector<std::pair<TriangleSpec, RGBA8>> tests;
673 std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests),
674 [&](const RGBA8& color) {
675 auto triangleSpec = TriangleSpec({{color}, {{0.2f, 0.4f, 0.6f, 0.8f}}});
676 std::array<float, 4> f = {{0.8f, 0.6f, 0.4f, 0.2f}};
677 RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, f);
678 return std::make_pair(triangleSpec, expected);
679 });
680 CheckDstBlendFactor(base, wgpu::BlendFactor::OneMinusConstant,
681 wgpu::BlendFactor::OneMinusConstant, tests);
682 }
683
684 // Check that the color write mask works
TEST_P(ColorStateTest,ColorWriteMask)685 TEST_P(ColorStateTest, ColorWriteMask) {
686 wgpu::BlendComponent blendComponent;
687 blendComponent.operation = wgpu::BlendOperation::Add;
688 blendComponent.srcFactor = wgpu::BlendFactor::One;
689 blendComponent.dstFactor = wgpu::BlendFactor::One;
690
691 wgpu::BlendState blend;
692 blend.color = blendComponent;
693 blend.alpha = blendComponent;
694
695 wgpu::ColorTargetState descriptor;
696 descriptor.blend = &blend;
697 {
698 // Test single channel color write
699 descriptor.writeMask = wgpu::ColorWriteMask::Red;
700 SetupSingleSourcePipelines(descriptor);
701
702 RGBA8 base(32, 64, 128, 192);
703 for (auto& color : kColors) {
704 RGBA8 expected = base + RGBA8(color.r, 0, 0, 0);
705 DoSingleSourceTest(base, {color}, expected);
706 }
707 }
708
709 {
710 // Test multi channel color write
711 descriptor.writeMask = wgpu::ColorWriteMask::Green | wgpu::ColorWriteMask::Alpha;
712 SetupSingleSourcePipelines(descriptor);
713
714 RGBA8 base(32, 64, 128, 192);
715 for (auto& color : kColors) {
716 RGBA8 expected = base + RGBA8(0, color.g, 0, color.a);
717 DoSingleSourceTest(base, {color}, expected);
718 }
719 }
720
721 {
722 // Test no channel color write
723 descriptor.writeMask = wgpu::ColorWriteMask::None;
724 SetupSingleSourcePipelines(descriptor);
725
726 RGBA8 base(32, 64, 128, 192);
727 for (auto& color : kColors) {
728 DoSingleSourceTest(base, {color}, base);
729 }
730 }
731 }
732
733 // Check that the color write mask works when blending is disabled
TEST_P(ColorStateTest,ColorWriteMaskBlendingDisabled)734 TEST_P(ColorStateTest, ColorWriteMaskBlendingDisabled) {
735 {
736 wgpu::BlendComponent blendComponent;
737 blendComponent.operation = wgpu::BlendOperation::Add;
738 blendComponent.srcFactor = wgpu::BlendFactor::One;
739 blendComponent.dstFactor = wgpu::BlendFactor::Zero;
740
741 wgpu::BlendState blend;
742 blend.color = blendComponent;
743 blend.alpha = blendComponent;
744
745 wgpu::ColorTargetState descriptor;
746 descriptor.blend = &blend;
747 descriptor.writeMask = wgpu::ColorWriteMask::Red;
748 SetupSingleSourcePipelines(descriptor);
749
750 RGBA8 base(32, 64, 128, 192);
751 RGBA8 expected(32, 0, 0, 0);
752
753 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
754 {
755 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
756 pass.SetPipeline(testPipeline);
757 pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({{base}})));
758 pass.Draw(3);
759 pass.EndPass();
760 }
761
762 wgpu::CommandBuffer commands = encoder.Finish();
763 queue.Submit(1, &commands);
764 EXPECT_PIXEL_RGBA8_EQ(expected, renderPass.color, kRTSize / 2, kRTSize / 2);
765 }
766 }
767
768 // Test that independent color states on render targets works
TEST_P(ColorStateTest,IndependentColorState)769 TEST_P(ColorStateTest, IndependentColorState) {
770 DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_indexed_draw_buffers"));
771
772 std::array<wgpu::Texture, 4> renderTargets;
773 std::array<wgpu::TextureView, 4> renderTargetViews;
774
775 wgpu::TextureDescriptor descriptor;
776 descriptor.dimension = wgpu::TextureDimension::e2D;
777 descriptor.size.width = kRTSize;
778 descriptor.size.height = kRTSize;
779 descriptor.size.depthOrArrayLayers = 1;
780 descriptor.sampleCount = 1;
781 descriptor.format = wgpu::TextureFormat::RGBA8Unorm;
782 descriptor.mipLevelCount = 1;
783 descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
784
785 for (uint32_t i = 0; i < 4; ++i) {
786 renderTargets[i] = device.CreateTexture(&descriptor);
787 renderTargetViews[i] = renderTargets[i].CreateView();
788 }
789
790 utils::ComboRenderPassDescriptor renderPass(
791 {renderTargetViews[0], renderTargetViews[1], renderTargetViews[2], renderTargetViews[3]});
792
793 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
794 [[block]] struct MyBlock {
795 color0 : vec4<f32>;
796 color1 : vec4<f32>;
797 color2 : vec4<f32>;
798 color3 : vec4<f32>;
799 };
800
801 [[group(0), binding(0)]] var<uniform> myUbo : MyBlock;
802
803 struct FragmentOut {
804 [[location(0)]] fragColor0 : vec4<f32>;
805 [[location(1)]] fragColor1 : vec4<f32>;
806 [[location(2)]] fragColor2 : vec4<f32>;
807 [[location(3)]] fragColor3 : vec4<f32>;
808 };
809
810 [[stage(fragment)]] fn main() -> FragmentOut {
811 var output : FragmentOut;
812 output.fragColor0 = myUbo.color0;
813 output.fragColor1 = myUbo.color1;
814 output.fragColor2 = myUbo.color2;
815 output.fragColor3 = myUbo.color3;
816 return output;
817 }
818 )");
819
820 utils::ComboRenderPipelineDescriptor baseDescriptor;
821 baseDescriptor.layout = pipelineLayout;
822 baseDescriptor.vertex.module = vsModule;
823 baseDescriptor.cFragment.module = fsModule;
824 baseDescriptor.cFragment.targetCount = 4;
825
826 basePipeline = device.CreateRenderPipeline(&baseDescriptor);
827
828 utils::ComboRenderPipelineDescriptor testDescriptor;
829 testDescriptor.layout = pipelineLayout;
830 testDescriptor.vertex.module = vsModule;
831 testDescriptor.cFragment.module = fsModule;
832 testDescriptor.cFragment.targetCount = 4;
833
834 // set color states
835 wgpu::BlendComponent blendComponent0;
836 blendComponent0.operation = wgpu::BlendOperation::Add;
837 blendComponent0.srcFactor = wgpu::BlendFactor::One;
838 blendComponent0.dstFactor = wgpu::BlendFactor::One;
839
840 wgpu::BlendState blend0;
841 blend0.color = blendComponent0;
842 blend0.alpha = blendComponent0;
843
844 wgpu::BlendComponent blendComponent1;
845 blendComponent1.operation = wgpu::BlendOperation::Subtract;
846 blendComponent1.srcFactor = wgpu::BlendFactor::One;
847 blendComponent1.dstFactor = wgpu::BlendFactor::One;
848
849 wgpu::BlendState blend1;
850 blend1.color = blendComponent1;
851 blend1.alpha = blendComponent1;
852
853 // Blend state intentionally omitted for target 2
854
855 wgpu::BlendComponent blendComponent3;
856 blendComponent3.operation = wgpu::BlendOperation::Min;
857 blendComponent3.srcFactor = wgpu::BlendFactor::One;
858 blendComponent3.dstFactor = wgpu::BlendFactor::One;
859
860 wgpu::BlendState blend3;
861 blend3.color = blendComponent3;
862 blend3.alpha = blendComponent3;
863
864 testDescriptor.cTargets[0].blend = &blend0;
865 testDescriptor.cTargets[1].blend = &blend1;
866 testDescriptor.cTargets[3].blend = &blend3;
867
868 testPipeline = device.CreateRenderPipeline(&testDescriptor);
869
870 for (unsigned int c = 0; c < kColors.size(); ++c) {
871 RGBA8 base = kColors[((c + 31) * 29) % kColors.size()];
872 RGBA8 color0 = kColors[((c + 19) * 13) % kColors.size()];
873 RGBA8 color1 = kColors[((c + 11) * 43) % kColors.size()];
874 RGBA8 color2 = kColors[((c + 7) * 3) % kColors.size()];
875 RGBA8 color3 = kColors[((c + 13) * 71) % kColors.size()];
876
877 RGBA8 expected0 = color0 + base;
878 RGBA8 expected1 = color1 - base;
879 RGBA8 expected2 = color2;
880 RGBA8 expected3 = min(color3, base);
881
882 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
883 {
884 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
885 pass.SetPipeline(basePipeline);
886 pass.SetBindGroup(
887 0, MakeBindGroupForColors(std::array<RGBA8, 4>({{base, base, base, base}})));
888 pass.Draw(3);
889
890 pass.SetPipeline(testPipeline);
891 pass.SetBindGroup(0, MakeBindGroupForColors(
892 std::array<RGBA8, 4>({{color0, color1, color2, color3}})));
893 pass.Draw(3);
894 pass.EndPass();
895 }
896
897 wgpu::CommandBuffer commands = encoder.Finish();
898 queue.Submit(1, &commands);
899
900 EXPECT_PIXEL_RGBA8_EQ(expected0, renderTargets[0], kRTSize / 2, kRTSize / 2)
901 << "Attachment slot 0 should have been " << color0 << " + " << base << " = "
902 << expected0;
903 EXPECT_PIXEL_RGBA8_EQ(expected1, renderTargets[1], kRTSize / 2, kRTSize / 2)
904 << "Attachment slot 1 should have been " << color1 << " - " << base << " = "
905 << expected1;
906 EXPECT_PIXEL_RGBA8_EQ(expected2, renderTargets[2], kRTSize / 2, kRTSize / 2)
907 << "Attachment slot 2 should have been " << color2 << " = " << expected2
908 << "(no blending)";
909 EXPECT_PIXEL_RGBA8_EQ(expected3, renderTargets[3], kRTSize / 2, kRTSize / 2)
910 << "Attachment slot 3 should have been min(" << color3 << ", " << base
911 << ") = " << expected3;
912 }
913 }
914
915 // Test that the default blend color is correctly set at the beginning of every subpass
TEST_P(ColorStateTest,DefaultBlendColor)916 TEST_P(ColorStateTest, DefaultBlendColor) {
917 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
918 [[block]] struct MyBlock {
919 color : vec4<f32>;
920 };
921
922 [[group(0), binding(0)]] var<uniform> myUbo : MyBlock;
923
924 [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
925 return myUbo.color;
926 }
927 )");
928
929 utils::ComboRenderPipelineDescriptor baseDescriptor;
930 baseDescriptor.layout = pipelineLayout;
931 baseDescriptor.vertex.module = vsModule;
932 baseDescriptor.cFragment.module = fsModule;
933 baseDescriptor.cTargets[0].format = renderPass.colorFormat;
934
935 basePipeline = device.CreateRenderPipeline(&baseDescriptor);
936
937 utils::ComboRenderPipelineDescriptor testDescriptor;
938 testDescriptor.layout = pipelineLayout;
939 testDescriptor.vertex.module = vsModule;
940 testDescriptor.cFragment.module = fsModule;
941 testDescriptor.cTargets[0].format = renderPass.colorFormat;
942
943 wgpu::BlendComponent blendComponent;
944 blendComponent.operation = wgpu::BlendOperation::Add;
945 blendComponent.srcFactor = wgpu::BlendFactor::Constant;
946 blendComponent.dstFactor = wgpu::BlendFactor::One;
947
948 wgpu::BlendState blend;
949 blend.color = blendComponent;
950 blend.alpha = blendComponent;
951
952 testDescriptor.cTargets[0].blend = &blend;
953
954 testPipeline = device.CreateRenderPipeline(&testDescriptor);
955 constexpr wgpu::Color kWhite{1.0f, 1.0f, 1.0f, 1.0f};
956
957 // Check that the initial blend color is (0,0,0,0)
958 {
959 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
960 {
961 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
962 pass.SetPipeline(basePipeline);
963 pass.SetBindGroup(0,
964 MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(0, 0, 0, 0)}})));
965 pass.Draw(3);
966 pass.SetPipeline(testPipeline);
967 pass.SetBindGroup(
968 0, MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(255, 255, 255, 255)}})));
969 pass.Draw(3);
970 pass.EndPass();
971 }
972
973 wgpu::CommandBuffer commands = encoder.Finish();
974 queue.Submit(1, &commands);
975
976 EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, kRTSize / 2, kRTSize / 2);
977 }
978
979 // Check that setting the blend color works
980 {
981 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
982 {
983 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
984 pass.SetPipeline(basePipeline);
985 pass.SetBindGroup(0,
986 MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(0, 0, 0, 0)}})));
987 pass.Draw(3);
988 pass.SetPipeline(testPipeline);
989 pass.SetBlendConstant(&kWhite);
990 pass.SetBindGroup(
991 0, MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(255, 255, 255, 255)}})));
992 pass.Draw(3);
993 pass.EndPass();
994 }
995
996 wgpu::CommandBuffer commands = encoder.Finish();
997 queue.Submit(1, &commands);
998
999 EXPECT_PIXEL_RGBA8_EQ(RGBA8(255, 255, 255, 255), renderPass.color, kRTSize / 2,
1000 kRTSize / 2);
1001 }
1002
1003 // Check that the blend color is not inherited between render passes
1004 {
1005 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1006 {
1007 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
1008 pass.SetPipeline(basePipeline);
1009 pass.SetBindGroup(0,
1010 MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(0, 0, 0, 0)}})));
1011 pass.Draw(3);
1012 pass.SetPipeline(testPipeline);
1013 pass.SetBlendConstant(&kWhite);
1014 pass.SetBindGroup(
1015 0, MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(255, 255, 255, 255)}})));
1016 pass.Draw(3);
1017 pass.EndPass();
1018 }
1019 {
1020 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
1021 pass.SetPipeline(basePipeline);
1022 pass.SetBindGroup(0,
1023 MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(0, 0, 0, 0)}})));
1024 pass.Draw(3);
1025 pass.SetPipeline(testPipeline);
1026 pass.SetBindGroup(
1027 0, MakeBindGroupForColors(std::array<RGBA8, 1>({{RGBA8(255, 255, 255, 255)}})));
1028 pass.Draw(3);
1029 pass.EndPass();
1030 }
1031
1032 wgpu::CommandBuffer commands = encoder.Finish();
1033 queue.Submit(1, &commands);
1034
1035 EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, kRTSize / 2, kRTSize / 2);
1036 }
1037 }
1038
1039 // This tests a problem in the OpenGL backend where a previous color write mask
1040 // persisted and prevented a render pass loadOp from fully clearing the output
1041 // attachment.
TEST_P(ColorStateTest,ColorWriteMaskDoesNotAffectRenderPassLoadOpClear)1042 TEST_P(ColorStateTest, ColorWriteMaskDoesNotAffectRenderPassLoadOpClear) {
1043 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
1044 [[block]] struct MyBlock {
1045 color : vec4<f32>;
1046 };
1047
1048 [[group(0), binding(0)]] var<uniform> myUbo : MyBlock;
1049
1050 [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
1051 return myUbo.color;
1052 }
1053 )");
1054
1055 utils::ComboRenderPipelineDescriptor baseDescriptor;
1056 baseDescriptor.layout = pipelineLayout;
1057 baseDescriptor.vertex.module = vsModule;
1058 baseDescriptor.cFragment.module = fsModule;
1059 baseDescriptor.cTargets[0].format = renderPass.colorFormat;
1060
1061 basePipeline = device.CreateRenderPipeline(&baseDescriptor);
1062
1063 utils::ComboRenderPipelineDescriptor testDescriptor;
1064 testDescriptor.layout = pipelineLayout;
1065 testDescriptor.vertex.module = vsModule;
1066 testDescriptor.cFragment.module = fsModule;
1067 testDescriptor.cTargets[0].format = renderPass.colorFormat;
1068 testDescriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::Red;
1069
1070 testPipeline = device.CreateRenderPipeline(&testDescriptor);
1071
1072 RGBA8 base(32, 64, 128, 192);
1073 RGBA8 expected(0, 0, 0, 0);
1074
1075 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1076 {
1077 // Clear the render attachment to |base|
1078 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
1079 pass.SetPipeline(basePipeline);
1080 pass.SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({{base}})));
1081 pass.Draw(3);
1082
1083 // Set a pipeline that will dirty the color write mask
1084 pass.SetPipeline(testPipeline);
1085 pass.EndPass();
1086 }
1087 {
1088 // This renderpass' loadOp should clear all channels of the render attachment
1089 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
1090 pass.EndPass();
1091 }
1092 wgpu::CommandBuffer commands = encoder.Finish();
1093 queue.Submit(1, &commands);
1094
1095 EXPECT_PIXEL_RGBA8_EQ(expected, renderPass.color, kRTSize / 2, kRTSize / 2);
1096 }
1097
1098 DAWN_INSTANTIATE_TEST(ColorStateTest,
1099 D3D12Backend(),
1100 MetalBackend(),
1101 OpenGLBackend(),
1102 OpenGLESBackend(),
1103 VulkanBackend());
1104