| export const description = ` |
| Tests submit validation. |
| |
| Note: destroyed buffer/texture/querySet are tested in destroyed/. (unless it gets moved here) |
| Note: buffer map state is tested in ./buffer_mapped.spec.ts. |
| `; |
| |
| import { makeTestGroup } from '../../../../common/framework/test_group.js'; |
| import { AllFeaturesMaxLimitsGPUTest } from '../../../gpu_test.js'; |
| |
| interface CommandBufferOptions { |
| device?: GPUDevice; |
| valid?: boolean; |
| } |
| |
| class F extends AllFeaturesMaxLimitsGPUTest { |
| createCommandBuffer(options: CommandBufferOptions = {}): GPUCommandBuffer { |
| const device = options.device ?? this.device; |
| |
| let cb!: GPUCommandBuffer; |
| |
| this.expectValidationError(() => { |
| const encoder = device.createCommandEncoder(); |
| if (options.valid === false) { |
| // Popping a debug group when none are pushed results in an invalid command buffer. |
| encoder.popDebugGroup(); |
| } |
| cb = encoder.finish(); |
| }, options.valid === false); |
| |
| return cb; |
| } |
| } |
| |
| export const g = makeTestGroup(F); |
| |
| g.test('command_buffer,device_mismatch') |
| .desc( |
| ` |
| Tests submit cannot be called with command buffers created from another device |
| Test with two command buffers to make sure all command buffers can be validated: |
| - cb0 and cb1 from same device |
| - cb0 and cb1 from different device |
| ` |
| ) |
| .paramsSubcasesOnly([ |
| { cb0Mismatched: false, cb1Mismatched: false }, // control case |
| { cb0Mismatched: true, cb1Mismatched: false }, |
| { cb0Mismatched: false, cb1Mismatched: true }, |
| ]) |
| .beforeAllSubcases(t => t.usesMismatchedDevice()) |
| .fn(t => { |
| const { cb0Mismatched, cb1Mismatched } = t.params; |
| const mismatched = cb0Mismatched || cb1Mismatched; |
| |
| const cb0 = t.createCommandBuffer({ device: cb0Mismatched ? t.mismatchedDevice : t.device }); |
| const cb1 = t.createCommandBuffer({ device: cb1Mismatched ? t.mismatchedDevice : t.device }); |
| |
| t.expectValidationError(() => { |
| t.device.queue.submit([cb0, cb1]); |
| }, mismatched); |
| }); |
| |
| g.test('command_buffer,duplicate_buffers') |
| .desc( |
| ` |
| Tests submit cannot be called with the same command buffer listed multiple times: |
| ` |
| ) |
| .fn(t => { |
| const cb = t.createCommandBuffer(); |
| |
| t.expectValidationError(() => { |
| t.device.queue.submit([cb, cb]); |
| }, true); |
| }); |
| |
| g.test('command_buffer,submit_invalidates') |
| .desc( |
| ` |
| Tests that calling submit invalidates the command buffers passed to it: |
| ` |
| ) |
| .fn(t => { |
| const cb = t.createCommandBuffer(); |
| |
| // Initial submit of a valid command buffer should pass. |
| t.device.queue.submit([cb]); |
| |
| // Subsequent submits of the same command buffer should fail. |
| t.expectValidationError(() => { |
| t.device.queue.submit([cb]); |
| }); |
| }); |
| |
| g.test('command_buffer,invalid_submit_invalidates') |
| .desc( |
| ` |
| Tests that calling submit invalidates all command buffers passed to it, even |
| if they're part of an invalid submit. |
| ` |
| ) |
| .fn(t => { |
| const cb1 = t.createCommandBuffer(); |
| const cb1_invalid = t.createCommandBuffer({ valid: false }); |
| |
| // Submit should fail because on of the command buffers is invalid |
| t.expectValidationError(() => { |
| t.device.queue.submit([cb1, cb1_invalid]); |
| }); |
| |
| // Subsequent submits of the previously valid command buffer should fail. |
| t.expectValidationError(() => { |
| t.device.queue.submit([cb1]); |
| }); |
| |
| // The order of the invalid and valid command buffers in the submit array should not matter. |
| const cb2 = t.createCommandBuffer(); |
| const cb2_invalid = t.createCommandBuffer({ valid: false }); |
| |
| t.expectValidationError(() => { |
| t.device.queue.submit([cb2_invalid, cb2]); |
| }); |
| t.expectValidationError(() => { |
| t.device.queue.submit([cb2]); |
| }); |
| }); |