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