Skip to content

Commit 44dac85

Browse files
Additional test cases for encoder states (#4401)
1 parent ca68f03 commit 44dac85

2 files changed

Lines changed: 107 additions & 15 deletions

File tree

src/webgpu/api/validation/encoding/encoder_open_state.spec.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -275,14 +275,16 @@ g.test('render_pass_commands')
275275
Test that functions of GPURenderPassEncoder generate a validation error if the encoder or the
276276
pass is already finished.
277277
278-
- TODO: Consider testing: nothing before command, end before command, end+finish before command.
278+
TODO(https://github.com/gpuweb/gpuweb/issues/5207): Resolve whether the error condition
279+
\`finishBeforeCommand !== 'no'\` is correct, or should be changed to
280+
\`finishBeforeCommand === 'encoder'\`.
279281
`
280282
)
281283
.params(u =>
282284
u
283285
.combine('command', kRenderPassEncoderCommands)
284286
.beginSubcases()
285-
.combine('finishBeforeCommand', [false, true])
287+
.combine('finishBeforeCommand', ['no', 'pass', 'encoder'])
286288
)
287289
.fn(t => {
288290
const { command, finishBeforeCommand } = t.params;
@@ -305,8 +307,10 @@ g.test('render_pass_commands')
305307

306308
const bindGroup = t.createBindGroupForTest();
307309

308-
if (finishBeforeCommand) {
310+
if (finishBeforeCommand !== 'no') {
309311
renderPass.end();
312+
}
313+
if (finishBeforeCommand === 'encoder') {
310314
encoder.finish();
311315
}
312316

@@ -404,23 +408,23 @@ g.test('render_pass_commands')
404408
break;
405409
case 'pushDebugGroup':
406410
{
407-
encoder.pushDebugGroup('group');
411+
renderPass.pushDebugGroup('group');
408412
}
409413
break;
410414
case 'popDebugGroup':
411415
{
412-
encoder.popDebugGroup();
416+
renderPass.popDebugGroup();
413417
}
414418
break;
415419
case 'insertDebugMarker':
416420
{
417-
encoder.insertDebugMarker('marker');
421+
renderPass.insertDebugMarker('marker');
418422
}
419423
break;
420424
default:
421425
unreachable();
422426
}
423-
}, finishBeforeCommand);
427+
}, finishBeforeCommand !== 'no');
424428
});
425429

426430
g.test('render_bundle_commands')
@@ -525,14 +529,16 @@ g.test('compute_pass_commands')
525529
Test that functions of GPUComputePassEncoder generate a validation error if the encoder or the
526530
pass is already finished.
527531
528-
- TODO: Consider testing: nothing before command, end before command, end+finish before command.
532+
TODO(https://github.com/gpuweb/gpuweb/issues/5207): Resolve whether the error condition
533+
\`finishBeforeCommand !== 'no'\` is correct, or should be changed to
534+
\`finishBeforeCommand === 'encoder'\`.
529535
`
530536
)
531537
.params(u =>
532538
u
533539
.combine('command', kComputePassEncoderCommands)
534540
.beginSubcases()
535-
.combine('finishBeforeCommand', [false, true])
541+
.combine('finishBeforeCommand', ['no', 'pass', 'encoder'])
536542
)
537543
.fn(t => {
538544
const { command, finishBeforeCommand } = t.params;
@@ -549,8 +555,10 @@ g.test('compute_pass_commands')
549555

550556
const bindGroup = t.createBindGroupForTest();
551557

552-
if (finishBeforeCommand) {
558+
if (finishBeforeCommand !== 'no') {
553559
computePass.end();
560+
}
561+
if (finishBeforeCommand === 'encoder') {
554562
encoder.finish();
555563
}
556564

@@ -594,5 +602,5 @@ g.test('compute_pass_commands')
594602
default:
595603
unreachable();
596604
}
597-
}, finishBeforeCommand);
605+
}, finishBeforeCommand !== 'no');
598606
});

