export const description = ` API validation tests for clearBuffer. `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { kBufferUsages } from '../../../../capability_info.js'; import { kResourceStates, AllFeaturesMaxLimitsGPUTest } from '../../../../gpu_test.js'; import { kMaxSafeMultipleOf8 } from '../../../../util/math.js'; import * as vtu from '../../validation_test_utils.js'; class F extends AllFeaturesMaxLimitsGPUTest { testClearBuffer(options: { buffer: GPUBuffer; offset: number | undefined; size: number | undefined; isSuccess: boolean; }): void { const { buffer, offset, size, isSuccess } = options; const commandEncoder = this.device.createCommandEncoder(); commandEncoder.clearBuffer(buffer, offset, size); this.expectValidationError(() => { commandEncoder.finish(); }, !isSuccess); } } export const g = makeTestGroup(F); g.test('buffer_state') .desc(`Test that clearing an invalid or destroyed buffer fails.`) .params(u => u.combine('bufferState', kResourceStates)) .fn(t => { const { bufferState } = t.params; const buffer = vtu.createBufferWithState(t, bufferState, { size: 8, usage: GPUBufferUsage.COPY_DST, }); const commandEncoder = t.device.createCommandEncoder(); commandEncoder.clearBuffer(buffer, 0, 8); if (bufferState === 'invalid') { t.expectValidationError(() => { commandEncoder.finish(); }); } else { const cmd = commandEncoder.finish(); t.expectValidationError(() => { t.device.queue.submit([cmd]); }, bufferState === 'destroyed'); } }); g.test('buffer,device_mismatch') .desc(`Tests clearBuffer cannot be called with buffer created from another device.`) .paramsSubcasesOnly(u => u.combine('mismatched', [true, false])) .beforeAllSubcases(t => t.usesMismatchedDevice()) .fn(t => { const { mismatched } = t.params; const sourceDevice = mismatched ? t.mismatchedDevice : t.device; const size = 8; const buffer = t.trackForCleanup( sourceDevice.createBuffer({ size, usage: GPUBufferUsage.COPY_DST, }) ); t.testClearBuffer({ buffer, offset: 0, size, isSuccess: !mismatched, }); }); g.test('default_args') .desc(`Test that calling clearBuffer with a default offset and size is valid.`) .paramsSubcasesOnly([ { offset: undefined, size: undefined }, { offset: 4, size: undefined }, { offset: undefined, size: 8 }, ] as const) .fn(t => { const { offset, size } = t.params; const buffer = t.createBufferTracked({ size: 16, usage: GPUBufferUsage.COPY_DST, }); t.testClearBuffer({ buffer, offset, size, isSuccess: true, }); }); g.test('buffer_usage') .desc(`Test that only buffers with COPY_DST usage are valid to use with copyBuffers.`) .paramsSubcasesOnly(u => u // .combine('usage', kBufferUsages) ) .fn(t => { const { usage } = t.params; const buffer = t.createBufferTracked({ size: 16, usage, }); t.testClearBuffer({ buffer, offset: 0, size: 16, isSuccess: usage === GPUBufferUsage.COPY_DST, }); }); g.test('size_alignment') .desc( ` Test that the clear size must be 4 byte aligned. - Test size is not a multiple of 4. - Test size is 0. - Test size overflows the buffer size. - Test size is omitted. ` ) .paramsSubcasesOnly([ { size: 0, _isSuccess: true }, { size: 2, _isSuccess: false }, { size: 4, _isSuccess: true }, { size: 5, _isSuccess: false }, { size: 8, _isSuccess: true }, { size: 20, _isSuccess: false }, { size: undefined, _isSuccess: true }, ] as const) .fn(t => { const { size, _isSuccess: isSuccess } = t.params; const buffer = t.createBufferTracked({ size: 16, usage: GPUBufferUsage.COPY_DST, }); t.testClearBuffer({ buffer, offset: 0, size, isSuccess, }); }); g.test('offset_alignment') .desc( ` Test that the clear offsets must be 4 byte aligned. - Test offset is not a multiple of 4. - Test offset is larger than the buffer size. - Test offset is omitted. ` ) .paramsSubcasesOnly([ { offset: 0, _isSuccess: true }, { offset: 2, _isSuccess: false }, { offset: 4, _isSuccess: true }, { offset: 5, _isSuccess: false }, { offset: 8, _isSuccess: true }, { offset: 20, _isSuccess: false }, { offset: undefined, _isSuccess: true }, ] as const) .fn(t => { const { offset, _isSuccess: isSuccess } = t.params; const buffer = t.createBufferTracked({ size: 16, usage: GPUBufferUsage.COPY_DST, }); t.testClearBuffer({ buffer, offset, size: 8, isSuccess, }); }); g.test('overflow') .desc(`Test that clears which may cause arithmetic overflows are invalid.`) .paramsSubcasesOnly([ { offset: 0, size: kMaxSafeMultipleOf8 }, { offset: 16, size: kMaxSafeMultipleOf8 }, { offset: kMaxSafeMultipleOf8, size: 16 }, { offset: kMaxSafeMultipleOf8, size: kMaxSafeMultipleOf8 }, ] as const) .fn(t => { const { offset, size } = t.params; const buffer = t.createBufferTracked({ size: 16, usage: GPUBufferUsage.COPY_DST, }); t.testClearBuffer({ buffer, offset, size, isSuccess: false, }); }); g.test('out_of_bounds') .desc(`Test that clears which exceed the buffer bounds are invalid.`) .paramsSubcasesOnly([ { offset: 0, size: 32, _isSuccess: true }, { offset: 0, size: 36 }, { offset: 32, size: 0, _isSuccess: true }, { offset: 32, size: 4 }, { offset: 36, size: 4 }, { offset: 36, size: 0 }, { offset: 20, size: 16 }, { offset: 20, size: 12, _isSuccess: true }, ] as const) .fn(t => { const { offset, size, _isSuccess = false } = t.params; const buffer = t.createBufferTracked({ size: 32, usage: GPUBufferUsage.COPY_DST, }); t.testClearBuffer({ buffer, offset, size, isSuccess: _isSuccess, }); });