1 // Copyright 2019 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "tests/unittests/validation/ValidationTest.h"
16
17 #include "utils/ComboRenderPipelineDescriptor.h"
18 #include "utils/WGPUHelpers.h"
19
20 class GetBindGroupLayoutTests : public ValidationTest {
21 protected:
RenderPipelineFromFragmentShader(const char * shader)22 wgpu::RenderPipeline RenderPipelineFromFragmentShader(const char* shader) {
23 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
24 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
25 return vec4<f32>();
26 })");
27
28 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, shader);
29
30 utils::ComboRenderPipelineDescriptor descriptor;
31 descriptor.layout = nullptr;
32 descriptor.vertex.module = vsModule;
33 descriptor.cFragment.module = fsModule;
34 descriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
35
36 return device.CreateRenderPipeline(&descriptor);
37 }
38 };
39
40 // Test that GetBindGroupLayout returns the same object for the same index
41 // and for matching layouts.
TEST_F(GetBindGroupLayoutTests,SameObject)42 TEST_F(GetBindGroupLayoutTests, SameObject) {
43 // This test works assuming Dawn Native's object deduplication.
44 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
45 // Native.
46 DAWN_SKIP_TEST_IF(UsesWire());
47
48 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
49 [[block]] struct S {
50 pos : vec4<f32>;
51 };
52 [[group(0), binding(0)]] var<uniform> uniform0 : S;
53 [[group(1), binding(0)]] var<uniform> uniform1 : S;
54
55 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
56 var pos : vec4<f32> = uniform0.pos;
57 pos = uniform1.pos;
58 return vec4<f32>();
59 })");
60
61 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
62 [[block]] struct S2 {
63 pos : vec4<f32>;
64 };
65 [[group(2), binding(0)]] var<uniform> uniform2 : S2;
66
67 [[block]] struct S3 {
68 pos : mat4x4<f32>;
69 };
70 [[group(3), binding(0)]] var<storage, read_write> storage3 : S3;
71
72 [[stage(fragment)]] fn main() {
73 var pos_u : vec4<f32> = uniform2.pos;
74 var pos_s : mat4x4<f32> = storage3.pos;
75 })");
76
77 utils::ComboRenderPipelineDescriptor descriptor;
78 descriptor.layout = nullptr;
79 descriptor.vertex.module = vsModule;
80 descriptor.cFragment.module = fsModule;
81 descriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
82
83 wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
84
85 // The same value is returned for the same index.
86 EXPECT_EQ(pipeline.GetBindGroupLayout(0).Get(), pipeline.GetBindGroupLayout(0).Get());
87
88 // Matching bind group layouts at different indices are the same object.
89 EXPECT_EQ(pipeline.GetBindGroupLayout(0).Get(), pipeline.GetBindGroupLayout(1).Get());
90
91 // BGLs with different bindings types are different objects.
92 EXPECT_NE(pipeline.GetBindGroupLayout(2).Get(), pipeline.GetBindGroupLayout(3).Get());
93
94 // BGLs with different visibilities are different objects.
95 EXPECT_NE(pipeline.GetBindGroupLayout(0).Get(), pipeline.GetBindGroupLayout(2).Get());
96 }
97
98 // Test that default BindGroupLayouts cannot be used in the creation of a new PipelineLayout
TEST_F(GetBindGroupLayoutTests,DefaultBindGroupLayoutPipelineCompatibility)99 TEST_F(GetBindGroupLayoutTests, DefaultBindGroupLayoutPipelineCompatibility) {
100 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
101 [[block]] struct S {
102 pos : vec4<f32>;
103 };
104 [[group(0), binding(0)]] var<uniform> uniforms : S;
105
106 [[stage(fragment)]] fn main() {
107 var pos : vec4<f32> = uniforms.pos;
108 })");
109
110 ASSERT_DEVICE_ERROR(utils::MakePipelineLayout(device, {pipeline.GetBindGroupLayout(0)}));
111 }
112
113 // Test that getBindGroupLayout defaults are correct
114 // - shader stage visibility is the stage that adds the binding.
115 // - dynamic offsets is false
TEST_F(GetBindGroupLayoutTests,DefaultShaderStageAndDynamicOffsets)116 TEST_F(GetBindGroupLayoutTests, DefaultShaderStageAndDynamicOffsets) {
117 // This test works assuming Dawn Native's object deduplication.
118 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
119 // Native.
120 DAWN_SKIP_TEST_IF(UsesWire());
121
122 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
123 [[block]] struct S {
124 pos : vec4<f32>;
125 };
126 [[group(0), binding(0)]] var<uniform> uniforms : S;
127
128 [[stage(fragment)]] fn main() {
129 var pos : vec4<f32> = uniforms.pos;
130 })");
131
132 wgpu::BindGroupLayoutEntry binding = {};
133 binding.binding = 0;
134 binding.buffer.type = wgpu::BufferBindingType::Uniform;
135 binding.buffer.minBindingSize = 4 * sizeof(float);
136
137 wgpu::BindGroupLayoutDescriptor desc = {};
138 desc.entryCount = 1;
139 desc.entries = &binding;
140
141 // Check that an otherwise compatible bind group layout doesn't match one created as part of a
142 // default pipeline layout.
143 binding.buffer.hasDynamicOffset = false;
144 binding.visibility = wgpu::ShaderStage::Fragment;
145 EXPECT_NE(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get());
146
147 // Check that any change in visibility doesn't match.
148 binding.visibility = wgpu::ShaderStage::Vertex;
149 EXPECT_NE(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get());
150
151 binding.visibility = wgpu::ShaderStage::Compute;
152 EXPECT_NE(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get());
153
154 // Check that any change in hasDynamicOffsets doesn't match.
155 binding.buffer.hasDynamicOffset = true;
156 binding.visibility = wgpu::ShaderStage::Fragment;
157 EXPECT_NE(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get());
158 }
159
TEST_F(GetBindGroupLayoutTests,DefaultTextureSampleType)160 TEST_F(GetBindGroupLayoutTests, DefaultTextureSampleType) {
161 // This test works assuming Dawn Native's object deduplication.
162 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
163 // Native.
164 DAWN_SKIP_TEST_IF(UsesWire());
165
166 wgpu::BindGroupLayout filteringBGL = utils::MakeBindGroupLayout(
167 device, {{0, wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
168 wgpu::TextureSampleType::Float},
169 {1, wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
170 wgpu::SamplerBindingType::Filtering}});
171
172 wgpu::BindGroupLayout nonFilteringBGL = utils::MakeBindGroupLayout(
173 device, {{0, wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
174 wgpu::TextureSampleType::UnfilterableFloat},
175 {1, wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
176 wgpu::SamplerBindingType::Filtering}});
177
178 wgpu::ShaderModule emptyVertexModule = utils::CreateShaderModule(device, R"(
179 [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
180 [[group(0), binding(1)]] var mySampler : sampler;
181 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
182 _ = myTexture;
183 _ = mySampler;
184 return vec4<f32>();
185 })");
186
187 wgpu::ShaderModule textureLoadVertexModule = utils::CreateShaderModule(device, R"(
188 [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
189 [[group(0), binding(1)]] var mySampler : sampler;
190 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
191 textureLoad(myTexture, vec2<i32>(), 0);
192 _ = mySampler;
193 return vec4<f32>();
194 })");
195
196 wgpu::ShaderModule textureSampleVertexModule = utils::CreateShaderModule(device, R"(
197 [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
198 [[group(0), binding(1)]] var mySampler : sampler;
199 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
200 textureSampleLevel(myTexture, mySampler, vec2<f32>(), 0.0);
201 return vec4<f32>();
202 })");
203
204 wgpu::ShaderModule unusedTextureFragmentModule = utils::CreateShaderModule(device, R"(
205 [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
206 [[group(0), binding(1)]] var mySampler : sampler;
207 [[stage(fragment)]] fn main() {
208 _ = myTexture;
209 _ = mySampler;
210 })");
211
212 wgpu::ShaderModule textureLoadFragmentModule = utils::CreateShaderModule(device, R"(
213 [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
214 [[group(0), binding(1)]] var mySampler : sampler;
215 [[stage(fragment)]] fn main() {
216 textureLoad(myTexture, vec2<i32>(), 0);
217 _ = mySampler;
218 })");
219
220 wgpu::ShaderModule textureSampleFragmentModule = utils::CreateShaderModule(device, R"(
221 [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
222 [[group(0), binding(1)]] var mySampler : sampler;
223 [[stage(fragment)]] fn main() {
224 textureSample(myTexture, mySampler, vec2<f32>());
225 })");
226
227 auto BGLFromModules = [this](wgpu::ShaderModule vertexModule,
228 wgpu::ShaderModule fragmentModule) {
229 utils::ComboRenderPipelineDescriptor descriptor;
230 descriptor.vertex.module = vertexModule;
231 descriptor.cFragment.module = fragmentModule;
232 descriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
233 return device.CreateRenderPipeline(&descriptor).GetBindGroupLayout(0);
234 };
235
236 // Textures not used default to non-filtering
237 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
238 BGLFromModules(emptyVertexModule, unusedTextureFragmentModule).Get(),
239 nonFilteringBGL.Get()));
240 EXPECT_FALSE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
241 BGLFromModules(emptyVertexModule, unusedTextureFragmentModule).Get(), filteringBGL.Get()));
242
243 // Textures used with textureLoad default to non-filtering
244 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
245 BGLFromModules(emptyVertexModule, textureLoadFragmentModule).Get(), nonFilteringBGL.Get()));
246 EXPECT_FALSE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
247 BGLFromModules(emptyVertexModule, textureLoadFragmentModule).Get(), filteringBGL.Get()));
248
249 // Textures used with textureLoad on both stages default to non-filtering
250 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
251 BGLFromModules(textureLoadVertexModule, textureLoadFragmentModule).Get(),
252 nonFilteringBGL.Get()));
253 EXPECT_FALSE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
254 BGLFromModules(textureLoadVertexModule, textureLoadFragmentModule).Get(),
255 filteringBGL.Get()));
256
257 // Textures used with textureSample default to filtering
258 EXPECT_FALSE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
259 BGLFromModules(emptyVertexModule, textureSampleFragmentModule).Get(),
260 nonFilteringBGL.Get()));
261 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
262 BGLFromModules(emptyVertexModule, textureSampleFragmentModule).Get(), filteringBGL.Get()));
263 EXPECT_FALSE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
264 BGLFromModules(textureSampleVertexModule, unusedTextureFragmentModule).Get(),
265 nonFilteringBGL.Get()));
266 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
267 BGLFromModules(textureSampleVertexModule, unusedTextureFragmentModule).Get(),
268 filteringBGL.Get()));
269
270 // Textures used with both textureLoad and textureSample default to filtering
271 EXPECT_FALSE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
272 BGLFromModules(textureLoadVertexModule, textureSampleFragmentModule).Get(),
273 nonFilteringBGL.Get()));
274 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
275 BGLFromModules(textureLoadVertexModule, textureSampleFragmentModule).Get(),
276 filteringBGL.Get()));
277 EXPECT_FALSE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
278 BGLFromModules(textureSampleVertexModule, textureLoadFragmentModule).Get(),
279 nonFilteringBGL.Get()));
280 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
281 BGLFromModules(textureSampleVertexModule, textureLoadFragmentModule).Get(),
282 filteringBGL.Get()));
283 }
284
285 // Test GetBindGroupLayout works with a compute pipeline
TEST_F(GetBindGroupLayoutTests,ComputePipeline)286 TEST_F(GetBindGroupLayoutTests, ComputePipeline) {
287 // This test works assuming Dawn Native's object deduplication.
288 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
289 // Native.
290 DAWN_SKIP_TEST_IF(UsesWire());
291
292 wgpu::ShaderModule csModule = utils::CreateShaderModule(device, R"(
293 [[block]] struct S {
294 pos : vec4<f32>;
295 };
296 [[group(0), binding(0)]] var<uniform> uniforms : S;
297
298 [[stage(compute), workgroup_size(1)]] fn main() {
299 var pos : vec4<f32> = uniforms.pos;
300 })");
301
302 wgpu::ComputePipelineDescriptor descriptor;
303 descriptor.layout = nullptr;
304 descriptor.compute.module = csModule;
305 descriptor.compute.entryPoint = "main";
306
307 wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&descriptor);
308
309 wgpu::BindGroupLayoutEntry binding = {};
310 binding.binding = 0;
311 binding.buffer.type = wgpu::BufferBindingType::Uniform;
312 binding.visibility = wgpu::ShaderStage::Compute;
313 binding.buffer.hasDynamicOffset = false;
314 binding.buffer.minBindingSize = 4 * sizeof(float);
315
316 wgpu::BindGroupLayoutDescriptor desc = {};
317 desc.entryCount = 1;
318 desc.entries = &binding;
319
320 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
321 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
322 }
323
324 // Test that the binding type matches the shader.
TEST_F(GetBindGroupLayoutTests,BindingType)325 TEST_F(GetBindGroupLayoutTests, BindingType) {
326 // This test works assuming Dawn Native's object deduplication.
327 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
328 // Native.
329 DAWN_SKIP_TEST_IF(UsesWire());
330
331 wgpu::BindGroupLayoutEntry binding = {};
332 binding.binding = 0;
333 binding.buffer.hasDynamicOffset = false;
334 binding.buffer.minBindingSize = 4 * sizeof(float);
335 binding.visibility = wgpu::ShaderStage::Fragment;
336
337 wgpu::BindGroupLayoutDescriptor desc = {};
338 desc.entryCount = 1;
339 desc.entries = &binding;
340
341 {
342 // Storage buffer binding is not supported in vertex shader.
343 binding.visibility = wgpu::ShaderStage::Fragment;
344 binding.buffer.type = wgpu::BufferBindingType::Storage;
345 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
346 [[block]] struct S {
347 pos : vec4<f32>;
348 };
349 [[group(0), binding(0)]] var<storage, read_write> ssbo : S;
350
351 [[stage(fragment)]] fn main() {
352 var pos : vec4<f32> = ssbo.pos;
353 })");
354 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
355 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
356 }
357 {
358 binding.buffer.type = wgpu::BufferBindingType::Uniform;
359 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
360 [[block]] struct S {
361 pos : vec4<f32>;
362 };
363 [[group(0), binding(0)]] var<uniform> uniforms : S;
364
365 [[stage(fragment)]] fn main() {
366 var pos : vec4<f32> = uniforms.pos;
367 })");
368 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
369 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
370 }
371
372 {
373 binding.buffer.type = wgpu::BufferBindingType::ReadOnlyStorage;
374 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
375 [[block]] struct S {
376 pos : vec4<f32>;
377 };
378 [[group(0), binding(0)]] var<storage, read> ssbo : S;
379
380 [[stage(fragment)]] fn main() {
381 var pos : vec4<f32> = ssbo.pos;
382 })");
383 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
384 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
385 }
386
387 binding.buffer.type = wgpu::BufferBindingType::Undefined;
388 binding.buffer.minBindingSize = 0;
389 {
390 binding.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
391 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
392 [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
393
394 [[stage(fragment)]] fn main() {
395 textureDimensions(myTexture);
396 })");
397 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
398 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
399 }
400
401 {
402 binding.texture.multisampled = true;
403 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
404 [[group(0), binding(0)]] var myTexture : texture_multisampled_2d<f32>;
405
406 [[stage(fragment)]] fn main() {
407 textureDimensions(myTexture);
408 })");
409 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
410 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
411 }
412
413 binding.texture.sampleType = wgpu::TextureSampleType::Undefined;
414 {
415 binding.sampler.type = wgpu::SamplerBindingType::Filtering;
416 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
417 [[group(0), binding(0)]] var mySampler: sampler;
418
419 [[stage(fragment)]] fn main() {
420 _ = mySampler;
421 })");
422 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
423 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
424 }
425 }
426
427 // Tests that the external texture binding type matches with a texture_external declared in the
428 // shader.
TEST_F(GetBindGroupLayoutTests,ExternalTextureBindingType)429 TEST_F(GetBindGroupLayoutTests, ExternalTextureBindingType) {
430 // This test works assuming Dawn Native's object deduplication.
431 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
432 // Native.
433 DAWN_SKIP_TEST_IF(UsesWire());
434
435 wgpu::BindGroupLayoutEntry binding = {};
436 binding.binding = 0;
437 binding.visibility = wgpu::ShaderStage::Fragment;
438
439 wgpu::BindGroupLayoutDescriptor desc = {};
440 desc.entryCount = 1;
441 desc.entries = &binding;
442
443 binding.nextInChain = &utils::kExternalTextureBindingLayout;
444 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
445 [[group(0), binding(0)]] var myExternalTexture: texture_external;
446
447 [[stage(fragment)]] fn main() {
448 _ = myExternalTexture;
449 })");
450 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
451 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
452 }
453
454 // Test that texture view dimension matches the shader.
TEST_F(GetBindGroupLayoutTests,ViewDimension)455 TEST_F(GetBindGroupLayoutTests, ViewDimension) {
456 // This test works assuming Dawn Native's object deduplication.
457 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
458 // Native.
459 DAWN_SKIP_TEST_IF(UsesWire());
460
461 wgpu::BindGroupLayoutEntry binding = {};
462 binding.binding = 0;
463 binding.visibility = wgpu::ShaderStage::Fragment;
464 binding.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
465
466 wgpu::BindGroupLayoutDescriptor desc = {};
467 desc.entryCount = 1;
468 desc.entries = &binding;
469
470 {
471 binding.texture.viewDimension = wgpu::TextureViewDimension::e1D;
472 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
473 [[group(0), binding(0)]] var myTexture : texture_1d<f32>;
474
475 [[stage(fragment)]] fn main() {
476 textureDimensions(myTexture);
477 })");
478 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
479 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
480 }
481
482 {
483 binding.texture.viewDimension = wgpu::TextureViewDimension::e2D;
484 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
485 [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
486
487 [[stage(fragment)]] fn main() {
488 textureDimensions(myTexture);
489 })");
490 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
491 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
492 }
493
494 {
495 binding.texture.viewDimension = wgpu::TextureViewDimension::e2DArray;
496 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
497 [[group(0), binding(0)]] var myTexture : texture_2d_array<f32>;
498
499 [[stage(fragment)]] fn main() {
500 textureDimensions(myTexture);
501 })");
502 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
503 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
504 }
505
506 {
507 binding.texture.viewDimension = wgpu::TextureViewDimension::e3D;
508 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
509 [[group(0), binding(0)]] var myTexture : texture_3d<f32>;
510
511 [[stage(fragment)]] fn main() {
512 textureDimensions(myTexture);
513 })");
514 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
515 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
516 }
517
518 {
519 binding.texture.viewDimension = wgpu::TextureViewDimension::Cube;
520 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
521 [[group(0), binding(0)]] var myTexture : texture_cube<f32>;
522
523 [[stage(fragment)]] fn main() {
524 textureDimensions(myTexture);
525 })");
526 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
527 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
528 }
529
530 {
531 binding.texture.viewDimension = wgpu::TextureViewDimension::CubeArray;
532 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
533 [[group(0), binding(0)]] var myTexture : texture_cube_array<f32>;
534
535 [[stage(fragment)]] fn main() {
536 textureDimensions(myTexture);
537 })");
538 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
539 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
540 }
541 }
542
543 // Test that texture component type matches the shader.
TEST_F(GetBindGroupLayoutTests,TextureComponentType)544 TEST_F(GetBindGroupLayoutTests, TextureComponentType) {
545 // This test works assuming Dawn Native's object deduplication.
546 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
547 // Native.
548 DAWN_SKIP_TEST_IF(UsesWire());
549
550 wgpu::BindGroupLayoutEntry binding = {};
551 binding.binding = 0;
552 binding.visibility = wgpu::ShaderStage::Fragment;
553
554 wgpu::BindGroupLayoutDescriptor desc = {};
555 desc.entryCount = 1;
556 desc.entries = &binding;
557
558 {
559 binding.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
560 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
561 [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
562
563 [[stage(fragment)]] fn main() {
564 textureDimensions(myTexture);
565 })");
566 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
567 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
568 }
569
570 {
571 binding.texture.sampleType = wgpu::TextureSampleType::Sint;
572 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
573 [[group(0), binding(0)]] var myTexture : texture_2d<i32>;
574
575 [[stage(fragment)]] fn main() {
576 textureDimensions(myTexture);
577 })");
578 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
579 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
580 }
581
582 {
583 binding.texture.sampleType = wgpu::TextureSampleType::Uint;
584 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
585 [[group(0), binding(0)]] var myTexture : texture_2d<u32>;
586
587 [[stage(fragment)]] fn main() {
588 textureDimensions(myTexture);
589 })");
590 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
591 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
592 }
593 }
594
595 // Test that binding= indices match.
TEST_F(GetBindGroupLayoutTests,BindingIndices)596 TEST_F(GetBindGroupLayoutTests, BindingIndices) {
597 // This test works assuming Dawn Native's object deduplication.
598 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
599 // Native.
600 DAWN_SKIP_TEST_IF(UsesWire());
601
602 wgpu::BindGroupLayoutEntry binding = {};
603 binding.visibility = wgpu::ShaderStage::Fragment;
604 binding.buffer.type = wgpu::BufferBindingType::Uniform;
605 binding.buffer.hasDynamicOffset = false;
606 binding.buffer.minBindingSize = 4 * sizeof(float);
607
608 wgpu::BindGroupLayoutDescriptor desc = {};
609 desc.entryCount = 1;
610 desc.entries = &binding;
611
612 {
613 binding.binding = 0;
614 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
615 [[block]] struct S {
616 pos : vec4<f32>;
617 };
618 [[group(0), binding(0)]] var<uniform> uniforms : S;
619
620 [[stage(fragment)]] fn main() {
621 var pos : vec4<f32> = uniforms.pos;
622 })");
623 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
624 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
625 }
626
627 {
628 binding.binding = 1;
629 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
630 [[block]] struct S {
631 pos : vec4<f32>;
632 };
633 [[group(0), binding(1)]] var<uniform> uniforms : S;
634
635 [[stage(fragment)]] fn main() {
636 var pos : vec4<f32> = uniforms.pos;
637 })");
638 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
639 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
640 }
641
642 {
643 binding.binding = 2;
644 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
645 [[block]] struct S {
646 pos : vec4<f32>;
647 };
648 [[group(0), binding(1)]] var<uniform> uniforms : S;
649
650 [[stage(fragment)]] fn main() {
651 var pos : vec4<f32> = uniforms.pos;
652 })");
653 EXPECT_FALSE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
654 device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()));
655 }
656 }
657
658 // Test it is valid to have duplicate bindings in the shaders.
TEST_F(GetBindGroupLayoutTests,DuplicateBinding)659 TEST_F(GetBindGroupLayoutTests, DuplicateBinding) {
660 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
661 [[block]] struct S {
662 pos : vec4<f32>;
663 };
664 [[group(0), binding(0)]] var<uniform> uniform0 : S;
665 [[group(1), binding(0)]] var<uniform> uniform1 : S;
666
667 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
668 var pos : vec4<f32> = uniform0.pos;
669 pos = uniform1.pos;
670 return vec4<f32>();
671 })");
672
673 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
674 [[block]] struct S {
675 pos : vec4<f32>;
676 };
677 [[group(1), binding(0)]] var<uniform> uniforms : S;
678
679 [[stage(fragment)]] fn main() {
680 var pos : vec4<f32> = uniforms.pos;
681 })");
682
683 utils::ComboRenderPipelineDescriptor descriptor;
684 descriptor.layout = nullptr;
685 descriptor.vertex.module = vsModule;
686 descriptor.cFragment.module = fsModule;
687 descriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
688
689 device.CreateRenderPipeline(&descriptor);
690 }
691
692 // Test that minBufferSize is set on the BGL and that the max of the min buffer sizes is used.
TEST_F(GetBindGroupLayoutTests,MinBufferSize)693 TEST_F(GetBindGroupLayoutTests, MinBufferSize) {
694 // This test works assuming Dawn Native's object deduplication.
695 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
696 // Native.
697 DAWN_SKIP_TEST_IF(UsesWire());
698
699 wgpu::ShaderModule vsModule4 = utils::CreateShaderModule(device, R"(
700 [[block]] struct S {
701 pos : f32;
702 };
703 [[group(0), binding(0)]] var<uniform> uniforms : S;
704
705 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
706 var pos : f32 = uniforms.pos;
707 return vec4<f32>();
708 })");
709
710 wgpu::ShaderModule vsModule64 = utils::CreateShaderModule(device, R"(
711 [[block]] struct S {
712 pos : mat4x4<f32>;
713 };
714 [[group(0), binding(0)]] var<uniform> uniforms : S;
715
716 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
717 var pos : mat4x4<f32> = uniforms.pos;
718 return vec4<f32>();
719 })");
720
721 wgpu::ShaderModule fsModule4 = utils::CreateShaderModule(device, R"(
722 [[block]] struct S {
723 pos : f32;
724 };
725 [[group(0), binding(0)]] var<uniform> uniforms : S;
726
727 [[stage(fragment)]] fn main() {
728 var pos : f32 = uniforms.pos;
729 })");
730
731 wgpu::ShaderModule fsModule64 = utils::CreateShaderModule(device, R"(
732 [[block]] struct S {
733 pos : mat4x4<f32>;
734 };
735 [[group(0), binding(0)]] var<uniform> uniforms : S;
736
737 [[stage(fragment)]] fn main() {
738 var pos : mat4x4<f32> = uniforms.pos;
739 })");
740
741 // Create BGLs with minBufferBindingSize 4 and 64.
742 wgpu::BindGroupLayoutEntry binding = {};
743 binding.binding = 0;
744 binding.buffer.type = wgpu::BufferBindingType::Uniform;
745 binding.visibility = wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Vertex;
746
747 wgpu::BindGroupLayoutDescriptor desc = {};
748 desc.entryCount = 1;
749 desc.entries = &binding;
750
751 binding.buffer.minBindingSize = 4;
752 wgpu::BindGroupLayout bgl4 = device.CreateBindGroupLayout(&desc);
753 binding.buffer.minBindingSize = 64;
754 wgpu::BindGroupLayout bgl64 = device.CreateBindGroupLayout(&desc);
755
756 utils::ComboRenderPipelineDescriptor descriptor;
757 descriptor.layout = nullptr;
758 descriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
759
760 // Check with both stages using 4 bytes.
761 {
762 descriptor.vertex.module = vsModule4;
763 descriptor.cFragment.module = fsModule4;
764 wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
765 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
766 pipeline.GetBindGroupLayout(0).Get(), bgl4.Get()));
767 }
768
769 // Check that the max is taken between 4 and 64.
770 {
771 descriptor.vertex.module = vsModule64;
772 descriptor.cFragment.module = fsModule4;
773 wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
774 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
775 pipeline.GetBindGroupLayout(0).Get(), bgl64.Get()));
776 }
777
778 // Check that the order doesn't change that the max is taken.
779 {
780 descriptor.vertex.module = vsModule4;
781 descriptor.cFragment.module = fsModule64;
782 wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
783 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
784 pipeline.GetBindGroupLayout(0).Get(), bgl64.Get()));
785 }
786 }
787
788 // Test that the visibility is correctly aggregated if two stages have the exact same binding.
TEST_F(GetBindGroupLayoutTests,StageAggregation)789 TEST_F(GetBindGroupLayoutTests, StageAggregation) {
790 // This test works assuming Dawn Native's object deduplication.
791 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
792 // Native.
793 DAWN_SKIP_TEST_IF(UsesWire());
794
795 wgpu::ShaderModule vsModuleNoSampler = utils::CreateShaderModule(device, R"(
796 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
797 return vec4<f32>();
798 })");
799
800 wgpu::ShaderModule vsModuleSampler = utils::CreateShaderModule(device, R"(
801 [[group(0), binding(0)]] var mySampler: sampler;
802 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
803 _ = mySampler;
804 return vec4<f32>();
805 })");
806
807 wgpu::ShaderModule fsModuleNoSampler = utils::CreateShaderModule(device, R"(
808 [[stage(fragment)]] fn main() {
809 })");
810
811 wgpu::ShaderModule fsModuleSampler = utils::CreateShaderModule(device, R"(
812 [[group(0), binding(0)]] var mySampler: sampler;
813 [[stage(fragment)]] fn main() {
814 _ = mySampler;
815 })");
816
817 // Create BGLs with minBufferBindingSize 4 and 64.
818 wgpu::BindGroupLayoutEntry binding = {};
819 binding.binding = 0;
820 binding.sampler.type = wgpu::SamplerBindingType::Filtering;
821
822 wgpu::BindGroupLayoutDescriptor desc = {};
823 desc.entryCount = 1;
824 desc.entries = &binding;
825
826 utils::ComboRenderPipelineDescriptor descriptor;
827 descriptor.layout = nullptr;
828 descriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
829
830 // Check with only the vertex shader using the sampler
831 {
832 descriptor.vertex.module = vsModuleSampler;
833 descriptor.cFragment.module = fsModuleNoSampler;
834 wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
835
836 binding.visibility = wgpu::ShaderStage::Vertex;
837 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
838 pipeline.GetBindGroupLayout(0).Get(), device.CreateBindGroupLayout(&desc).Get()));
839 }
840
841 // Check with only the fragment shader using the sampler
842 {
843 descriptor.vertex.module = vsModuleNoSampler;
844 descriptor.cFragment.module = fsModuleSampler;
845 wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
846
847 binding.visibility = wgpu::ShaderStage::Fragment;
848 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
849 pipeline.GetBindGroupLayout(0).Get(), device.CreateBindGroupLayout(&desc).Get()));
850 }
851
852 // Check with both shaders using the sampler
853 {
854 descriptor.vertex.module = vsModuleSampler;
855 descriptor.cFragment.module = fsModuleSampler;
856 wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
857
858 binding.visibility = wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Vertex;
859 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
860 pipeline.GetBindGroupLayout(0).Get(), device.CreateBindGroupLayout(&desc).Get()));
861 }
862 }
863
864 // Test it is invalid to have conflicting binding types in the shaders.
TEST_F(GetBindGroupLayoutTests,ConflictingBindingType)865 TEST_F(GetBindGroupLayoutTests, ConflictingBindingType) {
866 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
867 [[block]] struct S {
868 pos : vec4<f32>;
869 };
870 [[group(0), binding(0)]] var<uniform> ubo : S;
871
872 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
873 var pos : vec4<f32> = ubo.pos;
874 return vec4<f32>();
875 })");
876
877 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
878 [[block]] struct S {
879 pos : vec4<f32>;
880 };
881 [[group(0), binding(0)]] var<storage, read_write> ssbo : S;
882
883 [[stage(fragment)]] fn main() {
884 var pos : vec4<f32> = ssbo.pos;
885 })");
886
887 utils::ComboRenderPipelineDescriptor descriptor;
888 descriptor.layout = nullptr;
889 descriptor.vertex.module = vsModule;
890 descriptor.cFragment.module = fsModule;
891
892 ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
893 }
894
895 // Test it is invalid to have conflicting binding texture multisampling in the shaders.
TEST_F(GetBindGroupLayoutTests,ConflictingBindingTextureMultisampling)896 TEST_F(GetBindGroupLayoutTests, ConflictingBindingTextureMultisampling) {
897 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
898 [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
899
900 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
901 textureDimensions(myTexture);
902 return vec4<f32>();
903 })");
904
905 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
906 [[group(0), binding(0)]] var myTexture : texture_multisampled_2d<f32>;
907
908 [[stage(fragment)]] fn main() {
909 textureDimensions(myTexture);
910 })");
911
912 utils::ComboRenderPipelineDescriptor descriptor;
913 descriptor.layout = nullptr;
914 descriptor.vertex.module = vsModule;
915 descriptor.cFragment.module = fsModule;
916
917 ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
918 }
919
920 // Test it is invalid to have conflicting binding texture dimension in the shaders.
TEST_F(GetBindGroupLayoutTests,ConflictingBindingViewDimension)921 TEST_F(GetBindGroupLayoutTests, ConflictingBindingViewDimension) {
922 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
923 [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
924
925 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
926 textureDimensions(myTexture);
927 return vec4<f32>();
928 })");
929
930 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
931 [[group(0), binding(0)]] var myTexture : texture_3d<f32>;
932
933 [[stage(fragment)]] fn main() {
934 textureDimensions(myTexture);
935 })");
936
937 utils::ComboRenderPipelineDescriptor descriptor;
938 descriptor.layout = nullptr;
939 descriptor.vertex.module = vsModule;
940 descriptor.cFragment.module = fsModule;
941
942 ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
943 }
944
945 // Test it is invalid to have conflicting binding texture component type in the shaders.
TEST_F(GetBindGroupLayoutTests,ConflictingBindingTextureComponentType)946 TEST_F(GetBindGroupLayoutTests, ConflictingBindingTextureComponentType) {
947 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
948 [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
949
950 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
951 textureDimensions(myTexture);
952 return vec4<f32>();
953 })");
954
955 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
956 [[group(0), binding(0)]] var myTexture : texture_2d<i32>;
957
958 [[stage(fragment)]] fn main() {
959 textureDimensions(myTexture);
960 })");
961
962 utils::ComboRenderPipelineDescriptor descriptor;
963 descriptor.layout = nullptr;
964 descriptor.vertex.module = vsModule;
965 descriptor.cFragment.module = fsModule;
966
967 ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
968 }
969
970 // Test it is an error to query an out of range bind group layout.
TEST_F(GetBindGroupLayoutTests,OutOfRangeIndex)971 TEST_F(GetBindGroupLayoutTests, OutOfRangeIndex) {
972 ASSERT_DEVICE_ERROR(RenderPipelineFromFragmentShader(R"(
973 [[stage(fragment)]] fn main() {
974 })")
975 .GetBindGroupLayout(kMaxBindGroups));
976
977 ASSERT_DEVICE_ERROR(RenderPipelineFromFragmentShader(R"(
978 [[stage(fragment)]] fn main() {
979 })")
980 .GetBindGroupLayout(kMaxBindGroups + 1));
981 }
982
983 // Test that unused indices return the empty bind group layout.
TEST_F(GetBindGroupLayoutTests,UnusedIndex)984 TEST_F(GetBindGroupLayoutTests, UnusedIndex) {
985 // This test works assuming Dawn Native's object deduplication.
986 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
987 // Native.
988 DAWN_SKIP_TEST_IF(UsesWire());
989
990 wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
991 [[block]] struct S {
992 pos : vec4<f32>;
993 };
994 [[group(0), binding(0)]] var<uniform> uniforms0 : S;
995 [[group(2), binding(0)]] var<uniform> uniforms2 : S;
996
997 [[stage(fragment)]] fn main() {
998 var pos : vec4<f32> = uniforms0.pos;
999 pos = uniforms2.pos;
1000 })");
1001
1002 wgpu::BindGroupLayoutDescriptor desc = {};
1003 desc.entryCount = 0;
1004 desc.entries = nullptr;
1005
1006 wgpu::BindGroupLayout emptyBindGroupLayout = device.CreateBindGroupLayout(&desc);
1007
1008 EXPECT_FALSE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
1009 pipeline.GetBindGroupLayout(0).Get(), emptyBindGroupLayout.Get())); // Used
1010 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
1011 pipeline.GetBindGroupLayout(1).Get(), emptyBindGroupLayout.Get())); // Not Used.
1012 EXPECT_FALSE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
1013 pipeline.GetBindGroupLayout(2).Get(), emptyBindGroupLayout.Get())); // Used.
1014 EXPECT_TRUE(dawn_native::BindGroupLayoutBindingsEqualForTesting(
1015 pipeline.GetBindGroupLayout(3).Get(), emptyBindGroupLayout.Get())); // Not used
1016 }
1017
1018 // Test that after explicitly creating a pipeline with a pipeline layout, calling
1019 // GetBindGroupLayout reflects the same bind group layouts.
TEST_F(GetBindGroupLayoutTests,Reflection)1020 TEST_F(GetBindGroupLayoutTests, Reflection) {
1021 // This test works assuming Dawn Native's object deduplication.
1022 // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
1023 // Native.
1024 DAWN_SKIP_TEST_IF(UsesWire());
1025
1026 wgpu::BindGroupLayoutEntry binding = {};
1027 binding.binding = 0;
1028 binding.buffer.type = wgpu::BufferBindingType::Uniform;
1029 binding.visibility = wgpu::ShaderStage::Vertex;
1030
1031 wgpu::BindGroupLayoutDescriptor bglDesc = {};
1032 bglDesc.entryCount = 1;
1033 bglDesc.entries = &binding;
1034
1035 wgpu::BindGroupLayout bindGroupLayout = device.CreateBindGroupLayout(&bglDesc);
1036
1037 wgpu::PipelineLayoutDescriptor pipelineLayoutDesc = {};
1038 pipelineLayoutDesc.bindGroupLayoutCount = 1;
1039 pipelineLayoutDesc.bindGroupLayouts = &bindGroupLayout;
1040
1041 wgpu::PipelineLayout pipelineLayout = device.CreatePipelineLayout(&pipelineLayoutDesc);
1042
1043 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
1044 [[block]] struct S {
1045 pos : vec4<f32>;
1046 };
1047 [[group(0), binding(0)]] var<uniform> uniforms : S;
1048
1049 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
1050 var pos : vec4<f32> = uniforms.pos;
1051 return vec4<f32>();
1052 })");
1053
1054 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
1055 [[stage(fragment)]] fn main() {
1056 })");
1057
1058 utils::ComboRenderPipelineDescriptor pipelineDesc;
1059 pipelineDesc.layout = pipelineLayout;
1060 pipelineDesc.vertex.module = vsModule;
1061 pipelineDesc.cFragment.module = fsModule;
1062 pipelineDesc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
1063
1064 wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDesc);
1065
1066 EXPECT_EQ(pipeline.GetBindGroupLayout(0).Get(), bindGroupLayout.Get());
1067
1068 {
1069 wgpu::BindGroupLayoutDescriptor emptyDesc = {};
1070 emptyDesc.entryCount = 0;
1071 emptyDesc.entries = nullptr;
1072
1073 wgpu::BindGroupLayout emptyBindGroupLayout = device.CreateBindGroupLayout(&emptyDesc);
1074
1075 // Check that the rest of the bind group layouts reflect the empty one.
1076 EXPECT_EQ(pipeline.GetBindGroupLayout(1).Get(), emptyBindGroupLayout.Get());
1077 EXPECT_EQ(pipeline.GetBindGroupLayout(2).Get(), emptyBindGroupLayout.Get());
1078 EXPECT_EQ(pipeline.GetBindGroupLayout(3).Get(), emptyBindGroupLayout.Get());
1079 }
1080 }
1081
1082 // Test that fragment output validation is for the correct entryPoint
TEST_F(GetBindGroupLayoutTests,FromCorrectEntryPoint)1083 TEST_F(GetBindGroupLayoutTests, FromCorrectEntryPoint) {
1084 wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
1085 [[block]] struct Data {
1086 data : f32;
1087 };
1088 [[group(0), binding(0)]] var<storage, read_write> data0 : Data;
1089 [[group(0), binding(1)]] var<storage, read_write> data1 : Data;
1090
1091 [[stage(compute), workgroup_size(1)]] fn compute0() {
1092 data0.data = 0.0;
1093 }
1094
1095 [[stage(compute), workgroup_size(1)]] fn compute1() {
1096 data1.data = 0.0;
1097 }
1098 )");
1099
1100 wgpu::ComputePipelineDescriptor pipelineDesc;
1101 pipelineDesc.compute.module = module;
1102
1103 // Get each entryPoint's BGL.
1104 pipelineDesc.compute.entryPoint = "compute0";
1105 wgpu::ComputePipeline pipeline0 = device.CreateComputePipeline(&pipelineDesc);
1106 wgpu::BindGroupLayout bgl0 = pipeline0.GetBindGroupLayout(0);
1107
1108 pipelineDesc.compute.entryPoint = "compute1";
1109 wgpu::ComputePipeline pipeline1 = device.CreateComputePipeline(&pipelineDesc);
1110 wgpu::BindGroupLayout bgl1 = pipeline1.GetBindGroupLayout(0);
1111
1112 // Create the buffer used in the bindgroups.
1113 wgpu::BufferDescriptor bufferDesc;
1114 bufferDesc.size = 4;
1115 bufferDesc.usage = wgpu::BufferUsage::Storage;
1116 wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc);
1117
1118 // Success case, the BGL matches the descriptor for the bindgroup.
1119 utils::MakeBindGroup(device, bgl0, {{0, buffer}});
1120 utils::MakeBindGroup(device, bgl1, {{1, buffer}});
1121
1122 // Error case, the BGL doesn't match the descriptor for the bindgroup.
1123 ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bgl0, {{1, buffer}}));
1124 ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bgl1, {{0, buffer}}));
1125 }
1126