src/webgpu/api/validation/encoding/encoder_state.spec.ts

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ g.test('pass_end_invalid_order')
5151
`
5252
Test that beginning a {compute,render} pass before ending the previous {compute,render} pass
5353
causes an error.
54+
55+
TODO(https://github.com/gpuweb/gpuweb/issues/5207): Resolve whether a validation error
56+
should be raised immediately if '!firstPassEnd && endPasses = [1, 0]'.
5457
`
5558
)
5659
.params(u =>
@@ -95,7 +98,13 @@ g.test('call_after_successful_finish')
9598
.desc(`Test that encoding command after a successful finish generates a validation error.`)
9699
.params(u =>
97100
u
98-
.combine('callCmd', ['beginComputePass', 'beginRenderPass', 'insertDebugMarker'])
101+
.combine('callCmd', [
102+
'beginComputePass',
103+
'beginRenderPass',
104+
'finishAndSubmitFirst',
105+
'finishAndSubmitSecond',
106+
'insertDebugMarker',
107+
])
99108
.beginSubcases()
100109
.combine('prePassType', ['compute', 'render', 'no-op'])
101110
.combine('IsEncoderFinished', [false, true])
@@ -112,8 +121,9 @@ g.test('call_after_successful_finish')
112121
pass.end();
113122
}
114123

124+
let buffer;
115125
if (IsEncoderFinished) {
116-
encoder.finish();
126+
buffer = encoder.finish();
117127
}
118128

119129
switch (callCmd) {
@@ -126,6 +136,9 @@ g.test('call_after_successful_finish')
126136
t.expectValidationError(() => {
127137
pass.end();
128138
}, IsEncoderFinished);
139+
if (buffer) {
140+
t.device.queue.submit([buffer]);
141+
}
129142
}
130143
break;
131144
case 'beginRenderPass':
@@ -137,24 +150,49 @@ g.test('call_after_successful_finish')
137150
t.expectValidationError(() => {
138151
pass.end();
139152
}, IsEncoderFinished);
153+
if (buffer) {
154+
t.device.queue.submit([buffer]);
155+
}
156+
}
157+
break;
158+
case 'finishAndSubmitFirst':
159+
t.expectValidationError(() => {
160+
encoder.finish();
161+
}, IsEncoderFinished);
162+
if (buffer) {
163+
t.device.queue.submit([buffer]);
164+
}
165+
break;
166+
case 'finishAndSubmitSecond':
167+
{
168+
let secondBuffer: GPUCommandBuffer;
169+
t.expectValidationError(() => {
170+
secondBuffer = encoder.finish();
171+
}, IsEncoderFinished);
172+
t.expectValidationError(() => {
173+
t.device.queue.submit([secondBuffer]);
174+
}, IsEncoderFinished);
140175
}
141176
break;
142177
case 'insertDebugMarker':
143178
t.expectValidationError(() => {
144179
encoder.insertDebugMarker('');
145180
}, IsEncoderFinished);
181+
if (buffer) {
182+
t.device.queue.submit([buffer]);
183+
}
146184
break;
147185
}
148186

149-
if (!IsEncoderFinished) {
187+
if (!IsEncoderFinished && !callCmd.startsWith('finish')) {
150188
encoder.finish();
151189
}
152190
});
153191

154192
g.test('pass_end_none')
155193
.desc(
156194
`
157-
Test that ending a {compute,render} pass without ending the passes generates a validation error.
195+
Test that finishing an encoder without ending a child {compute,render} pass generates a validation error.
158196
`
159197
)
160198
.paramsSubcasesOnly(u => u.combine('passType', ['compute', 'render']).combine('endCount', [0, 1]))
@@ -247,3 +285,49 @@ g.test('pass_end_twice,render_pass_invalid')
247285
encoder.finish();
248286
});
249287
});
288+
289+
g.test('pass_begin_invalid_encoder')
290+
.desc(
291+
`
292+
Test that {compute,render} passes can still be opened on an invalid encoder.
293+
`
294+
)
295+
.params(u =>
296+
u
297+
.combine('pass0Type', ['compute', 'render'])
298+
.combine('pass1Type', ['compute', 'render'])
299+
.beginSubcases()
300+
.combine('firstPassInvalid', [false, true])
301+
)
302+
.fn(t => {
303+
const { pass0Type, pass1Type, firstPassInvalid } = t.params;
304+
305+
const view = t.createAttachmentTextureView();
306+
307+
const encoder = t.device.createCommandEncoder();
308+
309+
let firstPass;
310+
if (pass0Type === 'compute') {
311+
firstPass = encoder.beginComputePass();
312+
} else {
313+
firstPass = t.beginRenderPass(encoder, view);
314+
}
315+
316+
if (firstPassInvalid) {
317+
// Popping an empty debug group stack invalidates the pass.
318+
firstPass.popDebugGroup();
319+
}
320+
321+
// Ending an invalid pass invalidates the encoder
322+
firstPass.end();
323+
324+
// Passes can still be opened on an invalid encoder
325+
const secondPass =
326+
pass1Type === 'compute' ? encoder.beginComputePass() : t.beginRenderPass(encoder, view);
327+
328+
secondPass.end();
329+
330+
t.expectValidationError(() => {
331+
encoder.finish();
332+
}, firstPassInvalid);
333+
});

0 commit comments

Comments
 (0)