• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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 "common/Assert.h"
16 #include "common/Constants.h"
17 #include "common/Math.h"
18 #include "tests/DawnTest.h"
19 #include "utils/ComboRenderPipelineDescriptor.h"
20 #include "utils/WGPUHelpers.h"
21 
22 constexpr static uint32_t kRTSize = 8;
23 
24 class BindGroupTests : public DawnTest {
25   protected:
SetUp()26     void SetUp() override {
27         DawnTest::SetUp();
28         mMinUniformBufferOffsetAlignment =
29             GetSupportedLimits().limits.minUniformBufferOffsetAlignment;
30     }
CreateSimpleComputeCommandBuffer(const wgpu::ComputePipeline & pipeline,const wgpu::BindGroup & bindGroup)31     wgpu::CommandBuffer CreateSimpleComputeCommandBuffer(const wgpu::ComputePipeline& pipeline,
32                                                          const wgpu::BindGroup& bindGroup) {
33         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
34         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
35         pass.SetPipeline(pipeline);
36         pass.SetBindGroup(0, bindGroup);
37         pass.Dispatch(1);
38         pass.EndPass();
39         return encoder.Finish();
40     }
41 
MakeBasicPipelineLayout(std::vector<wgpu::BindGroupLayout> bindingInitializer) const42     wgpu::PipelineLayout MakeBasicPipelineLayout(
43         std::vector<wgpu::BindGroupLayout> bindingInitializer) const {
44         wgpu::PipelineLayoutDescriptor descriptor;
45 
46         descriptor.bindGroupLayoutCount = bindingInitializer.size();
47         descriptor.bindGroupLayouts = bindingInitializer.data();
48 
49         return device.CreatePipelineLayout(&descriptor);
50     }
51 
MakeSimpleVSModule() const52     wgpu::ShaderModule MakeSimpleVSModule() const {
53         return utils::CreateShaderModule(device, R"(
54         [[stage(vertex)]]
55         fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
56              var pos = array<vec2<f32>, 3>(
57                 vec2<f32>(-1.0, 1.0),
58                 vec2<f32>( 1.0, 1.0),
59                 vec2<f32>(-1.0, -1.0));
60 
61             return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
62         })");
63     }
64 
MakeFSModule(std::vector<wgpu::BufferBindingType> bindingTypes) const65     wgpu::ShaderModule MakeFSModule(std::vector<wgpu::BufferBindingType> bindingTypes) const {
66         ASSERT(bindingTypes.size() <= kMaxBindGroups);
67 
68         std::ostringstream fs;
69         for (size_t i = 0; i < bindingTypes.size(); ++i) {
70             fs << "[[block]] struct Buffer" << i << R"( {
71                 color : vec4<f32>;
72             };)";
73 
74             switch (bindingTypes[i]) {
75                 case wgpu::BufferBindingType::Uniform:
76                     fs << "\n[[group(" << i << "), binding(0)]] var<uniform> buffer" << i
77                        << " : Buffer" << i << ";";
78                     break;
79                 case wgpu::BufferBindingType::Storage:
80                     fs << "\n[[group(" << i << "), binding(0)]] var<storage, read> buffer" << i
81                        << " : Buffer" << i << ";";
82                     break;
83                 default:
84                     UNREACHABLE();
85             }
86         }
87 
88         fs << "\n[[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32>{\n";
89         fs << "var fragColor : vec4<f32> = vec4<f32>();\n";
90         for (size_t i = 0; i < bindingTypes.size(); ++i) {
91             fs << "fragColor = fragColor + buffer" << i << ".color;\n";
92         }
93         fs << "return fragColor;\n";
94         fs << "}\n";
95         return utils::CreateShaderModule(device, fs.str().c_str());
96     }
97 
MakeTestPipeline(const utils::BasicRenderPass & renderPass,std::vector<wgpu::BufferBindingType> bindingTypes,std::vector<wgpu::BindGroupLayout> bindGroupLayouts)98     wgpu::RenderPipeline MakeTestPipeline(const utils::BasicRenderPass& renderPass,
99                                           std::vector<wgpu::BufferBindingType> bindingTypes,
100                                           std::vector<wgpu::BindGroupLayout> bindGroupLayouts) {
101         wgpu::ShaderModule vsModule = MakeSimpleVSModule();
102         wgpu::ShaderModule fsModule = MakeFSModule(bindingTypes);
103 
104         wgpu::PipelineLayout pipelineLayout = MakeBasicPipelineLayout(bindGroupLayouts);
105 
106         utils::ComboRenderPipelineDescriptor pipelineDescriptor;
107         pipelineDescriptor.layout = pipelineLayout;
108         pipelineDescriptor.vertex.module = vsModule;
109         pipelineDescriptor.cFragment.module = fsModule;
110         pipelineDescriptor.cTargets[0].format = renderPass.colorFormat;
111 
112         wgpu::BlendState blend;
113         blend.color.operation = wgpu::BlendOperation::Add;
114         blend.color.srcFactor = wgpu::BlendFactor::One;
115         blend.color.dstFactor = wgpu::BlendFactor::One;
116         blend.alpha.operation = wgpu::BlendOperation::Add;
117         blend.alpha.srcFactor = wgpu::BlendFactor::One;
118         blend.alpha.dstFactor = wgpu::BlendFactor::One;
119 
120         pipelineDescriptor.cTargets[0].blend = &blend;
121 
122         return device.CreateRenderPipeline(&pipelineDescriptor);
123     }
124 
125     uint32_t mMinUniformBufferOffsetAlignment;
126 };
127 
128 // Test a bindgroup reused in two command buffers in the same call to queue.Submit().
129 // This test passes by not asserting or crashing.
TEST_P(BindGroupTests,ReusedBindGroupSingleSubmit)130 TEST_P(BindGroupTests, ReusedBindGroupSingleSubmit) {
131     wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
132         [[block]] struct Contents {
133             f : f32;
134         };
135         [[group(0), binding(0)]] var <uniform> contents: Contents;
136 
137         [[stage(compute), workgroup_size(1)]] fn main() {
138           var f : f32 = contents.f;
139         })");
140 
141     wgpu::ComputePipelineDescriptor cpDesc;
142     cpDesc.compute.module = module;
143     cpDesc.compute.entryPoint = "main";
144     wgpu::ComputePipeline cp = device.CreateComputePipeline(&cpDesc);
145 
146     wgpu::BufferDescriptor bufferDesc;
147     bufferDesc.size = sizeof(float);
148     bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform;
149     wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc);
150     wgpu::BindGroup bindGroup =
151         utils::MakeBindGroup(device, cp.GetBindGroupLayout(0), {{0, buffer}});
152 
153     wgpu::CommandBuffer cb[2];
154     cb[0] = CreateSimpleComputeCommandBuffer(cp, bindGroup);
155     cb[1] = CreateSimpleComputeCommandBuffer(cp, bindGroup);
156     queue.Submit(2, cb);
157 }
158 
159 // Test a bindgroup containing a UBO which is used in both the vertex and fragment shader.
160 // It contains a transformation matrix for the VS and the fragment color for the FS.
161 // These must result in different register offsets in the native APIs.
TEST_P(BindGroupTests,ReusedUBO)162 TEST_P(BindGroupTests, ReusedUBO) {
163     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
164 
165     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
166         // TODO(crbug.com/tint/369): Use a mat2x2 when Tint translates it correctly.
167         [[block]] struct VertexUniformBuffer {
168             transform : vec4<f32>;
169         };
170 
171         [[group(0), binding(0)]] var <uniform> vertexUbo : VertexUniformBuffer;
172 
173         [[stage(vertex)]]
174         fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
175             var pos = array<vec2<f32>, 3>(
176                 vec2<f32>(-1.0, 1.0),
177                 vec2<f32>( 1.0, 1.0),
178                 vec2<f32>(-1.0, -1.0));
179 
180             var transform = mat2x2<f32>(vertexUbo.transform.xy, vertexUbo.transform.zw);
181             return vec4<f32>(transform * pos[VertexIndex], 0.0, 1.0);
182         })");
183 
184     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
185         [[block]] struct FragmentUniformBuffer {
186             color : vec4<f32>;
187         };
188         [[group(0), binding(1)]] var <uniform> fragmentUbo : FragmentUniformBuffer;
189 
190         [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
191             return fragmentUbo.color;
192         })");
193 
194     utils::ComboRenderPipelineDescriptor textureDescriptor;
195     textureDescriptor.vertex.module = vsModule;
196     textureDescriptor.cFragment.module = fsModule;
197     textureDescriptor.cTargets[0].format = renderPass.colorFormat;
198 
199     wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&textureDescriptor);
200 
201     struct Data {
202         float transform[8];
203         char padding[256 - 8 * sizeof(float)];
204         float color[4];
205     };
206     ASSERT(offsetof(Data, color) == 256);
207     Data data{
208         {1.f, 0.f, 0.f, 1.0f},
209         {0},
210         {0.f, 1.f, 0.f, 1.f},
211     };
212     wgpu::Buffer buffer =
213         utils::CreateBufferFromData(device, &data, sizeof(data), wgpu::BufferUsage::Uniform);
214     wgpu::BindGroup bindGroup = utils::MakeBindGroup(
215         device, pipeline.GetBindGroupLayout(0),
216         {{0, buffer, 0, sizeof(Data::transform)}, {1, buffer, 256, sizeof(Data::color)}});
217 
218     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
219     wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
220     pass.SetPipeline(pipeline);
221     pass.SetBindGroup(0, bindGroup);
222     pass.Draw(3);
223     pass.EndPass();
224 
225     wgpu::CommandBuffer commands = encoder.Finish();
226     queue.Submit(1, &commands);
227 
228     RGBA8 filled(0, 255, 0, 255);
229     RGBA8 notFilled(0, 0, 0, 0);
230     uint32_t min = 1, max = kRTSize - 3;
231     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min);
232     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min);
233     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max);
234     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max);
235 }
236 
237 // Test a bindgroup containing a UBO in the vertex shader and a sampler and texture in the fragment
238 // shader. In D3D12 for example, these different types of bindings end up in different namespaces,
239 // but the register offsets used must match between the shader module and descriptor range.
TEST_P(BindGroupTests,UBOSamplerAndTexture)240 TEST_P(BindGroupTests, UBOSamplerAndTexture) {
241     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
242 
243     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
244         // TODO(crbug.com/tint/369): Use a mat2x2 when Tint translates it correctly.
245         [[block]] struct VertexUniformBuffer {
246             transform : vec4<f32>;
247         };
248         [[group(0), binding(0)]] var <uniform> vertexUbo : VertexUniformBuffer;
249 
250         [[stage(vertex)]]
251         fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
252             var pos = array<vec2<f32>, 3>(
253                 vec2<f32>(-1.0, 1.0),
254                 vec2<f32>( 1.0, 1.0),
255                 vec2<f32>(-1.0, -1.0));
256 
257             var transform = mat2x2<f32>(vertexUbo.transform.xy, vertexUbo.transform.zw);
258             return vec4<f32>(transform * pos[VertexIndex], 0.0, 1.0);
259         })");
260 
261     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
262         [[group(0), binding(1)]] var samp : sampler;
263         [[group(0), binding(2)]] var tex : texture_2d<f32>;
264 
265         [[stage(fragment)]]
266         fn main([[builtin(position)]] FragCoord : vec4<f32>) -> [[location(0)]] vec4<f32> {
267             return textureSample(tex, samp, FragCoord.xy);
268         })");
269 
270     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
271     pipelineDescriptor.vertex.module = vsModule;
272     pipelineDescriptor.cFragment.module = fsModule;
273     pipelineDescriptor.cTargets[0].format = renderPass.colorFormat;
274 
275     wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
276 
277     constexpr float transform[] = {1.f, 0.f, 0.f, 1.f};
278     wgpu::Buffer buffer = utils::CreateBufferFromData(device, &transform, sizeof(transform),
279                                                       wgpu::BufferUsage::Uniform);
280 
281     wgpu::SamplerDescriptor samplerDescriptor = {};
282     samplerDescriptor.minFilter = wgpu::FilterMode::Nearest;
283     samplerDescriptor.magFilter = wgpu::FilterMode::Nearest;
284     samplerDescriptor.mipmapFilter = wgpu::FilterMode::Nearest;
285     samplerDescriptor.addressModeU = wgpu::AddressMode::ClampToEdge;
286     samplerDescriptor.addressModeV = wgpu::AddressMode::ClampToEdge;
287     samplerDescriptor.addressModeW = wgpu::AddressMode::ClampToEdge;
288 
289     wgpu::Sampler sampler = device.CreateSampler(&samplerDescriptor);
290 
291     wgpu::TextureDescriptor descriptor;
292     descriptor.dimension = wgpu::TextureDimension::e2D;
293     descriptor.size.width = kRTSize;
294     descriptor.size.height = kRTSize;
295     descriptor.size.depthOrArrayLayers = 1;
296     descriptor.sampleCount = 1;
297     descriptor.format = wgpu::TextureFormat::RGBA8Unorm;
298     descriptor.mipLevelCount = 1;
299     descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding;
300     wgpu::Texture texture = device.CreateTexture(&descriptor);
301     wgpu::TextureView textureView = texture.CreateView();
302 
303     uint32_t width = kRTSize, height = kRTSize;
304     uint32_t widthInBytes = width * sizeof(RGBA8);
305     widthInBytes = (widthInBytes + 255) & ~255;
306     uint32_t sizeInBytes = widthInBytes * height;
307     uint32_t size = sizeInBytes / sizeof(RGBA8);
308     std::vector<RGBA8> data = std::vector<RGBA8>(size);
309     for (uint32_t i = 0; i < size; i++) {
310         data[i] = RGBA8(0, 255, 0, 255);
311     }
312     wgpu::Buffer stagingBuffer =
313         utils::CreateBufferFromData(device, data.data(), sizeInBytes, wgpu::BufferUsage::CopySrc);
314 
315     wgpu::BindGroup bindGroup =
316         utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
317                              {{0, buffer, 0, sizeof(transform)}, {1, sampler}, {2, textureView}});
318 
319     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
320     wgpu::ImageCopyBuffer imageCopyBuffer =
321         utils::CreateImageCopyBuffer(stagingBuffer, 0, widthInBytes);
322     wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
323     wgpu::Extent3D copySize = {width, height, 1};
324     encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, &copySize);
325     wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
326     pass.SetPipeline(pipeline);
327     pass.SetBindGroup(0, bindGroup);
328     pass.Draw(3);
329     pass.EndPass();
330 
331     wgpu::CommandBuffer commands = encoder.Finish();
332     queue.Submit(1, &commands);
333 
334     RGBA8 filled(0, 255, 0, 255);
335     RGBA8 notFilled(0, 0, 0, 0);
336     uint32_t min = 1, max = kRTSize - 3;
337     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min);
338     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min);
339     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max);
340     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max);
341 }
342 
TEST_P(BindGroupTests,MultipleBindLayouts)343 TEST_P(BindGroupTests, MultipleBindLayouts) {
344     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
345 
346     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
347         // TODO(crbug.com/tint/369): Use a mat2x2 when Tint translates it correctly.
348         [[block]] struct VertexUniformBuffer {
349             transform : vec4<f32>;
350         };
351 
352         [[group(0), binding(0)]] var <uniform> vertexUbo1 : VertexUniformBuffer;
353         [[group(1), binding(0)]] var <uniform> vertexUbo2 : VertexUniformBuffer;
354 
355         [[stage(vertex)]]
356         fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
357             var pos = array<vec2<f32>, 3>(
358                 vec2<f32>(-1.0, 1.0),
359                 vec2<f32>( 1.0, 1.0),
360                 vec2<f32>(-1.0, -1.0));
361 
362             return vec4<f32>(mat2x2<f32>(
363                 vertexUbo1.transform.xy + vertexUbo2.transform.xy,
364                 vertexUbo1.transform.zw + vertexUbo2.transform.zw
365             ) * pos[VertexIndex], 0.0, 1.0);
366         })");
367 
368     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
369         [[block]] struct FragmentUniformBuffer {
370             color : vec4<f32>;
371         };
372 
373         [[group(0), binding(1)]] var <uniform> fragmentUbo1 : FragmentUniformBuffer;
374         [[group(1), binding(1)]] var <uniform> fragmentUbo2 : FragmentUniformBuffer;
375 
376         [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
377             return fragmentUbo1.color + fragmentUbo2.color;
378         })");
379 
380     utils::ComboRenderPipelineDescriptor textureDescriptor;
381     textureDescriptor.vertex.module = vsModule;
382     textureDescriptor.cFragment.module = fsModule;
383     textureDescriptor.cTargets[0].format = renderPass.colorFormat;
384 
385     wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&textureDescriptor);
386 
387     struct Data {
388         float transform[4];
389         char padding[256 - 4 * sizeof(float)];
390         float color[4];
391     };
392     ASSERT(offsetof(Data, color) == 256);
393 
394     std::vector<Data> data;
395     std::vector<wgpu::Buffer> buffers;
396     std::vector<wgpu::BindGroup> bindGroups;
397 
398     data.push_back({{1.0f, 0.0f, 0.0f, 0.0f}, {0}, {0.0f, 1.0f, 0.0f, 1.0f}});
399 
400     data.push_back({{0.0f, 0.0f, 0.0f, 1.0f}, {0}, {1.0f, 0.0f, 0.0f, 1.0f}});
401 
402     for (int i = 0; i < 2; i++) {
403         wgpu::Buffer buffer =
404             utils::CreateBufferFromData(device, &data[i], sizeof(Data), wgpu::BufferUsage::Uniform);
405         buffers.push_back(buffer);
406         bindGroups.push_back(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
407                                                   {{0, buffers[i], 0, sizeof(Data::transform)},
408                                                    {1, buffers[i], 256, sizeof(Data::color)}}));
409     }
410 
411     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
412     wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
413     pass.SetPipeline(pipeline);
414     pass.SetBindGroup(0, bindGroups[0]);
415     pass.SetBindGroup(1, bindGroups[1]);
416     pass.Draw(3);
417     pass.EndPass();
418 
419     wgpu::CommandBuffer commands = encoder.Finish();
420     queue.Submit(1, &commands);
421 
422     RGBA8 filled(255, 255, 0, 255);
423     RGBA8 notFilled(0, 0, 0, 0);
424     uint32_t min = 1, max = kRTSize - 3;
425     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min);
426     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min);
427     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max);
428     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max);
429 }
430 
431 // This is a regression test for crbug.com/dawn/1170 that tests a module that contains multiple
432 // entry points, using non-zero binding groups. This has the potential to cause problems when we
433 // only remap bindings for one entry point, as the remaining unmapped binding numbers may be invalid
434 // for certain backends.
435 // This test passes by not asserting or crashing.
TEST_P(BindGroupTests,MultipleEntryPointsWithMultipleNonZeroGroups)436 TEST_P(BindGroupTests, MultipleEntryPointsWithMultipleNonZeroGroups) {
437     wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
438         [[block]] struct Contents {
439             f : f32;
440         };
441         [[group(0), binding(0)]] var <uniform> contents0: Contents;
442         [[group(1), binding(0)]] var <uniform> contents1: Contents;
443         [[group(2), binding(0)]] var <uniform> contents2: Contents;
444 
445         [[stage(compute), workgroup_size(1)]] fn main0() {
446           var a : f32 = contents0.f;
447         }
448 
449         [[stage(compute), workgroup_size(1)]] fn main1() {
450           var a : f32 = contents1.f;
451           var b : f32 = contents2.f;
452         }
453 
454         [[stage(compute), workgroup_size(1)]] fn main2() {
455           var a : f32 = contents0.f;
456           var b : f32 = contents1.f;
457           var c : f32 = contents2.f;
458         })");
459 
460     // main0: bind (0,0)
461     {
462         wgpu::ComputePipelineDescriptor cpDesc;
463         cpDesc.compute.module = module;
464         cpDesc.compute.entryPoint = "main0";
465         wgpu::ComputePipeline cp = device.CreateComputePipeline(&cpDesc);
466 
467         wgpu::BufferDescriptor bufferDesc;
468         bufferDesc.size = sizeof(float);
469         bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform;
470         wgpu::Buffer buffer0 = device.CreateBuffer(&bufferDesc);
471         wgpu::BindGroup bindGroup0 =
472             utils::MakeBindGroup(device, cp.GetBindGroupLayout(0), {{0, buffer0}});
473 
474         wgpu::CommandBuffer cb;
475         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
476         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
477         pass.SetPipeline(cp);
478         pass.SetBindGroup(0, bindGroup0);
479         pass.Dispatch(1);
480         pass.EndPass();
481         cb = encoder.Finish();
482         queue.Submit(1, &cb);
483     }
484 
485     // main1: bind (1,0) and (2,0)
486     {
487         wgpu::ComputePipelineDescriptor cpDesc;
488         cpDesc.compute.module = module;
489         cpDesc.compute.entryPoint = "main1";
490         wgpu::ComputePipeline cp = device.CreateComputePipeline(&cpDesc);
491 
492         wgpu::BufferDescriptor bufferDesc;
493         bufferDesc.size = sizeof(float);
494         bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform;
495         wgpu::Buffer buffer1 = device.CreateBuffer(&bufferDesc);
496         wgpu::Buffer buffer2 = device.CreateBuffer(&bufferDesc);
497         wgpu::BindGroup bindGroup0 = utils::MakeBindGroup(device, cp.GetBindGroupLayout(0), {});
498         wgpu::BindGroup bindGroup1 =
499             utils::MakeBindGroup(device, cp.GetBindGroupLayout(1), {{0, buffer1}});
500         wgpu::BindGroup bindGroup2 =
501             utils::MakeBindGroup(device, cp.GetBindGroupLayout(2), {{0, buffer2}});
502 
503         wgpu::CommandBuffer cb;
504         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
505         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
506         pass.SetPipeline(cp);
507         pass.SetBindGroup(0, bindGroup0);
508         pass.SetBindGroup(1, bindGroup1);
509         pass.SetBindGroup(2, bindGroup2);
510         pass.Dispatch(1);
511         pass.EndPass();
512         cb = encoder.Finish();
513         queue.Submit(1, &cb);
514     }
515 
516     // main2: bind (0,0), (1,0), and (2,0)
517     {
518         wgpu::ComputePipelineDescriptor cpDesc;
519         cpDesc.compute.module = module;
520         cpDesc.compute.entryPoint = "main2";
521         wgpu::ComputePipeline cp = device.CreateComputePipeline(&cpDesc);
522 
523         wgpu::BufferDescriptor bufferDesc;
524         bufferDesc.size = sizeof(float);
525         bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform;
526         wgpu::Buffer buffer0 = device.CreateBuffer(&bufferDesc);
527         wgpu::Buffer buffer1 = device.CreateBuffer(&bufferDesc);
528         wgpu::Buffer buffer2 = device.CreateBuffer(&bufferDesc);
529         wgpu::BindGroup bindGroup0 =
530             utils::MakeBindGroup(device, cp.GetBindGroupLayout(0), {{0, buffer0}});
531         wgpu::BindGroup bindGroup1 =
532             utils::MakeBindGroup(device, cp.GetBindGroupLayout(1), {{0, buffer1}});
533         wgpu::BindGroup bindGroup2 =
534             utils::MakeBindGroup(device, cp.GetBindGroupLayout(2), {{0, buffer2}});
535 
536         wgpu::CommandBuffer cb;
537         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
538         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
539         pass.SetPipeline(cp);
540         pass.SetBindGroup(0, bindGroup0);
541         pass.SetBindGroup(1, bindGroup1);
542         pass.SetBindGroup(2, bindGroup2);
543         pass.Dispatch(1);
544         pass.EndPass();
545         cb = encoder.Finish();
546         queue.Submit(1, &cb);
547     }
548 }
549 
550 // This test reproduces an out-of-bound bug on D3D12 backends when calling draw command twice with
551 // one pipeline that has 4 bind group sets in one render pass.
TEST_P(BindGroupTests,DrawTwiceInSamePipelineWithFourBindGroupSets)552 TEST_P(BindGroupTests, DrawTwiceInSamePipelineWithFourBindGroupSets) {
553     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
554 
555     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
556         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform}});
557 
558     wgpu::RenderPipeline pipeline =
559         MakeTestPipeline(renderPass,
560                          {wgpu::BufferBindingType::Uniform, wgpu::BufferBindingType::Uniform,
561                           wgpu::BufferBindingType::Uniform, wgpu::BufferBindingType::Uniform},
562                          {layout, layout, layout, layout});
563 
564     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
565     wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
566 
567     pass.SetPipeline(pipeline);
568 
569     // The color will be added 8 times, so the value should be 0.125. But we choose 0.126
570     // because of precision issues on some devices (for example NVIDIA bots).
571     std::array<float, 4> color = {0.126, 0, 0, 0.126};
572     wgpu::Buffer uniformBuffer =
573         utils::CreateBufferFromData(device, &color, sizeof(color), wgpu::BufferUsage::Uniform);
574     wgpu::BindGroup bindGroup =
575         utils::MakeBindGroup(device, layout, {{0, uniformBuffer, 0, sizeof(color)}});
576 
577     pass.SetBindGroup(0, bindGroup);
578     pass.SetBindGroup(1, bindGroup);
579     pass.SetBindGroup(2, bindGroup);
580     pass.SetBindGroup(3, bindGroup);
581     pass.Draw(3);
582 
583     pass.SetPipeline(pipeline);
584     pass.Draw(3);
585     pass.EndPass();
586 
587     wgpu::CommandBuffer commands = encoder.Finish();
588     queue.Submit(1, &commands);
589 
590     RGBA8 filled(255, 0, 0, 255);
591     RGBA8 notFilled(0, 0, 0, 0);
592     uint32_t min = 1, max = kRTSize - 3;
593     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min);
594     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min);
595     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max);
596     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max);
597 }
598 
599 // Test that bind groups can be set before the pipeline.
TEST_P(BindGroupTests,SetBindGroupBeforePipeline)600 TEST_P(BindGroupTests, SetBindGroupBeforePipeline) {
601     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
602 
603     // Create a bind group layout which uses a single uniform buffer.
604     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
605         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform}});
606 
607     // Create a pipeline that uses the uniform bind group layout.
608     wgpu::RenderPipeline pipeline =
609         MakeTestPipeline(renderPass, {wgpu::BufferBindingType::Uniform}, {layout});
610 
611     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
612     wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
613 
614     // Create a bind group with a uniform buffer and fill it with RGBAunorm(1, 0, 0, 1).
615     std::array<float, 4> color = {1, 0, 0, 1};
616     wgpu::Buffer uniformBuffer =
617         utils::CreateBufferFromData(device, &color, sizeof(color), wgpu::BufferUsage::Uniform);
618     wgpu::BindGroup bindGroup =
619         utils::MakeBindGroup(device, layout, {{0, uniformBuffer, 0, sizeof(color)}});
620 
621     // Set the bind group, then the pipeline, and draw.
622     pass.SetBindGroup(0, bindGroup);
623     pass.SetPipeline(pipeline);
624     pass.Draw(3);
625 
626     pass.EndPass();
627 
628     wgpu::CommandBuffer commands = encoder.Finish();
629     queue.Submit(1, &commands);
630 
631     // The result should be red.
632     RGBA8 filled(255, 0, 0, 255);
633     RGBA8 notFilled(0, 0, 0, 0);
634     uint32_t min = 1, max = kRTSize - 3;
635     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min);
636     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min);
637     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max);
638     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max);
639 }
640 
641 // Test that dynamic bind groups can be set before the pipeline.
TEST_P(BindGroupTests,SetDynamicBindGroupBeforePipeline)642 TEST_P(BindGroupTests, SetDynamicBindGroupBeforePipeline) {
643     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
644 
645     // Create a bind group layout which uses a single dynamic uniform buffer.
646     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
647         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform, true}});
648 
649     // Create a pipeline that uses the dynamic uniform bind group layout for two bind groups.
650     wgpu::RenderPipeline pipeline = MakeTestPipeline(
651         renderPass, {wgpu::BufferBindingType::Uniform, wgpu::BufferBindingType::Uniform},
652         {layout, layout});
653 
654     // Prepare data RGBAunorm(1, 0, 0, 0.5) and RGBAunorm(0, 1, 0, 0.5). They will be added in the
655     // shader.
656     std::array<float, 4> color0 = {1, 0, 0, 0.501};
657     std::array<float, 4> color1 = {0, 1, 0, 0.501};
658 
659     size_t color1Offset = Align(sizeof(color0), mMinUniformBufferOffsetAlignment);
660 
661     std::vector<uint8_t> data(color1Offset + sizeof(color1));
662     memcpy(data.data(), color0.data(), sizeof(color0));
663     memcpy(data.data() + color1Offset, color1.data(), sizeof(color1));
664 
665     // Create a bind group and uniform buffer with the color data. It will be bound at the offset
666     // to each color.
667     wgpu::Buffer uniformBuffer =
668         utils::CreateBufferFromData(device, data.data(), data.size(), wgpu::BufferUsage::Uniform);
669     wgpu::BindGroup bindGroup =
670         utils::MakeBindGroup(device, layout, {{0, uniformBuffer, 0, 4 * sizeof(float)}});
671 
672     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
673     wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
674 
675     // Set the first dynamic bind group.
676     uint32_t dynamicOffset = 0;
677     pass.SetBindGroup(0, bindGroup, 1, &dynamicOffset);
678 
679     // Set the second dynamic bind group.
680     dynamicOffset = color1Offset;
681     pass.SetBindGroup(1, bindGroup, 1, &dynamicOffset);
682 
683     // Set the pipeline and draw.
684     pass.SetPipeline(pipeline);
685     pass.Draw(3);
686 
687     pass.EndPass();
688 
689     wgpu::CommandBuffer commands = encoder.Finish();
690     queue.Submit(1, &commands);
691 
692     // The result should be RGBAunorm(1, 0, 0, 0.5) + RGBAunorm(0, 1, 0, 0.5)
693     RGBA8 filled(255, 255, 0, 255);
694     RGBA8 notFilled(0, 0, 0, 0);
695     uint32_t min = 1, max = kRTSize - 3;
696     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min);
697     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min);
698     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max);
699     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max);
700 }
701 
702 // Test that bind groups set for one pipeline are still set when the pipeline changes.
TEST_P(BindGroupTests,BindGroupsPersistAfterPipelineChange)703 TEST_P(BindGroupTests, BindGroupsPersistAfterPipelineChange) {
704     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
705 
706     // Create a bind group layout which uses a single dynamic uniform buffer.
707     wgpu::BindGroupLayout uniformLayout = utils::MakeBindGroupLayout(
708         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform, true}});
709 
710     // Create a bind group layout which uses a single dynamic storage buffer.
711     wgpu::BindGroupLayout storageLayout = utils::MakeBindGroupLayout(
712         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage, true}});
713 
714     // Create a pipeline which uses the uniform buffer and storage buffer bind groups.
715     wgpu::RenderPipeline pipeline0 = MakeTestPipeline(
716         renderPass, {wgpu::BufferBindingType::Uniform, wgpu::BufferBindingType::Storage},
717         {uniformLayout, storageLayout});
718 
719     // Create a pipeline which uses the uniform buffer bind group twice.
720     wgpu::RenderPipeline pipeline1 = MakeTestPipeline(
721         renderPass, {wgpu::BufferBindingType::Uniform, wgpu::BufferBindingType::Uniform},
722         {uniformLayout, uniformLayout});
723 
724     // Prepare data RGBAunorm(1, 0, 0, 0.5) and RGBAunorm(0, 1, 0, 0.5). They will be added in the
725     // shader.
726     std::array<float, 4> color0 = {1, 0, 0, 0.5};
727     std::array<float, 4> color1 = {0, 1, 0, 0.5};
728 
729     size_t color1Offset = Align(sizeof(color0), mMinUniformBufferOffsetAlignment);
730 
731     std::vector<uint8_t> data(color1Offset + sizeof(color1));
732     memcpy(data.data(), color0.data(), sizeof(color0));
733     memcpy(data.data() + color1Offset, color1.data(), sizeof(color1));
734 
735     // Create a bind group and uniform buffer with the color data. It will be bound at the offset
736     // to each color.
737     wgpu::Buffer uniformBuffer =
738         utils::CreateBufferFromData(device, data.data(), data.size(), wgpu::BufferUsage::Uniform);
739     wgpu::BindGroup bindGroup =
740         utils::MakeBindGroup(device, uniformLayout, {{0, uniformBuffer, 0, 4 * sizeof(float)}});
741 
742     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
743     wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
744 
745     // Set the first pipeline (uniform, storage).
746     pass.SetPipeline(pipeline0);
747 
748     // Set the first bind group at a dynamic offset.
749     // This bind group matches the slot in the pipeline layout.
750     uint32_t dynamicOffset = 0;
751     pass.SetBindGroup(0, bindGroup, 1, &dynamicOffset);
752 
753     // Set the second bind group at a dynamic offset.
754     // This bind group does not match the slot in the pipeline layout.
755     dynamicOffset = color1Offset;
756     pass.SetBindGroup(1, bindGroup, 1, &dynamicOffset);
757 
758     // Set the second pipeline (uniform, uniform).
759     // Both bind groups match the pipeline.
760     // They should persist and not need to be bound again.
761     pass.SetPipeline(pipeline1);
762     pass.Draw(3);
763 
764     pass.EndPass();
765 
766     wgpu::CommandBuffer commands = encoder.Finish();
767     queue.Submit(1, &commands);
768 
769     // The result should be RGBAunorm(1, 0, 0, 0.5) + RGBAunorm(0, 1, 0, 0.5)
770     RGBA8 filled(255, 255, 0, 255);
771     RGBA8 notFilled(0, 0, 0, 0);
772     uint32_t min = 1, max = kRTSize - 3;
773     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min);
774     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min);
775     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max);
776     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max);
777 }
778 
779 // Do a successful draw. Then, change the pipeline and one bind group.
780 // Draw to check that the all bind groups are set.
TEST_P(BindGroupTests,DrawThenChangePipelineAndBindGroup)781 TEST_P(BindGroupTests, DrawThenChangePipelineAndBindGroup) {
782     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
783 
784     // Create a bind group layout which uses a single dynamic uniform buffer.
785     wgpu::BindGroupLayout uniformLayout = utils::MakeBindGroupLayout(
786         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform, true}});
787 
788     // Create a bind group layout which uses a single dynamic storage buffer.
789     wgpu::BindGroupLayout storageLayout = utils::MakeBindGroupLayout(
790         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage, true}});
791 
792     // Create a pipeline with pipeline layout (uniform, uniform, storage).
793     wgpu::RenderPipeline pipeline0 =
794         MakeTestPipeline(renderPass,
795                          {wgpu::BufferBindingType::Uniform, wgpu::BufferBindingType::Uniform,
796                           wgpu::BufferBindingType::Storage},
797                          {uniformLayout, uniformLayout, storageLayout});
798 
799     // Create a pipeline with pipeline layout (uniform, storage, storage).
800     wgpu::RenderPipeline pipeline1 =
801         MakeTestPipeline(renderPass,
802                          {wgpu::BufferBindingType::Uniform, wgpu::BufferBindingType::Storage,
803                           wgpu::BufferBindingType::Storage},
804                          {uniformLayout, storageLayout, storageLayout});
805 
806     // Prepare color data.
807     // The first draw will use { color0, color1, color2 }.
808     // The second draw will use { color0, color3, color2 }.
809     // The pipeline uses additive color and alpha blending so the result of two draws should be
810     // { 2 * color0 + color1 + 2 * color2 + color3} = RGBAunorm(1, 1, 1, 1)
811     std::array<float, 4> color0 = {0.501, 0, 0, 0};
812     std::array<float, 4> color1 = {0, 1, 0, 0};
813     std::array<float, 4> color2 = {0, 0, 0, 0.501};
814     std::array<float, 4> color3 = {0, 0, 1, 0};
815 
816     size_t color1Offset = Align(sizeof(color0), mMinUniformBufferOffsetAlignment);
817     size_t color2Offset = Align(color1Offset + sizeof(color1), mMinUniformBufferOffsetAlignment);
818     size_t color3Offset = Align(color2Offset + sizeof(color2), mMinUniformBufferOffsetAlignment);
819 
820     std::vector<uint8_t> data(color3Offset + sizeof(color3), 0);
821     memcpy(data.data(), color0.data(), sizeof(color0));
822     memcpy(data.data() + color1Offset, color1.data(), sizeof(color1));
823     memcpy(data.data() + color2Offset, color2.data(), sizeof(color2));
824     memcpy(data.data() + color3Offset, color3.data(), sizeof(color3));
825 
826     // Create a uniform and storage buffer bind groups to bind the color data.
827     wgpu::Buffer uniformBuffer =
828         utils::CreateBufferFromData(device, data.data(), data.size(), wgpu::BufferUsage::Uniform);
829 
830     wgpu::Buffer storageBuffer =
831         utils::CreateBufferFromData(device, data.data(), data.size(), wgpu::BufferUsage::Storage);
832 
833     wgpu::BindGroup uniformBindGroup =
834         utils::MakeBindGroup(device, uniformLayout, {{0, uniformBuffer, 0, 4 * sizeof(float)}});
835     wgpu::BindGroup storageBindGroup =
836         utils::MakeBindGroup(device, storageLayout, {{0, storageBuffer, 0, 4 * sizeof(float)}});
837 
838     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
839     wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
840 
841     // Set the pipeline to (uniform, uniform, storage)
842     pass.SetPipeline(pipeline0);
843 
844     // Set the first bind group to color0 in the dynamic uniform buffer.
845     uint32_t dynamicOffset = 0;
846     pass.SetBindGroup(0, uniformBindGroup, 1, &dynamicOffset);
847 
848     // Set the first bind group to color1 in the dynamic uniform buffer.
849     dynamicOffset = color1Offset;
850     pass.SetBindGroup(1, uniformBindGroup, 1, &dynamicOffset);
851 
852     // Set the first bind group to color2 in the dynamic storage buffer.
853     dynamicOffset = color2Offset;
854     pass.SetBindGroup(2, storageBindGroup, 1, &dynamicOffset);
855 
856     pass.Draw(3);
857 
858     // Set the pipeline to (uniform, storage, storage)
859     //  - The first bind group should persist (inherited on some backends)
860     //  - The second bind group needs to be set again to pass validation.
861     //    It changed from uniform to storage.
862     //  - The third bind group should persist. It should be set again by the backend internally.
863     pass.SetPipeline(pipeline1);
864 
865     // Set the second bind group to color3 in the dynamic storage buffer.
866     dynamicOffset = color3Offset;
867     pass.SetBindGroup(1, storageBindGroup, 1, &dynamicOffset);
868 
869     pass.Draw(3);
870     pass.EndPass();
871 
872     wgpu::CommandBuffer commands = encoder.Finish();
873     queue.Submit(1, &commands);
874 
875     RGBA8 filled(255, 255, 255, 255);
876     RGBA8 notFilled(0, 0, 0, 0);
877     uint32_t min = 1, max = kRTSize - 3;
878     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min);
879     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min);
880     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max);
881     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max);
882 }
883 
884 // Test for crbug.com/dawn/1049, where setting a pipeline without drawing can prevent
885 // bind groups from being applied later
TEST_P(BindGroupTests,DrawThenChangePipelineTwiceAndBindGroup)886 TEST_P(BindGroupTests, DrawThenChangePipelineTwiceAndBindGroup) {
887     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
888 
889     // Create a bind group layout which uses a single dynamic uniform buffer.
890     wgpu::BindGroupLayout uniformLayout = utils::MakeBindGroupLayout(
891         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform, true}});
892 
893     // Create a pipeline with pipeline layout (uniform, uniform, uniform).
894     wgpu::RenderPipeline pipeline0 =
895         MakeTestPipeline(renderPass,
896                          {wgpu::BufferBindingType::Uniform, wgpu::BufferBindingType::Uniform,
897                           wgpu::BufferBindingType::Uniform},
898                          {uniformLayout, uniformLayout, uniformLayout});
899 
900     // Create a pipeline with pipeline layout (uniform).
901     wgpu::RenderPipeline pipeline1 = MakeTestPipeline(
902         renderPass, {wgpu::BufferBindingType::Uniform, wgpu::BufferBindingType::Uniform},
903         {uniformLayout, uniformLayout});
904 
905     // Prepare color data.
906     // The first draw will use { color0, color1, color2 }.
907     // The second draw will use { color0, color1, color3 }.
908     // The pipeline uses additive color and alpha so the result of two draws should be
909     // { 2 * color0 + 2 * color1 + color2 + color3} = RGBAunorm(1, 1, 1, 1)
910     std::array<float, 4> color0 = {0.501, 0, 0, 0};
911     std::array<float, 4> color1 = {0, 0.501, 0, 0};
912     std::array<float, 4> color2 = {0, 0, 1, 0};
913     std::array<float, 4> color3 = {0, 0, 0, 1};
914 
915     size_t color0Offset = 0;
916     size_t color1Offset = Align(color0Offset + sizeof(color0), mMinUniformBufferOffsetAlignment);
917     size_t color2Offset = Align(color1Offset + sizeof(color1), mMinUniformBufferOffsetAlignment);
918     size_t color3Offset = Align(color2Offset + sizeof(color2), mMinUniformBufferOffsetAlignment);
919 
920     std::vector<uint8_t> data(color3Offset + sizeof(color3), 0);
921     memcpy(data.data(), color0.data(), sizeof(color0));
922     memcpy(data.data() + color1Offset, color1.data(), sizeof(color1));
923     memcpy(data.data() + color2Offset, color2.data(), sizeof(color2));
924     memcpy(data.data() + color3Offset, color3.data(), sizeof(color3));
925 
926     // Create a uniform and storage buffer bind groups to bind the color data.
927     wgpu::Buffer uniformBuffer =
928         utils::CreateBufferFromData(device, data.data(), data.size(), wgpu::BufferUsage::Uniform);
929 
930     wgpu::BindGroup uniformBindGroup =
931         utils::MakeBindGroup(device, uniformLayout, {{0, uniformBuffer, 0, 4 * sizeof(float)}});
932 
933     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
934     wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
935 
936     // Set the pipeline to (uniform, uniform, uniform)
937     pass.SetPipeline(pipeline0);
938 
939     // Set the first bind group to color0 in the dynamic uniform buffer.
940     uint32_t dynamicOffset = color0Offset;
941     pass.SetBindGroup(0, uniformBindGroup, 1, &dynamicOffset);
942 
943     // Set the first bind group to color1 in the dynamic uniform buffer.
944     dynamicOffset = color1Offset;
945     pass.SetBindGroup(1, uniformBindGroup, 1, &dynamicOffset);
946 
947     // Set the first bind group to color2 in the dynamic uniform buffer.
948     dynamicOffset = color2Offset;
949     pass.SetBindGroup(2, uniformBindGroup, 1, &dynamicOffset);
950 
951     // This draw will internally apply bind groups for pipeline 0.
952     pass.Draw(3);
953 
954     // When we set pipeline 1, which has no bind group at index 2 in its layout, it
955     // should not prevent bind group 2 from being used after reverting to pipeline 0.
956     // More specifically, internally the pipeline 1 layout should not be saved,
957     // because we never applied the bind groups via a Draw or Dispatch.
958     pass.SetPipeline(pipeline1);
959 
960     // Set the second bind group to color3 in the dynamic uniform buffer.
961     dynamicOffset = color3Offset;
962     pass.SetBindGroup(2, uniformBindGroup, 1, &dynamicOffset);
963 
964     // Revert to pipeline 0
965     pass.SetPipeline(pipeline0);
966 
967     // Internally this should re-apply bind group 2. Because we already
968     // drew with this pipeline, and setting pipeline 1 did not dirty the bind groups,
969     // bind groups 0 and 1 should still be valid.
970     pass.Draw(3);
971 
972     pass.EndPass();
973 
974     wgpu::CommandBuffer commands = encoder.Finish();
975     queue.Submit(1, &commands);
976 
977     RGBA8 filled(255, 255, 255, 255);
978     RGBA8 notFilled(0, 0, 0, 0);
979     uint32_t min = 1, max = kRTSize - 3;
980     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min);
981     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min);
982     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max);
983     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max);
984 }
985 
986 // Regression test for crbug.com/dawn/408 where dynamic offsets were applied in the wrong order.
987 // Dynamic offsets should be applied in increasing order of binding number.
TEST_P(BindGroupTests,DynamicOffsetOrder)988 TEST_P(BindGroupTests, DynamicOffsetOrder) {
989     // We will put the following values and the respective offsets into a buffer.
990     // The test will ensure that the correct dynamic offset is applied to each buffer by reading the
991     // value from an offset binding.
992     std::array<uint32_t, 3> offsets = {3 * mMinUniformBufferOffsetAlignment,
993                                        1 * mMinUniformBufferOffsetAlignment,
994                                        2 * mMinUniformBufferOffsetAlignment};
995     std::array<uint32_t, 3> values = {21, 67, 32};
996 
997     // Create three buffers large enough to by offset by the largest offset.
998     wgpu::BufferDescriptor bufferDescriptor;
999     bufferDescriptor.size = 3 * mMinUniformBufferOffsetAlignment + sizeof(uint32_t);
1000     bufferDescriptor.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst;
1001 
1002     wgpu::Buffer buffer0 = device.CreateBuffer(&bufferDescriptor);
1003     wgpu::Buffer buffer3 = device.CreateBuffer(&bufferDescriptor);
1004 
1005     // This test uses both storage and uniform buffers to ensure buffer bindings are sorted first by
1006     // binding number before type.
1007     bufferDescriptor.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
1008     wgpu::Buffer buffer2 = device.CreateBuffer(&bufferDescriptor);
1009 
1010     // Populate the values
1011     queue.WriteBuffer(buffer0, offsets[0], &values[0], sizeof(uint32_t));
1012     queue.WriteBuffer(buffer2, offsets[1], &values[1], sizeof(uint32_t));
1013     queue.WriteBuffer(buffer3, offsets[2], &values[2], sizeof(uint32_t));
1014 
1015     wgpu::Buffer outputBuffer = utils::CreateBufferFromData(
1016         device, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::Storage, {0, 0, 0});
1017 
1018     // Create the bind group and bind group layout.
1019     // Note: The order of the binding numbers are intentionally different and not in increasing
1020     // order.
1021     wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1022         device, {
1023                     {3, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage, true},
1024                     {0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage, true},
1025                     {2, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform, true},
1026                     {4, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage},
1027                 });
1028     wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl,
1029                                                      {
1030                                                          {0, buffer0, 0, sizeof(uint32_t)},
1031                                                          {3, buffer3, 0, sizeof(uint32_t)},
1032                                                          {2, buffer2, 0, sizeof(uint32_t)},
1033                                                          {4, outputBuffer, 0, 3 * sizeof(uint32_t)},
1034                                                      });
1035 
1036     wgpu::ComputePipelineDescriptor pipelineDescriptor;
1037     pipelineDescriptor.compute.module = utils::CreateShaderModule(device, R"(
1038         [[block]] struct Buffer {
1039             value : u32;
1040         };
1041 
1042         [[block]] struct OutputBuffer {
1043             value : vec3<u32>;
1044         };
1045 
1046         [[group(0), binding(2)]] var<uniform> buffer2 : Buffer;
1047         [[group(0), binding(3)]] var<storage, read> buffer3 : Buffer;
1048         [[group(0), binding(0)]] var<storage, read> buffer0 : Buffer;
1049         [[group(0), binding(4)]] var<storage, read_write> outputBuffer : OutputBuffer;
1050 
1051         [[stage(compute), workgroup_size(1)]] fn main() {
1052             outputBuffer.value = vec3<u32>(buffer0.value, buffer2.value, buffer3.value);
1053         })");
1054     pipelineDescriptor.compute.entryPoint = "main";
1055     pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl);
1056     wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDescriptor);
1057 
1058     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1059     wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
1060     computePassEncoder.SetPipeline(pipeline);
1061     computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data());
1062     computePassEncoder.Dispatch(1);
1063     computePassEncoder.EndPass();
1064 
1065     wgpu::CommandBuffer commands = commandEncoder.Finish();
1066     queue.Submit(1, &commands);
1067 
1068     EXPECT_BUFFER_U32_RANGE_EQ(values.data(), outputBuffer, 0, values.size());
1069 }
1070 
1071 // Test that ensures that backends do not remap bindings such that dynamic and non-dynamic bindings
1072 // conflict. This can happen if the backend treats dynamic bindings separately from non-dynamic
1073 // bindings.
TEST_P(BindGroupTests,DynamicAndNonDynamicBindingsDoNotConflictAfterRemapping)1074 TEST_P(BindGroupTests, DynamicAndNonDynamicBindingsDoNotConflictAfterRemapping) {
1075     // // TODO(crbug.com/dawn/1106): Test output is wrong on D3D12 using WARP.
1076     DAWN_SUPPRESS_TEST_IF(IsWARP());
1077 
1078     auto RunTestWith = [&](bool dynamicBufferFirst) {
1079         uint32_t dynamicBufferBindingNumber = dynamicBufferFirst ? 0 : 1;
1080         uint32_t bufferBindingNumber = dynamicBufferFirst ? 1 : 0;
1081 
1082         std::array<uint32_t, 1> offsets{mMinUniformBufferOffsetAlignment};
1083         std::array<uint32_t, 2> values = {21, 67};
1084 
1085         // Create three buffers large enough to by offset by the largest offset.
1086         wgpu::BufferDescriptor bufferDescriptor;
1087         bufferDescriptor.size = 2 * mMinUniformBufferOffsetAlignment + sizeof(uint32_t);
1088         bufferDescriptor.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
1089 
1090         wgpu::Buffer dynamicBuffer = device.CreateBuffer(&bufferDescriptor);
1091         wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor);
1092 
1093         // Populate the values
1094         queue.WriteBuffer(dynamicBuffer, mMinUniformBufferOffsetAlignment,
1095                           &values[dynamicBufferBindingNumber], sizeof(uint32_t));
1096         queue.WriteBuffer(buffer, 0, &values[bufferBindingNumber], sizeof(uint32_t));
1097 
1098         wgpu::Buffer outputBuffer = utils::CreateBufferFromData(
1099             device, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::Storage, {0, 0});
1100 
1101         // Create a bind group layout which uses a single dynamic uniform buffer.
1102         wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1103             device,
1104             {
1105                 {dynamicBufferBindingNumber, wgpu::ShaderStage::Compute,
1106                  wgpu::BufferBindingType::Uniform, true},
1107                 {bufferBindingNumber, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform},
1108                 {2, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage},
1109             });
1110 
1111         wgpu::BindGroup bindGroup = utils::MakeBindGroup(
1112             device, bgl,
1113             {
1114                 {dynamicBufferBindingNumber, dynamicBuffer, 0, sizeof(uint32_t)},
1115                 {bufferBindingNumber, buffer, 0, sizeof(uint32_t)},
1116                 {2, outputBuffer, 0, 2 * sizeof(uint32_t)},
1117             });
1118 
1119         wgpu::ComputePipelineDescriptor pipelineDescriptor;
1120         pipelineDescriptor.compute.module = utils::CreateShaderModule(device, R"(
1121         [[block]] struct Buffer {
1122             value : u32;
1123         };
1124 
1125         [[block]] struct OutputBuffer {
1126             value : vec2<u32>;
1127         };
1128 
1129         [[group(0), binding(0)]] var<uniform> buffer0 : Buffer;
1130         [[group(0), binding(1)]] var<uniform> buffer1 : Buffer;
1131         [[group(0), binding(2)]] var<storage, read_write> outputBuffer : OutputBuffer;
1132 
1133         [[stage(compute), workgroup_size(1)]] fn main() {
1134             outputBuffer.value = vec2<u32>(buffer0.value, buffer1.value);
1135         })");
1136         pipelineDescriptor.compute.entryPoint = "main";
1137         pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl);
1138         wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDescriptor);
1139 
1140         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1141         wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
1142         computePassEncoder.SetPipeline(pipeline);
1143         computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data());
1144         computePassEncoder.Dispatch(1);
1145         computePassEncoder.EndPass();
1146 
1147         wgpu::CommandBuffer commands = commandEncoder.Finish();
1148         queue.Submit(1, &commands);
1149 
1150         EXPECT_BUFFER_U32_RANGE_EQ(values.data(), outputBuffer, 0, values.size());
1151     };
1152 
1153     // Run the test with the dynamic buffer in index 0 and with the non-dynamic buffer in index 1,
1154     // and vice versa. This should cause a conflict at index 0, if the binding remapping is too
1155     // aggressive.
1156     RunTestWith(true);
1157     RunTestWith(false);
1158 }
1159 
1160 // Test that visibility of bindings in BindGroupLayout can be none
1161 // This test passes by not asserting or crashing.
TEST_P(BindGroupTests,BindGroupLayoutVisibilityCanBeNone)1162 TEST_P(BindGroupTests, BindGroupLayoutVisibilityCanBeNone) {
1163     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
1164 
1165     wgpu::BindGroupLayoutEntry entry;
1166     entry.binding = 0;
1167     entry.visibility = wgpu::ShaderStage::None;
1168     entry.buffer.type = wgpu::BufferBindingType::Uniform;
1169     wgpu::BindGroupLayoutDescriptor descriptor;
1170     descriptor.entryCount = 1;
1171     descriptor.entries = &entry;
1172     wgpu::BindGroupLayout layout = device.CreateBindGroupLayout(&descriptor);
1173 
1174     wgpu::RenderPipeline pipeline = MakeTestPipeline(renderPass, {}, {layout});
1175 
1176     std::array<float, 4> color = {1, 0, 0, 1};
1177     wgpu::Buffer uniformBuffer =
1178         utils::CreateBufferFromData(device, &color, sizeof(color), wgpu::BufferUsage::Uniform);
1179     wgpu::BindGroup bindGroup =
1180         utils::MakeBindGroup(device, layout, {{0, uniformBuffer, 0, sizeof(color)}});
1181 
1182     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1183     wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
1184     pass.SetPipeline(pipeline);
1185     pass.SetBindGroup(0, bindGroup);
1186     pass.Draw(3);
1187     pass.EndPass();
1188 
1189     wgpu::CommandBuffer commands = encoder.Finish();
1190     queue.Submit(1, &commands);
1191 }
1192 
1193 // Regression test for crbug.com/dawn/448 that dynamic buffer bindings can have None visibility.
TEST_P(BindGroupTests,DynamicBindingNoneVisibility)1194 TEST_P(BindGroupTests, DynamicBindingNoneVisibility) {
1195     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
1196 
1197     wgpu::BindGroupLayoutEntry entry;
1198     entry.binding = 0;
1199     entry.visibility = wgpu::ShaderStage::None;
1200     entry.buffer.type = wgpu::BufferBindingType::Uniform;
1201     entry.buffer.hasDynamicOffset = true;
1202     wgpu::BindGroupLayoutDescriptor descriptor;
1203     descriptor.entryCount = 1;
1204     descriptor.entries = &entry;
1205     wgpu::BindGroupLayout layout = device.CreateBindGroupLayout(&descriptor);
1206 
1207     wgpu::RenderPipeline pipeline = MakeTestPipeline(renderPass, {}, {layout});
1208 
1209     std::array<float, 4> color = {1, 0, 0, 1};
1210     wgpu::Buffer uniformBuffer =
1211         utils::CreateBufferFromData(device, &color, sizeof(color), wgpu::BufferUsage::Uniform);
1212     wgpu::BindGroup bindGroup =
1213         utils::MakeBindGroup(device, layout, {{0, uniformBuffer, 0, sizeof(color)}});
1214 
1215     uint32_t dynamicOffset = 0;
1216 
1217     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1218     wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
1219     pass.SetPipeline(pipeline);
1220     pass.SetBindGroup(0, bindGroup, 1, &dynamicOffset);
1221     pass.Draw(3);
1222     pass.EndPass();
1223 
1224     wgpu::CommandBuffer commands = encoder.Finish();
1225     queue.Submit(1, &commands);
1226 }
1227 
1228 // Test that bind group bindings may have unbounded and arbitrary binding numbers
TEST_P(BindGroupTests,ArbitraryBindingNumbers)1229 TEST_P(BindGroupTests, ArbitraryBindingNumbers) {
1230     // TODO(crbug.com/dawn/736): Test output is wrong with D3D12 + WARP.
1231     DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsWARP());
1232 
1233     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
1234 
1235     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
1236         [[stage(vertex)]]
1237         fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
1238             var pos = array<vec2<f32>, 3>(
1239                 vec2<f32>(-1.0, 1.0),
1240                 vec2<f32>( 1.0, 1.0),
1241                 vec2<f32>(-1.0, -1.0));
1242 
1243             return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
1244         })");
1245 
1246     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
1247         [[block]] struct Ubo {
1248             color : vec4<f32>;
1249         };
1250 
1251         [[group(0), binding(953)]] var <uniform> ubo1 : Ubo;
1252         [[group(0), binding(47)]] var <uniform> ubo2 : Ubo;
1253         [[group(0), binding(111)]] var <uniform> ubo3 : Ubo;
1254 
1255         [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
1256             return ubo1.color + 2.0 * ubo2.color + 4.0 * ubo3.color;
1257         })");
1258 
1259     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
1260     pipelineDescriptor.vertex.module = vsModule;
1261     pipelineDescriptor.cFragment.module = fsModule;
1262     pipelineDescriptor.cTargets[0].format = renderPass.colorFormat;
1263 
1264     wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
1265 
1266     wgpu::Buffer black =
1267         utils::CreateBufferFromData(device, wgpu::BufferUsage::Uniform, {0.f, 0.f, 0.f, 0.f});
1268     wgpu::Buffer red =
1269         utils::CreateBufferFromData(device, wgpu::BufferUsage::Uniform, {0.251f, 0.0f, 0.0f, 0.0f});
1270     wgpu::Buffer green =
1271         utils::CreateBufferFromData(device, wgpu::BufferUsage::Uniform, {0.0f, 0.251f, 0.0f, 0.0f});
1272     wgpu::Buffer blue =
1273         utils::CreateBufferFromData(device, wgpu::BufferUsage::Uniform, {0.0f, 0.0f, 0.251f, 0.0f});
1274 
1275     auto DoTest = [&](wgpu::Buffer color1, wgpu::Buffer color2, wgpu::Buffer color3, RGBA8 filled) {
1276         auto DoTestInner = [&](wgpu::BindGroup bindGroup) {
1277             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1278             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
1279             pass.SetPipeline(pipeline);
1280             pass.SetBindGroup(0, bindGroup);
1281             pass.Draw(3);
1282             pass.EndPass();
1283 
1284             wgpu::CommandBuffer commands = encoder.Finish();
1285             queue.Submit(1, &commands);
1286 
1287             EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 1);
1288         };
1289 
1290         utils::BindingInitializationHelper bindings[] = {
1291             {953, color1, 0, 4 * sizeof(float)},  //
1292             {47, color2, 0, 4 * sizeof(float)},   //
1293             {111, color3, 0, 4 * sizeof(float)},  //
1294         };
1295 
1296         // Should work regardless of what order the bindings are specified in.
1297         DoTestInner(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
1298                                          {bindings[0], bindings[1], bindings[2]}));
1299         DoTestInner(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
1300                                          {bindings[1], bindings[0], bindings[2]}));
1301         DoTestInner(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
1302                                          {bindings[2], bindings[0], bindings[1]}));
1303     };
1304 
1305     // first color is normal, second is 2x, third is 3x.
1306     DoTest(black, black, black, RGBA8(0, 0, 0, 0));
1307 
1308     // Check the first binding maps to the first slot. We know this because the colors are
1309     // multiplied 1x.
1310     DoTest(red, black, black, RGBA8(64, 0, 0, 0));
1311     DoTest(green, black, black, RGBA8(0, 64, 0, 0));
1312     DoTest(blue, black, black, RGBA8(0, 0, 64, 0));
1313 
1314     // Use multiple bindings and check the second color maps to the second slot.
1315     // We know this because the second slot is multiplied 2x.
1316     DoTest(green, blue, black, RGBA8(0, 64, 128, 0));
1317     DoTest(blue, green, black, RGBA8(0, 128, 64, 0));
1318     DoTest(red, green, black, RGBA8(64, 128, 0, 0));
1319 
1320     // Use multiple bindings and check the third color maps to the third slot.
1321     // We know this because the third slot is multiplied 4x.
1322     DoTest(black, blue, red, RGBA8(255, 0, 128, 0));
1323     DoTest(blue, black, green, RGBA8(0, 255, 64, 0));
1324     DoTest(red, black, blue, RGBA8(64, 0, 255, 0));
1325 }
1326 
1327 // This is a regression test for crbug.com/dawn/355 which tests that destruction of a bind group
1328 // that holds the last reference to its bind group layout does not result in a use-after-free. In
1329 // the bug, the destructor of BindGroupBase, when destroying member mLayout,
1330 // Ref<BindGroupLayoutBase> assigns to Ref::mPointee, AFTER calling Release(). After the BGL is
1331 // destroyed, the storage for |mPointee| has been freed.
TEST_P(BindGroupTests,LastReferenceToBindGroupLayout)1332 TEST_P(BindGroupTests, LastReferenceToBindGroupLayout) {
1333     wgpu::BufferDescriptor bufferDesc;
1334     bufferDesc.size = sizeof(float);
1335     bufferDesc.usage = wgpu::BufferUsage::Uniform;
1336     wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc);
1337 
1338     wgpu::BindGroup bg;
1339     {
1340         wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1341             device, {{0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform}});
1342         bg = utils::MakeBindGroup(device, bgl, {{0, buffer, 0, sizeof(float)}});
1343     }
1344 }
1345 
1346 // Test that bind groups with an empty bind group layout may be created and used.
TEST_P(BindGroupTests,EmptyLayout)1347 TEST_P(BindGroupTests, EmptyLayout) {
1348     wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(device, {});
1349     wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {});
1350 
1351     wgpu::ComputePipelineDescriptor pipelineDesc;
1352     pipelineDesc.layout = utils::MakeBasicPipelineLayout(device, &bgl);
1353     pipelineDesc.compute.entryPoint = "main";
1354     pipelineDesc.compute.module = utils::CreateShaderModule(device, R"(
1355         [[stage(compute), workgroup_size(1)]] fn main() {
1356         })");
1357 
1358     wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc);
1359 
1360     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1361     wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1362     pass.SetPipeline(pipeline);
1363     pass.SetBindGroup(0, bg);
1364     pass.Dispatch(1);
1365     pass.EndPass();
1366 
1367     wgpu::CommandBuffer commands = encoder.Finish();
1368     queue.Submit(1, &commands);
1369 }
1370 
1371 // Test creating a BGL with a storage buffer binding but declared readonly in the shader works.
1372 // This is a regression test for crbug.com/dawn/410 which tests that it can successfully compile and
1373 // execute the shader.
TEST_P(BindGroupTests,ReadonlyStorage)1374 TEST_P(BindGroupTests, ReadonlyStorage) {
1375     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
1376 
1377     pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, R"(
1378         [[stage(vertex)]]
1379         fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
1380             var pos = array<vec2<f32>, 3>(
1381                 vec2<f32>(-1.0, 1.0),
1382                 vec2<f32>( 1.0, 1.0),
1383                 vec2<f32>(-1.0, -1.0));
1384 
1385             return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
1386         })");
1387 
1388     pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
1389         [[block]] struct Buffer0 {
1390             color : vec4<f32>;
1391         };
1392         [[group(0), binding(0)]] var<storage, read> buffer0 : Buffer0;
1393 
1394         [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
1395             return buffer0.color;
1396         })");
1397 
1398     constexpr uint32_t kRTSize = 4;
1399     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
1400     pipelineDescriptor.cTargets[0].format = renderPass.colorFormat;
1401 
1402     wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1403         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
1404 
1405     pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl);
1406 
1407     wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&pipelineDescriptor);
1408 
1409     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1410     wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
1411 
1412     std::array<float, 4> greenColor = {0, 1, 0, 1};
1413     wgpu::Buffer storageBuffer = utils::CreateBufferFromData(
1414         device, &greenColor, sizeof(greenColor), wgpu::BufferUsage::Storage);
1415 
1416     pass.SetPipeline(renderPipeline);
1417     pass.SetBindGroup(0, utils::MakeBindGroup(device, bgl, {{0, storageBuffer}}));
1418     pass.Draw(3);
1419     pass.EndPass();
1420 
1421     wgpu::CommandBuffer commands = encoder.Finish();
1422     queue.Submit(1, &commands);
1423 
1424     EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 0);
1425 }
1426 
1427 // Test that creating a large bind group, with each binding type at the max count, works and can be
1428 // used correctly. The test loads a different value from each binding, and writes 1 to a storage
1429 // buffer if all values are correct.
TEST_P(BindGroupTests,ReallyLargeBindGroup)1430 TEST_P(BindGroupTests, ReallyLargeBindGroup) {
1431     DAWN_SUPPRESS_TEST_IF(IsOpenGLES());
1432     std::ostringstream interface;
1433     std::ostringstream body;
1434     uint32_t binding = 0;
1435     uint32_t expectedValue = 42;
1436 
1437     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1438 
1439     auto CreateTextureWithRedData = [&](wgpu::TextureFormat format, uint32_t value,
1440                                         wgpu::TextureUsage usage) {
1441         wgpu::TextureDescriptor textureDesc = {};
1442         textureDesc.usage = wgpu::TextureUsage::CopyDst | usage;
1443         textureDesc.size = {1, 1, 1};
1444         textureDesc.format = format;
1445         wgpu::Texture texture = device.CreateTexture(&textureDesc);
1446 
1447         if (format == wgpu::TextureFormat::R8Unorm) {
1448             ASSERT(expectedValue < 255u);
1449         }
1450         wgpu::Buffer textureData =
1451             utils::CreateBufferFromData(device, wgpu::BufferUsage::CopySrc, {value});
1452 
1453         wgpu::ImageCopyBuffer imageCopyBuffer = {};
1454         imageCopyBuffer.buffer = textureData;
1455         imageCopyBuffer.layout.bytesPerRow = 256;
1456 
1457         wgpu::ImageCopyTexture imageCopyTexture = {};
1458         imageCopyTexture.texture = texture;
1459 
1460         wgpu::Extent3D copySize = {1, 1, 1};
1461 
1462         commandEncoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, &copySize);
1463         return texture;
1464     };
1465 
1466     std::vector<wgpu::BindGroupEntry> bgEntries;
1467     static_assert(kMaxSampledTexturesPerShaderStage == kMaxSamplersPerShaderStage,
1468                   "Please update this test");
1469     for (uint32_t i = 0; i < kMaxSampledTexturesPerShaderStage; ++i) {
1470         wgpu::Texture texture = CreateTextureWithRedData(
1471             wgpu::TextureFormat::R8Unorm, expectedValue, wgpu::TextureUsage::TextureBinding);
1472         bgEntries.push_back({nullptr, binding, nullptr, 0, 0, nullptr, texture.CreateView()});
1473 
1474         interface << "[[group(0), binding(" << binding++ << ")]] "
1475                   << "var tex" << i << " : texture_2d<f32>;\n";
1476 
1477         bgEntries.push_back({nullptr, binding, nullptr, 0, 0, device.CreateSampler(), nullptr});
1478 
1479         interface << "[[group(0), binding(" << binding++ << ")]]"
1480                   << "var samp" << i << " : sampler;\n";
1481 
1482         body << "if (abs(textureSampleLevel(tex" << i << ", samp" << i
1483              << ", vec2<f32>(0.5, 0.5), 0.0).r - " << expectedValue++
1484              << ".0 / 255.0) > 0.0001) {\n";
1485         body << "    return;\n";
1486         body << "}\n";
1487     }
1488     for (uint32_t i = 0; i < kMaxStorageTexturesPerShaderStage; ++i) {
1489         wgpu::Texture texture = CreateTextureWithRedData(
1490             wgpu::TextureFormat::R32Uint, expectedValue, wgpu::TextureUsage::StorageBinding);
1491         bgEntries.push_back({nullptr, binding, nullptr, 0, 0, nullptr, texture.CreateView()});
1492 
1493         interface << "[[group(0), binding(" << binding++ << ")]] "
1494                   << "var image" << i << " : texture_storage_2d<r32uint, write>;\n";
1495 
1496         body << "_ = image" << i << ";";
1497     }
1498 
1499     for (uint32_t i = 0; i < kMaxUniformBuffersPerShaderStage; ++i) {
1500         wgpu::Buffer buffer = utils::CreateBufferFromData<uint32_t>(
1501             device, wgpu::BufferUsage::Uniform, {expectedValue, 0, 0, 0});
1502         bgEntries.push_back({nullptr, binding, buffer, 0, 4 * sizeof(uint32_t), nullptr, nullptr});
1503 
1504         interface << "[[block]] struct UniformBuffer" << i << R"({
1505                 value : u32;
1506             };
1507         )";
1508         interface << "[[group(0), binding(" << binding++ << ")]] "
1509                   << "var<uniform> ubuf" << i << " : UniformBuffer" << i << ";\n";
1510 
1511         body << "if (ubuf" << i << ".value != " << expectedValue++ << "u) {\n";
1512         body << "    return;\n";
1513         body << "}\n";
1514     }
1515     // Save one storage buffer for writing the result
1516     for (uint32_t i = 0; i < kMaxStorageBuffersPerShaderStage - 1; ++i) {
1517         wgpu::Buffer buffer = utils::CreateBufferFromData<uint32_t>(
1518             device, wgpu::BufferUsage::Storage, {expectedValue});
1519         bgEntries.push_back({nullptr, binding, buffer, 0, sizeof(uint32_t), nullptr, nullptr});
1520 
1521         interface << "[[block]] struct ReadOnlyStorageBuffer" << i << R"({
1522                 value : u32;
1523             };
1524         )";
1525         interface << "[[group(0), binding(" << binding++ << ")]] "
1526                   << "var<storage, read> sbuf" << i << " : ReadOnlyStorageBuffer" << i << ";\n";
1527 
1528         body << "if (sbuf" << i << ".value != " << expectedValue++ << "u) {\n";
1529         body << "    return;\n";
1530         body << "}\n";
1531     }
1532 
1533     wgpu::Buffer result = utils::CreateBufferFromData<uint32_t>(
1534         device, wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc, {0});
1535     bgEntries.push_back({nullptr, binding, result, 0, sizeof(uint32_t), nullptr, nullptr});
1536 
1537     interface << R"([[block]] struct ReadWriteStorageBuffer{
1538             value : u32;
1539         };
1540     )";
1541     interface << "[[group(0), binding(" << binding++ << ")]] "
1542               << "var<storage, read_write> result : ReadWriteStorageBuffer;\n";
1543 
1544     body << "result.value = 1u;\n";
1545 
1546     std::string shader = interface.str() + "[[stage(compute), workgroup_size(1)]] fn main() {\n" +
1547                          body.str() + "}\n";
1548     wgpu::ComputePipelineDescriptor cpDesc;
1549     cpDesc.compute.module = utils::CreateShaderModule(device, shader.c_str());
1550     cpDesc.compute.entryPoint = "main";
1551     wgpu::ComputePipeline cp = device.CreateComputePipeline(&cpDesc);
1552 
1553     wgpu::BindGroupDescriptor bgDesc = {};
1554     bgDesc.layout = cp.GetBindGroupLayout(0);
1555     bgDesc.entryCount = static_cast<uint32_t>(bgEntries.size());
1556     bgDesc.entries = bgEntries.data();
1557 
1558     wgpu::BindGroup bg = device.CreateBindGroup(&bgDesc);
1559 
1560     wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
1561     pass.SetPipeline(cp);
1562     pass.SetBindGroup(0, bg);
1563     pass.Dispatch(1, 1, 1);
1564     pass.EndPass();
1565 
1566     wgpu::CommandBuffer commands = commandEncoder.Finish();
1567     queue.Submit(1, &commands);
1568 
1569     EXPECT_BUFFER_U32_EQ(1, result, 0);
1570 }
1571 
1572 // This is a regression test for crbug.com/dawn/319 where creating a bind group with a
1573 // destroyed resource would crash the backend.
TEST_P(BindGroupTests,CreateWithDestroyedResource)1574 TEST_P(BindGroupTests, CreateWithDestroyedResource) {
1575     auto doBufferTest = [&](wgpu::BufferBindingType bindingType, wgpu::BufferUsage usage) {
1576         wgpu::BindGroupLayout bgl =
1577             utils::MakeBindGroupLayout(device, {{0, wgpu::ShaderStage::Fragment, bindingType}});
1578 
1579         wgpu::BufferDescriptor bufferDesc;
1580         bufferDesc.size = sizeof(float);
1581         bufferDesc.usage = usage;
1582         wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc);
1583         buffer.Destroy();
1584 
1585         wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer, 0, sizeof(float)}});
1586     };
1587 
1588     // Test various usages and binding types since they take different backend code paths.
1589     doBufferTest(wgpu::BufferBindingType::Uniform, wgpu::BufferUsage::Uniform);
1590     doBufferTest(wgpu::BufferBindingType::Storage, wgpu::BufferUsage::Storage);
1591     doBufferTest(wgpu::BufferBindingType::ReadOnlyStorage, wgpu::BufferUsage::Storage);
1592 
1593     // Test a sampled texture.
1594     {
1595         wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1596             device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
1597 
1598         wgpu::TextureDescriptor textureDesc;
1599         textureDesc.usage = wgpu::TextureUsage::TextureBinding;
1600         textureDesc.size = {1, 1, 1};
1601         textureDesc.format = wgpu::TextureFormat::BGRA8Unorm;
1602 
1603         // Create view, then destroy.
1604         {
1605             wgpu::Texture texture = device.CreateTexture(&textureDesc);
1606             wgpu::TextureView textureView = texture.CreateView();
1607 
1608             texture.Destroy();
1609             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, textureView}});
1610         }
1611         // Destroy, then create view.
1612         {
1613             wgpu::Texture texture = device.CreateTexture(&textureDesc);
1614             texture.Destroy();
1615             wgpu::TextureView textureView = texture.CreateView();
1616 
1617             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, textureView}});
1618         }
1619     }
1620 
1621     // Test a storage texture.
1622     {
1623         wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1624             device, {{0, wgpu::ShaderStage::Fragment, wgpu::StorageTextureAccess::WriteOnly,
1625                       wgpu::TextureFormat::R32Uint}});
1626 
1627         wgpu::TextureDescriptor textureDesc;
1628         textureDesc.usage = wgpu::TextureUsage::StorageBinding;
1629         textureDesc.size = {1, 1, 1};
1630         textureDesc.format = wgpu::TextureFormat::R32Uint;
1631 
1632         // Create view, then destroy.
1633         {
1634             wgpu::Texture texture = device.CreateTexture(&textureDesc);
1635             wgpu::TextureView textureView = texture.CreateView();
1636 
1637             texture.Destroy();
1638             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, textureView}});
1639         }
1640         // Destroy, then create view.
1641         {
1642             wgpu::Texture texture = device.CreateTexture(&textureDesc);
1643             texture.Destroy();
1644             wgpu::TextureView textureView = texture.CreateView();
1645 
1646             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, textureView}});
1647         }
1648     }
1649 }
1650 
1651 DAWN_INSTANTIATE_TEST(BindGroupTests,
1652                       D3D12Backend(),
1653                       MetalBackend(),
1654                       OpenGLBackend(),
1655                       OpenGLESBackend(),
1656                       VulkanBackend());
1657