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