• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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 "tests/DawnTest.h"
16 
17 #include "common/Assert.h"
18 #include "utils/ComboRenderPipelineDescriptor.h"
19 #include "utils/WGPUHelpers.h"
20 
21 class MultisampledRenderingTest : public DawnTest {
22   protected:
SetUp()23     void SetUp() override {
24         DawnTest::SetUp();
25 
26         // TODO(crbug.com/dawn/738): Test output is wrong with D3D12 + WARP.
27         DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsWARP());
28 
29         InitTexturesForTest();
30     }
31 
InitTexturesForTest()32     void InitTexturesForTest() {
33         mMultisampledColorTexture = CreateTextureForRenderAttachment(kColorFormat, kSampleCount);
34         mMultisampledColorView = mMultisampledColorTexture.CreateView();
35         mResolveTexture = CreateTextureForRenderAttachment(kColorFormat, 1);
36         mResolveView = mResolveTexture.CreateView();
37 
38         mDepthStencilTexture = CreateTextureForRenderAttachment(kDepthStencilFormat, kSampleCount);
39         mDepthStencilView = mDepthStencilTexture.CreateView();
40     }
41 
CreateRenderPipelineWithOneOutputForTest(bool testDepth,uint32_t sampleMask=0xFFFFFFFF,bool alphaToCoverageEnabled=false,bool flipTriangle=false)42     wgpu::RenderPipeline CreateRenderPipelineWithOneOutputForTest(
43         bool testDepth,
44         uint32_t sampleMask = 0xFFFFFFFF,
45         bool alphaToCoverageEnabled = false,
46         bool flipTriangle = false) {
47         const char* kFsOneOutputWithDepth = R"(
48             [[block]] struct U {
49                 color : vec4<f32>;
50                 depth : f32;
51             };
52             [[group(0), binding(0)]] var<uniform> uBuffer : U;
53 
54             struct FragmentOut {
55                 [[location(0)]] color : vec4<f32>;
56                 [[builtin(frag_depth)]] depth : f32;
57             };
58 
59             [[stage(fragment)]] fn main() -> FragmentOut {
60                 var output : FragmentOut;
61                 output.color = uBuffer.color;
62                 output.depth = uBuffer.depth;
63                 return output;
64             })";
65 
66         const char* kFsOneOutputWithoutDepth = R"(
67             [[block]] struct U {
68                 color : vec4<f32>;
69             };
70             [[group(0), binding(0)]] var<uniform> uBuffer : U;
71 
72             [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
73                 return uBuffer.color;
74             })";
75 
76         const char* fs = testDepth ? kFsOneOutputWithDepth : kFsOneOutputWithoutDepth;
77 
78         return CreateRenderPipelineForTest(fs, 1, testDepth, sampleMask, alphaToCoverageEnabled,
79                                            flipTriangle);
80     }
81 
CreateRenderPipelineWithTwoOutputsForTest(uint32_t sampleMask=0xFFFFFFFF,bool alphaToCoverageEnabled=false)82     wgpu::RenderPipeline CreateRenderPipelineWithTwoOutputsForTest(
83         uint32_t sampleMask = 0xFFFFFFFF,
84         bool alphaToCoverageEnabled = false) {
85         const char* kFsTwoOutputs = R"(
86             [[block]] struct U {
87                 color0 : vec4<f32>;
88                 color1 : vec4<f32>;
89             };
90             [[group(0), binding(0)]] var<uniform> uBuffer : U;
91 
92             struct FragmentOut {
93                 [[location(0)]] color0 : vec4<f32>;
94                 [[location(1)]] color1 : vec4<f32>;
95             };
96 
97             [[stage(fragment)]] fn main() -> FragmentOut {
98                 var output : FragmentOut;
99                 output.color0 = uBuffer.color0;
100                 output.color1 = uBuffer.color1;
101                 return output;
102             })";
103 
104         return CreateRenderPipelineForTest(kFsTwoOutputs, 2, false, sampleMask,
105                                            alphaToCoverageEnabled);
106     }
107 
CreateTextureForRenderAttachment(wgpu::TextureFormat format,uint32_t sampleCount,uint32_t mipLevelCount=1,uint32_t arrayLayerCount=1)108     wgpu::Texture CreateTextureForRenderAttachment(wgpu::TextureFormat format,
109                                                    uint32_t sampleCount,
110                                                    uint32_t mipLevelCount = 1,
111                                                    uint32_t arrayLayerCount = 1) {
112         wgpu::TextureDescriptor descriptor;
113         descriptor.dimension = wgpu::TextureDimension::e2D;
114         descriptor.size.width = kWidth << (mipLevelCount - 1);
115         descriptor.size.height = kHeight << (mipLevelCount - 1);
116         descriptor.size.depthOrArrayLayers = arrayLayerCount;
117         descriptor.sampleCount = sampleCount;
118         descriptor.format = format;
119         descriptor.mipLevelCount = mipLevelCount;
120         descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
121         return device.CreateTexture(&descriptor);
122     }
123 
EncodeRenderPassForTest(wgpu::CommandEncoder commandEncoder,const wgpu::RenderPassDescriptor & renderPass,const wgpu::RenderPipeline & pipeline,const float * uniformData,uint32_t uniformDataSize)124     void EncodeRenderPassForTest(wgpu::CommandEncoder commandEncoder,
125                                  const wgpu::RenderPassDescriptor& renderPass,
126                                  const wgpu::RenderPipeline& pipeline,
127                                  const float* uniformData,
128                                  uint32_t uniformDataSize) {
129         wgpu::Buffer uniformBuffer = utils::CreateBufferFromData(
130             device, uniformData, uniformDataSize, wgpu::BufferUsage::Uniform);
131         wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
132                                                          {{0, uniformBuffer, 0, uniformDataSize}});
133 
134         wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
135         renderPassEncoder.SetPipeline(pipeline);
136         renderPassEncoder.SetBindGroup(0, bindGroup);
137         renderPassEncoder.Draw(3);
138         renderPassEncoder.EndPass();
139     }
140 
EncodeRenderPassForTest(wgpu::CommandEncoder commandEncoder,const wgpu::RenderPassDescriptor & renderPass,const wgpu::RenderPipeline & pipeline,const wgpu::Color & color)141     void EncodeRenderPassForTest(wgpu::CommandEncoder commandEncoder,
142                                  const wgpu::RenderPassDescriptor& renderPass,
143                                  const wgpu::RenderPipeline& pipeline,
144                                  const wgpu::Color& color) {
145         const float uniformData[4] = {static_cast<float>(color.r), static_cast<float>(color.g),
146                                       static_cast<float>(color.b), static_cast<float>(color.a)};
147         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, uniformData,
148                                 sizeof(float) * 4);
149     }
150 
CreateComboRenderPassDescriptorForTest(std::initializer_list<wgpu::TextureView> colorViews,std::initializer_list<wgpu::TextureView> resolveTargetViews,wgpu::LoadOp colorLoadOp,wgpu::LoadOp depthStencilLoadOp,bool hasDepthStencilAttachment)151     utils::ComboRenderPassDescriptor CreateComboRenderPassDescriptorForTest(
152         std::initializer_list<wgpu::TextureView> colorViews,
153         std::initializer_list<wgpu::TextureView> resolveTargetViews,
154         wgpu::LoadOp colorLoadOp,
155         wgpu::LoadOp depthStencilLoadOp,
156         bool hasDepthStencilAttachment) {
157         ASSERT(colorViews.size() == resolveTargetViews.size());
158 
159         constexpr wgpu::Color kClearColor = {0.0f, 0.0f, 0.0f, 0.0f};
160         constexpr float kClearDepth = 1.0f;
161 
162         utils::ComboRenderPassDescriptor renderPass(colorViews);
163         uint32_t i = 0;
164         for (const wgpu::TextureView& resolveTargetView : resolveTargetViews) {
165             renderPass.cColorAttachments[i].loadOp = colorLoadOp;
166             renderPass.cColorAttachments[i].clearColor = kClearColor;
167             renderPass.cColorAttachments[i].resolveTarget = resolveTargetView;
168             ++i;
169         }
170 
171         renderPass.cDepthStencilAttachmentInfo.clearDepth = kClearDepth;
172         renderPass.cDepthStencilAttachmentInfo.depthLoadOp = depthStencilLoadOp;
173 
174         if (hasDepthStencilAttachment) {
175             renderPass.cDepthStencilAttachmentInfo.view = mDepthStencilView;
176             renderPass.depthStencilAttachment = &renderPass.cDepthStencilAttachmentInfo;
177         }
178 
179         return renderPass;
180     }
181 
VerifyResolveTarget(const wgpu::Color & inputColor,wgpu::Texture resolveTexture,uint32_t mipmapLevel=0,uint32_t arrayLayer=0,const float msaaCoverage=0.5f)182     void VerifyResolveTarget(const wgpu::Color& inputColor,
183                              wgpu::Texture resolveTexture,
184                              uint32_t mipmapLevel = 0,
185                              uint32_t arrayLayer = 0,
186                              const float msaaCoverage = 0.5f) {
187         // In this test we only check the pixel in the middle of the texture.
188         constexpr uint32_t kMiddleX = (kWidth - 1) / 2;
189         constexpr uint32_t kMiddleY = (kHeight - 1) / 2;
190 
191         RGBA8 expectedColor = ExpectedMSAAColor(inputColor, msaaCoverage);
192         EXPECT_TEXTURE_EQ(&expectedColor, resolveTexture, {kMiddleX, kMiddleY, arrayLayer}, {1, 1},
193                           mipmapLevel);
194     }
195 
196     constexpr static uint32_t kWidth = 3;
197     constexpr static uint32_t kHeight = 3;
198     constexpr static uint32_t kSampleCount = 4;
199     constexpr static wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm;
200     constexpr static wgpu::TextureFormat kDepthStencilFormat =
201         wgpu::TextureFormat::Depth24PlusStencil8;
202 
203     constexpr static uint32_t kFirstSampleMaskBit = 0x00000001;
204     constexpr static uint32_t kSecondSampleMaskBit = 0x00000002;
205     constexpr static uint32_t kThirdSampleMaskBit = 0x00000004;
206     constexpr static uint32_t kFourthSampleMaskBit = 0x00000008;
207 
208     wgpu::Texture mMultisampledColorTexture;
209     wgpu::TextureView mMultisampledColorView;
210     wgpu::Texture mResolveTexture;
211     wgpu::TextureView mResolveView;
212     wgpu::Texture mDepthStencilTexture;
213     wgpu::TextureView mDepthStencilView;
214 
CreateRenderPipelineForTest(const char * fs,uint32_t numColorAttachments,bool hasDepthStencilAttachment,uint32_t sampleMask=0xFFFFFFFF,bool alphaToCoverageEnabled=false,bool flipTriangle=false)215     wgpu::RenderPipeline CreateRenderPipelineForTest(const char* fs,
216                                                      uint32_t numColorAttachments,
217                                                      bool hasDepthStencilAttachment,
218                                                      uint32_t sampleMask = 0xFFFFFFFF,
219                                                      bool alphaToCoverageEnabled = false,
220                                                      bool flipTriangle = false) {
221         utils::ComboRenderPipelineDescriptor pipelineDescriptor;
222 
223         // Draw a bottom-right triangle. In standard 4xMSAA pattern, for the pixels on diagonal,
224         // only two of the samples will be touched.
225         const char* vs = R"(
226             [[stage(vertex)]]
227             fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
228                 var pos = array<vec2<f32>, 3>(
229                     vec2<f32>(-1.0,  1.0),
230                     vec2<f32>( 1.0,  1.0),
231                     vec2<f32>( 1.0, -1.0)
232                 );
233                 return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
234             })";
235 
236         // Draw a bottom-left triangle.
237         const char* vsFlipped = R"(
238             [[stage(vertex)]]
239             fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
240                 var pos = array<vec2<f32>, 3>(
241                     vec2<f32>(-1.0,  1.0),
242                     vec2<f32>( 1.0,  1.0),
243                     vec2<f32>(-1.0, -1.0)
244                 );
245                 return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
246             })";
247 
248         if (flipTriangle) {
249             pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, vsFlipped);
250         } else {
251             pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, vs);
252         }
253 
254         pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, fs);
255 
256         if (hasDepthStencilAttachment) {
257             wgpu::DepthStencilState* depthStencil =
258                 pipelineDescriptor.EnableDepthStencil(kDepthStencilFormat);
259             depthStencil->depthWriteEnabled = true;
260             depthStencil->depthCompare = wgpu::CompareFunction::Less;
261         }
262 
263         pipelineDescriptor.multisample.count = kSampleCount;
264         pipelineDescriptor.multisample.mask = sampleMask;
265         pipelineDescriptor.multisample.alphaToCoverageEnabled = alphaToCoverageEnabled;
266 
267         pipelineDescriptor.cFragment.targetCount = numColorAttachments;
268         for (uint32_t i = 0; i < numColorAttachments; ++i) {
269             pipelineDescriptor.cTargets[i].format = kColorFormat;
270         }
271 
272         wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
273         return pipeline;
274     }
275 
ExpectedMSAAColor(const wgpu::Color color,const double msaaCoverage)276     RGBA8 ExpectedMSAAColor(const wgpu::Color color, const double msaaCoverage) {
277         RGBA8 result;
278         result.r = static_cast<uint8_t>(std::min(255.0, 256 * color.r * msaaCoverage));
279         result.g = static_cast<uint8_t>(std::min(255.0, 256 * color.g * msaaCoverage));
280         result.b = static_cast<uint8_t>(std::min(255.0, 256 * color.b * msaaCoverage));
281         result.a = static_cast<uint8_t>(std::min(255.0, 256 * color.a * msaaCoverage));
282         return result;
283     }
284 };
285 
286 // Test using one multisampled color attachment with resolve target can render correctly.
TEST_P(MultisampledRenderingTest,ResolveInto2DTexture)287 TEST_P(MultisampledRenderingTest, ResolveInto2DTexture) {
288     constexpr bool kTestDepth = false;
289     wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth);
290 
291     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
292 
293     // storeOp should not affect the result in the resolve target.
294     for (wgpu::StoreOp storeOp : {wgpu::StoreOp::Store, wgpu::StoreOp::Discard}) {
295         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
296 
297         // Draw a green triangle.
298         {
299             utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
300                 {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
301                 kTestDepth);
302             renderPass.cColorAttachments[0].storeOp = storeOp;
303             std::array<float, 4> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a};
304             constexpr uint32_t kSize = sizeof(kUniformData);
305             EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(),
306                                     kSize);
307         }
308 
309         wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
310         queue.Submit(1, &commandBuffer);
311 
312         VerifyResolveTarget(kGreen, mResolveTexture);
313     }
314 }
315 
316 // Test that a single-layer multisampled texture view can be created and resolved from.
TEST_P(MultisampledRenderingTest,ResolveFromSingleLayerArrayInto2DTexture)317 TEST_P(MultisampledRenderingTest, ResolveFromSingleLayerArrayInto2DTexture) {
318     constexpr bool kTestDepth = false;
319     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
320     wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth);
321 
322     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
323 
324     // Draw a green triangle.
325     {
326         wgpu::TextureViewDescriptor desc = {};
327         desc.dimension = wgpu::TextureViewDimension::e2DArray;
328         desc.arrayLayerCount = 1;
329 
330         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
331             {mMultisampledColorTexture.CreateView(&desc)}, {mResolveView}, wgpu::LoadOp::Clear,
332             wgpu::LoadOp::Clear, kTestDepth);
333 
334         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen);
335     }
336 
337     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
338     queue.Submit(1, &commandBuffer);
339 
340     VerifyResolveTarget(kGreen, mResolveTexture);
341 }
342 
343 // Test multisampled rendering with depth test works correctly.
TEST_P(MultisampledRenderingTest,MultisampledRenderingWithDepthTest)344 TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTest) {
345     constexpr bool kTestDepth = true;
346     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
347     wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth);
348 
349     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
350     constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
351 
352     // In first render pass we draw a green triangle with depth value == 0.2f.
353     {
354         utils::ComboRenderPassDescriptor renderPass =
355             CreateComboRenderPassDescriptorForTest({mMultisampledColorView}, {mResolveView},
356                                                    wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, true);
357         std::array<float, 5> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a,  // Color
358                                              0.2f};                                   // depth
359         constexpr uint32_t kSize = sizeof(kUniformData);
360         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
361     }
362 
363     // In second render pass we draw a red triangle with depth value == 0.5f.
364     // This red triangle should not be displayed because it is behind the green one that is drawn in
365     // the last render pass.
366     {
367         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
368             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load,
369             kTestDepth);
370 
371         std::array<float, 5> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a,  // color
372                                              0.5f};                           // depth
373         constexpr uint32_t kSize = sizeof(kUniformData);
374         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
375     }
376 
377     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
378     queue.Submit(1, &commandBuffer);
379 
380     // The color of the pixel in the middle of mResolveTexture should be green if MSAA resolve runs
381     // correctly with depth test.
382     VerifyResolveTarget(kGreen, mResolveTexture);
383 }
384 
385 // Test rendering into a multisampled color attachment and doing MSAA resolve in another render pass
386 // works correctly.
TEST_P(MultisampledRenderingTest,ResolveInAnotherRenderPass)387 TEST_P(MultisampledRenderingTest, ResolveInAnotherRenderPass) {
388     constexpr bool kTestDepth = false;
389     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
390     wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth);
391 
392     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
393 
394     // In first render pass we draw a green triangle and do not set the resolve target.
395     {
396         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
397             {mMultisampledColorView}, {nullptr}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
398             kTestDepth);
399 
400         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen);
401     }
402 
403     // In second render pass we ony do MSAA resolve with no draw call.
404     {
405         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
406             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load,
407             kTestDepth);
408 
409         wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
410         renderPassEncoder.EndPass();
411     }
412 
413     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
414     queue.Submit(1, &commandBuffer);
415 
416     VerifyResolveTarget(kGreen, mResolveTexture);
417 }
418 
419 // Test doing MSAA resolve into multiple resolve targets works correctly.
TEST_P(MultisampledRenderingTest,ResolveIntoMultipleResolveTargets)420 TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargets) {
421     // TODO(dawn:462): Issue in the D3D12 validation layers.
422     DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsNvidia() && IsBackendValidationEnabled());
423 
424     wgpu::TextureView multisampledColorView2 =
425         CreateTextureForRenderAttachment(kColorFormat, kSampleCount).CreateView();
426     wgpu::Texture resolveTexture2 = CreateTextureForRenderAttachment(kColorFormat, 1);
427     wgpu::TextureView resolveView2 = resolveTexture2.CreateView();
428 
429     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
430     wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest();
431 
432     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
433     constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
434     constexpr bool kTestDepth = false;
435 
436     // Draw a red triangle to the first color attachment, and a blue triangle to the second color
437     // attachment, and do MSAA resolve on two render targets in one render pass.
438     {
439         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
440             {mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2},
441             wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth);
442 
443         std::array<float, 8> kUniformData = {
444             static_cast<float>(kRed.r),   static_cast<float>(kRed.g),
445             static_cast<float>(kRed.b),   static_cast<float>(kRed.a),
446             static_cast<float>(kGreen.r), static_cast<float>(kGreen.g),
447             static_cast<float>(kGreen.b), static_cast<float>(kGreen.a)};
448         constexpr uint32_t kSize = sizeof(kUniformData);
449 
450         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
451     }
452 
453     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
454     queue.Submit(1, &commandBuffer);
455 
456     VerifyResolveTarget(kRed, mResolveTexture);
457     VerifyResolveTarget(kGreen, resolveTexture2);
458 }
459 
460 // Test doing MSAA resolve on one multisampled texture twice works correctly.
TEST_P(MultisampledRenderingTest,ResolveOneMultisampledTextureTwice)461 TEST_P(MultisampledRenderingTest, ResolveOneMultisampledTextureTwice) {
462     constexpr bool kTestDepth = false;
463     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
464     wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth);
465 
466     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
467 
468     wgpu::Texture resolveTexture2 = CreateTextureForRenderAttachment(kColorFormat, 1);
469 
470     // In first render pass we draw a green triangle and specify mResolveView as the resolve target.
471     {
472         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
473             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
474             kTestDepth);
475 
476         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen);
477     }
478 
479     // In second render pass we do MSAA resolve into resolveTexture2.
480     {
481         wgpu::TextureView resolveView2 = resolveTexture2.CreateView();
482         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
483             {mMultisampledColorView}, {resolveView2}, wgpu::LoadOp::Load, wgpu::LoadOp::Load,
484             kTestDepth);
485 
486         wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
487         renderPassEncoder.EndPass();
488     }
489 
490     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
491     queue.Submit(1, &commandBuffer);
492 
493     VerifyResolveTarget(kGreen, mResolveTexture);
494     VerifyResolveTarget(kGreen, resolveTexture2);
495 }
496 
497 // Test using a layer of a 2D texture as resolve target works correctly.
TEST_P(MultisampledRenderingTest,ResolveIntoOneMipmapLevelOf2DTexture)498 TEST_P(MultisampledRenderingTest, ResolveIntoOneMipmapLevelOf2DTexture) {
499     // TODO(dawn:462): Issue in the D3D12 validation layers.
500     DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsBackendValidationEnabled());
501 
502     constexpr uint32_t kBaseMipLevel = 2;
503 
504     wgpu::TextureViewDescriptor textureViewDescriptor;
505     textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
506     textureViewDescriptor.format = kColorFormat;
507     textureViewDescriptor.baseArrayLayer = 0;
508     textureViewDescriptor.arrayLayerCount = 1;
509     textureViewDescriptor.mipLevelCount = 1;
510     textureViewDescriptor.baseMipLevel = kBaseMipLevel;
511 
512     wgpu::Texture resolveTexture =
513         CreateTextureForRenderAttachment(kColorFormat, 1, kBaseMipLevel + 1, 1);
514     wgpu::TextureView resolveView = resolveTexture.CreateView(&textureViewDescriptor);
515 
516     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
517     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
518     constexpr bool kTestDepth = false;
519 
520     // Draw a green triangle and do MSAA resolve.
521     {
522         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
523             {mMultisampledColorView}, {resolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
524             kTestDepth);
525         wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth);
526         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen);
527     }
528 
529     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
530     queue.Submit(1, &commandBuffer);
531 
532     VerifyResolveTarget(kGreen, resolveTexture, kBaseMipLevel, 0);
533 }
534 
535 // Test using a level or a layer of a 2D array texture as resolve target works correctly.
TEST_P(MultisampledRenderingTest,ResolveInto2DArrayTexture)536 TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) {
537     // TODO(dawn:462): Issue in the D3D12 validation layers.
538     DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsBackendValidationEnabled());
539 
540     wgpu::TextureView multisampledColorView2 =
541         CreateTextureForRenderAttachment(kColorFormat, kSampleCount).CreateView();
542 
543     wgpu::TextureViewDescriptor baseTextureViewDescriptor;
544     baseTextureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
545     baseTextureViewDescriptor.format = kColorFormat;
546     baseTextureViewDescriptor.arrayLayerCount = 1;
547     baseTextureViewDescriptor.mipLevelCount = 1;
548 
549     // Create resolveTexture1 with only 1 mipmap level.
550     constexpr uint32_t kBaseArrayLayer1 = 2;
551     constexpr uint32_t kBaseMipLevel1 = 0;
552     wgpu::Texture resolveTexture1 =
553         CreateTextureForRenderAttachment(kColorFormat, 1, kBaseMipLevel1 + 1, kBaseArrayLayer1 + 1);
554     wgpu::TextureViewDescriptor resolveViewDescriptor1 = baseTextureViewDescriptor;
555     resolveViewDescriptor1.baseArrayLayer = kBaseArrayLayer1;
556     resolveViewDescriptor1.baseMipLevel = kBaseMipLevel1;
557     wgpu::TextureView resolveView1 = resolveTexture1.CreateView(&resolveViewDescriptor1);
558 
559     // Create resolveTexture2 with (kBaseMipLevel2 + 1) mipmap levels and resolve into its last
560     // mipmap level.
561     constexpr uint32_t kBaseArrayLayer2 = 5;
562     constexpr uint32_t kBaseMipLevel2 = 3;
563     wgpu::Texture resolveTexture2 =
564         CreateTextureForRenderAttachment(kColorFormat, 1, kBaseMipLevel2 + 1, kBaseArrayLayer2 + 1);
565     wgpu::TextureViewDescriptor resolveViewDescriptor2 = baseTextureViewDescriptor;
566     resolveViewDescriptor2.baseArrayLayer = kBaseArrayLayer2;
567     resolveViewDescriptor2.baseMipLevel = kBaseMipLevel2;
568     wgpu::TextureView resolveView2 = resolveTexture2.CreateView(&resolveViewDescriptor2);
569 
570     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
571     wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest();
572 
573     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
574     constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
575     constexpr bool kTestDepth = false;
576 
577     // Draw a red triangle to the first color attachment, and a green triangle to the second color
578     // attachment, and do MSAA resolve on two render targets in one render pass.
579     {
580         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
581             {mMultisampledColorView, multisampledColorView2}, {resolveView1, resolveView2},
582             wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth);
583 
584         std::array<float, 8> kUniformData = {kRed.r,   kRed.g,   kRed.b,   kRed.a,     // color1
585                                              kGreen.r, kGreen.g, kGreen.b, kGreen.a};  // color2
586         constexpr uint32_t kSize = sizeof(kUniformData);
587         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
588     }
589 
590     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
591     queue.Submit(1, &commandBuffer);
592 
593     VerifyResolveTarget(kRed, resolveTexture1, kBaseMipLevel1, kBaseArrayLayer1);
594     VerifyResolveTarget(kGreen, resolveTexture2, kBaseMipLevel2, kBaseArrayLayer2);
595 }
596 
597 // Test using one multisampled color attachment with resolve target can render correctly
598 // with a non-default sample mask.
TEST_P(MultisampledRenderingTest,ResolveInto2DTextureWithSampleMask)599 TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithSampleMask) {
600     constexpr bool kTestDepth = false;
601     // The second and third samples are included,
602     // only the second one is covered by the triangle.
603     constexpr uint32_t kSampleMask = kSecondSampleMaskBit | kThirdSampleMaskBit;
604     constexpr float kMSAACoverage = 0.25f;
605     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
606     wgpu::RenderPipeline pipeline =
607         CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMask);
608 
609     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
610 
611     // Draw a green triangle.
612     {
613         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
614             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
615             kTestDepth);
616 
617         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen);
618     }
619 
620     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
621     queue.Submit(1, &commandBuffer);
622 
623     VerifyResolveTarget(kGreen, mResolveTexture, 0, 0, kMSAACoverage);
624 }
625 
626 // Test using one multisampled color attachment with resolve target can render correctly
627 // with the final sample mask empty.
TEST_P(MultisampledRenderingTest,ResolveInto2DTextureWithEmptyFinalSampleMask)628 TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithEmptyFinalSampleMask) {
629     constexpr bool kTestDepth = false;
630     // The third and fourth samples are included,
631     // none of which is covered by the triangle.
632     constexpr uint32_t kSampleMask = kThirdSampleMaskBit | kFourthSampleMaskBit;
633     constexpr float kMSAACoverage = 0.00f;
634     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
635     wgpu::RenderPipeline pipeline =
636         CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMask);
637 
638     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
639 
640     // Draw a green triangle.
641     {
642         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
643             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
644             kTestDepth);
645 
646         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen);
647     }
648 
649     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
650     queue.Submit(1, &commandBuffer);
651 
652     VerifyResolveTarget(kGreen, mResolveTexture, 0, 0, kMSAACoverage);
653 }
654 
655 // Test doing MSAA resolve into multiple resolve targets works correctly with a non-default sample
656 // mask.
TEST_P(MultisampledRenderingTest,ResolveIntoMultipleResolveTargetsWithSampleMask)657 TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargetsWithSampleMask) {
658     wgpu::TextureView multisampledColorView2 =
659         CreateTextureForRenderAttachment(kColorFormat, kSampleCount).CreateView();
660     wgpu::Texture resolveTexture2 = CreateTextureForRenderAttachment(kColorFormat, 1);
661     wgpu::TextureView resolveView2 = resolveTexture2.CreateView();
662 
663     // The first and fourth samples are included,
664     // only the first one is covered by the triangle.
665     constexpr uint32_t kSampleMask = kFirstSampleMaskBit | kFourthSampleMaskBit;
666     constexpr float kMSAACoverage = 0.25f;
667 
668     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
669     wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(kSampleMask);
670 
671     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
672     constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
673     constexpr bool kTestDepth = false;
674 
675     // Draw a red triangle to the first color attachment, and a blue triangle to the second color
676     // attachment, and do MSAA resolve on two render targets in one render pass.
677     {
678         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
679             {mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2},
680             wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth);
681 
682         std::array<float, 8> kUniformData = {kRed.r,   kRed.g,   kRed.b,   kRed.a,     // color1
683                                              kGreen.r, kGreen.g, kGreen.b, kGreen.a};  // color2
684         constexpr uint32_t kSize = sizeof(kUniformData);
685         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
686     }
687 
688     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
689     queue.Submit(1, &commandBuffer);
690 
691     VerifyResolveTarget(kRed, mResolveTexture, 0, 0, kMSAACoverage);
692     VerifyResolveTarget(kGreen, resolveTexture2, 0, 0, kMSAACoverage);
693 }
694 
695 // Test multisampled rendering with depth test works correctly with a non-default sample mask.
TEST_P(MultisampledRenderingTest,MultisampledRenderingWithDepthTestAndSampleMask)696 TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTestAndSampleMask) {
697     constexpr bool kTestDepth = true;
698     // The second sample is included in the first render pass and it's covered by the triangle.
699     constexpr uint32_t kSampleMaskGreen = kSecondSampleMaskBit;
700     // The first and second samples are included in the second render pass,
701     // both are covered by the triangle.
702     constexpr uint32_t kSampleMaskRed = kFirstSampleMaskBit | kSecondSampleMaskBit;
703     constexpr float kMSAACoverage = 0.50f;
704 
705     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
706     wgpu::RenderPipeline pipelineGreen =
707         CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMaskGreen);
708     wgpu::RenderPipeline pipelineRed =
709         CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMaskRed);
710 
711     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
712     constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
713 
714     // In first render pass we draw a green triangle with depth value == 0.2f.
715     // We will only write to the second sample.
716     {
717         utils::ComboRenderPassDescriptor renderPass =
718             CreateComboRenderPassDescriptorForTest({mMultisampledColorView}, {mResolveView},
719                                                    wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, true);
720         std::array<float, 5> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a,  // Color
721                                              0.2f};                                   // depth
722         constexpr uint32_t kSize = sizeof(kUniformData);
723         EncodeRenderPassForTest(commandEncoder, renderPass, pipelineGreen, kUniformData.data(),
724                                 kSize);
725     }
726 
727     // In second render pass we draw a red triangle with depth value == 0.5f.
728     // We will only write to the first sample, since the second one is red with a smaller depth
729     // value.
730     {
731         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
732             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load,
733             kTestDepth);
734 
735         std::array<float, 5> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a,  // color
736                                              0.5f};                           // depth
737         constexpr uint32_t kSize = sizeof(kUniformData);
738         EncodeRenderPassForTest(commandEncoder, renderPass, pipelineRed, kUniformData.data(),
739                                 kSize);
740     }
741 
742     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
743     queue.Submit(1, &commandBuffer);
744 
745     constexpr wgpu::Color kHalfGreenHalfRed = {(kGreen.r + kRed.r) / 2.0, (kGreen.g + kRed.g) / 2.0,
746                                                (kGreen.b + kRed.b) / 2.0,
747                                                (kGreen.a + kRed.a) / 2.0};
748 
749     // The color of the pixel in the middle of mResolveTexture should be half green and half
750     // red if MSAA resolve runs correctly with depth test.
751     VerifyResolveTarget(kHalfGreenHalfRed, mResolveTexture, 0, 0, kMSAACoverage);
752 }
753 
754 // Test using one multisampled color attachment with resolve target can render correctly
755 // with non-default sample mask and shader-output mask.
TEST_P(MultisampledRenderingTest,ResolveInto2DTextureWithSampleMaskAndShaderOutputMask)756 TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithSampleMaskAndShaderOutputMask) {
757     // TODO(github.com/KhronosGroup/SPIRV-Cross/issues/1626): SPIRV-Cross produces bad GLSL for
758     // unsigned SampleMask builtins
759     DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
760 
761     // TODO(crbug.com/dawn/673): Work around or enforce via validation that sample variables are not
762     // supported on some platforms.
763     DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_sample_variables"));
764 
765     // TODO(crbug.com/dawn/571): Fails on Metal / D3D12 because SPIRV-Cross produces bad shaders
766     // for the SPIR-V outputted by Tint. Reenable once we use Tint's MSL / HLSL generators.
767     DAWN_SUPPRESS_TEST_IF(IsD3D12() || IsMetal());
768 
769     constexpr bool kTestDepth = false;
770     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
771 
772     // The second and third samples are included in the shader-output mask.
773     // The first and third samples are included in the sample mask.
774     // Since we're now looking at a fully covered pixel, the rasterization mask
775     // includes all the samples.
776     // Thus the final mask includes only the third sample.
777     constexpr float kMSAACoverage = 0.25f;
778     constexpr uint32_t kSampleMask = kFirstSampleMaskBit | kThirdSampleMaskBit;
779     const char* fs = R"(
780         [[block]] struct U {
781             color : vec4<f32>;
782         };
783         [[group(0), binding(0)]] var<uniform> uBuffer : U;
784 
785         struct FragmentOut {
786             [[location(0)]] color : vec4<f32>;
787             [[builtin(sample_mask)]] sampleMask : u32;
788         };
789 
790         [[stage(fragment)]] fn main() -> FragmentOut {
791             var output : FragmentOut;
792             output.color = uBuffer.color;
793             output.sampleMask = 6u;
794             return output;
795         })";
796 
797     wgpu::RenderPipeline pipeline = CreateRenderPipelineForTest(fs, 1, false, kSampleMask);
798     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
799 
800     // Draw a green triangle.
801     {
802         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
803             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
804             kTestDepth);
805 
806         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen);
807     }
808 
809     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
810     queue.Submit(1, &commandBuffer);
811 
812     RGBA8 expectedColor = ExpectedMSAAColor(kGreen, kMSAACoverage);
813     EXPECT_TEXTURE_EQ(&expectedColor, mResolveTexture, {1, 0}, {1, 1});
814 }
815 
816 // Test doing MSAA resolve into multiple resolve targets works correctly with a non-default
817 // shader-output mask.
TEST_P(MultisampledRenderingTest,ResolveIntoMultipleResolveTargetsWithShaderOutputMask)818 TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargetsWithShaderOutputMask) {
819     // TODO(github.com/KhronosGroup/SPIRV-Cross/issues/1626): SPIRV-Cross produces bad GLSL for
820     // unsigned SampleMask builtins
821     DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
822 
823     // TODO(crbug.com/dawn/673): Work around or enforce via validation that sample variables are not
824     // supported on some platforms.
825     DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_sample_variables"));
826 
827     // TODO(crbug.com/dawn/571): Fails on Metal / D3D12 because SPIRV-Cross produces bad shaders
828     // for the SPIR-V outputted by Tint. Reenable once we use Tint's MSL / HLSL generators.
829     DAWN_SUPPRESS_TEST_IF(IsD3D12() || IsMetal());
830 
831     wgpu::TextureView multisampledColorView2 =
832         CreateTextureForRenderAttachment(kColorFormat, kSampleCount).CreateView();
833     wgpu::Texture resolveTexture2 = CreateTextureForRenderAttachment(kColorFormat, 1);
834     wgpu::TextureView resolveView2 = resolveTexture2.CreateView();
835 
836     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
837     // The second and third samples are included in the shader-output mask,
838     // only the first one is covered by the triangle.
839     constexpr float kMSAACoverage = 0.25f;
840     const char* fs = R"(
841         [[block]] struct U {
842             color0 : vec4<f32>;
843             color1 : vec4<f32>;
844         };
845         [[group(0), binding(0)]] var<uniform> uBuffer : U;
846 
847         struct FragmentOut {
848             [[location(0)]] color0 : vec4<f32>;
849             [[location(1)]] color1 : vec4<f32>;
850             [[builtin(sample_mask)]] sampleMask : u32;
851         };
852 
853         [[stage(fragment)]] fn main() -> FragmentOut {
854             var output : FragmentOut;
855             output.color0 = uBuffer.color0;
856             output.color1 = uBuffer.color1;
857             output.sampleMask = 6u;
858             return output;
859         })";
860 
861     wgpu::RenderPipeline pipeline = CreateRenderPipelineForTest(fs, 2, false);
862     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
863     constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
864     constexpr bool kTestDepth = false;
865 
866     // Draw a red triangle to the first color attachment, and a blue triangle to the second color
867     // attachment, and do MSAA resolve on two render targets in one render pass.
868     {
869         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
870             {mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2},
871             wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth);
872 
873         std::array<float, 8> kUniformData = {kRed.r,   kRed.g,   kRed.b,   kRed.a,     // color1
874                                              kGreen.r, kGreen.g, kGreen.b, kGreen.a};  // color2
875         constexpr uint32_t kSize = sizeof(kUniformData);
876         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
877     }
878 
879     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
880     queue.Submit(1, &commandBuffer);
881 
882     VerifyResolveTarget(kRed, mResolveTexture, 0, 0, kMSAACoverage);
883     VerifyResolveTarget(kGreen, resolveTexture2, 0, 0, kMSAACoverage);
884 }
885 
886 // Test using one multisampled color attachment with resolve target can render correctly
887 // with alphaToCoverageEnabled.
TEST_P(MultisampledRenderingTest,ResolveInto2DTextureWithAlphaToCoverage)888 TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithAlphaToCoverage) {
889     constexpr bool kTestDepth = false;
890     constexpr uint32_t kSampleMask = 0xFFFFFFFF;
891     constexpr bool kAlphaToCoverageEnabled = true;
892 
893     // Setting alpha <= 0 must result in alpha-to-coverage mask being empty.
894     // Setting alpha = 0.5f should result in alpha-to-coverage mask including half the samples,
895     // but this is not guaranteed by the spec. The Metal spec seems to guarantee that this is
896     // indeed the case.
897     // Setting alpha >= 1 must result in alpha-to-coverage mask being full.
898     for (float alpha : {-1.0f, 0.0f, 0.5f, 1.0f, 2.0f}) {
899         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
900         wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(
901             kTestDepth, kSampleMask, kAlphaToCoverageEnabled);
902 
903         const wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, alpha};
904 
905         // Draw a green triangle.
906         {
907             utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
908                 {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
909                 kTestDepth);
910 
911             EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen);
912         }
913 
914         wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
915         queue.Submit(1, &commandBuffer);
916 
917         // For alpha = {0, 0.5, 1} we expect msaaCoverage to correspond to the value of alpha.
918         float msaaCoverage = alpha;
919         if (alpha < 0.0f) {
920             msaaCoverage = 0.0f;
921         }
922         if (alpha > 1.0f) {
923             msaaCoverage = 1.0f;
924         }
925 
926         RGBA8 expectedColor = ExpectedMSAAColor(kGreen, msaaCoverage);
927         EXPECT_TEXTURE_EQ(&expectedColor, mResolveTexture, {1, 0}, {1, 1});
928     }
929 }
930 
931 // Test doing MSAA resolve into multiple resolve targets works correctly with
932 // alphaToCoverage. The alphaToCoverage mask is computed based on the alpha
933 // component of the first color render attachment.
TEST_P(MultisampledRenderingTest,ResolveIntoMultipleResolveTargetsWithAlphaToCoverage)934 TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargetsWithAlphaToCoverage) {
935     wgpu::TextureView multisampledColorView2 =
936         CreateTextureForRenderAttachment(kColorFormat, kSampleCount).CreateView();
937     wgpu::Texture resolveTexture2 = CreateTextureForRenderAttachment(kColorFormat, 1);
938     wgpu::TextureView resolveView2 = resolveTexture2.CreateView();
939     constexpr uint32_t kSampleMask = 0xFFFFFFFF;
940     constexpr float kMSAACoverage = 0.50f;
941     constexpr bool kAlphaToCoverageEnabled = true;
942 
943     // The alpha-to-coverage mask should not depend on the alpha component of the
944     // second color render attachment.
945     // We test alpha = 0.51f and 0.99f instead of 0.50f and 1.00f because there are some rounding
946     // differences on QuadroP400 devices in that case.
947     for (float alpha : {0.0f, 0.51f, 0.99f}) {
948         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
949         wgpu::RenderPipeline pipeline =
950             CreateRenderPipelineWithTwoOutputsForTest(kSampleMask, kAlphaToCoverageEnabled);
951 
952         constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.51f};
953         const wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, alpha};
954         constexpr bool kTestDepth = false;
955 
956         // Draw a red triangle to the first color attachment, and a blue triangle to the second
957         // color attachment, and do MSAA resolve on two render targets in one render pass.
958         {
959             utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
960                 {mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2},
961                 wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth);
962 
963             std::array<float, 8> kUniformData = {
964                 static_cast<float>(kRed.r),   static_cast<float>(kRed.g),
965                 static_cast<float>(kRed.b),   static_cast<float>(kRed.a),
966                 static_cast<float>(kGreen.r), static_cast<float>(kGreen.g),
967                 static_cast<float>(kGreen.b), static_cast<float>(kGreen.a)};
968             constexpr uint32_t kSize = sizeof(kUniformData);
969             EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(),
970                                     kSize);
971         }
972 
973         wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
974         queue.Submit(1, &commandBuffer);
975 
976         // Alpha to coverage affects both the color outputs, but the mask is computed
977         // using only the first one.
978         RGBA8 expectedRed = ExpectedMSAAColor(kRed, kMSAACoverage);
979         RGBA8 expectedGreen = ExpectedMSAAColor(kGreen, kMSAACoverage);
980         EXPECT_TEXTURE_EQ(&expectedRed, mResolveTexture, {1, 0}, {1, 1});
981         EXPECT_TEXTURE_EQ(&expectedGreen, resolveTexture2, {1, 0}, {1, 1});
982     }
983 }
984 
985 // Test multisampled rendering with depth test works correctly with alphaToCoverage.
TEST_P(MultisampledRenderingTest,MultisampledRenderingWithDepthTestAndAlphaToCoverage)986 TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTestAndAlphaToCoverage) {
987     // This test fails because Swiftshader is off-by-one with its ((a+b)/2 + (c+d)/2)/2 fast resolve
988     // algorithm.
989     DAWN_SUPPRESS_TEST_IF(IsSwiftshader() || IsANGLE());
990 
991     constexpr bool kTestDepth = true;
992     constexpr uint32_t kSampleMask = 0xFFFFFFFF;
993 
994     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
995     wgpu::RenderPipeline pipelineGreen =
996         CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMask, true);
997     wgpu::RenderPipeline pipelineRed =
998         CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMask, false);
999 
1000     // We test alpha = 0.51f and 0.81f instead of 0.50f and 0.80f because there are some
1001     // rounding differences on QuadroP400 devices in that case.
1002     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.51f};
1003     constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.81f};
1004 
1005     // In first render pass we draw a green triangle with depth value == 0.2f.
1006     // We will only write to half the samples since the alphaToCoverage mode
1007     // is enabled for that render pass.
1008     {
1009         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
1010             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
1011             kTestDepth);
1012         std::array<float, 5> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a,  // Color
1013                                              0.2f};                                   // depth
1014         constexpr uint32_t kSize = sizeof(kUniformData);
1015         EncodeRenderPassForTest(commandEncoder, renderPass, pipelineGreen, kUniformData.data(),
1016                                 kSize);
1017     }
1018 
1019     // In second render pass we draw a red triangle with depth value == 0.5f.
1020     // We will write to all the samples since the alphaToCoverageMode is diabled for
1021     // that render pass.
1022     {
1023         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
1024             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load,
1025             kTestDepth);
1026 
1027         std::array<float, 5> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a,  // color
1028                                              0.5f};                           // depth
1029         constexpr uint32_t kSize = sizeof(kUniformData);
1030         EncodeRenderPassForTest(commandEncoder, renderPass, pipelineRed, kUniformData.data(),
1031                                 kSize);
1032     }
1033 
1034     wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
1035     queue.Submit(1, &commandBuffer);
1036 
1037     constexpr wgpu::Color kHalfGreenHalfRed = {(kGreen.r + kRed.r) / 2.0, (kGreen.g + kRed.g) / 2.0,
1038                                                (kGreen.b + kRed.b) / 2.0,
1039                                                (kGreen.a + kRed.a) / 2.0};
1040     RGBA8 expectedColor = ExpectedMSAAColor(kHalfGreenHalfRed, 1.0f);
1041 
1042     EXPECT_TEXTURE_EQ(&expectedColor, mResolveTexture, {1, 0}, {1, 1});
1043 }
1044 
1045 // Test using one multisampled color attachment with resolve target can render correctly
1046 // with alphaToCoverageEnabled and a sample mask.
TEST_P(MultisampledRenderingTest,ResolveInto2DTextureWithAlphaToCoverageAndSampleMask)1047 TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithAlphaToCoverageAndSampleMask) {
1048     // This test fails because Swiftshader is off-by-one with its ((a+b)/2 + (c+d)/2)/2 fast resolve
1049     // algorithm.
1050     DAWN_SUPPRESS_TEST_IF(IsSwiftshader() || IsANGLE());
1051 
1052     // TODO(dawn:491): This doesn't work on Metal, because we're using both the shader-output
1053     // mask (emulting the sampleMask from RenderPipeline) and alpha-to-coverage at the same
1054     // time. See the issue: https://github.com/gpuweb/gpuweb/issues/959.
1055     DAWN_SUPPRESS_TEST_IF(IsMetal());
1056 
1057     constexpr bool kTestDepth = false;
1058     constexpr float kMSAACoverage = 0.50f;
1059     constexpr uint32_t kSampleMask = kFirstSampleMaskBit | kThirdSampleMaskBit;
1060     constexpr bool kAlphaToCoverageEnabled = true;
1061 
1062     // For those values of alpha we expect the proportion of samples to be covered
1063     // to correspond to the value of alpha.
1064     // We're assuming in the case of alpha = 0.50f that the implementation
1065     // dependendent algorithm will choose exactly one of the first and third samples.
1066     for (float alpha : {0.0f, 0.50f, 1.00f}) {
1067         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1068         wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(
1069             kTestDepth, kSampleMask, kAlphaToCoverageEnabled);
1070 
1071         const wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, alpha - 0.01f};
1072 
1073         // Draw a green triangle.
1074         {
1075             utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
1076                 {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
1077                 kTestDepth);
1078 
1079             EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen);
1080         }
1081 
1082         wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
1083         queue.Submit(1, &commandBuffer);
1084 
1085         RGBA8 expectedColor = ExpectedMSAAColor(kGreen, kMSAACoverage * alpha);
1086         EXPECT_TEXTURE_EQ(&expectedColor, mResolveTexture, {1, 0}, {1, 1});
1087     }
1088 }
1089 
1090 // Test using one multisampled color attachment with resolve target can render correctly
1091 // with alphaToCoverageEnabled and a rasterization mask.
TEST_P(MultisampledRenderingTest,ResolveInto2DTextureWithAlphaToCoverageAndRasterizationMask)1092 TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithAlphaToCoverageAndRasterizationMask) {
1093     // This test fails because Swiftshader is off-by-one with its ((a+b)/2 + (c+d)/2)/2 fast resolve
1094     // algorithm.
1095     DAWN_SUPPRESS_TEST_IF(IsSwiftshader() || IsANGLE());
1096 
1097     constexpr bool kTestDepth = false;
1098     constexpr float kMSAACoverage = 0.50f;
1099     constexpr uint32_t kSampleMask = 0xFFFFFFFF;
1100     constexpr bool kAlphaToCoverageEnabled = true;
1101     constexpr bool kFlipTriangle = true;
1102 
1103     // For those values of alpha we expect the proportion of samples to be covered
1104     // to correspond to the value of alpha.
1105     // We're assuming in the case of alpha = 0.50f that the implementation
1106     // dependendent algorithm will choose exactly one of the samples covered by the
1107     // triangle.
1108     for (float alpha : {0.0f, 0.50f, 1.00f}) {
1109         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1110         wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(
1111             kTestDepth, kSampleMask, kAlphaToCoverageEnabled, kFlipTriangle);
1112 
1113         const wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, alpha - 0.01f};
1114 
1115         // Draw a green triangle.
1116         {
1117             utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
1118                 {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
1119                 kTestDepth);
1120 
1121             EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen);
1122         }
1123 
1124         wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
1125         queue.Submit(1, &commandBuffer);
1126 
1127         VerifyResolveTarget(kGreen, mResolveTexture, 0, 0, kMSAACoverage * alpha);
1128     }
1129 }
1130 
1131 DAWN_INSTANTIATE_TEST(MultisampledRenderingTest,
1132                       D3D12Backend(),
1133                       D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}),
1134                       D3D12Backend({}, {"use_d3d12_render_pass"}),
1135                       MetalBackend(),
1136                       OpenGLBackend(),
1137                       OpenGLESBackend(),
1138                       VulkanBackend(),
1139                       MetalBackend({"emulate_store_and_msaa_resolve"}),
1140                       MetalBackend({"always_resolve_into_zero_level_and_layer"}),
1141                       MetalBackend({"always_resolve_into_zero_level_and_layer",
1142                                     "emulate_store_and_msaa_resolve"}));
1143