• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "tests/unittests/validation/ValidationTest.h"
16 
17 #include "common/Assert.h"
18 #include "common/Constants.h"
19 #include "utils/ComboRenderPipelineDescriptor.h"
20 #include "utils/WGPUHelpers.h"
21 
22 class BindGroupValidationTest : public ValidationTest {
23   public:
CreateTexture(wgpu::TextureUsage usage,wgpu::TextureFormat format,uint32_t layerCount)24     wgpu::Texture CreateTexture(wgpu::TextureUsage usage,
25                                 wgpu::TextureFormat format,
26                                 uint32_t layerCount) {
27         wgpu::TextureDescriptor descriptor;
28         descriptor.dimension = wgpu::TextureDimension::e2D;
29         descriptor.size = {16, 16, layerCount};
30         descriptor.sampleCount = 1;
31         descriptor.mipLevelCount = 1;
32         descriptor.usage = usage;
33         descriptor.format = format;
34 
35         return device.CreateTexture(&descriptor);
36     }
37 
SetUp()38     void SetUp() override {
39         ValidationTest::SetUp();
40 
41         // Create objects to use as resources inside test bind groups.
42         {
43             wgpu::BufferDescriptor descriptor;
44             descriptor.size = 1024;
45             descriptor.usage = wgpu::BufferUsage::Uniform;
46             mUBO = device.CreateBuffer(&descriptor);
47         }
48         {
49             wgpu::BufferDescriptor descriptor;
50             descriptor.size = 1024;
51             descriptor.usage = wgpu::BufferUsage::Storage;
52             mSSBO = device.CreateBuffer(&descriptor);
53         }
54         { mSampler = device.CreateSampler(); }
55         {
56             mSampledTexture =
57                 CreateTexture(wgpu::TextureUsage::TextureBinding, kDefaultTextureFormat, 1);
58             mSampledTextureView = mSampledTexture.CreateView();
59 
60             wgpu::ExternalTextureDescriptor externalTextureDesc;
61             externalTextureDesc.format = kDefaultTextureFormat;
62             externalTextureDesc.plane0 = mSampledTextureView;
63             mExternalTexture = device.CreateExternalTexture(&externalTextureDesc);
64             mExternalTextureBindingEntry.externalTexture = mExternalTexture;
65         }
66     }
67 
68   protected:
69     wgpu::Buffer mUBO;
70     wgpu::Buffer mSSBO;
71     wgpu::Sampler mSampler;
72     wgpu::Texture mSampledTexture;
73     wgpu::TextureView mSampledTextureView;
74     wgpu::ExternalTextureBindingEntry mExternalTextureBindingEntry;
75 
76     static constexpr wgpu::TextureFormat kDefaultTextureFormat = wgpu::TextureFormat::RGBA8Unorm;
77 
78   private:
79     wgpu::ExternalTexture mExternalTexture;
80 };
81 
82 // Test the validation of BindGroupDescriptor::nextInChain
TEST_F(BindGroupValidationTest,NextInChainNullptr)83 TEST_F(BindGroupValidationTest, NextInChainNullptr) {
84     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {});
85 
86     wgpu::BindGroupDescriptor descriptor;
87     descriptor.layout = layout;
88     descriptor.entryCount = 0;
89     descriptor.entries = nullptr;
90 
91     // Control case: check that nextInChain = nullptr is valid
92     descriptor.nextInChain = nullptr;
93     device.CreateBindGroup(&descriptor);
94 
95     // Check that nextInChain != nullptr is an error.
96     wgpu::ChainedStruct chainedDescriptor;
97     chainedDescriptor.sType = wgpu::SType::Invalid;
98     descriptor.nextInChain = &chainedDescriptor;
99     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
100 }
101 
102 // Check constraints on entryCount
TEST_F(BindGroupValidationTest,EntryCountMismatch)103 TEST_F(BindGroupValidationTest, EntryCountMismatch) {
104     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
105         device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
106 
107     // Control case: check that a descriptor with one binding is ok
108     utils::MakeBindGroup(device, layout, {{0, mSampler}});
109 
110     // Check that entryCount != layout.entryCount fails.
111     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {}));
112 }
113 
114 // Check constraints on BindGroupEntry::binding
TEST_F(BindGroupValidationTest,WrongBindings)115 TEST_F(BindGroupValidationTest, WrongBindings) {
116     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
117         device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
118 
119     // Control case: check that a descriptor with a binding matching the layout's is ok
120     utils::MakeBindGroup(device, layout, {{0, mSampler}});
121 
122     // Check that binding must be present in the layout
123     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{1, mSampler}}));
124 }
125 
126 // Check that the same binding cannot be set twice
TEST_F(BindGroupValidationTest,BindingSetTwice)127 TEST_F(BindGroupValidationTest, BindingSetTwice) {
128     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
129         device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering},
130                  {1, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
131 
132     // Control case: check that different bindings work
133     utils::MakeBindGroup(device, layout, {{0, mSampler}, {1, mSampler}});
134 
135     // Check that setting the same binding twice is invalid
136     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mSampler}, {0, mSampler}}));
137 }
138 
139 // Check that a sampler binding must contain exactly one sampler
TEST_F(BindGroupValidationTest,SamplerBindingType)140 TEST_F(BindGroupValidationTest, SamplerBindingType) {
141     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
142         device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
143 
144     wgpu::BindGroupEntry binding;
145     binding.binding = 0;
146     binding.sampler = nullptr;
147     binding.textureView = nullptr;
148     binding.buffer = nullptr;
149     binding.offset = 0;
150     binding.size = 0;
151 
152     wgpu::BindGroupDescriptor descriptor;
153     descriptor.layout = layout;
154     descriptor.entryCount = 1;
155     descriptor.entries = &binding;
156 
157     // Not setting anything fails
158     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
159 
160     // Control case: setting just the sampler works
161     binding.sampler = mSampler;
162     device.CreateBindGroup(&descriptor);
163 
164     // Setting the texture view as well is an error
165     binding.textureView = mSampledTextureView;
166     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
167     binding.textureView = nullptr;
168 
169     // Setting the buffer as well is an error
170     binding.buffer = mUBO;
171     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
172     binding.buffer = nullptr;
173 
174     // Setting the external texture view as well is an error
175     binding.nextInChain = &mExternalTextureBindingEntry;
176     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
177     binding.nextInChain = nullptr;
178 
179     // Setting the sampler to an error sampler is an error.
180     {
181         wgpu::SamplerDescriptor samplerDesc;
182         samplerDesc.minFilter = static_cast<wgpu::FilterMode>(0xFFFFFFFF);
183 
184         wgpu::Sampler errorSampler;
185         ASSERT_DEVICE_ERROR(errorSampler = device.CreateSampler(&samplerDesc));
186 
187         binding.sampler = errorSampler;
188         ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
189         binding.sampler = nullptr;
190     }
191 }
192 
193 // Check that a texture binding must contain exactly a texture view
TEST_F(BindGroupValidationTest,TextureBindingType)194 TEST_F(BindGroupValidationTest, TextureBindingType) {
195     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
196         device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
197 
198     wgpu::BindGroupEntry binding;
199     binding.binding = 0;
200     binding.sampler = nullptr;
201     binding.textureView = nullptr;
202     binding.buffer = nullptr;
203     binding.offset = 0;
204     binding.size = 0;
205 
206     wgpu::BindGroupDescriptor descriptor;
207     descriptor.layout = layout;
208     descriptor.entryCount = 1;
209     descriptor.entries = &binding;
210 
211     // Not setting anything fails
212     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
213 
214     // Control case: setting just the texture view works
215     binding.textureView = mSampledTextureView;
216     device.CreateBindGroup(&descriptor);
217 
218     // Setting the sampler as well is an error
219     binding.sampler = mSampler;
220     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
221     binding.sampler = nullptr;
222 
223     // Setting the buffer as well is an error
224     binding.buffer = mUBO;
225     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
226     binding.buffer = nullptr;
227 
228     // Setting the external texture view as well is an error
229     binding.nextInChain = &mExternalTextureBindingEntry;
230     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
231     binding.nextInChain = nullptr;
232 
233     // Setting the texture view to an error texture view is an error.
234     {
235         wgpu::TextureViewDescriptor viewDesc;
236         viewDesc.format = kDefaultTextureFormat;
237         viewDesc.dimension = wgpu::TextureViewDimension::e2D;
238         viewDesc.baseMipLevel = 0;
239         viewDesc.mipLevelCount = 0;
240         viewDesc.baseArrayLayer = 0;
241         viewDesc.arrayLayerCount = 1000;
242 
243         wgpu::TextureView errorView;
244         ASSERT_DEVICE_ERROR(errorView = mSampledTexture.CreateView(&viewDesc));
245 
246         binding.textureView = errorView;
247         ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
248         binding.textureView = nullptr;
249     }
250 }
251 
252 // Check that a buffer binding must contain exactly a buffer
TEST_F(BindGroupValidationTest,BufferBindingType)253 TEST_F(BindGroupValidationTest, BufferBindingType) {
254     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
255         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform}});
256 
257     wgpu::BindGroupEntry binding;
258     binding.binding = 0;
259     binding.sampler = nullptr;
260     binding.textureView = nullptr;
261     binding.buffer = nullptr;
262     binding.offset = 0;
263     binding.size = 1024;
264 
265     wgpu::BindGroupDescriptor descriptor;
266     descriptor.layout = layout;
267     descriptor.entryCount = 1;
268     descriptor.entries = &binding;
269 
270     // Not setting anything fails
271     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
272 
273     // Control case: setting just the buffer works
274     binding.buffer = mUBO;
275     device.CreateBindGroup(&descriptor);
276 
277     // Setting the texture view as well is an error
278     binding.textureView = mSampledTextureView;
279     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
280     binding.textureView = nullptr;
281 
282     // Setting the sampler as well is an error
283     binding.sampler = mSampler;
284     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
285     binding.sampler = nullptr;
286 
287     // Setting the external texture view as well is an error
288     binding.nextInChain = &mExternalTextureBindingEntry;
289     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
290     binding.nextInChain = nullptr;
291 
292     // Setting the buffer to an error buffer is an error.
293     {
294         wgpu::BufferDescriptor bufferDesc;
295         bufferDesc.size = 1024;
296         bufferDesc.usage = static_cast<wgpu::BufferUsage>(0xFFFFFFFF);
297 
298         wgpu::Buffer errorBuffer;
299         ASSERT_DEVICE_ERROR(errorBuffer = device.CreateBuffer(&bufferDesc));
300 
301         binding.buffer = errorBuffer;
302         ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
303         binding.buffer = nullptr;
304     }
305 }
306 
307 // Check that an external texture binding must contain exactly an external texture
TEST_F(BindGroupValidationTest,ExternalTextureBindingType)308 TEST_F(BindGroupValidationTest, ExternalTextureBindingType) {
309     // Create an external texture
310     wgpu::Texture texture =
311         CreateTexture(wgpu::TextureUsage::TextureBinding, kDefaultTextureFormat, 1);
312     wgpu::ExternalTextureDescriptor externalDesc;
313     externalDesc.plane0 = texture.CreateView();
314     externalDesc.format = kDefaultTextureFormat;
315     wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc);
316 
317     // Create a bind group layout for a single external texture
318     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
319         device, {{0, wgpu::ShaderStage::Fragment, &utils::kExternalTextureBindingLayout}});
320 
321     wgpu::BindGroupEntry binding;
322     binding.binding = 0;
323     binding.sampler = nullptr;
324     binding.textureView = nullptr;
325     binding.buffer = nullptr;
326     binding.offset = 0;
327     binding.size = 0;
328 
329     wgpu::BindGroupDescriptor descriptor;
330     descriptor.layout = layout;
331     descriptor.entryCount = 1;
332     descriptor.entries = &binding;
333 
334     // Not setting anything fails
335     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
336 
337     // Control case: setting just the external texture works
338     wgpu::ExternalTextureBindingEntry externalBindingEntry;
339     externalBindingEntry.externalTexture = externalTexture;
340     binding.nextInChain = &externalBindingEntry;
341     device.CreateBindGroup(&descriptor);
342 
343     // Setting the texture view as well is an error
344     binding.textureView = mSampledTextureView;
345     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
346     binding.textureView = nullptr;
347 
348     // Setting the sampler as well is an error
349     binding.sampler = mSampler;
350     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
351     binding.sampler = nullptr;
352 
353     // Setting the buffer as well is an error
354     binding.buffer = mUBO;
355     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
356     binding.buffer = nullptr;
357 
358     // Setting the external texture to an error external texture is an error.
359     {
360         wgpu::ExternalTextureDescriptor errorExternalDesciptor;
361         errorExternalDesciptor.plane0 = texture.CreateView();
362         errorExternalDesciptor.format = wgpu::TextureFormat::R8Uint;
363 
364         wgpu::ExternalTexture errorExternalTexture;
365         ASSERT_DEVICE_ERROR(errorExternalTexture =
366                                 device.CreateExternalTexture(&errorExternalDesciptor));
367 
368         wgpu::ExternalTextureBindingEntry errorExternalBindingEntry;
369         errorExternalBindingEntry.externalTexture = errorExternalTexture;
370         binding.nextInChain = &errorExternalBindingEntry;
371         ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
372         binding.nextInChain = nullptr;
373     }
374 
375     // Setting an external texture with another external texture chained is an error.
376     {
377         wgpu::ExternalTexture externalTexture2 = device.CreateExternalTexture(&externalDesc);
378         wgpu::ExternalTextureBindingEntry externalBindingEntry2;
379         externalBindingEntry2.externalTexture = externalTexture2;
380         externalBindingEntry.nextInChain = &externalBindingEntry2;
381 
382         ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
383     }
384 
385     // Chaining a struct that isn't an external texture binding entry is an error.
386     {
387         wgpu::ExternalTextureBindingLayout externalBindingLayout;
388         binding.nextInChain = &externalBindingLayout;
389         ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
390     }
391 }
392 
393 // Check that a texture binding must have the correct usage
TEST_F(BindGroupValidationTest,TextureUsage)394 TEST_F(BindGroupValidationTest, TextureUsage) {
395     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
396         device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
397 
398     // Control case: setting a sampleable texture view works.
399     utils::MakeBindGroup(device, layout, {{0, mSampledTextureView}});
400 
401     // Make an render attachment texture and try to set it for a SampledTexture binding
402     wgpu::Texture outputTexture =
403         CreateTexture(wgpu::TextureUsage::RenderAttachment, wgpu::TextureFormat::RGBA8Unorm, 1);
404     wgpu::TextureView outputTextureView = outputTexture.CreateView();
405     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, outputTextureView}}));
406 }
407 
408 // Check that a storage texture binding must have the correct usage
TEST_F(BindGroupValidationTest,StorageTextureUsage)409 TEST_F(BindGroupValidationTest, StorageTextureUsage) {
410     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
411         device, {{0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly,
412                   wgpu::TextureFormat::RGBA8Uint}});
413 
414     wgpu::TextureDescriptor descriptor;
415     descriptor.dimension = wgpu::TextureDimension::e2D;
416     descriptor.size = {16, 16, 1};
417     descriptor.sampleCount = 1;
418     descriptor.mipLevelCount = 1;
419     descriptor.usage = wgpu::TextureUsage::StorageBinding;
420     descriptor.format = wgpu::TextureFormat::RGBA8Uint;
421 
422     wgpu::TextureView view = device.CreateTexture(&descriptor).CreateView();
423 
424     // Control case: setting a storage texture view works.
425     utils::MakeBindGroup(device, layout, {{0, view}});
426 
427     // Sampled texture is invalid with storage buffer binding
428     descriptor.usage = wgpu::TextureUsage::TextureBinding;
429     descriptor.format = wgpu::TextureFormat::RGBA8Unorm;
430     view = device.CreateTexture(&descriptor).CreateView();
431     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, view}}));
432 
433     // Multisampled texture is invalid with storage buffer binding
434     // Regression case for crbug.com/dawn/614 where this hit an ASSERT.
435     descriptor.sampleCount = 4;
436     view = device.CreateTexture(&descriptor).CreateView();
437     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, view}}));
438 }
439 
440 // Check that a texture must have the correct sample type
TEST_F(BindGroupValidationTest,TextureSampleType)441 TEST_F(BindGroupValidationTest, TextureSampleType) {
442     auto DoTest = [this](bool success, wgpu::TextureFormat format,
443                          wgpu::TextureSampleType sampleType) {
444         wgpu::BindGroupLayout layout =
445             utils::MakeBindGroupLayout(device, {{0, wgpu::ShaderStage::Fragment, sampleType}});
446 
447         wgpu::TextureDescriptor descriptor;
448         descriptor.size = {4, 4, 1};
449         descriptor.usage = wgpu::TextureUsage::TextureBinding;
450         descriptor.format = format;
451 
452         wgpu::TextureView view = device.CreateTexture(&descriptor).CreateView();
453 
454         if (success) {
455             utils::MakeBindGroup(device, layout, {{0, view}});
456         } else {
457             ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, view}}));
458         }
459     };
460 
461     // Test that RGBA8Unorm is only compatible with float/unfilterable-float
462     DoTest(true, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureSampleType::Float);
463     DoTest(true, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureSampleType::UnfilterableFloat);
464     DoTest(false, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureSampleType::Depth);
465     DoTest(false, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureSampleType::Uint);
466     DoTest(false, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureSampleType::Sint);
467 
468     // Test that R32Float is only compatible with unfilterable-float
469     DoTest(false, wgpu::TextureFormat::R32Float, wgpu::TextureSampleType::Float);
470     DoTest(true, wgpu::TextureFormat::R32Float, wgpu::TextureSampleType::UnfilterableFloat);
471     DoTest(false, wgpu::TextureFormat::R32Float, wgpu::TextureSampleType::Depth);
472     DoTest(false, wgpu::TextureFormat::R32Float, wgpu::TextureSampleType::Uint);
473     DoTest(false, wgpu::TextureFormat::R32Float, wgpu::TextureSampleType::Sint);
474 
475     // Test that Depth32Float is only compatible with depth.
476     DoTest(false, wgpu::TextureFormat::Depth32Float, wgpu::TextureSampleType::Float);
477     DoTest(false, wgpu::TextureFormat::Depth32Float, wgpu::TextureSampleType::UnfilterableFloat);
478     DoTest(true, wgpu::TextureFormat::Depth32Float, wgpu::TextureSampleType::Depth);
479     DoTest(false, wgpu::TextureFormat::Depth32Float, wgpu::TextureSampleType::Uint);
480     DoTest(false, wgpu::TextureFormat::Depth32Float, wgpu::TextureSampleType::Sint);
481 
482     // Test that RG8Uint is only compatible with uint
483     DoTest(false, wgpu::TextureFormat::RG8Uint, wgpu::TextureSampleType::Float);
484     DoTest(false, wgpu::TextureFormat::RG8Uint, wgpu::TextureSampleType::UnfilterableFloat);
485     DoTest(false, wgpu::TextureFormat::RG8Uint, wgpu::TextureSampleType::Depth);
486     DoTest(true, wgpu::TextureFormat::RG8Uint, wgpu::TextureSampleType::Uint);
487     DoTest(false, wgpu::TextureFormat::RG8Uint, wgpu::TextureSampleType::Sint);
488 
489     // Test that R16Sint is only compatible with sint
490     DoTest(false, wgpu::TextureFormat::R16Sint, wgpu::TextureSampleType::Float);
491     DoTest(false, wgpu::TextureFormat::R16Sint, wgpu::TextureSampleType::UnfilterableFloat);
492     DoTest(false, wgpu::TextureFormat::R16Sint, wgpu::TextureSampleType::Depth);
493     DoTest(false, wgpu::TextureFormat::R16Sint, wgpu::TextureSampleType::Uint);
494     DoTest(true, wgpu::TextureFormat::R16Sint, wgpu::TextureSampleType::Sint);
495 }
496 
497 // Test which depth-stencil formats are allowed to be sampled (all).
TEST_F(BindGroupValidationTest,SamplingDepthStencilTexture)498 TEST_F(BindGroupValidationTest, SamplingDepthStencilTexture) {
499     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
500         device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth}});
501 
502     wgpu::TextureDescriptor desc;
503     desc.size = {1, 1, 1};
504     desc.usage = wgpu::TextureUsage::TextureBinding;
505 
506     // Depth32Float is allowed to be sampled.
507     {
508         desc.format = wgpu::TextureFormat::Depth32Float;
509         wgpu::Texture texture = device.CreateTexture(&desc);
510 
511         utils::MakeBindGroup(device, layout, {{0, texture.CreateView()}});
512     }
513 
514     // Depth24Plus is allowed to be sampled.
515     {
516         desc.format = wgpu::TextureFormat::Depth24Plus;
517         wgpu::Texture texture = device.CreateTexture(&desc);
518 
519         utils::MakeBindGroup(device, layout, {{0, texture.CreateView()}});
520     }
521 
522     // Depth24PlusStencil8 is allowed to be sampled, if the depth or stencil aspect is selected.
523     {
524         desc.format = wgpu::TextureFormat::Depth24PlusStencil8;
525         wgpu::Texture texture = device.CreateTexture(&desc);
526         wgpu::TextureViewDescriptor viewDesc = {};
527 
528         viewDesc.aspect = wgpu::TextureAspect::DepthOnly;
529         utils::MakeBindGroup(device, layout, {{0, texture.CreateView(&viewDesc)}});
530 
531         layout = utils::MakeBindGroupLayout(
532             device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Uint}});
533 
534         viewDesc.aspect = wgpu::TextureAspect::StencilOnly;
535         utils::MakeBindGroup(device, layout, {{0, texture.CreateView(&viewDesc)}});
536     }
537 }
538 
539 // Check that a texture must have the correct dimension
TEST_F(BindGroupValidationTest,TextureDimension)540 TEST_F(BindGroupValidationTest, TextureDimension) {
541     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
542         device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
543 
544     // Control case: setting a 2D texture view works.
545     utils::MakeBindGroup(device, layout, {{0, mSampledTextureView}});
546 
547     // Make a 2DArray texture and try to set it to a 2D binding.
548     wgpu::Texture arrayTexture =
549         CreateTexture(wgpu::TextureUsage::TextureBinding, wgpu::TextureFormat::RGBA8Uint, 2);
550     wgpu::TextureView arrayTextureView = arrayTexture.CreateView();
551 
552     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, arrayTextureView}}));
553 }
554 
555 // Check that a storage texture binding must have a texture view with a mipLevelCount of 1
TEST_F(BindGroupValidationTest,StorageTextureViewLayerCount)556 TEST_F(BindGroupValidationTest, StorageTextureViewLayerCount) {
557     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
558         device, {{0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly,
559                   wgpu::TextureFormat::RGBA8Uint}});
560 
561     wgpu::TextureDescriptor descriptor;
562     descriptor.dimension = wgpu::TextureDimension::e2D;
563     descriptor.size = {16, 16, 1};
564     descriptor.sampleCount = 1;
565     descriptor.mipLevelCount = 1;
566     descriptor.usage = wgpu::TextureUsage::StorageBinding;
567     descriptor.format = wgpu::TextureFormat::RGBA8Uint;
568 
569     wgpu::Texture textureNoMip = device.CreateTexture(&descriptor);
570 
571     descriptor.mipLevelCount = 3;
572     wgpu::Texture textureMip = device.CreateTexture(&descriptor);
573 
574     // Control case: setting a storage texture view on a texture with only one mip level works
575     {
576         wgpu::TextureView view = textureNoMip.CreateView();
577         utils::MakeBindGroup(device, layout, {{0, view}});
578     }
579 
580     // Setting a storage texture view with mipLevelCount=1 on a texture of multiple mip levels is
581     // valid
582     {
583         wgpu::TextureViewDescriptor viewDesc = {};
584         viewDesc.aspect = wgpu::TextureAspect::All;
585         viewDesc.dimension = wgpu::TextureViewDimension::e2D;
586         viewDesc.format = wgpu::TextureFormat::RGBA8Uint;
587         viewDesc.baseMipLevel = 0;
588         viewDesc.mipLevelCount = 1;
589 
590         // Setting texture view with lod 0 is valid
591         wgpu::TextureView view = textureMip.CreateView(&viewDesc);
592         utils::MakeBindGroup(device, layout, {{0, view}});
593 
594         // Setting texture view with other lod is also valid
595         viewDesc.baseMipLevel = 2;
596         view = textureMip.CreateView(&viewDesc);
597         utils::MakeBindGroup(device, layout, {{0, view}});
598     }
599 
600     // Texture view with mipLevelCount > 1 is invalid
601     {
602         wgpu::TextureViewDescriptor viewDesc = {};
603         viewDesc.aspect = wgpu::TextureAspect::All;
604         viewDesc.dimension = wgpu::TextureViewDimension::e2D;
605         viewDesc.format = wgpu::TextureFormat::RGBA8Uint;
606         viewDesc.baseMipLevel = 0;
607         viewDesc.mipLevelCount = 2;
608 
609         // Setting texture view with lod 0 and 1 is invalid
610         wgpu::TextureView view = textureMip.CreateView(&viewDesc);
611         ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, view}}));
612 
613         // Setting texture view with lod 1 and 2 is invalid
614         viewDesc.baseMipLevel = 1;
615         view = textureMip.CreateView(&viewDesc);
616         ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, view}}));
617     }
618 }
619 
620 // Check that a UBO must have the correct usage
TEST_F(BindGroupValidationTest,BufferUsageUBO)621 TEST_F(BindGroupValidationTest, BufferUsageUBO) {
622     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
623         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform}});
624 
625     // Control case: using a buffer with the uniform usage works
626     utils::MakeBindGroup(device, layout, {{0, mUBO, 0, 256}});
627 
628     // Using a buffer without the uniform usage fails
629     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mSSBO, 0, 256}}));
630 }
631 
632 // Check that a SSBO must have the correct usage
TEST_F(BindGroupValidationTest,BufferUsageSSBO)633 TEST_F(BindGroupValidationTest, BufferUsageSSBO) {
634     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
635         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
636 
637     // Control case: using a buffer with the storage usage works
638     utils::MakeBindGroup(device, layout, {{0, mSSBO, 0, 256}});
639 
640     // Using a buffer without the storage usage fails
641     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mUBO, 0, 256}}));
642 }
643 
644 // Check that a readonly SSBO must have the correct usage
TEST_F(BindGroupValidationTest,BufferUsageReadonlySSBO)645 TEST_F(BindGroupValidationTest, BufferUsageReadonlySSBO) {
646     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
647         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage}});
648 
649     // Control case: using a buffer with the storage usage works
650     utils::MakeBindGroup(device, layout, {{0, mSSBO, 0, 256}});
651 
652     // Using a buffer without the storage usage fails
653     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mUBO, 0, 256}}));
654 }
655 
656 // Check that a resolve buffer with internal storge usage cannot be used as SSBO
TEST_F(BindGroupValidationTest,BufferUsageQueryResolve)657 TEST_F(BindGroupValidationTest, BufferUsageQueryResolve) {
658     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
659         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
660 
661     // Control case: using a buffer with the storage usage works
662     utils::MakeBindGroup(device, layout, {{0, mSSBO, 0, 256}});
663 
664     // Using a resolve buffer with the internal storage usage fails
665     wgpu::BufferDescriptor descriptor;
666     descriptor.size = 1024;
667     descriptor.usage = wgpu::BufferUsage::QueryResolve;
668     wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
669     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 0, 256}}));
670 }
671 
672 // Tests constraints on the buffer offset for bind groups.
TEST_F(BindGroupValidationTest,BufferOffsetAlignment)673 TEST_F(BindGroupValidationTest, BufferOffsetAlignment) {
674     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
675         device, {
676                     {0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform},
677                 });
678 
679     // Check that offset 0 is valid
680     utils::MakeBindGroup(device, layout, {{0, mUBO, 0, 512}});
681 
682     // Check that offset 256 (aligned) is valid
683     utils::MakeBindGroup(device, layout, {{0, mUBO, 256, 256}});
684 
685     // Check cases where unaligned buffer offset is invalid
686     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mUBO, 1, 256}}));
687     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mUBO, 128, 256}}));
688     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mUBO, 255, 256}}));
689 }
690 
691 // Tests constraints on the texture for MultisampledTexture bindings
TEST_F(BindGroupValidationTest,MultisampledTexture)692 TEST_F(BindGroupValidationTest, MultisampledTexture) {
693     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
694         device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float,
695                   wgpu::TextureViewDimension::e2D, true}});
696 
697     wgpu::BindGroupEntry binding;
698     binding.binding = 0;
699     binding.sampler = nullptr;
700     binding.textureView = nullptr;
701     binding.buffer = nullptr;
702     binding.offset = 0;
703     binding.size = 0;
704 
705     wgpu::BindGroupDescriptor descriptor;
706     descriptor.layout = layout;
707     descriptor.entryCount = 1;
708     descriptor.entries = &binding;
709 
710     // Not setting anything fails
711     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
712 
713     // Control case: setting a multisampled 2D texture works
714     wgpu::TextureDescriptor textureDesc;
715     textureDesc.sampleCount = 4;
716     textureDesc.usage = wgpu::TextureUsage::TextureBinding;
717     textureDesc.dimension = wgpu::TextureDimension::e2D;
718     textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
719     textureDesc.size = {1, 1, 1};
720     wgpu::Texture msTexture = device.CreateTexture(&textureDesc);
721 
722     binding.textureView = msTexture.CreateView();
723     device.CreateBindGroup(&descriptor);
724     binding.textureView = nullptr;
725 
726     // Error case: setting a single sampled 2D texture is an error.
727     binding.textureView = mSampledTextureView;
728     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
729     binding.textureView = nullptr;
730 }
731 
732 // Tests constraints to be sure the buffer binding fits in the buffer
TEST_F(BindGroupValidationTest,BufferBindingOOB)733 TEST_F(BindGroupValidationTest, BufferBindingOOB) {
734     wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
735         device, {
736                     {0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform},
737                 });
738 
739     wgpu::BufferDescriptor descriptor;
740     descriptor.size = 1024;
741     descriptor.usage = wgpu::BufferUsage::Uniform;
742     wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
743 
744     // Success case, touching the start of the buffer works
745     utils::MakeBindGroup(device, layout, {{0, buffer, 0, 256}});
746 
747     // Success case, touching the end of the buffer works
748     utils::MakeBindGroup(device, layout, {{0, buffer, 3 * 256, 256}});
749 
750     // Error case, zero size is invalid.
751     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 1024, 0}}));
752 
753     // Success case, touching the full buffer works
754     utils::MakeBindGroup(device, layout, {{0, buffer, 0, 1024}});
755     utils::MakeBindGroup(device, layout, {{0, buffer, 0, wgpu::kWholeSize}});
756 
757     // Success case, whole size causes the rest of the buffer to be used but not beyond.
758     utils::MakeBindGroup(device, layout, {{0, buffer, 256, wgpu::kWholeSize}});
759 
760     // Error case, offset is OOB
761     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 256 * 5, 0}}));
762 
763     // Error case, size is OOB
764     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 0, 256 * 5}}));
765 
766     // Error case, offset+size is OOB
767     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 1024, 256}}));
768 
769     // Error case, offset+size overflows to be 0
770     ASSERT_DEVICE_ERROR(
771         utils::MakeBindGroup(device, layout, {{0, buffer, 256, uint32_t(0) - uint32_t(256)}}));
772 }
773 
774 // Tests constraints to be sure the uniform buffer binding isn't too large
TEST_F(BindGroupValidationTest,MaxUniformBufferBindingSize)775 TEST_F(BindGroupValidationTest, MaxUniformBufferBindingSize) {
776     wgpu::Limits supportedLimits = GetSupportedLimits().limits;
777 
778     wgpu::BufferDescriptor descriptor;
779     descriptor.size = 2 * supportedLimits.maxUniformBufferBindingSize;
780     descriptor.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage;
781     wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
782 
783     wgpu::BindGroupLayout uniformLayout = utils::MakeBindGroupLayout(
784         device, {{0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform}});
785 
786     // Success case, this is exactly the limit
787     utils::MakeBindGroup(device, uniformLayout,
788                          {{0, buffer, 0, supportedLimits.maxUniformBufferBindingSize}});
789 
790     wgpu::BindGroupLayout doubleUniformLayout = utils::MakeBindGroupLayout(
791         device, {{0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform},
792                  {1, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform}});
793 
794     // Success case, individual bindings don't exceed the limit
795     utils::MakeBindGroup(device, doubleUniformLayout,
796                          {{0, buffer, 0, supportedLimits.maxUniformBufferBindingSize},
797                           {1, buffer, supportedLimits.maxUniformBufferBindingSize,
798                            supportedLimits.maxUniformBufferBindingSize}});
799 
800     // Error case, this is above the limit
801     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(
802         device, uniformLayout, {{0, buffer, 0, supportedLimits.maxUniformBufferBindingSize + 1}}));
803 
804     // Making sure the constraint doesn't apply to storage buffers
805     wgpu::BindGroupLayout readonlyStorageLayout = utils::MakeBindGroupLayout(
806         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage}});
807     wgpu::BindGroupLayout storageLayout = utils::MakeBindGroupLayout(
808         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
809 
810     // Success case, storage buffer can still be created.
811     utils::MakeBindGroup(device, readonlyStorageLayout,
812                          {{0, buffer, 0, 2 * supportedLimits.maxUniformBufferBindingSize}});
813     utils::MakeBindGroup(device, storageLayout,
814                          {{0, buffer, 0, 2 * supportedLimits.maxUniformBufferBindingSize}});
815 }
816 
817 // Tests constraints to be sure the storage buffer binding isn't too large
TEST_F(BindGroupValidationTest,MaxStorageBufferBindingSize)818 TEST_F(BindGroupValidationTest, MaxStorageBufferBindingSize) {
819     wgpu::Limits supportedLimits = GetSupportedLimits().limits;
820 
821     wgpu::BufferDescriptor descriptor;
822     descriptor.size = 2 * supportedLimits.maxStorageBufferBindingSize;
823     descriptor.usage = wgpu::BufferUsage::Storage;
824     wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
825 
826     wgpu::BindGroupLayout uniformLayout = utils::MakeBindGroupLayout(
827         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
828 
829     // Success case, this is exactly the limit
830     utils::MakeBindGroup(device, uniformLayout,
831                          {{0, buffer, 0, supportedLimits.maxStorageBufferBindingSize}});
832 
833     // Success case, this is one less than the limit (check it is not an alignment constraint)
834     utils::MakeBindGroup(device, uniformLayout,
835                          {{0, buffer, 0, supportedLimits.maxStorageBufferBindingSize - 1}});
836 
837     wgpu::BindGroupLayout doubleUniformLayout = utils::MakeBindGroupLayout(
838         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage},
839                  {1, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
840 
841     // Success case, individual bindings don't exceed the limit
842     utils::MakeBindGroup(device, doubleUniformLayout,
843                          {{0, buffer, 0, supportedLimits.maxStorageBufferBindingSize},
844                           {1, buffer, supportedLimits.maxStorageBufferBindingSize,
845                            supportedLimits.maxStorageBufferBindingSize}});
846 
847     // Error case, this is above the limit
848     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(
849         device, uniformLayout, {{0, buffer, 0, supportedLimits.maxStorageBufferBindingSize + 1}}));
850 }
851 
852 // Test what happens when the layout is an error.
TEST_F(BindGroupValidationTest,ErrorLayout)853 TEST_F(BindGroupValidationTest, ErrorLayout) {
854     wgpu::BindGroupLayout goodLayout = utils::MakeBindGroupLayout(
855         device, {
856                     {0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform},
857                 });
858 
859     wgpu::BindGroupLayout errorLayout;
860     ASSERT_DEVICE_ERROR(
861         errorLayout = utils::MakeBindGroupLayout(
862             device, {
863                         {0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform},
864                         {0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform},
865                     }));
866 
867     // Control case, creating with the good layout works
868     utils::MakeBindGroup(device, goodLayout, {{0, mUBO, 0, 256}});
869 
870     // Creating with an error layout fails
871     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, errorLayout, {{0, mUBO, 0, 256}}));
872 }
873 
874 class BindGroupLayoutValidationTest : public ValidationTest {
875   public:
MakeBindGroupLayout(wgpu::BindGroupLayoutEntry * binding,uint32_t count)876     wgpu::BindGroupLayout MakeBindGroupLayout(wgpu::BindGroupLayoutEntry* binding, uint32_t count) {
877         wgpu::BindGroupLayoutDescriptor descriptor;
878         descriptor.entryCount = count;
879         descriptor.entries = binding;
880         return device.CreateBindGroupLayout(&descriptor);
881     }
882 
TestCreateBindGroupLayout(wgpu::BindGroupLayoutEntry * binding,uint32_t count,bool expected)883     void TestCreateBindGroupLayout(wgpu::BindGroupLayoutEntry* binding,
884                                    uint32_t count,
885                                    bool expected) {
886         wgpu::BindGroupLayoutDescriptor descriptor;
887 
888         descriptor.entryCount = count;
889         descriptor.entries = binding;
890 
891         if (!expected) {
892             ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&descriptor));
893         } else {
894             device.CreateBindGroupLayout(&descriptor);
895         }
896     }
897 
TestCreatePipelineLayout(wgpu::BindGroupLayout * bgl,uint32_t count,bool expected)898     void TestCreatePipelineLayout(wgpu::BindGroupLayout* bgl, uint32_t count, bool expected) {
899         wgpu::PipelineLayoutDescriptor descriptor;
900 
901         descriptor.bindGroupLayoutCount = count;
902         descriptor.bindGroupLayouts = bgl;
903 
904         if (!expected) {
905             ASSERT_DEVICE_ERROR(device.CreatePipelineLayout(&descriptor));
906         } else {
907             device.CreatePipelineLayout(&descriptor);
908         }
909     }
910 };
911 
912 // Tests setting storage buffer and readonly storage buffer bindings in vertex and fragment shader.
TEST_F(BindGroupLayoutValidationTest,BindGroupLayoutStorageBindingsInVertexShader)913 TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutStorageBindingsInVertexShader) {
914     // Checks that storage buffer binding is not supported in vertex shader.
915     ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(
916         device, {{0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Storage}}));
917 
918     utils::MakeBindGroupLayout(
919         device, {{0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::ReadOnlyStorage}});
920 
921     utils::MakeBindGroupLayout(
922         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
923 
924     utils::MakeBindGroupLayout(
925         device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage}});
926 }
927 
928 // Tests setting that bind group layout bindings numbers may be very large.
TEST_F(BindGroupLayoutValidationTest,BindGroupLayoutEntryNumberLarge)929 TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutEntryNumberLarge) {
930     // Checks that uint32_t max is valid.
931     utils::MakeBindGroupLayout(device,
932                                {{std::numeric_limits<uint32_t>::max(), wgpu::ShaderStage::Vertex,
933                                  wgpu::BufferBindingType::Uniform}});
934 }
935 
936 // This test verifies that the BindGroupLayout bindings are correctly validated, even if the
937 // binding ids are out-of-order.
TEST_F(BindGroupLayoutValidationTest,BindGroupEntry)938 TEST_F(BindGroupLayoutValidationTest, BindGroupEntry) {
939     utils::MakeBindGroupLayout(device,
940                                {
941                                    {1, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform},
942                                    {0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform},
943                                });
944 }
945 
946 // Check that dynamic = true is only allowed buffer bindings.
TEST_F(BindGroupLayoutValidationTest,DynamicAndTypeCompatibility)947 TEST_F(BindGroupLayoutValidationTest, DynamicAndTypeCompatibility) {
948     utils::MakeBindGroupLayout(
949         device, {
950                     {0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform, true},
951                 });
952 
953     utils::MakeBindGroupLayout(
954         device, {
955                     {0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage, true},
956                 });
957 
958     utils::MakeBindGroupLayout(
959         device, {
960                     {0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage, true},
961                 });
962 }
963 
964 // This test verifies that visibility of bindings in BindGroupLayout can be none
TEST_F(BindGroupLayoutValidationTest,BindGroupLayoutVisibilityNone)965 TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutVisibilityNone) {
966     utils::MakeBindGroupLayout(device,
967                                {
968                                    {0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform},
969                                });
970 
971     wgpu::BindGroupLayoutEntry entry;
972     entry.binding = 0;
973     entry.visibility = wgpu::ShaderStage::None;
974     entry.buffer.type = wgpu::BufferBindingType::Uniform;
975     wgpu::BindGroupLayoutDescriptor descriptor;
976     descriptor.entryCount = 1;
977     descriptor.entries = &entry;
978     device.CreateBindGroupLayout(&descriptor);
979 }
980 
981 // This test verifies that binding with none visibility in bind group layout can be supported in
982 // bind group
TEST_F(BindGroupLayoutValidationTest,BindGroupLayoutVisibilityNoneExpectsBindGroupEntry)983 TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutVisibilityNoneExpectsBindGroupEntry) {
984     wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
985         device, {
986                     {0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform},
987                     {1, wgpu::ShaderStage::None, wgpu::BufferBindingType::Uniform},
988                 });
989     wgpu::BufferDescriptor descriptor;
990     descriptor.size = 4;
991     descriptor.usage = wgpu::BufferUsage::Uniform;
992     wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
993 
994     utils::MakeBindGroup(device, bgl, {{0, buffer}, {1, buffer}});
995 
996     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bgl, {{0, buffer}}));
997 }
998 
999 #define BGLEntryType(...) \
1000     utils::BindingLayoutEntryInitializationHelper(0, wgpu::ShaderStage::Compute, __VA_ARGS__)
1001 
TEST_F(BindGroupLayoutValidationTest,PerStageLimits)1002 TEST_F(BindGroupLayoutValidationTest, PerStageLimits) {
1003     struct TestInfo {
1004         uint32_t maxCount;
1005         wgpu::BindGroupLayoutEntry entry;
1006         wgpu::BindGroupLayoutEntry otherEntry;
1007     };
1008 
1009     std::array<TestInfo, 7> kTestInfos = {
1010         TestInfo{kMaxSampledTexturesPerShaderStage, BGLEntryType(wgpu::TextureSampleType::Float),
1011                  BGLEntryType(wgpu::BufferBindingType::Uniform)},
1012         TestInfo{kMaxSamplersPerShaderStage, BGLEntryType(wgpu::SamplerBindingType::Filtering),
1013                  BGLEntryType(wgpu::BufferBindingType::Uniform)},
1014         TestInfo{kMaxSamplersPerShaderStage, BGLEntryType(wgpu::SamplerBindingType::Comparison),
1015                  BGLEntryType(wgpu::BufferBindingType::Uniform)},
1016         TestInfo{kMaxStorageBuffersPerShaderStage, BGLEntryType(wgpu::BufferBindingType::Storage),
1017                  BGLEntryType(wgpu::BufferBindingType::Uniform)},
1018         TestInfo{
1019             kMaxStorageTexturesPerShaderStage,
1020             BGLEntryType(wgpu::StorageTextureAccess::WriteOnly, wgpu::TextureFormat::RGBA8Unorm),
1021             BGLEntryType(wgpu::BufferBindingType::Uniform)},
1022         TestInfo{kMaxUniformBuffersPerShaderStage, BGLEntryType(wgpu::BufferBindingType::Uniform),
1023                  BGLEntryType(wgpu::TextureSampleType::Float)},
1024         // External textures use multiple bindings (3 sampled textures, 1 sampler, 1 uniform buffer)
1025         // that count towards the per stage binding limits. The number of external textures are
1026         // currently restricted by the maximum number of sampled textures.
1027         TestInfo{kMaxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture,
1028                  BGLEntryType(&utils::kExternalTextureBindingLayout),
1029                  BGLEntryType(wgpu::BufferBindingType::Uniform)}};
1030 
1031     for (TestInfo info : kTestInfos) {
1032         wgpu::BindGroupLayout bgl[2];
1033         std::vector<utils::BindingLayoutEntryInitializationHelper> maxBindings;
1034 
1035         for (uint32_t i = 0; i < info.maxCount; ++i) {
1036             wgpu::BindGroupLayoutEntry entry = info.entry;
1037             entry.binding = i;
1038             maxBindings.push_back(entry);
1039         }
1040 
1041         // Creating with the maxes works.
1042         bgl[0] = MakeBindGroupLayout(maxBindings.data(), maxBindings.size());
1043 
1044         // Adding an extra binding of a different type works.
1045         {
1046             std::vector<utils::BindingLayoutEntryInitializationHelper> bindings = maxBindings;
1047             wgpu::BindGroupLayoutEntry entry = info.otherEntry;
1048             entry.binding = info.maxCount;
1049             bindings.push_back(entry);
1050             MakeBindGroupLayout(bindings.data(), bindings.size());
1051         }
1052 
1053         // Adding an extra binding of the maxed type in a different stage works
1054         {
1055             std::vector<utils::BindingLayoutEntryInitializationHelper> bindings = maxBindings;
1056             wgpu::BindGroupLayoutEntry entry = info.entry;
1057             entry.binding = info.maxCount;
1058             entry.visibility = wgpu::ShaderStage::Fragment;
1059             bindings.push_back(entry);
1060             MakeBindGroupLayout(bindings.data(), bindings.size());
1061         }
1062 
1063         // Adding an extra binding of the maxed type and stage exceeds the per stage limit.
1064         {
1065             std::vector<utils::BindingLayoutEntryInitializationHelper> bindings = maxBindings;
1066             wgpu::BindGroupLayoutEntry entry = info.entry;
1067             entry.binding = info.maxCount;
1068             bindings.push_back(entry);
1069             ASSERT_DEVICE_ERROR(MakeBindGroupLayout(bindings.data(), bindings.size()));
1070         }
1071 
1072         // Creating a pipeline layout from the valid BGL works.
1073         TestCreatePipelineLayout(bgl, 1, true);
1074 
1075         // Adding an extra binding of a different type in a different BGL works
1076         bgl[1] = utils::MakeBindGroupLayout(device, {info.otherEntry});
1077         TestCreatePipelineLayout(bgl, 2, true);
1078 
1079         {
1080             // Adding an extra binding of the maxed type in a different stage works
1081             wgpu::BindGroupLayoutEntry entry = info.entry;
1082             entry.visibility = wgpu::ShaderStage::Fragment;
1083             bgl[1] = utils::MakeBindGroupLayout(device, {entry});
1084             TestCreatePipelineLayout(bgl, 2, true);
1085         }
1086 
1087         // Adding an extra binding of the maxed type in a different BGL exceeds the per stage limit.
1088         bgl[1] = utils::MakeBindGroupLayout(device, {info.entry});
1089         TestCreatePipelineLayout(bgl, 2, false);
1090     }
1091 }
1092 
1093 // External textures require multiple binding slots (3 sampled texture, 1 uniform buffer, 1
1094 // sampler), so ensure that these count towards the limit when combined non-external texture
1095 // bindings.
TEST_F(BindGroupLayoutValidationTest,PerStageLimitsWithExternalTexture)1096 TEST_F(BindGroupLayoutValidationTest, PerStageLimitsWithExternalTexture) {
1097     struct TestInfo {
1098         uint32_t maxCount;
1099         uint32_t bindingsPerExternalTexture;
1100         wgpu::BindGroupLayoutEntry entry;
1101         wgpu::BindGroupLayoutEntry otherEntry;
1102     };
1103 
1104     std::array<TestInfo, 3> kTestInfos = {
1105         TestInfo{kMaxSampledTexturesPerShaderStage, kSampledTexturesPerExternalTexture,
1106                  BGLEntryType(wgpu::TextureSampleType::Float),
1107                  BGLEntryType(wgpu::BufferBindingType::Uniform)},
1108         TestInfo{kMaxSamplersPerShaderStage, kSamplersPerExternalTexture,
1109                  BGLEntryType(wgpu::SamplerBindingType::Filtering),
1110                  BGLEntryType(wgpu::BufferBindingType::Uniform)},
1111         TestInfo{kMaxUniformBuffersPerShaderStage, kUniformsPerExternalTexture,
1112                  BGLEntryType(wgpu::BufferBindingType::Uniform),
1113                  BGLEntryType(wgpu::TextureSampleType::Float)},
1114     };
1115 
1116     for (TestInfo info : kTestInfos) {
1117         wgpu::BindGroupLayout bgl[2];
1118         std::vector<utils::BindingLayoutEntryInitializationHelper> maxBindings;
1119 
1120         // Create an external texture binding layout entry
1121         wgpu::BindGroupLayoutEntry entry = BGLEntryType(&utils::kExternalTextureBindingLayout);
1122         entry.binding = 0;
1123         maxBindings.push_back(entry);
1124 
1125         // Create the other bindings such that we reach the max bindings per stage when including
1126         // the external texture.
1127         for (uint32_t i = 1; i <= info.maxCount - info.bindingsPerExternalTexture; ++i) {
1128             wgpu::BindGroupLayoutEntry entry = info.entry;
1129             entry.binding = i;
1130             maxBindings.push_back(entry);
1131         }
1132 
1133         // Ensure that creation without the external texture works.
1134         bgl[0] = MakeBindGroupLayout(maxBindings.data(), maxBindings.size());
1135 
1136         // Adding an extra binding of a different type works.
1137         {
1138             std::vector<utils::BindingLayoutEntryInitializationHelper> bindings = maxBindings;
1139             wgpu::BindGroupLayoutEntry entry = info.otherEntry;
1140             entry.binding = info.maxCount;
1141             bindings.push_back(entry);
1142             MakeBindGroupLayout(bindings.data(), bindings.size());
1143         }
1144 
1145         // Adding an extra binding of the maxed type in a different stage works
1146         {
1147             std::vector<utils::BindingLayoutEntryInitializationHelper> bindings = maxBindings;
1148             wgpu::BindGroupLayoutEntry entry = info.entry;
1149             entry.binding = info.maxCount;
1150             entry.visibility = wgpu::ShaderStage::Fragment;
1151             bindings.push_back(entry);
1152             MakeBindGroupLayout(bindings.data(), bindings.size());
1153         }
1154 
1155         // Adding an extra binding of the maxed type and stage exceeds the per stage limit.
1156         {
1157             std::vector<utils::BindingLayoutEntryInitializationHelper> bindings = maxBindings;
1158             wgpu::BindGroupLayoutEntry entry = info.entry;
1159             entry.binding = info.maxCount;
1160             bindings.push_back(entry);
1161             ASSERT_DEVICE_ERROR(MakeBindGroupLayout(bindings.data(), bindings.size()));
1162         }
1163 
1164         // Creating a pipeline layout from the valid BGL works.
1165         TestCreatePipelineLayout(bgl, 1, true);
1166 
1167         // Adding an extra binding of a different type in a different BGL works
1168         bgl[1] = utils::MakeBindGroupLayout(device, {info.otherEntry});
1169         TestCreatePipelineLayout(bgl, 2, true);
1170 
1171         {
1172             // Adding an extra binding of the maxed type in a different stage works
1173             wgpu::BindGroupLayoutEntry entry = info.entry;
1174             entry.visibility = wgpu::ShaderStage::Fragment;
1175             bgl[1] = utils::MakeBindGroupLayout(device, {entry});
1176             TestCreatePipelineLayout(bgl, 2, true);
1177         }
1178 
1179         // Adding an extra binding of the maxed type in a different BGL exceeds the per stage limit.
1180         bgl[1] = utils::MakeBindGroupLayout(device, {info.entry});
1181         TestCreatePipelineLayout(bgl, 2, false);
1182     }
1183 }
1184 
1185 // Check that dynamic buffer numbers exceed maximum value in one bind group layout.
TEST_F(BindGroupLayoutValidationTest,DynamicBufferNumberLimit)1186 TEST_F(BindGroupLayoutValidationTest, DynamicBufferNumberLimit) {
1187     wgpu::BindGroupLayout bgl[2];
1188     std::vector<wgpu::BindGroupLayoutEntry> maxUniformDB;
1189     std::vector<wgpu::BindGroupLayoutEntry> maxStorageDB;
1190     std::vector<wgpu::BindGroupLayoutEntry> maxReadonlyStorageDB;
1191 
1192     // In this test, we use all the same shader stage. Ensure that this does not exceed the
1193     // per-stage limit.
1194     static_assert(kMaxDynamicUniformBuffersPerPipelineLayout <= kMaxUniformBuffersPerShaderStage,
1195                   "");
1196     static_assert(kMaxDynamicStorageBuffersPerPipelineLayout <= kMaxStorageBuffersPerShaderStage,
1197                   "");
1198 
1199     for (uint32_t i = 0; i < kMaxDynamicUniformBuffersPerPipelineLayout; ++i) {
1200         maxUniformDB.push_back(utils::BindingLayoutEntryInitializationHelper(
1201             i, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform, true));
1202     }
1203 
1204     for (uint32_t i = 0; i < kMaxDynamicStorageBuffersPerPipelineLayout; ++i) {
1205         maxStorageDB.push_back(utils::BindingLayoutEntryInitializationHelper(
1206             i, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage, true));
1207     }
1208 
1209     for (uint32_t i = 0; i < kMaxDynamicStorageBuffersPerPipelineLayout; ++i) {
1210         maxReadonlyStorageDB.push_back(utils::BindingLayoutEntryInitializationHelper(
1211             i, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage, true));
1212     }
1213 
1214     // Test creating with the maxes works
1215     {
1216         bgl[0] = MakeBindGroupLayout(maxUniformDB.data(), maxUniformDB.size());
1217         TestCreatePipelineLayout(bgl, 1, true);
1218 
1219         bgl[0] = MakeBindGroupLayout(maxStorageDB.data(), maxStorageDB.size());
1220         TestCreatePipelineLayout(bgl, 1, true);
1221 
1222         bgl[0] = MakeBindGroupLayout(maxReadonlyStorageDB.data(), maxReadonlyStorageDB.size());
1223         TestCreatePipelineLayout(bgl, 1, true);
1224     }
1225 
1226     // The following tests exceed the per-pipeline layout limits. We use the Fragment stage to
1227     // ensure we don't hit the per-stage limit.
1228 
1229     // Check dynamic uniform buffers exceed maximum in pipeline layout.
1230     {
1231         bgl[0] = MakeBindGroupLayout(maxUniformDB.data(), maxUniformDB.size());
1232         bgl[1] = utils::MakeBindGroupLayout(
1233             device, {
1234                         {0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform, true},
1235                     });
1236 
1237         TestCreatePipelineLayout(bgl, 2, false);
1238     }
1239 
1240     // Check dynamic storage buffers exceed maximum in pipeline layout
1241     {
1242         bgl[0] = MakeBindGroupLayout(maxStorageDB.data(), maxStorageDB.size());
1243         bgl[1] = utils::MakeBindGroupLayout(
1244             device, {
1245                         {0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage, true},
1246                     });
1247 
1248         TestCreatePipelineLayout(bgl, 2, false);
1249     }
1250 
1251     // Check dynamic readonly storage buffers exceed maximum in pipeline layout
1252     {
1253         bgl[0] = MakeBindGroupLayout(maxReadonlyStorageDB.data(), maxReadonlyStorageDB.size());
1254         bgl[1] = utils::MakeBindGroupLayout(
1255             device,
1256             {
1257                 {0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage, true},
1258             });
1259 
1260         TestCreatePipelineLayout(bgl, 2, false);
1261     }
1262 
1263     // Check dynamic storage buffers + dynamic readonly storage buffers exceed maximum storage
1264     // buffers in pipeline layout
1265     {
1266         bgl[0] = MakeBindGroupLayout(maxStorageDB.data(), maxStorageDB.size());
1267         bgl[1] = utils::MakeBindGroupLayout(
1268             device,
1269             {
1270                 {0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage, true},
1271             });
1272 
1273         TestCreatePipelineLayout(bgl, 2, false);
1274     }
1275 
1276     // Check dynamic uniform buffers exceed maximum in bind group layout.
1277     {
1278         maxUniformDB.push_back(utils::BindingLayoutEntryInitializationHelper(
1279             kMaxDynamicUniformBuffersPerPipelineLayout, wgpu::ShaderStage::Fragment,
1280             wgpu::BufferBindingType::Uniform, true));
1281         TestCreateBindGroupLayout(maxUniformDB.data(), maxUniformDB.size(), false);
1282     }
1283 
1284     // Check dynamic storage buffers exceed maximum in bind group layout.
1285     {
1286         maxStorageDB.push_back(utils::BindingLayoutEntryInitializationHelper(
1287             kMaxDynamicStorageBuffersPerPipelineLayout, wgpu::ShaderStage::Fragment,
1288             wgpu::BufferBindingType::Storage, true));
1289         TestCreateBindGroupLayout(maxStorageDB.data(), maxStorageDB.size(), false);
1290     }
1291 
1292     // Check dynamic readonly storage buffers exceed maximum in bind group layout.
1293     {
1294         maxReadonlyStorageDB.push_back(utils::BindingLayoutEntryInitializationHelper(
1295             kMaxDynamicStorageBuffersPerPipelineLayout, wgpu::ShaderStage::Fragment,
1296             wgpu::BufferBindingType::ReadOnlyStorage, true));
1297         TestCreateBindGroupLayout(maxReadonlyStorageDB.data(), maxReadonlyStorageDB.size(), false);
1298     }
1299 }
1300 
1301 // Test that multisampled textures must be 2D sampled textures
TEST_F(BindGroupLayoutValidationTest,MultisampledTextureViewDimension)1302 TEST_F(BindGroupLayoutValidationTest, MultisampledTextureViewDimension) {
1303     // Multisampled 2D texture works.
1304     utils::MakeBindGroupLayout(device,
1305                                {
1306                                    {0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float,
1307                                     wgpu::TextureViewDimension::e2D, true},
1308                                });
1309 
1310     // Multisampled 2D (defaulted) texture works.
1311     utils::MakeBindGroupLayout(device,
1312                                {
1313                                    {0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float,
1314                                     wgpu::TextureViewDimension::Undefined, true},
1315                                });
1316 
1317     // Multisampled 2D array texture is invalid.
1318     ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(
1319         device, {
1320                     {0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float,
1321                      wgpu::TextureViewDimension::e2DArray, true},
1322                 }));
1323 
1324     // Multisampled cube texture is invalid.
1325     ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(
1326         device, {
1327                     {0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float,
1328                      wgpu::TextureViewDimension::Cube, true},
1329                 }));
1330 
1331     // Multisampled cube array texture is invalid.
1332     ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(
1333         device, {
1334                     {0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float,
1335                      wgpu::TextureViewDimension::CubeArray, true},
1336                 }));
1337 
1338     // Multisampled 3D texture is invalid.
1339     ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(
1340         device, {
1341                     {0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float,
1342                      wgpu::TextureViewDimension::e3D, true},
1343                 }));
1344 
1345     // Multisampled 1D texture is invalid.
1346     ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(
1347         device, {
1348                     {0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float,
1349                      wgpu::TextureViewDimension::e1D, true},
1350                 }));
1351 }
1352 
1353 // Test that multisampled texture bindings are valid
TEST_F(BindGroupLayoutValidationTest,MultisampledTextureSampleType)1354 TEST_F(BindGroupLayoutValidationTest, MultisampledTextureSampleType) {
1355     // Multisampled float sample type works.
1356     utils::MakeBindGroupLayout(device,
1357                                {
1358                                    {0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float,
1359                                     wgpu::TextureViewDimension::e2D, true},
1360                                });
1361 
1362     // Multisampled uint sample type works.
1363     utils::MakeBindGroupLayout(device,
1364                                {
1365                                    {0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Uint,
1366                                     wgpu::TextureViewDimension::e2D, true},
1367                                });
1368 
1369     // Multisampled sint sample type works.
1370     utils::MakeBindGroupLayout(device,
1371                                {
1372                                    {0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Sint,
1373                                     wgpu::TextureViewDimension::e2D, true},
1374                                });
1375 
1376     // Multisampled depth sample type works.
1377     utils::MakeBindGroupLayout(device,
1378                                {
1379                                    {0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Depth,
1380                                     wgpu::TextureViewDimension::e2D, true},
1381                                });
1382 }
1383 
1384 constexpr uint32_t kBindingSize = 9;
1385 
1386 class SetBindGroupValidationTest : public ValidationTest {
1387   public:
SetUp()1388     void SetUp() override {
1389         ValidationTest::SetUp();
1390 
1391         mBindGroupLayout = utils::MakeBindGroupLayout(
1392             device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
1393                       wgpu::BufferBindingType::Uniform, true},
1394                      {1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
1395                       wgpu::BufferBindingType::Uniform, false},
1396                      {2, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
1397                       wgpu::BufferBindingType::Storage, true},
1398                      {3, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
1399                       wgpu::BufferBindingType::ReadOnlyStorage, true}});
1400         mMinUniformBufferOffsetAlignment =
1401             GetSupportedLimits().limits.minUniformBufferOffsetAlignment;
1402         mBufferSize = 3 * mMinUniformBufferOffsetAlignment + 8;
1403     }
1404 
CreateBuffer(uint64_t bufferSize,wgpu::BufferUsage usage)1405     wgpu::Buffer CreateBuffer(uint64_t bufferSize, wgpu::BufferUsage usage) {
1406         wgpu::BufferDescriptor bufferDescriptor;
1407         bufferDescriptor.size = bufferSize;
1408         bufferDescriptor.usage = usage;
1409 
1410         return device.CreateBuffer(&bufferDescriptor);
1411     }
1412 
1413     wgpu::BindGroupLayout mBindGroupLayout;
1414 
CreateRenderPipeline()1415     wgpu::RenderPipeline CreateRenderPipeline() {
1416         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
1417                 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
1418                     return vec4<f32>();
1419                 })");
1420 
1421         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
1422                 [[block]] struct S {
1423                     value : vec2<f32>;
1424                 };
1425 
1426                 [[group(0), binding(0)]] var<uniform> uBufferDynamic : S;
1427                 [[group(0), binding(1)]] var<uniform> uBuffer : S;
1428                 [[group(0), binding(2)]] var<storage, read_write> sBufferDynamic : S;
1429                 [[group(0), binding(3)]] var<storage, read> sReadonlyBufferDynamic : S;
1430 
1431                 [[stage(fragment)]] fn main() {
1432                 })");
1433 
1434         utils::ComboRenderPipelineDescriptor pipelineDescriptor;
1435         pipelineDescriptor.vertex.module = vsModule;
1436         pipelineDescriptor.cFragment.module = fsModule;
1437         pipelineDescriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
1438         wgpu::PipelineLayout pipelineLayout =
1439             utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
1440         pipelineDescriptor.layout = pipelineLayout;
1441         return device.CreateRenderPipeline(&pipelineDescriptor);
1442     }
1443 
CreateComputePipeline()1444     wgpu::ComputePipeline CreateComputePipeline() {
1445         wgpu::ShaderModule csModule = utils::CreateShaderModule(device, R"(
1446                 [[block]] struct S {
1447                     value : vec2<f32>;
1448                 };
1449 
1450                 [[group(0), binding(0)]] var<uniform> uBufferDynamic : S;
1451                 [[group(0), binding(1)]] var<uniform> uBuffer : S;
1452                 [[group(0), binding(2)]] var<storage, read_write> sBufferDynamic : S;
1453                 [[group(0), binding(3)]] var<storage, read> sReadonlyBufferDynamic : S;
1454 
1455                 [[stage(compute), workgroup_size(4, 4, 1)]] fn main() {
1456                 })");
1457 
1458         wgpu::PipelineLayout pipelineLayout =
1459             utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
1460 
1461         wgpu::ComputePipelineDescriptor csDesc;
1462         csDesc.layout = pipelineLayout;
1463         csDesc.compute.module = csModule;
1464         csDesc.compute.entryPoint = "main";
1465 
1466         return device.CreateComputePipeline(&csDesc);
1467     }
1468 
TestRenderPassBindGroup(wgpu::BindGroup bindGroup,uint32_t * offsets,uint32_t count,bool expectation)1469     void TestRenderPassBindGroup(wgpu::BindGroup bindGroup,
1470                                  uint32_t* offsets,
1471                                  uint32_t count,
1472                                  bool expectation) {
1473         wgpu::RenderPipeline renderPipeline = CreateRenderPipeline();
1474         DummyRenderPass renderPass(device);
1475 
1476         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1477         wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
1478         renderPassEncoder.SetPipeline(renderPipeline);
1479         if (bindGroup != nullptr) {
1480             renderPassEncoder.SetBindGroup(0, bindGroup, count, offsets);
1481         }
1482         renderPassEncoder.Draw(3);
1483         renderPassEncoder.EndPass();
1484         if (!expectation) {
1485             ASSERT_DEVICE_ERROR(commandEncoder.Finish());
1486         } else {
1487             commandEncoder.Finish();
1488         }
1489     }
1490 
TestComputePassBindGroup(wgpu::BindGroup bindGroup,uint32_t * offsets,uint32_t count,bool expectation)1491     void TestComputePassBindGroup(wgpu::BindGroup bindGroup,
1492                                   uint32_t* offsets,
1493                                   uint32_t count,
1494                                   bool expectation) {
1495         wgpu::ComputePipeline computePipeline = CreateComputePipeline();
1496 
1497         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1498         wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
1499         computePassEncoder.SetPipeline(computePipeline);
1500         if (bindGroup != nullptr) {
1501             computePassEncoder.SetBindGroup(0, bindGroup, count, offsets);
1502         }
1503         computePassEncoder.Dispatch(1);
1504         computePassEncoder.EndPass();
1505         if (!expectation) {
1506             ASSERT_DEVICE_ERROR(commandEncoder.Finish());
1507         } else {
1508             commandEncoder.Finish();
1509         }
1510     }
1511 
1512   protected:
1513     uint32_t mMinUniformBufferOffsetAlignment;
1514     uint64_t mBufferSize;
1515 };
1516 
1517 // This is the test case that should work.
TEST_F(SetBindGroupValidationTest,Basic)1518 TEST_F(SetBindGroupValidationTest, Basic) {
1519     // Set up the bind group.
1520     wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
1521     wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1522     wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1523     wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
1524                                                      {{0, uniformBuffer, 0, kBindingSize},
1525                                                       {1, uniformBuffer, 0, kBindingSize},
1526                                                       {2, storageBuffer, 0, kBindingSize},
1527                                                       {3, readonlyStorageBuffer, 0, kBindingSize}});
1528 
1529     std::array<uint32_t, 3> offsets = {512, 256, 0};
1530 
1531     TestRenderPassBindGroup(bindGroup, offsets.data(), 3, true);
1532 
1533     TestComputePassBindGroup(bindGroup, offsets.data(), 3, true);
1534 }
1535 
1536 // Draw/dispatch with a bind group missing is invalid
TEST_F(SetBindGroupValidationTest,MissingBindGroup)1537 TEST_F(SetBindGroupValidationTest, MissingBindGroup) {
1538     TestRenderPassBindGroup(nullptr, nullptr, 0, false);
1539     TestComputePassBindGroup(nullptr, nullptr, 0, false);
1540 }
1541 
1542 // Setting bind group after a draw / dispatch should re-verify the layout is compatible
TEST_F(SetBindGroupValidationTest,VerifyGroupIfChangedAfterAction)1543 TEST_F(SetBindGroupValidationTest, VerifyGroupIfChangedAfterAction) {
1544     // Set up the bind group
1545     wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
1546     wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1547     wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1548     wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
1549                                                      {{0, uniformBuffer, 0, kBindingSize},
1550                                                       {1, uniformBuffer, 0, kBindingSize},
1551                                                       {2, storageBuffer, 0, kBindingSize},
1552                                                       {3, readonlyStorageBuffer, 0, kBindingSize}});
1553 
1554     std::array<uint32_t, 3> offsets = {512, 256, 0};
1555 
1556     // Set up bind group that is incompatible
1557     wgpu::BindGroupLayout invalidLayout = utils::MakeBindGroupLayout(
1558         device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
1559                   wgpu::BufferBindingType::Storage}});
1560     wgpu::BindGroup invalidGroup =
1561         utils::MakeBindGroup(device, invalidLayout, {{0, storageBuffer, 0, kBindingSize}});
1562 
1563     {
1564         wgpu::ComputePipeline computePipeline = CreateComputePipeline();
1565         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1566         wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
1567         computePassEncoder.SetPipeline(computePipeline);
1568         computePassEncoder.SetBindGroup(0, bindGroup, 3, offsets.data());
1569         computePassEncoder.Dispatch(1);
1570         computePassEncoder.SetBindGroup(0, invalidGroup, 0, nullptr);
1571         computePassEncoder.Dispatch(1);
1572         computePassEncoder.EndPass();
1573         ASSERT_DEVICE_ERROR(commandEncoder.Finish());
1574     }
1575     {
1576         wgpu::RenderPipeline renderPipeline = CreateRenderPipeline();
1577         DummyRenderPass renderPass(device);
1578 
1579         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1580         wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
1581         renderPassEncoder.SetPipeline(renderPipeline);
1582         renderPassEncoder.SetBindGroup(0, bindGroup, 3, offsets.data());
1583         renderPassEncoder.Draw(3);
1584         renderPassEncoder.SetBindGroup(0, invalidGroup, 0, nullptr);
1585         renderPassEncoder.Draw(3);
1586         renderPassEncoder.EndPass();
1587         ASSERT_DEVICE_ERROR(commandEncoder.Finish());
1588     }
1589 }
1590 
1591 // Test cases that test dynamic offsets count mismatch with bind group layout.
TEST_F(SetBindGroupValidationTest,DynamicOffsetsMismatch)1592 TEST_F(SetBindGroupValidationTest, DynamicOffsetsMismatch) {
1593     // Set up bind group.
1594     wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
1595     wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1596     wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1597     wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
1598                                                      {{0, uniformBuffer, 0, kBindingSize},
1599                                                       {1, uniformBuffer, 0, kBindingSize},
1600                                                       {2, storageBuffer, 0, kBindingSize},
1601                                                       {3, readonlyStorageBuffer, 0, kBindingSize}});
1602 
1603     // Number of offsets mismatch.
1604     std::array<uint32_t, 4> mismatchOffsets = {768, 512, 256, 0};
1605 
1606     TestRenderPassBindGroup(bindGroup, mismatchOffsets.data(), 1, false);
1607     TestRenderPassBindGroup(bindGroup, mismatchOffsets.data(), 2, false);
1608     TestRenderPassBindGroup(bindGroup, mismatchOffsets.data(), 4, false);
1609 
1610     TestComputePassBindGroup(bindGroup, mismatchOffsets.data(), 1, false);
1611     TestComputePassBindGroup(bindGroup, mismatchOffsets.data(), 2, false);
1612     TestComputePassBindGroup(bindGroup, mismatchOffsets.data(), 4, false);
1613 }
1614 
1615 // Test cases that test dynamic offsets not aligned
TEST_F(SetBindGroupValidationTest,DynamicOffsetsNotAligned)1616 TEST_F(SetBindGroupValidationTest, DynamicOffsetsNotAligned) {
1617     // Set up bind group.
1618     wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
1619     wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1620     wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1621     wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
1622                                                      {{0, uniformBuffer, 0, kBindingSize},
1623                                                       {1, uniformBuffer, 0, kBindingSize},
1624                                                       {2, storageBuffer, 0, kBindingSize},
1625                                                       {3, readonlyStorageBuffer, 0, kBindingSize}});
1626 
1627     // Dynamic offsets are not aligned.
1628     std::array<uint32_t, 3> notAlignedOffsets = {512, 128, 0};
1629 
1630     TestRenderPassBindGroup(bindGroup, notAlignedOffsets.data(), 3, false);
1631 
1632     TestComputePassBindGroup(bindGroup, notAlignedOffsets.data(), 3, false);
1633 }
1634 
1635 // Test cases that test dynamic uniform buffer out of bound situation.
TEST_F(SetBindGroupValidationTest,OffsetOutOfBoundDynamicUniformBuffer)1636 TEST_F(SetBindGroupValidationTest, OffsetOutOfBoundDynamicUniformBuffer) {
1637     // Set up bind group.
1638     wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
1639     wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1640     wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1641     wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
1642                                                      {{0, uniformBuffer, 0, kBindingSize},
1643                                                       {1, uniformBuffer, 0, kBindingSize},
1644                                                       {2, storageBuffer, 0, kBindingSize},
1645                                                       {3, readonlyStorageBuffer, 0, kBindingSize}});
1646 
1647     // Dynamic offset + offset is larger than buffer size.
1648     std::array<uint32_t, 3> overFlowOffsets = {1024, 256, 0};
1649 
1650     TestRenderPassBindGroup(bindGroup, overFlowOffsets.data(), 3, false);
1651 
1652     TestComputePassBindGroup(bindGroup, overFlowOffsets.data(), 3, false);
1653 }
1654 
1655 // Test cases that test dynamic storage buffer out of bound situation.
TEST_F(SetBindGroupValidationTest,OffsetOutOfBoundDynamicStorageBuffer)1656 TEST_F(SetBindGroupValidationTest, OffsetOutOfBoundDynamicStorageBuffer) {
1657     // Set up bind group.
1658     wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
1659     wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1660     wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1661     wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
1662                                                      {{0, uniformBuffer, 0, kBindingSize},
1663                                                       {1, uniformBuffer, 0, kBindingSize},
1664                                                       {2, storageBuffer, 0, kBindingSize},
1665                                                       {3, readonlyStorageBuffer, 0, kBindingSize}});
1666 
1667     // Dynamic offset + offset is larger than buffer size.
1668     std::array<uint32_t, 3> overFlowOffsets = {0, 256, 1024};
1669 
1670     TestRenderPassBindGroup(bindGroup, overFlowOffsets.data(), 3, false);
1671 
1672     TestComputePassBindGroup(bindGroup, overFlowOffsets.data(), 3, false);
1673 }
1674 
1675 // Test cases that test dynamic uniform buffer out of bound situation because of binding size.
TEST_F(SetBindGroupValidationTest,BindingSizeOutOfBoundDynamicUniformBuffer)1676 TEST_F(SetBindGroupValidationTest, BindingSizeOutOfBoundDynamicUniformBuffer) {
1677     // Set up bind group, but binding size is larger than
1678     wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
1679     wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1680     wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1681     wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
1682                                                      {{0, uniformBuffer, 0, kBindingSize},
1683                                                       {1, uniformBuffer, 0, kBindingSize},
1684                                                       {2, storageBuffer, 0, kBindingSize},
1685                                                       {3, readonlyStorageBuffer, 0, kBindingSize}});
1686 
1687     // Dynamic offset + offset isn't larger than buffer size.
1688     // But with binding size, it will trigger OOB error.
1689     std::array<uint32_t, 3> offsets = {768, 256, 0};
1690 
1691     TestRenderPassBindGroup(bindGroup, offsets.data(), 3, false);
1692 
1693     TestComputePassBindGroup(bindGroup, offsets.data(), 3, false);
1694 }
1695 
1696 // Test cases that test dynamic storage buffer out of bound situation because of binding size.
TEST_F(SetBindGroupValidationTest,BindingSizeOutOfBoundDynamicStorageBuffer)1697 TEST_F(SetBindGroupValidationTest, BindingSizeOutOfBoundDynamicStorageBuffer) {
1698     wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
1699     wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1700     wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1701     wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
1702                                                      {{0, uniformBuffer, 0, kBindingSize},
1703                                                       {1, uniformBuffer, 0, kBindingSize},
1704                                                       {2, storageBuffer, 0, kBindingSize},
1705                                                       {3, readonlyStorageBuffer, 0, kBindingSize}});
1706     // Dynamic offset + offset isn't larger than buffer size.
1707     // But with binding size, it will trigger OOB error.
1708     std::array<uint32_t, 3> offsets = {0, 256, 768};
1709 
1710     TestRenderPassBindGroup(bindGroup, offsets.data(), 3, false);
1711 
1712     TestComputePassBindGroup(bindGroup, offsets.data(), 3, false);
1713 }
1714 
1715 // Regression test for crbug.com/dawn/408 where dynamic offsets were applied in the wrong order.
1716 // Dynamic offsets should be applied in increasing order of binding number.
TEST_F(SetBindGroupValidationTest,DynamicOffsetOrder)1717 TEST_F(SetBindGroupValidationTest, DynamicOffsetOrder) {
1718     // Note: The order of the binding numbers of the bind group and bind group layout are
1719     // intentionally different and not in increasing order.
1720     // This test uses both storage and uniform buffers to ensure buffer bindings are sorted first by
1721     // binding number before type.
1722     wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1723         device, {
1724                     {3, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage, true},
1725                     {0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage, true},
1726                     {2, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform, true},
1727                 });
1728 
1729     // Create buffers which are 3x, 2x, and 1x the size of the minimum buffer offset, plus 4 bytes
1730     // to spare (to avoid zero-sized bindings). We will offset the bindings so they reach the very
1731     // end of the buffer. Any mismatch applying too-large of an offset to a smaller buffer will hit
1732     // the out-of-bounds condition during validation.
1733     wgpu::Buffer buffer3x =
1734         CreateBuffer(3 * mMinUniformBufferOffsetAlignment + 4, wgpu::BufferUsage::Storage);
1735     wgpu::Buffer buffer2x =
1736         CreateBuffer(2 * mMinUniformBufferOffsetAlignment + 4, wgpu::BufferUsage::Storage);
1737     wgpu::Buffer buffer1x =
1738         CreateBuffer(1 * mMinUniformBufferOffsetAlignment + 4, wgpu::BufferUsage::Uniform);
1739     wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl,
1740                                                      {
1741                                                          {0, buffer3x, 0, 4},
1742                                                          {3, buffer2x, 0, 4},
1743                                                          {2, buffer1x, 0, 4},
1744                                                      });
1745 
1746     std::array<uint32_t, 3> offsets;
1747     {
1748         // Base case works.
1749         offsets = {/* binding 0 */ 0,
1750                    /* binding 2 */ 0,
1751                    /* binding 3 */ 0};
1752         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1753         wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
1754         computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data());
1755         computePassEncoder.EndPass();
1756         commandEncoder.Finish();
1757     }
1758     {
1759         // Offset the first binding to touch the end of the buffer. Should succeed.
1760         // Will fail if the offset is applied to the first or second bindings since their buffers
1761         // are too small.
1762         offsets = {/* binding 0 */ 3 * mMinUniformBufferOffsetAlignment,
1763                    /* binding 2 */ 0,
1764                    /* binding 3 */ 0};
1765         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1766         wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
1767         computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data());
1768         computePassEncoder.EndPass();
1769         commandEncoder.Finish();
1770     }
1771     {
1772         // Offset the second binding to touch the end of the buffer. Should succeed.
1773         offsets = {/* binding 0 */ 0,
1774                    /* binding 2 */ 1 * mMinUniformBufferOffsetAlignment,
1775                    /* binding 3 */ 0};
1776         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1777         wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
1778         computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data());
1779         computePassEncoder.EndPass();
1780         commandEncoder.Finish();
1781     }
1782     {
1783         // Offset the third binding to touch the end of the buffer. Should succeed.
1784         // Will fail if the offset is applied to the second binding since its buffer
1785         // is too small.
1786         offsets = {/* binding 0 */ 0,
1787                    /* binding 2 */ 0,
1788                    /* binding 3 */ 2 * mMinUniformBufferOffsetAlignment};
1789         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1790         wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
1791         computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data());
1792         computePassEncoder.EndPass();
1793         commandEncoder.Finish();
1794     }
1795     {
1796         // Offset each binding to touch the end of their buffer. Should succeed.
1797         offsets = {/* binding 0 */ 3 * mMinUniformBufferOffsetAlignment,
1798                    /* binding 2 */ 1 * mMinUniformBufferOffsetAlignment,
1799                    /* binding 3 */ 2 * mMinUniformBufferOffsetAlignment};
1800         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1801         wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
1802         computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data());
1803         computePassEncoder.EndPass();
1804         commandEncoder.Finish();
1805     }
1806 }
1807 
1808 // Test that an error is produced (and no ASSERTs fired) when using an error bindgroup in
1809 // SetBindGroup
TEST_F(SetBindGroupValidationTest,ErrorBindGroup)1810 TEST_F(SetBindGroupValidationTest, ErrorBindGroup) {
1811     // Bindgroup creation fails because not all bindings are specified.
1812     wgpu::BindGroup bindGroup;
1813     ASSERT_DEVICE_ERROR(bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, {}));
1814 
1815     TestRenderPassBindGroup(bindGroup, nullptr, 0, false);
1816 
1817     TestComputePassBindGroup(bindGroup, nullptr, 0, false);
1818 }
1819 
1820 class SetBindGroupPersistenceValidationTest : public ValidationTest {
1821   protected:
SetUp()1822     void SetUp() override {
1823         ValidationTest::SetUp();
1824 
1825         mVsModule = utils::CreateShaderModule(device, R"(
1826                 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
1827                     return vec4<f32>();
1828                 })");
1829 
1830         mBufferSize = 3 * GetSupportedLimits().limits.minUniformBufferOffsetAlignment + 8;
1831     }
1832 
CreateBuffer(uint64_t bufferSize,wgpu::BufferUsage usage)1833     wgpu::Buffer CreateBuffer(uint64_t bufferSize, wgpu::BufferUsage usage) {
1834         wgpu::BufferDescriptor bufferDescriptor;
1835         bufferDescriptor.size = bufferSize;
1836         bufferDescriptor.usage = usage;
1837 
1838         return device.CreateBuffer(&bufferDescriptor);
1839     }
1840 
1841     // Generates bind group layouts and a pipeline from a 2D list of binding types.
SetUpLayoutsAndPipeline(std::vector<std::vector<wgpu::BufferBindingType>> layouts)1842     std::tuple<std::vector<wgpu::BindGroupLayout>, wgpu::RenderPipeline> SetUpLayoutsAndPipeline(
1843         std::vector<std::vector<wgpu::BufferBindingType>> layouts) {
1844         std::vector<wgpu::BindGroupLayout> bindGroupLayouts(layouts.size());
1845 
1846         // Iterate through the desired bind group layouts.
1847         for (uint32_t l = 0; l < layouts.size(); ++l) {
1848             const auto& layout = layouts[l];
1849             std::vector<wgpu::BindGroupLayoutEntry> bindings(layout.size());
1850 
1851             // Iterate through binding types and populate a list of BindGroupLayoutEntrys.
1852             for (uint32_t b = 0; b < layout.size(); ++b) {
1853                 bindings[b] = utils::BindingLayoutEntryInitializationHelper(
1854                     b, wgpu::ShaderStage::Fragment, layout[b]);
1855             }
1856 
1857             // Create the bind group layout.
1858             wgpu::BindGroupLayoutDescriptor bglDescriptor;
1859             bglDescriptor.entryCount = static_cast<uint32_t>(bindings.size());
1860             bglDescriptor.entries = bindings.data();
1861             bindGroupLayouts[l] = device.CreateBindGroupLayout(&bglDescriptor);
1862         }
1863 
1864         // Create a pipeline layout from the list of bind group layouts.
1865         wgpu::PipelineLayoutDescriptor pipelineLayoutDescriptor;
1866         pipelineLayoutDescriptor.bindGroupLayoutCount =
1867             static_cast<uint32_t>(bindGroupLayouts.size());
1868         pipelineLayoutDescriptor.bindGroupLayouts = bindGroupLayouts.data();
1869 
1870         wgpu::PipelineLayout pipelineLayout =
1871             device.CreatePipelineLayout(&pipelineLayoutDescriptor);
1872 
1873         std::stringstream ss;
1874         ss << "[[block]] struct S { value : vec2<f32>; };";
1875 
1876         // Build a shader which has bindings that match the pipeline layout.
1877         for (uint32_t l = 0; l < layouts.size(); ++l) {
1878             const auto& layout = layouts[l];
1879 
1880             for (uint32_t b = 0; b < layout.size(); ++b) {
1881                 wgpu::BufferBindingType binding = layout[b];
1882                 ss << "[[group(" << l << "), binding(" << b << ")]] ";
1883                 switch (binding) {
1884                     case wgpu::BufferBindingType::Storage:
1885                         ss << "var<storage, read_write> set" << l << "_binding" << b << " : S;";
1886                         break;
1887                     case wgpu::BufferBindingType::Uniform:
1888                         ss << "var<uniform> set" << l << "_binding" << b << " : S;";
1889                         break;
1890                     default:
1891                         UNREACHABLE();
1892                 }
1893             }
1894         }
1895 
1896         ss << "[[stage(fragment)]] fn main() {}";
1897 
1898         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, ss.str().c_str());
1899 
1900         utils::ComboRenderPipelineDescriptor pipelineDescriptor;
1901         pipelineDescriptor.vertex.module = mVsModule;
1902         pipelineDescriptor.cFragment.module = fsModule;
1903         pipelineDescriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
1904         pipelineDescriptor.layout = pipelineLayout;
1905         wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
1906 
1907         return std::make_tuple(bindGroupLayouts, pipeline);
1908     }
1909 
1910   protected:
1911     uint32_t mBufferSize;
1912 
1913   private:
1914     wgpu::ShaderModule mVsModule;
1915 };
1916 
1917 // Test it is valid to set bind groups before setting the pipeline.
TEST_F(SetBindGroupPersistenceValidationTest,BindGroupBeforePipeline)1918 TEST_F(SetBindGroupPersistenceValidationTest, BindGroupBeforePipeline) {
1919     std::vector<wgpu::BindGroupLayout> bindGroupLayouts;
1920     wgpu::RenderPipeline pipeline;
1921     std::tie(bindGroupLayouts, pipeline) = SetUpLayoutsAndPipeline({{
1922         {{
1923             wgpu::BufferBindingType::Uniform,
1924             wgpu::BufferBindingType::Uniform,
1925         }},
1926         {{
1927             wgpu::BufferBindingType::Storage,
1928             wgpu::BufferBindingType::Uniform,
1929         }},
1930     }});
1931 
1932     wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
1933     wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1934 
1935     wgpu::BindGroup bindGroup0 = utils::MakeBindGroup(
1936         device, bindGroupLayouts[0],
1937         {{0, uniformBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize}});
1938 
1939     wgpu::BindGroup bindGroup1 = utils::MakeBindGroup(
1940         device, bindGroupLayouts[1],
1941         {{0, storageBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize}});
1942 
1943     DummyRenderPass renderPass(device);
1944     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1945     wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
1946 
1947     renderPassEncoder.SetBindGroup(0, bindGroup0);
1948     renderPassEncoder.SetBindGroup(1, bindGroup1);
1949     renderPassEncoder.SetPipeline(pipeline);
1950     renderPassEncoder.Draw(3);
1951 
1952     renderPassEncoder.EndPass();
1953     commandEncoder.Finish();
1954 }
1955 
1956 // Dawn does not have a concept of bind group inheritance though the backing APIs may.
1957 // Test that it is valid to draw with bind groups that are not "inherited". They persist
1958 // after a pipeline change.
TEST_F(SetBindGroupPersistenceValidationTest,NotVulkanInheritance)1959 TEST_F(SetBindGroupPersistenceValidationTest, NotVulkanInheritance) {
1960     std::vector<wgpu::BindGroupLayout> bindGroupLayoutsA;
1961     wgpu::RenderPipeline pipelineA;
1962     std::tie(bindGroupLayoutsA, pipelineA) = SetUpLayoutsAndPipeline({{
1963         {{
1964             wgpu::BufferBindingType::Uniform,
1965             wgpu::BufferBindingType::Storage,
1966         }},
1967         {{
1968             wgpu::BufferBindingType::Uniform,
1969             wgpu::BufferBindingType::Uniform,
1970         }},
1971     }});
1972 
1973     std::vector<wgpu::BindGroupLayout> bindGroupLayoutsB;
1974     wgpu::RenderPipeline pipelineB;
1975     std::tie(bindGroupLayoutsB, pipelineB) = SetUpLayoutsAndPipeline({{
1976         {{
1977             wgpu::BufferBindingType::Storage,
1978             wgpu::BufferBindingType::Uniform,
1979         }},
1980         {{
1981             wgpu::BufferBindingType::Uniform,
1982             wgpu::BufferBindingType::Uniform,
1983         }},
1984     }});
1985 
1986     wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
1987     wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
1988 
1989     wgpu::BindGroup bindGroupA0 = utils::MakeBindGroup(
1990         device, bindGroupLayoutsA[0],
1991         {{0, uniformBuffer, 0, kBindingSize}, {1, storageBuffer, 0, kBindingSize}});
1992 
1993     wgpu::BindGroup bindGroupA1 = utils::MakeBindGroup(
1994         device, bindGroupLayoutsA[1],
1995         {{0, uniformBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize}});
1996 
1997     wgpu::BindGroup bindGroupB0 = utils::MakeBindGroup(
1998         device, bindGroupLayoutsB[0],
1999         {{0, storageBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize}});
2000 
2001     DummyRenderPass renderPass(device);
2002     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
2003     wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
2004 
2005     renderPassEncoder.SetPipeline(pipelineA);
2006     renderPassEncoder.SetBindGroup(0, bindGroupA0);
2007     renderPassEncoder.SetBindGroup(1, bindGroupA1);
2008     renderPassEncoder.Draw(3);
2009 
2010     renderPassEncoder.SetPipeline(pipelineB);
2011     renderPassEncoder.SetBindGroup(0, bindGroupB0);
2012     // This draw is valid.
2013     // Bind group 1 persists even though it is not "inherited".
2014     renderPassEncoder.Draw(3);
2015 
2016     renderPassEncoder.EndPass();
2017     commandEncoder.Finish();
2018 }
2019 
2020 class BindGroupLayoutCompatibilityTest : public ValidationTest {
2021   public:
CreateBuffer(uint64_t bufferSize,wgpu::BufferUsage usage)2022     wgpu::Buffer CreateBuffer(uint64_t bufferSize, wgpu::BufferUsage usage) {
2023         wgpu::BufferDescriptor bufferDescriptor;
2024         bufferDescriptor.size = bufferSize;
2025         bufferDescriptor.usage = usage;
2026 
2027         return device.CreateBuffer(&bufferDescriptor);
2028     }
2029 
CreateFSRenderPipeline(const char * fsShader,std::vector<wgpu::BindGroupLayout> bindGroupLayout)2030     wgpu::RenderPipeline CreateFSRenderPipeline(
2031         const char* fsShader,
2032         std::vector<wgpu::BindGroupLayout> bindGroupLayout) {
2033         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
2034                 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
2035                     return vec4<f32>();
2036                 })");
2037 
2038         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, fsShader);
2039 
2040         wgpu::PipelineLayoutDescriptor descriptor;
2041         descriptor.bindGroupLayoutCount = bindGroupLayout.size();
2042         descriptor.bindGroupLayouts = bindGroupLayout.data();
2043         utils::ComboRenderPipelineDescriptor pipelineDescriptor;
2044         pipelineDescriptor.vertex.module = vsModule;
2045         pipelineDescriptor.cFragment.module = fsModule;
2046         pipelineDescriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
2047         wgpu::PipelineLayout pipelineLayout = device.CreatePipelineLayout(&descriptor);
2048         pipelineDescriptor.layout = pipelineLayout;
2049         return device.CreateRenderPipeline(&pipelineDescriptor);
2050     }
2051 
CreateRenderPipeline(std::vector<wgpu::BindGroupLayout> bindGroupLayouts)2052     wgpu::RenderPipeline CreateRenderPipeline(std::vector<wgpu::BindGroupLayout> bindGroupLayouts) {
2053         return CreateFSRenderPipeline(R"(
2054             [[block]] struct S {
2055                 value : vec2<f32>;
2056             };
2057 
2058             [[group(0), binding(0)]] var<storage, read_write> sBufferDynamic : S;
2059             [[group(1), binding(0)]] var<storage, read> sReadonlyBufferDynamic : S;
2060 
2061             [[stage(fragment)]] fn main() {
2062                 var val : vec2<f32> = sBufferDynamic.value;
2063                 val = sReadonlyBufferDynamic.value;
2064             })",
2065                                       std::move(bindGroupLayouts));
2066     }
2067 
CreateComputePipeline(const char * shader,std::vector<wgpu::BindGroupLayout> bindGroupLayout)2068     wgpu::ComputePipeline CreateComputePipeline(
2069         const char* shader,
2070         std::vector<wgpu::BindGroupLayout> bindGroupLayout) {
2071         wgpu::ShaderModule csModule = utils::CreateShaderModule(device, shader);
2072 
2073         wgpu::PipelineLayoutDescriptor descriptor;
2074         descriptor.bindGroupLayoutCount = bindGroupLayout.size();
2075         descriptor.bindGroupLayouts = bindGroupLayout.data();
2076         wgpu::PipelineLayout pipelineLayout = device.CreatePipelineLayout(&descriptor);
2077 
2078         wgpu::ComputePipelineDescriptor csDesc;
2079         csDesc.layout = pipelineLayout;
2080         csDesc.compute.module = csModule;
2081         csDesc.compute.entryPoint = "main";
2082 
2083         return device.CreateComputePipeline(&csDesc);
2084     }
2085 
CreateComputePipeline(std::vector<wgpu::BindGroupLayout> bindGroupLayouts)2086     wgpu::ComputePipeline CreateComputePipeline(
2087         std::vector<wgpu::BindGroupLayout> bindGroupLayouts) {
2088         return CreateComputePipeline(R"(
2089             [[block]] struct S {
2090                 value : vec2<f32>;
2091             };
2092 
2093             [[group(0), binding(0)]] var<storage, read_write> sBufferDynamic : S;
2094             [[group(1), binding(0)]] var<storage, read> sReadonlyBufferDynamic : S;
2095 
2096             [[stage(compute), workgroup_size(4, 4, 1)]] fn main() {
2097                 var val : vec2<f32> = sBufferDynamic.value;
2098                 val = sReadonlyBufferDynamic.value;
2099             })",
2100                                      std::move(bindGroupLayouts));
2101     }
2102 };
2103 
2104 // Test that it is valid to pass a writable storage buffer in the pipeline layout when the shader
2105 // uses the binding as a readonly storage buffer.
TEST_F(BindGroupLayoutCompatibilityTest,RWStorageInBGLWithROStorageInShader)2106 TEST_F(BindGroupLayoutCompatibilityTest, RWStorageInBGLWithROStorageInShader) {
2107     // Set up the bind group layout.
2108     wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout(
2109         device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2110                   wgpu::BufferBindingType::Storage}});
2111     wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(
2112         device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2113                   wgpu::BufferBindingType::Storage}});
2114 
2115     CreateRenderPipeline({bgl0, bgl1});
2116 
2117     CreateComputePipeline({bgl0, bgl1});
2118 }
2119 
2120 // Test that it is invalid to pass a readonly storage buffer in the pipeline layout when the shader
2121 // uses the binding as a writable storage buffer.
TEST_F(BindGroupLayoutCompatibilityTest,ROStorageInBGLWithRWStorageInShader)2122 TEST_F(BindGroupLayoutCompatibilityTest, ROStorageInBGLWithRWStorageInShader) {
2123     // Set up the bind group layout.
2124     wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout(
2125         device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2126                   wgpu::BufferBindingType::ReadOnlyStorage}});
2127     wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(
2128         device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2129                   wgpu::BufferBindingType::ReadOnlyStorage}});
2130 
2131     ASSERT_DEVICE_ERROR(CreateRenderPipeline({bgl0, bgl1}));
2132 
2133     ASSERT_DEVICE_ERROR(CreateComputePipeline({bgl0, bgl1}));
2134 }
2135 
TEST_F(BindGroupLayoutCompatibilityTest,TextureViewDimension)2136 TEST_F(BindGroupLayoutCompatibilityTest, TextureViewDimension) {
2137     constexpr char kTexture2DShaderFS[] = R"(
2138         [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
2139         [[stage(fragment)]] fn main() {
2140             textureDimensions(myTexture);
2141         })";
2142     constexpr char kTexture2DShaderCS[] = R"(
2143         [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
2144         [[stage(compute), workgroup_size(1)]] fn main() {
2145             textureDimensions(myTexture);
2146         })";
2147 
2148     // Render: Test that 2D texture with 2D view dimension works
2149     CreateFSRenderPipeline(
2150         kTexture2DShaderFS,
2151         {utils::MakeBindGroupLayout(
2152             device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float,
2153                       wgpu::TextureViewDimension::e2D}})});
2154 
2155     // Render: Test that 2D texture with 2D array view dimension is invalid
2156     ASSERT_DEVICE_ERROR(CreateFSRenderPipeline(
2157         kTexture2DShaderFS,
2158         {utils::MakeBindGroupLayout(
2159             device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float,
2160                       wgpu::TextureViewDimension::e2DArray}})}));
2161 
2162     // Compute: Test that 2D texture with 2D view dimension works
2163     CreateComputePipeline(
2164         kTexture2DShaderCS,
2165         {utils::MakeBindGroupLayout(device,
2166                                     {{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float,
2167                                       wgpu::TextureViewDimension::e2D}})});
2168 
2169     // Compute: Test that 2D texture with 2D array view dimension is invalid
2170     ASSERT_DEVICE_ERROR(CreateComputePipeline(
2171         kTexture2DShaderCS,
2172         {utils::MakeBindGroupLayout(device,
2173                                     {{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float,
2174                                       wgpu::TextureViewDimension::e2DArray}})}));
2175 
2176     constexpr char kTexture2DArrayShaderFS[] = R"(
2177         [[group(0), binding(0)]] var myTexture : texture_2d_array<f32>;
2178         [[stage(fragment)]] fn main() {
2179             textureDimensions(myTexture);
2180         })";
2181     constexpr char kTexture2DArrayShaderCS[] = R"(
2182         [[group(0), binding(0)]] var myTexture : texture_2d_array<f32>;
2183         [[stage(compute), workgroup_size(1)]] fn main() {
2184             textureDimensions(myTexture);
2185         })";
2186 
2187     // Render: Test that 2D texture array with 2D array view dimension works
2188     CreateFSRenderPipeline(
2189         kTexture2DArrayShaderFS,
2190         {utils::MakeBindGroupLayout(
2191             device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float,
2192                       wgpu::TextureViewDimension::e2DArray}})});
2193 
2194     // Render: Test that 2D texture array with 2D view dimension is invalid
2195     ASSERT_DEVICE_ERROR(CreateFSRenderPipeline(
2196         kTexture2DArrayShaderFS,
2197         {utils::MakeBindGroupLayout(
2198             device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float,
2199                       wgpu::TextureViewDimension::e2D}})}));
2200 
2201     // Compute: Test that 2D texture array with 2D array view dimension works
2202     CreateComputePipeline(
2203         kTexture2DArrayShaderCS,
2204         {utils::MakeBindGroupLayout(device,
2205                                     {{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float,
2206                                       wgpu::TextureViewDimension::e2DArray}})});
2207 
2208     // Compute: Test that 2D texture array with 2D view dimension is invalid
2209     ASSERT_DEVICE_ERROR(CreateComputePipeline(
2210         kTexture2DArrayShaderCS,
2211         {utils::MakeBindGroupLayout(device,
2212                                     {{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float,
2213                                       wgpu::TextureViewDimension::e2D}})}));
2214 }
2215 
2216 // Test that a bgl with an external texture is compatible with texture_external in a shader and that
2217 // an error is returned when the binding in the shader does not match.
TEST_F(BindGroupLayoutCompatibilityTest,ExternalTextureBindGroupLayoutCompatibility)2218 TEST_F(BindGroupLayoutCompatibilityTest, ExternalTextureBindGroupLayoutCompatibility) {
2219     wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
2220         device, {{0, wgpu::ShaderStage::Fragment, &utils::kExternalTextureBindingLayout}});
2221 
2222     // Test that an external texture binding works with a texture_external in the shader.
2223     CreateFSRenderPipeline(R"(
2224             [[group(0), binding(0)]] var myExternalTexture: texture_external;
2225             [[stage(fragment)]] fn main() {
2226                 _ = myExternalTexture;
2227             })",
2228                            {bgl});
2229 
2230     // Test that an external texture binding doesn't work with a texture_2d<f32> in the shader.
2231     ASSERT_DEVICE_ERROR(CreateFSRenderPipeline(R"(
2232             [[group(0), binding(0)]] var myTexture: texture_2d<f32>;
2233             [[stage(fragment)]] fn main() {
2234                 _ = myTexture;
2235             })",
2236                                                {bgl}));
2237 }
2238 
2239 class BindingsValidationTest : public BindGroupLayoutCompatibilityTest {
2240   public:
SetUp()2241     void SetUp() override {
2242         BindGroupLayoutCompatibilityTest::SetUp();
2243         mBufferSize = 3 * GetSupportedLimits().limits.minUniformBufferOffsetAlignment + 8;
2244     }
2245 
TestRenderPassBindings(const wgpu::BindGroup * bg,uint32_t count,wgpu::RenderPipeline pipeline,bool expectation)2246     void TestRenderPassBindings(const wgpu::BindGroup* bg,
2247                                 uint32_t count,
2248                                 wgpu::RenderPipeline pipeline,
2249                                 bool expectation) {
2250         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
2251         DummyRenderPass dummyRenderPass(device);
2252         wgpu::RenderPassEncoder rp = encoder.BeginRenderPass(&dummyRenderPass);
2253         for (uint32_t i = 0; i < count; ++i) {
2254             rp.SetBindGroup(i, bg[i]);
2255         }
2256         rp.SetPipeline(pipeline);
2257         rp.Draw(3);
2258         rp.EndPass();
2259         if (!expectation) {
2260             ASSERT_DEVICE_ERROR(encoder.Finish());
2261         } else {
2262             encoder.Finish();
2263         }
2264     }
2265 
TestComputePassBindings(const wgpu::BindGroup * bg,uint32_t count,wgpu::ComputePipeline pipeline,bool expectation)2266     void TestComputePassBindings(const wgpu::BindGroup* bg,
2267                                  uint32_t count,
2268                                  wgpu::ComputePipeline pipeline,
2269                                  bool expectation) {
2270         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
2271         wgpu::ComputePassEncoder cp = encoder.BeginComputePass();
2272         for (uint32_t i = 0; i < count; ++i) {
2273             cp.SetBindGroup(i, bg[i]);
2274         }
2275         cp.SetPipeline(pipeline);
2276         cp.Dispatch(1);
2277         cp.EndPass();
2278         if (!expectation) {
2279             ASSERT_DEVICE_ERROR(encoder.Finish());
2280         } else {
2281             encoder.Finish();
2282         }
2283     }
2284 
2285     uint32_t mBufferSize;
2286     static constexpr uint32_t kBindingNum = 3;
2287 };
2288 
2289 // Test that it is valid to set a pipeline layout with bindings unused by the pipeline.
TEST_F(BindingsValidationTest,PipelineLayoutWithMoreBindingsThanPipeline)2290 TEST_F(BindingsValidationTest, PipelineLayoutWithMoreBindingsThanPipeline) {
2291     // Set up bind group layouts.
2292     wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout(
2293         device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2294                   wgpu::BufferBindingType::Storage},
2295                  {1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2296                   wgpu::BufferBindingType::Uniform}});
2297     wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(
2298         device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2299                   wgpu::BufferBindingType::ReadOnlyStorage}});
2300     wgpu::BindGroupLayout bgl2 = utils::MakeBindGroupLayout(
2301         device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2302                   wgpu::BufferBindingType::Storage}});
2303 
2304     // pipelineLayout has unused binding set (bgl2) and unused entry in a binding set (bgl0).
2305     CreateRenderPipeline({bgl0, bgl1, bgl2});
2306 
2307     CreateComputePipeline({bgl0, bgl1, bgl2});
2308 }
2309 
2310 // Test that it is invalid to set a pipeline layout that doesn't have all necessary bindings
2311 // required by the pipeline.
TEST_F(BindingsValidationTest,PipelineLayoutWithLessBindingsThanPipeline)2312 TEST_F(BindingsValidationTest, PipelineLayoutWithLessBindingsThanPipeline) {
2313     // Set up bind group layout.
2314     wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout(
2315         device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2316                   wgpu::BufferBindingType::Storage}});
2317 
2318     // missing a binding set (bgl1) in pipeline layout
2319     {
2320         ASSERT_DEVICE_ERROR(CreateRenderPipeline({bgl0}));
2321 
2322         ASSERT_DEVICE_ERROR(CreateComputePipeline({bgl0}));
2323     }
2324 
2325     // bgl1 is not missing, but it is empty
2326     {
2327         wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(device, {});
2328 
2329         ASSERT_DEVICE_ERROR(CreateRenderPipeline({bgl0, bgl1}));
2330 
2331         ASSERT_DEVICE_ERROR(CreateComputePipeline({bgl0, bgl1}));
2332     }
2333 
2334     // bgl1 is neither missing nor empty, but it doesn't contain the necessary binding
2335     {
2336         wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(
2337             device, {{1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2338                       wgpu::BufferBindingType::Uniform}});
2339 
2340         ASSERT_DEVICE_ERROR(CreateRenderPipeline({bgl0, bgl1}));
2341 
2342         ASSERT_DEVICE_ERROR(CreateComputePipeline({bgl0, bgl1}));
2343     }
2344 }
2345 
2346 // Test that it is valid to set bind groups whose layout is not set in the pipeline layout.
2347 // But it's invalid to set extra entry for a given bind group's layout if that layout is set in
2348 // the pipeline layout.
TEST_F(BindingsValidationTest,BindGroupsWithMoreBindingsThanPipelineLayout)2349 TEST_F(BindingsValidationTest, BindGroupsWithMoreBindingsThanPipelineLayout) {
2350     // Set up bind group layouts, buffers, bind groups, pipeline layouts and pipelines.
2351     std::array<wgpu::BindGroupLayout, kBindingNum + 1> bgl;
2352     std::array<wgpu::BindGroup, kBindingNum + 1> bg;
2353     std::array<wgpu::Buffer, kBindingNum + 1> buffer;
2354     for (uint32_t i = 0; i < kBindingNum + 1; ++i) {
2355         bgl[i] = utils::MakeBindGroupLayout(
2356             device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2357                       wgpu::BufferBindingType::Storage}});
2358         buffer[i] = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
2359         bg[i] = utils::MakeBindGroup(device, bgl[i], {{0, buffer[i]}});
2360     }
2361 
2362     // Set 3 bindings (and 3 pipeline layouts) in pipeline.
2363     wgpu::RenderPipeline renderPipeline = CreateRenderPipeline({bgl[0], bgl[1], bgl[2]});
2364     wgpu::ComputePipeline computePipeline = CreateComputePipeline({bgl[0], bgl[1], bgl[2]});
2365 
2366     // Comprared to pipeline layout, there is an extra bind group (bg[3])
2367     TestRenderPassBindings(bg.data(), kBindingNum + 1, renderPipeline, true);
2368 
2369     TestComputePassBindings(bg.data(), kBindingNum + 1, computePipeline, true);
2370 
2371     // If a bind group has entry (like bgl1_1 below) unused by the pipeline layout, it is invalid.
2372     // Bind groups associated layout should exactly match bind group layout if that layout is
2373     // set in pipeline layout.
2374     bgl[1] = utils::MakeBindGroupLayout(
2375         device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2376                   wgpu::BufferBindingType::ReadOnlyStorage},
2377                  {1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2378                   wgpu::BufferBindingType::Uniform}});
2379     buffer[1] = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Uniform);
2380     bg[1] = utils::MakeBindGroup(device, bgl[1], {{0, buffer[1]}, {1, buffer[1]}});
2381 
2382     TestRenderPassBindings(bg.data(), kBindingNum, renderPipeline, false);
2383 
2384     TestComputePassBindings(bg.data(), kBindingNum, computePipeline, false);
2385 }
2386 
2387 // Test that it is invalid to set bind groups that don't have all necessary bindings required
2388 // by the pipeline layout. Note that both pipeline layout and bind group have enough bindings for
2389 // pipeline in the following test.
TEST_F(BindingsValidationTest,BindGroupsWithLessBindingsThanPipelineLayout)2390 TEST_F(BindingsValidationTest, BindGroupsWithLessBindingsThanPipelineLayout) {
2391     // Set up bind group layouts, buffers, bind groups, pipeline layouts and pipelines.
2392     std::array<wgpu::BindGroupLayout, kBindingNum> bgl;
2393     std::array<wgpu::BindGroup, kBindingNum> bg;
2394     std::array<wgpu::Buffer, kBindingNum> buffer;
2395     for (uint32_t i = 0; i < kBindingNum; ++i) {
2396         bgl[i] = utils::MakeBindGroupLayout(
2397             device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2398                       wgpu::BufferBindingType::Storage}});
2399         buffer[i] = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
2400         bg[i] = utils::MakeBindGroup(device, bgl[i], {{0, buffer[i]}});
2401     }
2402 
2403     wgpu::RenderPipeline renderPipeline = CreateRenderPipeline({bgl[0], bgl[1], bgl[2]});
2404     wgpu::ComputePipeline computePipeline = CreateComputePipeline({bgl[0], bgl[1], bgl[2]});
2405 
2406     // Compared to pipeline layout, a binding set (bgl2) related bind group is missing
2407     TestRenderPassBindings(bg.data(), kBindingNum - 1, renderPipeline, false);
2408 
2409     TestComputePassBindings(bg.data(), kBindingNum - 1, computePipeline, false);
2410 
2411     // bgl[2] related bind group is not missing, but its bind group is empty
2412     bgl[2] = utils::MakeBindGroupLayout(device, {});
2413     bg[2] = utils::MakeBindGroup(device, bgl[2], {});
2414 
2415     TestRenderPassBindings(bg.data(), kBindingNum, renderPipeline, false);
2416 
2417     TestComputePassBindings(bg.data(), kBindingNum, computePipeline, false);
2418 
2419     // bgl[2] related bind group is neither missing nor empty, but it doesn't contain the necessary
2420     // binding
2421     bgl[2] = utils::MakeBindGroupLayout(
2422         device, {{1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
2423                   wgpu::BufferBindingType::Uniform}});
2424     buffer[2] = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
2425     bg[2] = utils::MakeBindGroup(device, bgl[2], {{1, buffer[2]}});
2426 
2427     TestRenderPassBindings(bg.data(), kBindingNum, renderPipeline, false);
2428 
2429     TestComputePassBindings(bg.data(), kBindingNum, computePipeline, false);
2430 }
2431 
2432 class SamplerTypeBindingTest : public ValidationTest {
2433   protected:
CreateFragmentPipeline(wgpu::BindGroupLayout * bindGroupLayout,const char * fragmentSource)2434     wgpu::RenderPipeline CreateFragmentPipeline(wgpu::BindGroupLayout* bindGroupLayout,
2435                                                 const char* fragmentSource) {
2436         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
2437             [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
2438                 return vec4<f32>();
2439             })");
2440 
2441         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, fragmentSource);
2442 
2443         utils::ComboRenderPipelineDescriptor pipelineDescriptor;
2444         pipelineDescriptor.vertex.module = vsModule;
2445         pipelineDescriptor.cFragment.module = fsModule;
2446         pipelineDescriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
2447         wgpu::PipelineLayout pipelineLayout =
2448             utils::MakeBasicPipelineLayout(device, bindGroupLayout);
2449         pipelineDescriptor.layout = pipelineLayout;
2450         return device.CreateRenderPipeline(&pipelineDescriptor);
2451     }
2452 };
2453 
2454 // Test that the use of sampler and comparison_sampler in the shader must match the bind group
2455 // layout.
TEST_F(SamplerTypeBindingTest,ShaderAndBGLMatches)2456 TEST_F(SamplerTypeBindingTest, ShaderAndBGLMatches) {
2457     // Test that a filtering sampler binding works with normal sampler in the shader.
2458     {
2459         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2460             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
2461 
2462         CreateFragmentPipeline(&bindGroupLayout, R"(
2463             [[group(0), binding(0)]] var mySampler: sampler;
2464             [[stage(fragment)]] fn main() {
2465                 _ = mySampler;
2466             })");
2467     }
2468 
2469     // Test that a non-filtering sampler binding works with normal sampler in the shader.
2470     {
2471         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2472             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::NonFiltering}});
2473 
2474         CreateFragmentPipeline(&bindGroupLayout, R"(
2475             [[group(0), binding(0)]] var mySampler: sampler;
2476             [[stage(fragment)]] fn main() {
2477                 _ = mySampler;
2478             })");
2479     }
2480 
2481     // Test that comparison sampler binding works with comparison sampler in the shader.
2482     {
2483         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2484             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Comparison}});
2485 
2486         CreateFragmentPipeline(&bindGroupLayout, R"(
2487             [[group(0), binding(0)]] var mySampler: sampler_comparison;
2488             [[stage(fragment)]] fn main() {
2489                 _ = mySampler;
2490             })");
2491     }
2492 
2493     // Test that filtering sampler binding does not work with comparison sampler in the shader.
2494     {
2495         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2496             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
2497 
2498         ASSERT_DEVICE_ERROR(CreateFragmentPipeline(&bindGroupLayout, R"(
2499             [[group(0), binding(0)]] var mySampler: sampler_comparison;
2500             [[stage(fragment)]] fn main() {
2501                 _ = mySampler;
2502             })"));
2503     }
2504 
2505     // Test that non-filtering sampler binding does not work with comparison sampler in the shader.
2506     {
2507         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2508             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::NonFiltering}});
2509 
2510         ASSERT_DEVICE_ERROR(CreateFragmentPipeline(&bindGroupLayout, R"(
2511             [[group(0), binding(0)]] var mySampler: sampler_comparison;
2512             [[stage(fragment)]] fn main() {
2513                 _ = mySampler;
2514             })"));
2515     }
2516 
2517     // Test that comparison sampler binding does not work with normal sampler in the shader.
2518     {
2519         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2520             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Comparison}});
2521 
2522         ASSERT_DEVICE_ERROR(CreateFragmentPipeline(&bindGroupLayout, R"(
2523             [[group(0), binding(0)]] var mySampler: sampler;
2524             [[stage(fragment)]] fn main() {
2525                 _ = mySampler;
2526             })"));
2527     }
2528 
2529     // Test that a filtering sampler can be used to sample a float texture.
2530     {
2531         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2532             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering},
2533                      {1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
2534 
2535         CreateFragmentPipeline(&bindGroupLayout, R"(
2536             [[group(0), binding(0)]] var mySampler: sampler;
2537             [[group(0), binding(1)]] var myTexture: texture_2d<f32>;
2538             [[stage(fragment)]] fn main() {
2539                 textureSample(myTexture, mySampler, vec2<f32>(0.0, 0.0));
2540             })");
2541     }
2542 
2543     // Test that a non-filtering sampler can be used to sample a float texture.
2544     {
2545         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2546             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::NonFiltering},
2547                      {1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
2548 
2549         CreateFragmentPipeline(&bindGroupLayout, R"(
2550             [[group(0), binding(0)]] var mySampler: sampler;
2551             [[group(0), binding(1)]] var myTexture: texture_2d<f32>;
2552             [[stage(fragment)]] fn main() {
2553                 textureSample(myTexture, mySampler, vec2<f32>(0.0, 0.0));
2554             })");
2555     }
2556 
2557     // Test that a filtering sampler can be used to sample a depth texture.
2558     {
2559         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2560             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering},
2561                      {1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth}});
2562 
2563         CreateFragmentPipeline(&bindGroupLayout, R"(
2564             [[group(0), binding(0)]] var mySampler: sampler;
2565             [[group(0), binding(1)]] var myTexture: texture_depth_2d;
2566             [[stage(fragment)]] fn main() {
2567                 textureSample(myTexture, mySampler, vec2<f32>(0.0, 0.0));
2568             })");
2569     }
2570 
2571     // Test that a non-filtering sampler can be used to sample a depth texture.
2572     {
2573         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2574             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::NonFiltering},
2575                      {1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth}});
2576 
2577         CreateFragmentPipeline(&bindGroupLayout, R"(
2578             [[group(0), binding(0)]] var mySampler: sampler;
2579             [[group(0), binding(1)]] var myTexture: texture_depth_2d;
2580             [[stage(fragment)]] fn main() {
2581                 textureSample(myTexture, mySampler, vec2<f32>(0.0, 0.0));
2582             })");
2583     }
2584 
2585     // Test that a comparison sampler can be used to sample a depth texture.
2586     {
2587         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2588             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Comparison},
2589                      {1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth}});
2590 
2591         CreateFragmentPipeline(&bindGroupLayout, R"(
2592             [[group(0), binding(0)]] var mySampler: sampler_comparison;
2593             [[group(0), binding(1)]] var myTexture: texture_depth_2d;
2594             [[stage(fragment)]] fn main() {
2595                 textureSampleCompare(myTexture, mySampler, vec2<f32>(0.0, 0.0), 0.0);
2596             })");
2597     }
2598 
2599     // Test that a filtering sampler cannot be used to sample an unfilterable-float texture.
2600     {
2601         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2602             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering},
2603                      {1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::UnfilterableFloat}});
2604 
2605         ASSERT_DEVICE_ERROR(CreateFragmentPipeline(&bindGroupLayout, R"(
2606             [[group(0), binding(0)]] var mySampler: sampler;
2607             [[group(0), binding(1)]] var myTexture: texture_2d<f32>;
2608             [[stage(fragment)]] fn main() {
2609                 textureSample(myTexture, mySampler, vec2<f32>(0.0, 0.0));
2610             })"));
2611     }
2612 
2613     // Test that a non-filtering sampler can be used to sample an unfilterable-float texture.
2614     {
2615         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2616             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::NonFiltering},
2617                      {1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::UnfilterableFloat}});
2618 
2619         CreateFragmentPipeline(&bindGroupLayout, R"(
2620             [[group(0), binding(0)]] var mySampler: sampler;
2621             [[group(0), binding(1)]] var myTexture: texture_2d<f32>;
2622             [[stage(fragment)]] fn main() {
2623                 textureSample(myTexture, mySampler, vec2<f32>(0.0, 0.0));
2624             })");
2625     }
2626 }
2627 
TEST_F(SamplerTypeBindingTest,SamplerAndBindGroupMatches)2628 TEST_F(SamplerTypeBindingTest, SamplerAndBindGroupMatches) {
2629     // Test that sampler binding works with normal sampler.
2630     {
2631         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2632             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
2633 
2634         utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler()}});
2635     }
2636 
2637     // Test that comparison sampler binding works with sampler w/ compare function.
2638     {
2639         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2640             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Comparison}});
2641 
2642         wgpu::SamplerDescriptor desc = {};
2643         desc.compare = wgpu::CompareFunction::Never;
2644         utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}});
2645     }
2646 
2647     // Test that sampler binding does not work with sampler w/ compare function.
2648     {
2649         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2650             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
2651 
2652         wgpu::SamplerDescriptor desc;
2653         desc.compare = wgpu::CompareFunction::Never;
2654         ASSERT_DEVICE_ERROR(
2655             utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}}));
2656     }
2657 
2658     // Test that comparison sampler binding does not work with normal sampler.
2659     {
2660         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2661             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Comparison}});
2662 
2663         wgpu::SamplerDescriptor desc = {};
2664         ASSERT_DEVICE_ERROR(
2665             utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}}));
2666     }
2667 
2668     // Test that filtering sampler binding works with a filtering or non-filtering sampler.
2669     {
2670         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2671             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
2672 
2673         // Test each filter member
2674         {
2675             wgpu::SamplerDescriptor desc;
2676             desc.minFilter = wgpu::FilterMode::Linear;
2677             utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}});
2678         }
2679         {
2680             wgpu::SamplerDescriptor desc;
2681             desc.magFilter = wgpu::FilterMode::Linear;
2682             utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}});
2683         }
2684         {
2685             wgpu::SamplerDescriptor desc;
2686             desc.mipmapFilter = wgpu::FilterMode::Linear;
2687             utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}});
2688         }
2689 
2690         // Test non-filtering sampler
2691         utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler()}});
2692     }
2693 
2694     // Test that non-filtering sampler binding does not work with a filtering sampler.
2695     {
2696         wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
2697             device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::NonFiltering}});
2698 
2699         // Test each filter member
2700         {
2701             wgpu::SamplerDescriptor desc;
2702             desc.minFilter = wgpu::FilterMode::Linear;
2703             ASSERT_DEVICE_ERROR(
2704                 utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}}));
2705         }
2706         {
2707             wgpu::SamplerDescriptor desc;
2708             desc.magFilter = wgpu::FilterMode::Linear;
2709             ASSERT_DEVICE_ERROR(
2710                 utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}}));
2711         }
2712         {
2713             wgpu::SamplerDescriptor desc;
2714             desc.mipmapFilter = wgpu::FilterMode::Linear;
2715             ASSERT_DEVICE_ERROR(
2716                 utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}}));
2717         }
2718 
2719         // Test non-filtering sampler
2720         utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler()}});
2721     }
2722 }
2723