• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <gmock/gmock.h>
16 
17 #include "dawn_native/CommandEncoder.h"
18 
19 #include "tests/unittests/validation/ValidationTest.h"
20 
21 #include "utils/WGPUHelpers.h"
22 
23 using ::testing::HasSubstr;
24 
25 class CommandBufferValidationTest : public ValidationTest {};
26 
27 // Test for an empty command buffer
TEST_F(CommandBufferValidationTest,Empty)28 TEST_F(CommandBufferValidationTest, Empty) {
29     device.CreateCommandEncoder().Finish();
30 }
31 
32 // Test that a command buffer cannot be ended mid render pass
TEST_F(CommandBufferValidationTest,EndedMidRenderPass)33 TEST_F(CommandBufferValidationTest, EndedMidRenderPass) {
34     DummyRenderPass dummyRenderPass(device);
35 
36     // Control case, command buffer ended after the pass is ended.
37     {
38         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
39         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
40         pass.EndPass();
41         encoder.Finish();
42     }
43 
44     // Error case, command buffer ended mid-pass.
45     {
46         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
47         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
48         ASSERT_DEVICE_ERROR(
49             encoder.Finish(),
50             HasSubstr("Command buffer recording ended before [RenderPassEncoder] was ended."));
51     }
52 
53     // Error case, command buffer ended mid-pass. Trying to use encoders after Finish
54     // should fail too.
55     {
56         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
57         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
58         ASSERT_DEVICE_ERROR(
59             encoder.Finish(),
60             HasSubstr("Command buffer recording ended before [RenderPassEncoder] was ended."));
61         ASSERT_DEVICE_ERROR(
62             pass.EndPass(),
63             HasSubstr("Recording in an error or already ended [RenderPassEncoder]."));
64     }
65 }
66 
67 // Test that a command buffer cannot be ended mid compute pass
TEST_F(CommandBufferValidationTest,EndedMidComputePass)68 TEST_F(CommandBufferValidationTest, EndedMidComputePass) {
69     // Control case, command buffer ended after the pass is ended.
70     {
71         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
72         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
73         pass.EndPass();
74         encoder.Finish();
75     }
76 
77     // Error case, command buffer ended mid-pass.
78     {
79         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
80         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
81         ASSERT_DEVICE_ERROR(
82             encoder.Finish(),
83             HasSubstr("Command buffer recording ended before [ComputePassEncoder] was ended."));
84     }
85 
86     // Error case, command buffer ended mid-pass. Trying to use encoders after Finish
87     // should fail too.
88     {
89         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
90         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
91         ASSERT_DEVICE_ERROR(
92             encoder.Finish(),
93             HasSubstr("Command buffer recording ended before [ComputePassEncoder] was ended."));
94         ASSERT_DEVICE_ERROR(
95             pass.EndPass(),
96             HasSubstr("Recording in an error or already ended [ComputePassEncoder]."));
97     }
98 }
99 
100 // Test that a render pass cannot be ended twice
TEST_F(CommandBufferValidationTest,RenderPassEndedTwice)101 TEST_F(CommandBufferValidationTest, RenderPassEndedTwice) {
102     DummyRenderPass dummyRenderPass(device);
103 
104     // Control case, pass is ended once
105     {
106         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
107         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
108         pass.EndPass();
109         encoder.Finish();
110     }
111 
112     // Error case, pass ended twice
113     {
114         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
115         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
116         pass.EndPass();
117         pass.EndPass();
118         ASSERT_DEVICE_ERROR(
119             encoder.Finish(),
120             HasSubstr("Recording in an error or already ended [RenderPassEncoder]."));
121     }
122 }
123 
124 // Test that a compute pass cannot be ended twice
TEST_F(CommandBufferValidationTest,ComputePassEndedTwice)125 TEST_F(CommandBufferValidationTest, ComputePassEndedTwice) {
126     // Control case, pass is ended once.
127     {
128         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
129         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
130         pass.EndPass();
131         encoder.Finish();
132     }
133 
134     // Error case, pass ended twice
135     {
136         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
137         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
138         pass.EndPass();
139         pass.EndPass();
140         ASSERT_DEVICE_ERROR(
141             encoder.Finish(),
142             HasSubstr("Recording in an error or already ended [ComputePassEncoder]."));
143     }
144 }
145 
146 // Test that beginning a compute pass before ending the previous pass causes an error.
TEST_F(CommandBufferValidationTest,BeginComputePassBeforeEndPreviousPass)147 TEST_F(CommandBufferValidationTest, BeginComputePassBeforeEndPreviousPass) {
148     DummyRenderPass dummyRenderPass(device);
149 
150     // Beginning a compute pass before ending a render pass causes an error.
151     {
152         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
153         wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&dummyRenderPass);
154         wgpu::ComputePassEncoder computePass = encoder.BeginComputePass();
155         computePass.EndPass();
156         renderPass.EndPass();
157         ASSERT_DEVICE_ERROR(encoder.Finish());
158     }
159 
160     // Beginning a compute pass before ending a compute pass causes an error.
161     {
162         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
163         wgpu::ComputePassEncoder computePass1 = encoder.BeginComputePass();
164         wgpu::ComputePassEncoder computePass2 = encoder.BeginComputePass();
165         computePass2.EndPass();
166         computePass1.EndPass();
167         ASSERT_DEVICE_ERROR(encoder.Finish());
168     }
169 }
170 
171 // Test that beginning a render pass before ending the previous pass causes an error.
TEST_F(CommandBufferValidationTest,BeginRenderPassBeforeEndPreviousPass)172 TEST_F(CommandBufferValidationTest, BeginRenderPassBeforeEndPreviousPass) {
173     DummyRenderPass dummyRenderPass(device);
174 
175     // Beginning a render pass before ending the render pass causes an error.
176     {
177         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
178         wgpu::RenderPassEncoder renderPass1 = encoder.BeginRenderPass(&dummyRenderPass);
179         wgpu::RenderPassEncoder renderPass2 = encoder.BeginRenderPass(&dummyRenderPass);
180         renderPass2.EndPass();
181         renderPass1.EndPass();
182         ASSERT_DEVICE_ERROR(encoder.Finish());
183     }
184 
185     // Beginning a compute pass before ending a compute pass causes an error.
186     {
187         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
188         wgpu::ComputePassEncoder computePass = encoder.BeginComputePass();
189         wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&dummyRenderPass);
190         renderPass.EndPass();
191         computePass.EndPass();
192         ASSERT_DEVICE_ERROR(encoder.Finish());
193     }
194 }
195 
196 // Test that encoding command after a successful finish produces an error
TEST_F(CommandBufferValidationTest,CallsAfterASuccessfulFinish)197 TEST_F(CommandBufferValidationTest, CallsAfterASuccessfulFinish) {
198     // A buffer that can be used in CopyBufferToBuffer
199     wgpu::BufferDescriptor copyBufferDesc;
200     copyBufferDesc.size = 16;
201     copyBufferDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
202     wgpu::Buffer copyBuffer = device.CreateBuffer(&copyBufferDesc);
203 
204     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
205     encoder.Finish();
206 
207     ASSERT_DEVICE_ERROR(encoder.CopyBufferToBuffer(copyBuffer, 0, copyBuffer, 0, 0));
208 }
209 
210 // Test that encoding command after a failed finish produces an error
TEST_F(CommandBufferValidationTest,CallsAfterAFailedFinish)211 TEST_F(CommandBufferValidationTest, CallsAfterAFailedFinish) {
212     // A buffer that can be used in CopyBufferToBuffer
213     wgpu::BufferDescriptor copyBufferDesc;
214     copyBufferDesc.size = 16;
215     copyBufferDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
216     wgpu::Buffer copyBuffer = device.CreateBuffer(&copyBufferDesc);
217 
218     // A buffer that can't be used in CopyBufferToBuffer
219     wgpu::BufferDescriptor bufferDesc;
220     bufferDesc.size = 16;
221     bufferDesc.usage = wgpu::BufferUsage::Uniform;
222     wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc);
223 
224     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
225     encoder.CopyBufferToBuffer(buffer, 0, buffer, 0, 0);
226     ASSERT_DEVICE_ERROR(encoder.Finish());
227 
228     ASSERT_DEVICE_ERROR(encoder.CopyBufferToBuffer(copyBuffer, 0, copyBuffer, 0, 0));
229 }
230 
231 // Test that passes which are de-referenced prior to ending still allow the correct errors to be
232 // produced.
TEST_F(CommandBufferValidationTest,PassDereferenced)233 TEST_F(CommandBufferValidationTest, PassDereferenced) {
234     DummyRenderPass dummyRenderPass(device);
235 
236     // Control case, command buffer ended after the pass is ended.
237     {
238         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
239         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
240         pass.EndPass();
241         encoder.Finish();
242     }
243 
244     // Error case, no reference is kept to a render pass.
245     {
246         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
247         encoder.BeginRenderPass(&dummyRenderPass);
248         ASSERT_DEVICE_ERROR(
249             encoder.Finish(),
250             HasSubstr("Command buffer recording ended before [RenderPassEncoder] was ended."));
251     }
252 
253     // Error case, no reference is kept to a compute pass.
254     {
255         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
256         encoder.BeginComputePass();
257         ASSERT_DEVICE_ERROR(
258             encoder.Finish(),
259             HasSubstr("Command buffer recording ended before [ComputePassEncoder] was ended."));
260     }
261 
262     // Error case, beginning a new pass after failing to end a de-referenced pass.
263     {
264         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
265         encoder.BeginRenderPass(&dummyRenderPass);
266         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
267         pass.EndPass();
268         ASSERT_DEVICE_ERROR(
269             encoder.Finish(),
270             HasSubstr("Command buffer recording ended before [RenderPassEncoder] was ended."));
271     }
272 
273     // Error case, deleting the pass after finishing the command encoder shouldn't generate an
274     // uncaptured error.
275     {
276         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
277         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
278         ASSERT_DEVICE_ERROR(
279             encoder.Finish(),
280             HasSubstr("Command buffer recording ended before [ComputePassEncoder] was ended."));
281 
282         pass = nullptr;
283     }
284 
285     // Valid case, command encoder is never finished so the de-referenced pass shouldn't
286     // generate an uncaptured error.
287     {
288         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
289         encoder.BeginComputePass();
290     }
291 }
292 
293 // Test that calling inject validation error produces an error.
TEST_F(CommandBufferValidationTest,InjectValidationError)294 TEST_F(CommandBufferValidationTest, InjectValidationError) {
295     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
296     encoder.InjectValidationError("my error");
297     ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("my error"));
298 }
299 
TEST_F(CommandBufferValidationTest,DestroyEncoder)300 TEST_F(CommandBufferValidationTest, DestroyEncoder) {
301     // Skip these tests if we are using wire because the destroy functionality is not exposed
302     // and needs to use a cast to call manually. We cannot test this in the wire case since the
303     // only way to trigger the destroy call is by losing all references which means we cannot
304     // call finish.
305     DAWN_SKIP_TEST_IF(UsesWire());
306     DummyRenderPass dummyRenderPass(device);
307 
308     // Control case, command buffer ended after the pass is ended.
309     {
310         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
311         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
312         pass.EndPass();
313         encoder.Finish();
314     }
315 
316     // Destroyed encoder with encoded commands should emit error on finish.
317     {
318         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
319         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
320         pass.EndPass();
321         dawn_native::FromAPI(encoder.Get())->Destroy();
322         ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("Destroyed encoder cannot be finished."));
323     }
324 
325     // Destroyed encoder with encoded commands shouldn't emit an error if never finished.
326     {
327         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
328         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
329         pass.EndPass();
330         dawn_native::FromAPI(encoder.Get())->Destroy();
331     }
332 
333     // Destroyed encoder should allow encoding, and emit error on finish.
334     {
335         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
336         dawn_native::FromAPI(encoder.Get())->Destroy();
337         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
338         pass.EndPass();
339         ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("Destroyed encoder cannot be finished."));
340     }
341 
342     // Destroyed encoder should allow encoding and shouldn't emit an error if never finished.
343     {
344         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
345         dawn_native::FromAPI(encoder.Get())->Destroy();
346         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
347         pass.EndPass();
348     }
349 
350     // Destroying a finished encoder should not emit any errors.
351     {
352         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
353         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass);
354         pass.EndPass();
355         encoder.Finish();
356         dawn_native::FromAPI(encoder.Get())->Destroy();
357     }
358 
359     // Destroying an encoder twice should not emit any errors.
360     {
361         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
362         dawn_native::FromAPI(encoder.Get())->Destroy();
363         dawn_native::FromAPI(encoder.Get())->Destroy();
364     }
365 
366     // Destroying an encoder twice and then calling finish should fail.
367     {
368         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
369         dawn_native::FromAPI(encoder.Get())->Destroy();
370         dawn_native::FromAPI(encoder.Get())->Destroy();
371         ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("Destroyed encoder cannot be finished."));
372     }
373 }
374