• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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 namespace {
21 
22     class ResourceUsageTrackingTest : public ValidationTest {
23       protected:
CreateBuffer(uint64_t size,wgpu::BufferUsage usage)24         wgpu::Buffer CreateBuffer(uint64_t size, wgpu::BufferUsage usage) {
25             wgpu::BufferDescriptor descriptor;
26             descriptor.size = size;
27             descriptor.usage = usage;
28 
29             return device.CreateBuffer(&descriptor);
30         }
31 
CreateTexture(wgpu::TextureUsage usage,wgpu::TextureFormat format=wgpu::TextureFormat::RGBA8Unorm)32         wgpu::Texture CreateTexture(wgpu::TextureUsage usage,
33                                     wgpu::TextureFormat format = wgpu::TextureFormat::RGBA8Unorm) {
34             wgpu::TextureDescriptor descriptor;
35             descriptor.dimension = wgpu::TextureDimension::e2D;
36             descriptor.size = {1, 1, 1};
37             descriptor.sampleCount = 1;
38             descriptor.mipLevelCount = 1;
39             descriptor.usage = usage;
40             descriptor.format = format;
41 
42             return device.CreateTexture(&descriptor);
43         }
44 
45         // Note that it is valid to bind any bind groups for indices that the pipeline doesn't use.
46         // We create a no-op render or compute pipeline without any bindings, and set bind groups
47         // in the caller, so it is always correct for binding validation between bind groups and
48         // pipeline. But those bind groups in caller can be used for validation for other purposes.
CreateNoOpRenderPipeline()49         wgpu::RenderPipeline CreateNoOpRenderPipeline() {
50             wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
51                 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
52                     return vec4<f32>();
53                 })");
54 
55             wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
56                 [[stage(fragment)]] fn main() {
57                 })");
58             utils::ComboRenderPipelineDescriptor pipelineDescriptor;
59             pipelineDescriptor.vertex.module = vsModule;
60             pipelineDescriptor.cFragment.module = fsModule;
61             pipelineDescriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
62             pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, nullptr);
63             return device.CreateRenderPipeline(&pipelineDescriptor);
64         }
65 
CreateNoOpComputePipeline(std::vector<wgpu::BindGroupLayout> bgls)66         wgpu::ComputePipeline CreateNoOpComputePipeline(std::vector<wgpu::BindGroupLayout> bgls) {
67             wgpu::ShaderModule csModule = utils::CreateShaderModule(device, R"(
68                 [[stage(compute), workgroup_size(1)]] fn main() {
69                 })");
70             wgpu::ComputePipelineDescriptor pipelineDescriptor;
71             pipelineDescriptor.layout = utils::MakePipelineLayout(device, std::move(bgls));
72             pipelineDescriptor.compute.module = csModule;
73             pipelineDescriptor.compute.entryPoint = "main";
74             return device.CreateComputePipeline(&pipelineDescriptor);
75         }
76 
77         static constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm;
78     };
79 
80     // Test that using a single buffer in multiple read usages in the same pass is allowed.
TEST_F(ResourceUsageTrackingTest,BufferWithMultipleReadUsage)81     TEST_F(ResourceUsageTrackingTest, BufferWithMultipleReadUsage) {
82         // Test render pass
83         {
84             // Create a buffer, and use the buffer as both vertex and index buffer.
85             wgpu::Buffer buffer =
86                 CreateBuffer(4, wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Index);
87 
88             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
89             DummyRenderPass dummyRenderPass(device);
90             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
91             pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32);
92             pass.SetVertexBuffer(0, buffer);
93             pass.EndPass();
94             encoder.Finish();
95         }
96 
97         // Test compute pass
98         {
99             // Create buffer and bind group
100             wgpu::Buffer buffer =
101                 CreateBuffer(4, wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage);
102 
103             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
104                 device,
105                 {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform},
106                  {1, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage}});
107             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}, {1, buffer}});
108 
109             // Use the buffer as both uniform and readonly storage buffer in compute pass.
110             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
111             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
112             pass.SetBindGroup(0, bg);
113             pass.EndPass();
114             encoder.Finish();
115         }
116     }
117 
118     // Test that it is invalid to use the same buffer as both readable and writable in the same
119     // render pass. It is invalid in the same dispatch in compute pass.
TEST_F(ResourceUsageTrackingTest,BufferWithReadAndWriteUsage)120     TEST_F(ResourceUsageTrackingTest, BufferWithReadAndWriteUsage) {
121         // test render pass
122         {
123             // Create buffer and bind group
124             wgpu::Buffer buffer =
125                 CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index);
126 
127             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
128                 device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
129             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}});
130 
131             // It is invalid to use the buffer as both index and storage in render pass
132             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
133             DummyRenderPass dummyRenderPass(device);
134             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
135             pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32);
136             pass.SetBindGroup(0, bg);
137             pass.EndPass();
138             ASSERT_DEVICE_ERROR(encoder.Finish());
139         }
140 
141         // test compute pass
142         {
143             // Create buffer and bind group
144             wgpu::Buffer buffer = CreateBuffer(512, wgpu::BufferUsage::Storage);
145 
146             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
147                 device,
148                 {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage},
149                  {1, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage}});
150             wgpu::BindGroup bg =
151                 utils::MakeBindGroup(device, bgl, {{0, buffer, 0, 4}, {1, buffer, 256, 4}});
152 
153             // Create a no-op compute pipeline
154             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({bgl});
155 
156             // It is valid to use the buffer as both storage and readonly storage in a single
157             // compute pass if dispatch command is not called.
158             {
159                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
160                 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
161                 pass.SetBindGroup(0, bg);
162                 pass.EndPass();
163                 encoder.Finish();
164             }
165 
166             // It is invalid to use the buffer as both storage and readonly storage in a single
167             // dispatch.
168             {
169                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
170                 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
171                 pass.SetPipeline(cp);
172                 pass.SetBindGroup(0, bg);
173                 pass.Dispatch(1);
174                 pass.EndPass();
175                 ASSERT_DEVICE_ERROR(encoder.Finish());
176             }
177         }
178     }
179 
180     // Test the use of a buffer as a storage buffer multiple times in the same synchronization
181     // scope.
TEST_F(ResourceUsageTrackingTest,BufferUsedAsStorageMultipleTimes)182     TEST_F(ResourceUsageTrackingTest, BufferUsedAsStorageMultipleTimes) {
183         // Create buffer and bind group
184         wgpu::Buffer buffer = CreateBuffer(512, wgpu::BufferUsage::Storage);
185 
186         wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
187             device, {{0, wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute,
188                       wgpu::BufferBindingType::Storage},
189                      {1, wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute,
190                       wgpu::BufferBindingType::Storage}});
191         wgpu::BindGroup bg =
192             utils::MakeBindGroup(device, bgl, {{0, buffer, 0, 4}, {1, buffer, 256, 4}});
193 
194         // test render pass
195         {
196             // It is valid to use multiple storage usages on the same buffer in render pass
197             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
198             DummyRenderPass dummyRenderPass(device);
199             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
200             pass.SetBindGroup(0, bg);
201             pass.EndPass();
202             encoder.Finish();
203         }
204 
205         // test compute pass
206         {
207             // It is valid to use multiple storage usages on the same buffer in a dispatch
208             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({bgl});
209 
210             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
211             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
212             pass.SetPipeline(cp);
213             pass.SetBindGroup(0, bg);
214             pass.Dispatch(1);
215             pass.EndPass();
216             encoder.Finish();
217         }
218     }
219 
220     // Test that using the same buffer as both readable and writable in different passes is allowed
TEST_F(ResourceUsageTrackingTest,BufferWithReadAndWriteUsageInDifferentPasses)221     TEST_F(ResourceUsageTrackingTest, BufferWithReadAndWriteUsageInDifferentPasses) {
222         // Test render pass
223         {
224             // Create buffers that will be used as index and storage buffers
225             wgpu::Buffer buffer0 =
226                 CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index);
227             wgpu::Buffer buffer1 =
228                 CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index);
229 
230             // Create bind groups to use the buffer as storage
231             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
232                 device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
233             wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl, {{0, buffer0}});
234             wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl, {{0, buffer1}});
235 
236             // Use these two buffers as both index and storage in different render passes
237             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
238             DummyRenderPass dummyRenderPass(device);
239 
240             wgpu::RenderPassEncoder pass0 = encoder.BeginRenderPass(&dummyRenderPass);
241             pass0.SetIndexBuffer(buffer0, wgpu::IndexFormat::Uint32);
242             pass0.SetBindGroup(0, bg1);
243             pass0.EndPass();
244 
245             wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&dummyRenderPass);
246             pass1.SetIndexBuffer(buffer1, wgpu::IndexFormat::Uint32);
247             pass1.SetBindGroup(0, bg0);
248             pass1.EndPass();
249 
250             encoder.Finish();
251         }
252 
253         // Test compute pass
254         {
255             // Create buffer and bind groups that will be used as storage and uniform bindings
256             wgpu::Buffer buffer =
257                 CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Uniform);
258 
259             wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout(
260                 device, {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage}});
261             wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(
262                 device, {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform}});
263             wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl0, {{0, buffer}});
264             wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl1, {{0, buffer}});
265 
266             // Use the buffer as both storage and uniform in different compute passes
267             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
268 
269             wgpu::ComputePassEncoder pass0 = encoder.BeginComputePass();
270             pass0.SetBindGroup(0, bg0);
271             pass0.EndPass();
272 
273             wgpu::ComputePassEncoder pass1 = encoder.BeginComputePass();
274             pass1.SetBindGroup(1, bg1);
275             pass1.EndPass();
276 
277             encoder.Finish();
278         }
279 
280         // Test render pass and compute pass mixed together with resource dependency.
281         {
282             // Create buffer and bind groups that will be used as storage and uniform bindings
283             wgpu::Buffer buffer = CreateBuffer(4, wgpu::BufferUsage::Storage);
284 
285             wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout(
286                 device, {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage}});
287             wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(
288                 device,
289                 {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage}});
290             wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl0, {{0, buffer}});
291             wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl1, {{0, buffer}});
292 
293             // Use the buffer as storage and uniform in render pass and compute pass respectively
294             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
295 
296             wgpu::ComputePassEncoder pass0 = encoder.BeginComputePass();
297             pass0.SetBindGroup(0, bg0);
298             pass0.EndPass();
299 
300             DummyRenderPass dummyRenderPass(device);
301             wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&dummyRenderPass);
302             pass1.SetBindGroup(1, bg1);
303             pass1.EndPass();
304 
305             encoder.Finish();
306         }
307     }
308 
309     // Test that it is invalid to use the same buffer as both readable and writable in different
310     // draws in a single render pass. But it is valid in different dispatches in a single compute
311     // pass.
TEST_F(ResourceUsageTrackingTest,BufferWithReadAndWriteUsageInDifferentDrawsOrDispatches)312     TEST_F(ResourceUsageTrackingTest, BufferWithReadAndWriteUsageInDifferentDrawsOrDispatches) {
313         // Test render pass
314         {
315             // Create a buffer and a bind group
316             wgpu::Buffer buffer =
317                 CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index);
318             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
319                 device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
320             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}});
321 
322             // Create a no-op render pipeline.
323             wgpu::RenderPipeline rp = CreateNoOpRenderPipeline();
324 
325             // It is not allowed to use the same buffer as both readable and writable in different
326             // draws within the same render pass.
327             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
328             DummyRenderPass dummyRenderPass(device);
329             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
330             pass.SetPipeline(rp);
331 
332             pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32);
333             pass.Draw(3);
334 
335             pass.SetBindGroup(0, bg);
336             pass.Draw(3);
337 
338             pass.EndPass();
339             ASSERT_DEVICE_ERROR(encoder.Finish());
340         }
341 
342         // test compute pass
343         {
344             // Create a buffer and bind groups
345             wgpu::Buffer buffer = CreateBuffer(4, wgpu::BufferUsage::Storage);
346 
347             wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout(
348                 device,
349                 {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage}});
350             wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(
351                 device, {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage}});
352             wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl0, {{0, buffer}});
353             wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl1, {{0, buffer}});
354 
355             // Create a no-op compute pipeline.
356             wgpu::ComputePipeline cp0 = CreateNoOpComputePipeline({bgl0});
357             wgpu::ComputePipeline cp1 = CreateNoOpComputePipeline({bgl1});
358 
359             // It is valid to use the same buffer as both readable and writable in different
360             // dispatches within the same compute pass.
361             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
362             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
363 
364             pass.SetPipeline(cp0);
365             pass.SetBindGroup(0, bg0);
366             pass.Dispatch(1);
367 
368             pass.SetPipeline(cp1);
369             pass.SetBindGroup(0, bg1);
370             pass.Dispatch(1);
371 
372             pass.EndPass();
373             encoder.Finish();
374         }
375     }
376 
377     // Test that it is invalid to use the same buffer as both readable and writable in a single
378     // draw or dispatch.
TEST_F(ResourceUsageTrackingTest,BufferWithReadAndWriteUsageInSingleDrawOrDispatch)379     TEST_F(ResourceUsageTrackingTest, BufferWithReadAndWriteUsageInSingleDrawOrDispatch) {
380         // Test render pass
381         {
382             // Create a buffer and a bind group
383             wgpu::Buffer buffer =
384                 CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index);
385             wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout(
386                 device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
387             wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, buffer}});
388 
389             // Create a no-op render pipeline.
390             wgpu::RenderPipeline rp = CreateNoOpRenderPipeline();
391 
392             // It is invalid to use the same buffer as both readable and writable usages in a single
393             // draw
394             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
395             DummyRenderPass dummyRenderPass(device);
396             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
397             pass.SetPipeline(rp);
398 
399             pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32);
400             pass.SetBindGroup(0, writeBG);
401             pass.Draw(3);
402 
403             pass.EndPass();
404             ASSERT_DEVICE_ERROR(encoder.Finish());
405         }
406 
407         // test compute pass
408         {
409             // Create a buffer and bind groups
410             wgpu::Buffer buffer = CreateBuffer(4, wgpu::BufferUsage::Storage);
411 
412             wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout(
413                 device,
414                 {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage}});
415             wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout(
416                 device, {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage}});
417             wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, buffer}});
418             wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, buffer}});
419 
420             // Create a no-op compute pipeline.
421             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({readBGL, writeBGL});
422 
423             // It is invalid to use the same buffer as both readable and writable usages in a single
424             // dispatch
425             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
426             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
427             pass.SetPipeline(cp);
428 
429             pass.SetBindGroup(0, readBG);
430             pass.SetBindGroup(1, writeBG);
431             pass.Dispatch(1);
432 
433             pass.EndPass();
434             ASSERT_DEVICE_ERROR(encoder.Finish());
435         }
436     }
437 
438     // Test that using the same buffer as copy src/dst and writable/readable usage is allowed.
TEST_F(ResourceUsageTrackingTest,BufferCopyAndBufferUsageInPass)439     TEST_F(ResourceUsageTrackingTest, BufferCopyAndBufferUsageInPass) {
440         // Create buffers that will be used as both a copy src/dst buffer and a storage buffer
441         wgpu::Buffer bufferSrc =
442             CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc);
443         wgpu::Buffer bufferDst =
444             CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst);
445 
446         // Create the bind group to use the buffer as storage
447         wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout(
448             device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
449         wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl0, {{0, bufferSrc}});
450         wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(
451             device, {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage}});
452         wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl1, {{0, bufferDst}});
453 
454         // Use the buffer as both copy src and storage in render pass
455         {
456             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
457             encoder.CopyBufferToBuffer(bufferSrc, 0, bufferDst, 0, 4);
458             DummyRenderPass dummyRenderPass(device);
459             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
460             pass.SetBindGroup(0, bg0);
461             pass.EndPass();
462             encoder.Finish();
463         }
464 
465         // Use the buffer as both copy dst and readonly storage in compute pass
466         {
467             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({bgl1});
468 
469             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
470             encoder.CopyBufferToBuffer(bufferSrc, 0, bufferDst, 0, 4);
471 
472             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
473             pass.SetBindGroup(0, bg1);
474             pass.SetPipeline(cp);
475             pass.Dispatch(1);
476             pass.EndPass();
477 
478             encoder.Finish();
479         }
480     }
481 
482     // Test that all index buffers and vertex buffers take effect even though some buffers are
483     // not used because they are overwritten by another consecutive call.
TEST_F(ResourceUsageTrackingTest,BufferWithMultipleSetIndexOrVertexBuffer)484     TEST_F(ResourceUsageTrackingTest, BufferWithMultipleSetIndexOrVertexBuffer) {
485         // Create buffers that will be used as both vertex and index buffer.
486         wgpu::Buffer buffer0 = CreateBuffer(
487             4, wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Index | wgpu::BufferUsage::Storage);
488         wgpu::Buffer buffer1 =
489             CreateBuffer(4, wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Index);
490 
491         wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
492             device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
493         wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer0}});
494 
495         DummyRenderPass dummyRenderPass(device);
496 
497         // Set index buffer twice. The second one overwrites the first one. No buffer is used as
498         // both read and write in the same pass. But the overwritten index buffer (buffer0) still
499         // take effect during resource tracking.
500         {
501             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
502             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
503             pass.SetIndexBuffer(buffer0, wgpu::IndexFormat::Uint32);
504             pass.SetIndexBuffer(buffer1, wgpu::IndexFormat::Uint32);
505             pass.SetBindGroup(0, bg);
506             pass.EndPass();
507             ASSERT_DEVICE_ERROR(encoder.Finish());
508         }
509 
510         // Set index buffer twice. The second one overwrites the first one. buffer0 is used as both
511         // read and write in the same pass
512         {
513             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
514             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
515             pass.SetIndexBuffer(buffer1, wgpu::IndexFormat::Uint32);
516             pass.SetIndexBuffer(buffer0, wgpu::IndexFormat::Uint32);
517             pass.SetBindGroup(0, bg);
518             pass.EndPass();
519             ASSERT_DEVICE_ERROR(encoder.Finish());
520         }
521 
522         // Set vertex buffer on the same index twice. The second one overwrites the first one. No
523         // buffer is used as both read and write in the same pass. But the overwritten vertex buffer
524         // (buffer0) still take effect during resource tracking.
525         {
526             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
527             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
528             pass.SetVertexBuffer(0, buffer0);
529             pass.SetVertexBuffer(0, buffer1);
530             pass.SetBindGroup(0, bg);
531             pass.EndPass();
532             ASSERT_DEVICE_ERROR(encoder.Finish());
533         }
534 
535         // Set vertex buffer on the same index twice. The second one overwrites the first one.
536         // buffer0 is used as both read and write in the same pass
537         {
538             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
539             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
540             pass.SetVertexBuffer(0, buffer1);
541             pass.SetVertexBuffer(0, buffer0);
542             pass.SetBindGroup(0, bg);
543             pass.EndPass();
544             ASSERT_DEVICE_ERROR(encoder.Finish());
545         }
546     }
547 
548     // Test that all consecutive SetBindGroup()s take effect even though some bind groups are not
549     // used because they are overwritten by a consecutive call.
TEST_F(ResourceUsageTrackingTest,BufferWithMultipleSetBindGroupsOnSameIndex)550     TEST_F(ResourceUsageTrackingTest, BufferWithMultipleSetBindGroupsOnSameIndex) {
551         // test render pass
552         {
553             // Create buffers that will be used as index and storage buffers
554             wgpu::Buffer buffer0 =
555                 CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index);
556             wgpu::Buffer buffer1 =
557                 CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index);
558 
559             // Create the bind group to use the buffer as storage
560             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
561                 device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
562             wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl, {{0, buffer0}});
563             wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl, {{0, buffer1}});
564 
565             DummyRenderPass dummyRenderPass(device);
566 
567             // Set bind group on the same index twice. The second one overwrites the first one.
568             // No buffer is used as both read and write in the same pass. But the overwritten
569             // bind group still take effect during resource tracking.
570             {
571                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
572                 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
573                 pass.SetIndexBuffer(buffer0, wgpu::IndexFormat::Uint32);
574                 pass.SetBindGroup(0, bg0);
575                 pass.SetBindGroup(0, bg1);
576                 pass.EndPass();
577                 ASSERT_DEVICE_ERROR(encoder.Finish());
578             }
579 
580             // Set bind group on the same index twice. The second one overwrites the first one.
581             // buffer0 is used as both read and write in the same pass
582             {
583                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
584                 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
585                 pass.SetIndexBuffer(buffer0, wgpu::IndexFormat::Uint32);
586                 pass.SetBindGroup(0, bg1);
587                 pass.SetBindGroup(0, bg0);
588                 pass.EndPass();
589                 ASSERT_DEVICE_ERROR(encoder.Finish());
590             }
591         }
592 
593         // test compute pass
594         {
595             // Create buffers that will be used as readonly and writable storage buffers
596             wgpu::Buffer buffer0 = CreateBuffer(512, wgpu::BufferUsage::Storage);
597             wgpu::Buffer buffer1 = CreateBuffer(4, wgpu::BufferUsage::Storage);
598 
599             // Create the bind group to use the buffer as storage
600             wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout(
601                 device, {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage}});
602             wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout(
603                 device,
604                 {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage}});
605             wgpu::BindGroup writeBG0 = utils::MakeBindGroup(device, writeBGL, {{0, buffer0, 0, 4}});
606             wgpu::BindGroup readBG0 = utils::MakeBindGroup(device, readBGL, {{0, buffer0, 256, 4}});
607             wgpu::BindGroup readBG1 = utils::MakeBindGroup(device, readBGL, {{0, buffer1, 0, 4}});
608 
609             // Create a no-op compute pipeline.
610             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({writeBGL, readBGL});
611 
612             // Set bind group against the same index twice. The second one overwrites the first one.
613             // Then no buffer is used as both read and write in the same dispatch. But the
614             // overwritten bind group still take effect.
615             {
616                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
617                 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
618                 pass.SetBindGroup(0, writeBG0);
619                 pass.SetBindGroup(1, readBG0);
620                 pass.SetBindGroup(1, readBG1);
621                 pass.SetPipeline(cp);
622                 pass.Dispatch(1);
623                 pass.EndPass();
624                 encoder.Finish();
625             }
626 
627             // Set bind group against the same index twice. The second one overwrites the first one.
628             // Then buffer0 is used as both read and write in the same dispatch
629             {
630                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
631                 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
632                 pass.SetBindGroup(0, writeBG0);
633                 pass.SetBindGroup(1, readBG1);
634                 pass.SetBindGroup(1, readBG0);
635                 pass.SetPipeline(cp);
636                 pass.Dispatch(1);
637                 pass.EndPass();
638                 ASSERT_DEVICE_ERROR(encoder.Finish());
639             }
640         }
641     }
642 
643     // Test that it is invalid to have resource usage conflicts even when all bindings are not
644     // visible to the programmable pass where it is used.
TEST_F(ResourceUsageTrackingTest,BufferUsageConflictBetweenInvisibleStagesInBindGroup)645     TEST_F(ResourceUsageTrackingTest, BufferUsageConflictBetweenInvisibleStagesInBindGroup) {
646         wgpu::Buffer buffer = CreateBuffer(4, wgpu::BufferUsage::Storage);
647 
648         // Test render pass for bind group. The conflict of readonly storage and storage usage
649         // doesn't reside in render related stages at all
650         {
651             // Create a bind group whose bindings are not visible in render pass
652             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
653                 device, {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage},
654                          {1, wgpu::ShaderStage::None, wgpu::BufferBindingType::ReadOnlyStorage}});
655             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}, {1, buffer}});
656 
657             // These two bindings are invisible in render pass. But we still track these bindings.
658             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
659             DummyRenderPass dummyRenderPass(device);
660             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
661             pass.SetBindGroup(0, bg);
662             pass.EndPass();
663             ASSERT_DEVICE_ERROR(encoder.Finish());
664         }
665 
666         // Test compute pass for bind group. The conflict of readonly storage and storage usage
667         // doesn't reside in compute related stage at all
668         {
669             // Create a bind group whose bindings are not visible in compute pass
670             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
671                 device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage},
672                          {1, wgpu::ShaderStage::None, wgpu::BufferBindingType::Storage}});
673             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}, {1, buffer}});
674 
675             // Create a no-op compute pipeline.
676             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({bgl});
677 
678             // These two bindings are invisible in the dispatch. But we still track these bindings.
679             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
680             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
681             pass.SetPipeline(cp);
682             pass.SetBindGroup(0, bg);
683             pass.Dispatch(1);
684             pass.EndPass();
685             ASSERT_DEVICE_ERROR(encoder.Finish());
686         }
687     }
688 
689     // Test that it is invalid to have resource usage conflicts even when one of the bindings is not
690     // visible to the programmable pass where it is used.
TEST_F(ResourceUsageTrackingTest,BufferUsageConflictWithInvisibleStageInBindGroup)691     TEST_F(ResourceUsageTrackingTest, BufferUsageConflictWithInvisibleStageInBindGroup) {
692         // Test render pass for bind group and index buffer. The conflict of storage and index
693         // buffer usage resides between fragment stage and compute stage. But the compute stage
694         // binding is not visible in render pass.
695         {
696             wgpu::Buffer buffer =
697                 CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index);
698             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
699                 device, {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage}});
700             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}});
701 
702             // Buffer usage in compute stage in bind group conflicts with index buffer. And binding
703             // for compute stage is not visible in render pass. But we still track this binding.
704             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
705             DummyRenderPass dummyRenderPass(device);
706             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
707             pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32);
708             pass.SetBindGroup(0, bg);
709             pass.EndPass();
710             ASSERT_DEVICE_ERROR(encoder.Finish());
711         }
712 
713         // Test compute pass for bind group. The conflict of readonly storage and storage buffer
714         // usage resides between compute stage and fragment stage. But the fragment stage binding is
715         // not visible in the dispatch.
716         {
717             wgpu::Buffer buffer = CreateBuffer(4, wgpu::BufferUsage::Storage);
718             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
719                 device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage},
720                          {1, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage}});
721             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}, {1, buffer}});
722 
723             // Create a no-op compute pipeline.
724             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({bgl});
725 
726             // Buffer usage in compute stage conflicts with buffer usage in fragment stage. And
727             // binding for fragment stage is not visible in the dispatch. But we still track this
728             // invisible binding.
729             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
730             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
731             pass.SetPipeline(cp);
732             pass.SetBindGroup(0, bg);
733             pass.Dispatch(1);
734             pass.EndPass();
735             ASSERT_DEVICE_ERROR(encoder.Finish());
736         }
737     }
738 
739     // Test that it is invalid to have resource usage conflicts even when one of the bindings is not
740     // used in the pipeline.
TEST_F(ResourceUsageTrackingTest,BufferUsageConflictWithUnusedPipelineBindings)741     TEST_F(ResourceUsageTrackingTest, BufferUsageConflictWithUnusedPipelineBindings) {
742         wgpu::Buffer buffer = CreateBuffer(4, wgpu::BufferUsage::Storage);
743 
744         // Test render pass for bind groups with unused bindings. The conflict of readonly storage
745         // and storage usages resides in different bind groups, although some bindings may not be
746         // used because its bind group layout is not designated in pipeline layout.
747         {
748             // Create bind groups. The bindings are visible for render pass.
749             wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout(
750                 device,
751                 {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage}});
752             wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(
753                 device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
754             wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl0, {{0, buffer}});
755             wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl1, {{0, buffer}});
756 
757             // Create a passthrough render pipeline with a readonly buffer
758             wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
759                 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
760                     return vec4<f32>();
761                 })");
762 
763             wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
764                 [[block]] struct RBuffer {
765                     value : f32;
766                 };
767                 [[group(0), binding(0)]] var<storage, read> rBuffer : RBuffer;
768                 [[stage(fragment)]] fn main() {
769                 })");
770             utils::ComboRenderPipelineDescriptor pipelineDescriptor;
771             pipelineDescriptor.vertex.module = vsModule;
772             pipelineDescriptor.cFragment.module = fsModule;
773             pipelineDescriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
774             pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl0);
775             wgpu::RenderPipeline rp = device.CreateRenderPipeline(&pipelineDescriptor);
776 
777             // Resource in bg1 conflicts with resources used in bg0. However, bindings in bg1 is
778             // not used in pipeline. But we still track this binding.
779             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
780             DummyRenderPass dummyRenderPass(device);
781             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
782             pass.SetBindGroup(0, bg0);
783             pass.SetBindGroup(1, bg1);
784             pass.SetPipeline(rp);
785             pass.Draw(3);
786             pass.EndPass();
787             ASSERT_DEVICE_ERROR(encoder.Finish());
788         }
789 
790         // Test that an unused bind group is not used to detect conflicts between bindings in
791         // compute passes.
792         {
793             // Create bind groups. The bindings are visible for compute pass.
794             wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout(
795                 device,
796                 {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage}});
797             wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(
798                 device, {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage}});
799             wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl0, {{0, buffer}});
800             wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl1, {{0, buffer}});
801 
802             // Create a compute pipeline with only one of the two BGLs.
803             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({bgl0});
804 
805             // Resource in bg1 conflicts with resources used in bg0. However, the binding in bg1 is
806             // not used in pipeline so no error is produced in the dispatch.
807             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
808             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
809             pass.SetBindGroup(0, bg0);
810             pass.SetBindGroup(1, bg1);
811             pass.SetPipeline(cp);
812             pass.Dispatch(1);
813             pass.EndPass();
814             encoder.Finish();
815         }
816     }
817 
818     // Test that it is invalid to use the same texture as both readable and writable in the same
819     // render pass. It is invalid in the same dispatch in compute pass.
TEST_F(ResourceUsageTrackingTest,TextureWithReadAndWriteUsage)820     TEST_F(ResourceUsageTrackingTest, TextureWithReadAndWriteUsage) {
821         // Test render pass
822         {
823             // Create a texture
824             wgpu::Texture texture = CreateTexture(wgpu::TextureUsage::TextureBinding |
825                                                   wgpu::TextureUsage::RenderAttachment);
826             wgpu::TextureView view = texture.CreateView();
827 
828             // Create a bind group to use the texture as sampled binding
829             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
830                 device, {{0, wgpu::ShaderStage::Vertex, wgpu::TextureSampleType::Float}});
831             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}});
832 
833             // Create a render pass to use the texture as a render target
834             utils::ComboRenderPassDescriptor renderPass({view});
835 
836             // It is invalid to use the texture as both sampled and render target in the same pass
837             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
838             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
839             pass.SetBindGroup(0, bg);
840             pass.EndPass();
841             ASSERT_DEVICE_ERROR(encoder.Finish());
842         }
843 
844         // Test compute pass
845         {
846             // Create a texture
847             wgpu::Texture texture = CreateTexture(wgpu::TextureUsage::TextureBinding |
848                                                   wgpu::TextureUsage::StorageBinding);
849             wgpu::TextureView view = texture.CreateView();
850 
851             // Create a bind group to use the texture as sampled and writeonly bindings
852             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
853                 device,
854                 {{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float},
855                  {1, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
856             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}, {1, view}});
857 
858             // Create a no-op compute pipeline
859             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({bgl});
860 
861             // It is valid to use the texture as both sampled and writeonly storage in a single
862             // compute pass if dispatch command is not called.
863             {
864                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
865                 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
866                 pass.SetBindGroup(0, bg);
867                 pass.EndPass();
868                 encoder.Finish();
869             }
870 
871             // It is invalid to use the texture as both sampled and writeonly storage in a single
872             // dispatch
873             {
874                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
875                 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
876                 pass.SetPipeline(cp);
877                 pass.SetBindGroup(0, bg);
878                 pass.Dispatch(1);
879                 pass.EndPass();
880                 ASSERT_DEVICE_ERROR(encoder.Finish());
881             }
882         }
883     }
884 
885     // Test that it is invalid to use the same texture as both readable and writable depth/stencil
886     // attachment in the same render pass. But it is valid to use it as both readable and readonly
887     // depth/stencil attachment in the same render pass.
888     // Note that depth/stencil attachment is a special render attachment, it can be readonly.
TEST_F(ResourceUsageTrackingTest,TextureWithSamplingAndDepthStencilAttachment)889     TEST_F(ResourceUsageTrackingTest, TextureWithSamplingAndDepthStencilAttachment) {
890         // Create a texture
891         wgpu::Texture texture =
892             CreateTexture(wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment,
893                           wgpu::TextureFormat::Depth32Float);
894         wgpu::TextureView view = texture.CreateView();
895 
896         // Create a bind group to use the texture as sampled binding
897         wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
898             device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth}});
899         wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}});
900 
901         // Create a render pass to use the texture as a render target
902         utils::ComboRenderPassDescriptor passDescriptor({}, view);
903         passDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
904         passDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
905 
906         // It is invalid to use the texture as both sampled and writeable depth/stencil attachment
907         // in the same pass
908         {
909             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
910             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&passDescriptor);
911             pass.SetBindGroup(0, bg);
912             pass.EndPass();
913             ASSERT_DEVICE_ERROR(encoder.Finish());
914         }
915 
916         // It is valid to use the texture as both sampled and readonly depth/stencil attachment in
917         // the same pass
918         {
919             passDescriptor.cDepthStencilAttachmentInfo.depthReadOnly = true;
920 
921             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
922             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&passDescriptor);
923             pass.SetBindGroup(0, bg);
924             pass.EndPass();
925             encoder.Finish();
926         }
927     }
928 
929     // Test using multiple writable usages on the same texture in a single pass/dispatch
TEST_F(ResourceUsageTrackingTest,TextureWithMultipleWriteUsage)930     TEST_F(ResourceUsageTrackingTest, TextureWithMultipleWriteUsage) {
931         // Test render pass
932         {
933             // Create a texture
934             wgpu::Texture texture = CreateTexture(wgpu::TextureUsage::StorageBinding |
935                                                   wgpu::TextureUsage::RenderAttachment);
936             wgpu::TextureView view = texture.CreateView();
937 
938             // Create a bind group to use the texture as writeonly storage binding
939             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
940                 device,
941                 {{0, wgpu::ShaderStage::Fragment, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
942             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}});
943 
944             // It is invalid to use the texture as both writeonly storage and render target in
945             // the same pass
946             {
947                 utils::ComboRenderPassDescriptor renderPass({view});
948 
949                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
950                 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
951                 pass.SetBindGroup(0, bg);
952                 pass.EndPass();
953                 ASSERT_DEVICE_ERROR(encoder.Finish());
954             }
955 
956             // It is valid to use multiple writeonly storage usages on the same texture in render
957             // pass
958             {
959                 wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl, {{0, view}});
960 
961                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
962                 DummyRenderPass dummyRenderPass(device);
963                 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
964                 pass.SetBindGroup(0, bg);
965                 pass.SetBindGroup(1, bg1);
966                 pass.EndPass();
967                 encoder.Finish();
968             }
969         }
970 
971         // Test compute pass
972         {
973             // Create a texture
974             wgpu::Texture texture = CreateTexture(wgpu::TextureUsage::StorageBinding);
975             wgpu::TextureView view = texture.CreateView();
976 
977             // Create a bind group to use the texture as sampled and writeonly bindings
978             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
979                 device,
980                 {{0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly, kFormat},
981                  {1, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
982             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}, {1, view}});
983 
984             // Create a no-op compute pipeline
985             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({bgl});
986 
987             // It is valid to use the texture as multiple writeonly storage usages in a single
988             // dispatch
989             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
990             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
991             pass.SetPipeline(cp);
992             pass.SetBindGroup(0, bg);
993             pass.Dispatch(1);
994             pass.EndPass();
995             encoder.Finish();
996         }
997     }
998 
999     // Test that a single subresource of a texture cannot be used as a render attachment more than
1000     // once in the same pass.
TEST_F(ResourceUsageTrackingTest,TextureWithMultipleRenderAttachmentUsage)1001     TEST_F(ResourceUsageTrackingTest, TextureWithMultipleRenderAttachmentUsage) {
1002         // Create a texture with two array layers
1003         wgpu::TextureDescriptor descriptor;
1004         descriptor.dimension = wgpu::TextureDimension::e2D;
1005         descriptor.size = {1, 1, 2};
1006         descriptor.usage = wgpu::TextureUsage::RenderAttachment;
1007         descriptor.format = kFormat;
1008 
1009         wgpu::Texture texture = device.CreateTexture(&descriptor);
1010 
1011         wgpu::TextureViewDescriptor viewDesc = {};
1012         viewDesc.arrayLayerCount = 1;
1013 
1014         wgpu::TextureView viewLayer0 = texture.CreateView(&viewDesc);
1015 
1016         viewDesc.baseArrayLayer = 1;
1017         wgpu::TextureView viewLayer1 = texture.CreateView(&viewDesc);
1018 
1019         // Control: It is valid to use layer0 as a render target for one attachment, and
1020         // layer1 as the second attachment in the same pass
1021         {
1022             utils::ComboRenderPassDescriptor renderPass({viewLayer0, viewLayer1});
1023 
1024             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1025             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
1026             pass.EndPass();
1027             encoder.Finish();
1028         }
1029 
1030         // Control: It is valid to use layer0 as a render target in separate passes.
1031         {
1032             utils::ComboRenderPassDescriptor renderPass({viewLayer0});
1033 
1034             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1035             wgpu::RenderPassEncoder pass0 = encoder.BeginRenderPass(&renderPass);
1036             pass0.EndPass();
1037             wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&renderPass);
1038             pass1.EndPass();
1039             encoder.Finish();
1040         }
1041 
1042         // It is invalid to use layer0 as a render target for both attachments in the same pass
1043         {
1044             utils::ComboRenderPassDescriptor renderPass({viewLayer0, viewLayer0});
1045 
1046             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1047             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
1048             pass.EndPass();
1049             ASSERT_DEVICE_ERROR(encoder.Finish());
1050         }
1051 
1052         // It is invalid to use layer1 as a render target for both attachments in the same pass
1053         {
1054             utils::ComboRenderPassDescriptor renderPass({viewLayer1, viewLayer1});
1055 
1056             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1057             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
1058             pass.EndPass();
1059             ASSERT_DEVICE_ERROR(encoder.Finish());
1060         }
1061     }
1062 
1063     // Test that using the same texture as both readable and writable in different passes is
1064     // allowed
TEST_F(ResourceUsageTrackingTest,TextureWithReadAndWriteUsageInDifferentPasses)1065     TEST_F(ResourceUsageTrackingTest, TextureWithReadAndWriteUsageInDifferentPasses) {
1066         // Test render pass
1067         {
1068             // Create textures that will be used as both a sampled texture and a render target
1069             wgpu::Texture t0 = CreateTexture(wgpu::TextureUsage::TextureBinding |
1070                                              wgpu::TextureUsage::RenderAttachment);
1071             wgpu::TextureView v0 = t0.CreateView();
1072             wgpu::Texture t1 = CreateTexture(wgpu::TextureUsage::TextureBinding |
1073                                              wgpu::TextureUsage::RenderAttachment);
1074             wgpu::TextureView v1 = t1.CreateView();
1075 
1076             // Create bind groups to use the texture as sampled
1077             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1078                 device, {{0, wgpu::ShaderStage::Vertex, wgpu::TextureSampleType::Float}});
1079             wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl, {{0, v0}});
1080             wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl, {{0, v1}});
1081 
1082             // Create render passes that will use the textures as render attachments
1083             utils::ComboRenderPassDescriptor renderPass0({v1});
1084             utils::ComboRenderPassDescriptor renderPass1({v0});
1085 
1086             // Use the textures as both sampled and render attachments in different passes
1087             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1088 
1089             wgpu::RenderPassEncoder pass0 = encoder.BeginRenderPass(&renderPass0);
1090             pass0.SetBindGroup(0, bg0);
1091             pass0.EndPass();
1092 
1093             wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&renderPass1);
1094             pass1.SetBindGroup(0, bg1);
1095             pass1.EndPass();
1096 
1097             encoder.Finish();
1098         }
1099 
1100         // Test compute pass
1101         {
1102             // Create a texture that will be used storage texture
1103             wgpu::Texture texture = CreateTexture(wgpu::TextureUsage::TextureBinding |
1104                                                   wgpu::TextureUsage::StorageBinding);
1105             wgpu::TextureView view = texture.CreateView();
1106 
1107             // Create bind groups to use the texture as sampled and writeonly bindings
1108             wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout(
1109                 device, {{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float}});
1110             wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout(
1111                 device,
1112                 {{0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
1113             wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, view}});
1114             wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}});
1115 
1116             // Use the textures as both sampled and writeonly storages in different passes
1117             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1118 
1119             wgpu::ComputePassEncoder pass0 = encoder.BeginComputePass();
1120             pass0.SetBindGroup(0, readBG);
1121             pass0.EndPass();
1122 
1123             wgpu::ComputePassEncoder pass1 = encoder.BeginComputePass();
1124             pass1.SetBindGroup(0, writeBG);
1125             pass1.EndPass();
1126 
1127             encoder.Finish();
1128         }
1129 
1130         // Test compute pass and render pass mixed together with resource dependency
1131         {
1132             // Create a texture that will be used a storage texture
1133             wgpu::Texture texture = CreateTexture(wgpu::TextureUsage::TextureBinding |
1134                                                   wgpu::TextureUsage::StorageBinding);
1135             wgpu::TextureView view = texture.CreateView();
1136 
1137             // Create bind groups to use the texture as sampled and writeonly bindings
1138             wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout(
1139                 device,
1140                 {{0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
1141             wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout(
1142                 device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
1143             wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}});
1144             wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, view}});
1145 
1146             // Use the texture as writeonly and sampled storage in compute pass and render
1147             // pass respectively
1148             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1149 
1150             wgpu::ComputePassEncoder pass0 = encoder.BeginComputePass();
1151             pass0.SetBindGroup(0, writeBG);
1152             pass0.EndPass();
1153 
1154             DummyRenderPass dummyRenderPass(device);
1155             wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&dummyRenderPass);
1156             pass1.SetBindGroup(0, readBG);
1157             pass1.EndPass();
1158 
1159             encoder.Finish();
1160         }
1161     }
1162 
1163     // Test that it is invalid to use the same texture as both readable and writable in different
1164     // draws in a single render pass. But it is valid in different dispatches in a single compute
1165     // pass.
TEST_F(ResourceUsageTrackingTest,TextureWithReadAndWriteUsageOnDifferentDrawsOrDispatches)1166     TEST_F(ResourceUsageTrackingTest, TextureWithReadAndWriteUsageOnDifferentDrawsOrDispatches) {
1167         // Create a texture that will be used both as a sampled texture and a storage texture
1168         wgpu::Texture texture =
1169             CreateTexture(wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::StorageBinding);
1170         wgpu::TextureView view = texture.CreateView();
1171 
1172         // Test render pass
1173         {
1174             // Create bind groups to use the texture as sampled and writeonly storage bindings
1175             wgpu::BindGroupLayout sampledBGL = utils::MakeBindGroupLayout(
1176                 device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
1177             wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout(
1178                 device,
1179                 {{0, wgpu::ShaderStage::Fragment, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
1180             wgpu::BindGroup sampledBG = utils::MakeBindGroup(device, sampledBGL, {{0, view}});
1181             wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}});
1182 
1183             // Create a no-op render pipeline.
1184             wgpu::RenderPipeline rp = CreateNoOpRenderPipeline();
1185 
1186             // It is not allowed to use the same texture as both readable and writable in different
1187             // draws within the same render pass.
1188             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1189             DummyRenderPass dummyRenderPass(device);
1190             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
1191             pass.SetPipeline(rp);
1192 
1193             pass.SetBindGroup(0, sampledBG);
1194             pass.Draw(3);
1195 
1196             pass.SetBindGroup(0, writeBG);
1197             pass.Draw(3);
1198 
1199             pass.EndPass();
1200             ASSERT_DEVICE_ERROR(encoder.Finish());
1201         }
1202 
1203         // Test compute pass
1204         {
1205             // Create bind groups to use the texture as sampled and writeonly storage bindings
1206             wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout(
1207                 device, {{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float}});
1208             wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout(
1209                 device,
1210                 {{0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
1211             wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, view}});
1212             wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}});
1213 
1214             // Create a no-op compute pipeline.
1215             wgpu::ComputePipeline readCp = CreateNoOpComputePipeline({readBGL});
1216             wgpu::ComputePipeline writeCp = CreateNoOpComputePipeline({writeBGL});
1217 
1218             // It is valid to use the same texture as both readable and writable in different
1219             // dispatches within the same compute pass.
1220             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1221             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1222 
1223             pass.SetPipeline(readCp);
1224             pass.SetBindGroup(0, readBG);
1225             pass.Dispatch(1);
1226 
1227             pass.SetPipeline(writeCp);
1228             pass.SetBindGroup(0, writeBG);
1229             pass.Dispatch(1);
1230 
1231             pass.EndPass();
1232             encoder.Finish();
1233         }
1234     }
1235 
1236     // Test that it is invalid to use the same texture as both readable and writable in a single
1237     // draw or dispatch.
TEST_F(ResourceUsageTrackingTest,TextureWithReadAndWriteUsageInSingleDrawOrDispatch)1238     TEST_F(ResourceUsageTrackingTest, TextureWithReadAndWriteUsageInSingleDrawOrDispatch) {
1239         // Create a texture that will be used both as a sampled texture and a storage texture
1240         wgpu::Texture texture =
1241             CreateTexture(wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::StorageBinding);
1242         wgpu::TextureView view = texture.CreateView();
1243 
1244         // Test render pass
1245         {
1246             // Create the bind group to use the texture as sampled and writeonly storage bindings
1247             wgpu::BindGroupLayout sampledBGL = utils::MakeBindGroupLayout(
1248                 device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
1249             wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout(
1250                 device,
1251                 {{0, wgpu::ShaderStage::Fragment, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
1252             wgpu::BindGroup sampledBG = utils::MakeBindGroup(device, sampledBGL, {{0, view}});
1253             wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}});
1254 
1255             // Create a no-op render pipeline.
1256             wgpu::RenderPipeline rp = CreateNoOpRenderPipeline();
1257 
1258             // It is invalid to use the same texture as both readable and writable usages in a
1259             // single draw
1260             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1261             DummyRenderPass dummyRenderPass(device);
1262             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
1263             pass.SetPipeline(rp);
1264 
1265             pass.SetBindGroup(0, sampledBG);
1266             pass.SetBindGroup(1, writeBG);
1267             pass.Draw(3);
1268 
1269             pass.EndPass();
1270             ASSERT_DEVICE_ERROR(encoder.Finish());
1271         }
1272 
1273         // Test compute pass
1274         {
1275             // Create the bind group to use the texture as sampled and writeonly storage bindings
1276             wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout(
1277                 device, {{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float}});
1278             wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout(
1279                 device,
1280                 {{0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
1281             wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, view}});
1282             wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}});
1283 
1284             // Create a no-op compute pipeline.
1285             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({readBGL, writeBGL});
1286 
1287             // It is invalid to use the same texture as both readable and writable usages in a
1288             // single dispatch
1289             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1290             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1291             pass.SetPipeline(cp);
1292 
1293             pass.SetBindGroup(0, readBG);
1294             pass.SetBindGroup(1, writeBG);
1295             pass.Dispatch(1);
1296 
1297             pass.EndPass();
1298             ASSERT_DEVICE_ERROR(encoder.Finish());
1299         }
1300     }
1301 
1302     // Test that using a single texture as copy src/dst and writable/readable usage in pass is
1303     // allowed.
TEST_F(ResourceUsageTrackingTest,TextureCopyAndTextureUsageInPass)1304     TEST_F(ResourceUsageTrackingTest, TextureCopyAndTextureUsageInPass) {
1305         // Create textures that will be used as both a sampled texture and a render target
1306         wgpu::Texture texture0 = CreateTexture(wgpu::TextureUsage::CopySrc);
1307         wgpu::Texture texture1 =
1308             CreateTexture(wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding |
1309                           wgpu::TextureUsage::RenderAttachment);
1310         wgpu::TextureView view0 = texture0.CreateView();
1311         wgpu::TextureView view1 = texture1.CreateView();
1312 
1313         wgpu::ImageCopyTexture srcView = utils::CreateImageCopyTexture(texture0, 0, {0, 0, 0});
1314         wgpu::ImageCopyTexture dstView = utils::CreateImageCopyTexture(texture1, 0, {0, 0, 0});
1315         wgpu::Extent3D copySize = {1, 1, 1};
1316 
1317         // Use the texture as both copy dst and render attachment in render pass
1318         {
1319             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1320             encoder.CopyTextureToTexture(&srcView, &dstView, &copySize);
1321             utils::ComboRenderPassDescriptor renderPass({view1});
1322             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
1323             pass.EndPass();
1324             encoder.Finish();
1325         }
1326 
1327         // Use the texture as both copy dst and readable usage in compute pass
1328         {
1329             // Create the bind group to use the texture as sampled
1330             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1331                 device, {{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float}});
1332             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view1}});
1333 
1334             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({bgl});
1335 
1336             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1337             encoder.CopyTextureToTexture(&srcView, &dstView, &copySize);
1338             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1339             pass.SetBindGroup(0, bg);
1340             pass.SetPipeline(cp);
1341             pass.Dispatch(1);
1342             pass.EndPass();
1343             encoder.Finish();
1344         }
1345     }
1346 
1347     // Test that all consecutive SetBindGroup()s take effect even though some bind groups are not
1348     // used because they are overwritten by a consecutive call.
TEST_F(ResourceUsageTrackingTest,TextureWithMultipleSetBindGroupsOnSameIndex)1349     TEST_F(ResourceUsageTrackingTest, TextureWithMultipleSetBindGroupsOnSameIndex) {
1350         // Test render pass
1351         {
1352             // Create textures that will be used as both a sampled texture and a render target
1353             wgpu::Texture texture0 = CreateTexture(wgpu::TextureUsage::TextureBinding |
1354                                                    wgpu::TextureUsage::RenderAttachment);
1355             wgpu::TextureView view0 = texture0.CreateView();
1356             wgpu::Texture texture1 = CreateTexture(wgpu::TextureUsage::TextureBinding |
1357                                                    wgpu::TextureUsage::RenderAttachment);
1358             wgpu::TextureView view1 = texture1.CreateView();
1359 
1360             // Create the bind group to use the texture as sampled
1361             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1362                 device, {{0, wgpu::ShaderStage::Vertex, wgpu::TextureSampleType::Float}});
1363             wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl, {{0, view0}});
1364             wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl, {{0, view1}});
1365 
1366             // Create the render pass that will use the texture as an render attachment
1367             utils::ComboRenderPassDescriptor renderPass({view0});
1368 
1369             // Set bind group on the same index twice. The second one overwrites the first one.
1370             // No texture is used as both sampled and render attachment in the same pass. But the
1371             // overwritten texture still take effect during resource tracking.
1372             {
1373                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1374                 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
1375                 pass.SetBindGroup(0, bg0);
1376                 pass.SetBindGroup(0, bg1);
1377                 pass.EndPass();
1378                 ASSERT_DEVICE_ERROR(encoder.Finish());
1379             }
1380 
1381             // Set bind group on the same index twice. The second one overwrites the first one.
1382             // texture0 is used as both sampled and render attachment in the same pass
1383             {
1384                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1385                 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
1386                 pass.SetBindGroup(0, bg1);
1387                 pass.SetBindGroup(0, bg0);
1388                 pass.EndPass();
1389                 ASSERT_DEVICE_ERROR(encoder.Finish());
1390             }
1391         }
1392 
1393         // Test compute pass
1394         {
1395             // Create a texture that will be used both as storage texture
1396             wgpu::Texture texture0 = CreateTexture(wgpu::TextureUsage::TextureBinding |
1397                                                    wgpu::TextureUsage::StorageBinding);
1398             wgpu::TextureView view0 = texture0.CreateView();
1399             wgpu::Texture texture1 = CreateTexture(wgpu::TextureUsage::TextureBinding);
1400             wgpu::TextureView view1 = texture1.CreateView();
1401 
1402             // Create the bind group to use the texture as sampled and writeonly bindings
1403             wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout(
1404                 device,
1405                 {{0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
1406 
1407             wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout(
1408                 device, {{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float}});
1409 
1410             wgpu::BindGroup writeBG0 = utils::MakeBindGroup(device, writeBGL, {{0, view0}});
1411             wgpu::BindGroup readBG0 = utils::MakeBindGroup(device, readBGL, {{0, view0}});
1412             wgpu::BindGroup readBG1 = utils::MakeBindGroup(device, readBGL, {{0, view1}});
1413 
1414             // Create a no-op compute pipeline.
1415             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({writeBGL, readBGL});
1416 
1417             // Set bind group on the same index twice. The second one overwrites the first one.
1418             // No texture is used as both sampled and writeonly storage in the same dispatch so
1419             // there are no errors.
1420             {
1421                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1422                 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1423                 pass.SetBindGroup(0, writeBG0);
1424                 pass.SetBindGroup(1, readBG0);
1425                 pass.SetBindGroup(1, readBG1);
1426                 pass.SetPipeline(cp);
1427                 pass.Dispatch(1);
1428                 pass.EndPass();
1429                 encoder.Finish();
1430             }
1431 
1432             // Set bind group on the same index twice. The second one overwrites the first one.
1433             // texture0 is used as both writeonly and sampled storage in the same dispatch, which
1434             // is an error.
1435             {
1436                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1437                 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1438                 pass.SetBindGroup(0, writeBG0);
1439                 pass.SetBindGroup(1, readBG1);
1440                 pass.SetBindGroup(1, readBG0);
1441                 pass.SetPipeline(cp);
1442                 pass.Dispatch(1);
1443                 pass.EndPass();
1444                 ASSERT_DEVICE_ERROR(encoder.Finish());
1445             }
1446         }
1447     }
1448 
1449     // Test that it is invalid to have resource usage conflicts even when all bindings are not
1450     // visible to the programmable pass where it is used.
TEST_F(ResourceUsageTrackingTest,TextureUsageConflictBetweenInvisibleStagesInBindGroup)1451     TEST_F(ResourceUsageTrackingTest, TextureUsageConflictBetweenInvisibleStagesInBindGroup) {
1452         // Create texture and texture view
1453         wgpu::Texture texture =
1454             CreateTexture(wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::StorageBinding);
1455         wgpu::TextureView view = texture.CreateView();
1456 
1457         // Test render pass for bind group. The conflict of sampled storage and writeonly storage
1458         // usage doesn't reside in render related stages at all
1459         {
1460             // Create a bind group whose bindings are not visible in render pass
1461             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1462                 device,
1463                 {{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float},
1464                  {1, wgpu::ShaderStage::None, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
1465             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}, {1, view}});
1466 
1467             // These two bindings are invisible in render pass. But we still track these bindings.
1468             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1469             DummyRenderPass dummyRenderPass(device);
1470             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
1471             pass.SetBindGroup(0, bg);
1472             pass.EndPass();
1473             ASSERT_DEVICE_ERROR(encoder.Finish());
1474         }
1475 
1476         // Test compute pass for bind group. The conflict of sampled storage and writeonly storage
1477         // usage doesn't reside in compute related stage at all
1478         {
1479             // Create a bind group whose bindings are not visible in compute pass
1480             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1481                 device,
1482                 {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float},
1483                  {1, wgpu::ShaderStage::None, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
1484             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}, {1, view}});
1485 
1486             // Create a no-op compute pipeline.
1487             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({bgl});
1488 
1489             // These two bindings are invisible in compute pass. But we still track these bindings.
1490             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1491             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1492             pass.SetPipeline(cp);
1493             pass.SetBindGroup(0, bg);
1494             pass.Dispatch(1);
1495             pass.EndPass();
1496             ASSERT_DEVICE_ERROR(encoder.Finish());
1497         }
1498     }
1499 
1500     // Test that it is invalid to have resource usage conflicts even when one of the bindings is not
1501     // visible to the programmable pass where it is used.
TEST_F(ResourceUsageTrackingTest,TextureUsageConflictWithInvisibleStageInBindGroup)1502     TEST_F(ResourceUsageTrackingTest, TextureUsageConflictWithInvisibleStageInBindGroup) {
1503         // Create texture and texture view
1504         wgpu::Texture texture =
1505             CreateTexture(wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::StorageBinding |
1506                           wgpu::TextureUsage::RenderAttachment);
1507         wgpu::TextureView view = texture.CreateView();
1508 
1509         // Test render pass
1510         {
1511             // Create the render pass that will use the texture as an render attachment
1512             utils::ComboRenderPassDescriptor renderPass({view});
1513 
1514             // Create a bind group which use the texture as sampled storage in compute stage
1515             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1516                 device, {{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float}});
1517             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}});
1518 
1519             // Texture usage in compute stage in bind group conflicts with render target. And
1520             // binding for compute stage is not visible in render pass. But we still track this
1521             // binding.
1522             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1523             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
1524             pass.SetBindGroup(0, bg);
1525             pass.EndPass();
1526             ASSERT_DEVICE_ERROR(encoder.Finish());
1527         }
1528 
1529         // Test compute pass
1530         {
1531             // Create a bind group which contains both fragment and compute stages
1532             wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
1533                 device,
1534                 {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float},
1535                  {1, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
1536             wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}, {1, view}});
1537 
1538             // Create a no-op compute pipeline.
1539             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({bgl});
1540 
1541             // Texture usage in compute stage conflicts with texture usage in fragment stage. And
1542             // binding for fragment stage is not visible in compute pass. But we still track this
1543             // invisible binding.
1544             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1545             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1546             pass.SetPipeline(cp);
1547             pass.SetBindGroup(0, bg);
1548             pass.Dispatch(1);
1549             pass.EndPass();
1550             ASSERT_DEVICE_ERROR(encoder.Finish());
1551         }
1552     }
1553 
1554     // Test that it is invalid to have resource usage conflicts even when one of the bindings is not
1555     // used in the pipeline.
TEST_F(ResourceUsageTrackingTest,TextureUsageConflictWithUnusedPipelineBindings)1556     TEST_F(ResourceUsageTrackingTest, TextureUsageConflictWithUnusedPipelineBindings) {
1557         // Create texture and texture view
1558         wgpu::Texture texture =
1559             CreateTexture(wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::StorageBinding);
1560         wgpu::TextureView view = texture.CreateView();
1561 
1562         // Create bind groups.
1563         wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout(
1564             device, {{0, wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute,
1565                       wgpu::TextureSampleType::Float}});
1566         wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout(
1567             device, {{0, wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute,
1568                       wgpu::StorageTextureAccess::WriteOnly, kFormat}});
1569         wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, view}});
1570         wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}});
1571 
1572         // Test render pass
1573         {
1574             // Create a passthrough render pipeline with a sampled storage texture
1575             wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
1576                 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
1577                     return vec4<f32>();
1578                 })");
1579 
1580             wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
1581                 [[group(0), binding(0)]] var tex : texture_2d<f32>;
1582                 [[stage(fragment)]] fn main() {
1583                 })");
1584             utils::ComboRenderPipelineDescriptor pipelineDescriptor;
1585             pipelineDescriptor.vertex.module = vsModule;
1586             pipelineDescriptor.cFragment.module = fsModule;
1587             pipelineDescriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
1588             pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &readBGL);
1589             wgpu::RenderPipeline rp = device.CreateRenderPipeline(&pipelineDescriptor);
1590 
1591             // Texture binding in readBG conflicts with texture binding in writeBG. The binding
1592             // in writeBG is not used in pipeline. But we still track this binding.
1593             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1594             DummyRenderPass dummyRenderPass(device);
1595             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
1596             pass.SetBindGroup(0, readBG);
1597             pass.SetBindGroup(1, writeBG);
1598             pass.SetPipeline(rp);
1599             pass.Draw(3);
1600             pass.EndPass();
1601             ASSERT_DEVICE_ERROR(encoder.Finish());
1602         }
1603 
1604         // Test compute pass
1605         {
1606             wgpu::ComputePipeline cp = CreateNoOpComputePipeline({readBGL});
1607 
1608             // Texture binding in readBG conflicts with texture binding in writeBG. The binding
1609             // in writeBG is not used in pipeline's layout so it isn't an error.
1610             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1611             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1612             pass.SetBindGroup(0, readBG);
1613             pass.SetBindGroup(1, writeBG);
1614             pass.SetPipeline(cp);
1615             pass.Dispatch(1);
1616             pass.EndPass();
1617             encoder.Finish();
1618         }
1619     }
1620 
1621     // Test that using an indirect buffer is disallowed with a writable usage (like storage) but
1622     // allowed with a readable usage (like readonly storage).
TEST_F(ResourceUsageTrackingTest,IndirectBufferWithReadOrWriteStorage)1623     TEST_F(ResourceUsageTrackingTest, IndirectBufferWithReadOrWriteStorage) {
1624         wgpu::Buffer buffer =
1625             CreateBuffer(20, wgpu::BufferUsage::Indirect | wgpu::BufferUsage::Storage);
1626 
1627         wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout(
1628             device, {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage}});
1629         wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout(
1630             device, {{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage}});
1631 
1632         wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, buffer}});
1633         wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, buffer}});
1634 
1635         // Test pipelines
1636         wgpu::RenderPipeline rp = CreateNoOpRenderPipeline();
1637         wgpu::ComputePipeline readCp = CreateNoOpComputePipeline({readBGL});
1638         wgpu::ComputePipeline writeCp = CreateNoOpComputePipeline({writeBGL});
1639 
1640         // Test that indirect + readonly is allowed in the same render pass.
1641         {
1642             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1643             DummyRenderPass dummyRenderPass(device);
1644             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
1645             pass.SetPipeline(rp);
1646             pass.SetBindGroup(0, readBG);
1647             pass.DrawIndirect(buffer, 0);
1648             pass.EndPass();
1649             encoder.Finish();
1650         }
1651 
1652         // Test that indirect + writable is disallowed in the same render pass.
1653         {
1654             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1655             DummyRenderPass dummyRenderPass(device);
1656             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
1657             pass.SetPipeline(rp);
1658             pass.SetBindGroup(0, writeBG);
1659             pass.DrawIndirect(buffer, 0);
1660             pass.EndPass();
1661             ASSERT_DEVICE_ERROR(encoder.Finish());
1662         }
1663 
1664         // Test that indirect + readonly is allowed in the same dispatch
1665         {
1666             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1667             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1668             pass.SetPipeline(readCp);
1669             pass.SetBindGroup(0, readBG);
1670             pass.DispatchIndirect(buffer, 0);
1671             pass.EndPass();
1672             encoder.Finish();
1673         }
1674 
1675         // Test that indirect + writable is disallowed in the same dispatch
1676         {
1677             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1678             wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1679             pass.SetPipeline(writeCp);
1680             pass.SetBindGroup(0, writeBG);
1681             pass.DispatchIndirect(buffer, 0);
1682             pass.EndPass();
1683             ASSERT_DEVICE_ERROR(encoder.Finish());
1684         }
1685     }
1686 
1687     // TODO (yunchao.he@intel.com):
1688     //
1689     //	* Add tests for multiple encoders upon the same resource simultaneously. This situation fits
1690     //	some cases like VR, multi-threading, etc.
1691     //
1692     //	* Add tests for bundle
1693 
1694 }  // anonymous namespace
1695