blob: b0a9c84ff37b020e8557746a8052093c64af5626 [file] [log] [blame]
export const description = `
copyBufferToBuffer tests.
Test Plan:
* Buffer is valid/invalid
- the source buffer is invalid
- the destination buffer is invalid
* Buffer usages
- the source buffer is created without GPUBufferUsage::COPY_SRC
- the destination buffer is created without GPUBufferUsage::COPY_DEST
* CopySize
- copySize is not a multiple of 4
- copySize is 0
* copy offsets
- sourceOffset is not a multiple of 4
- destinationOffset is not a multiple of 4
* Arithmetic overflow
- (sourceOffset + copySize) is overflow
- (destinationOffset + copySize) is overflow
* Out of bounds
- (sourceOffset + copySize) > size of source buffer
- (destinationOffset + copySize) > size of destination buffer
* Source buffer and destination buffer are the same buffer
`;
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 {
testCopyBufferToBuffer(options: {
srcBuffer: GPUBuffer;
srcOffset: number;
dstBuffer: GPUBuffer;
dstOffset: number;
copySize: number;
expectation: 'Success' | 'FinishError' | 'SubmitError';
}): void {
const { srcBuffer, srcOffset, dstBuffer, dstOffset, copySize, expectation } = options;
const commandEncoder = this.device.createCommandEncoder();
commandEncoder.copyBufferToBuffer(srcBuffer, srcOffset, dstBuffer, dstOffset, copySize);
if (expectation === 'FinishError') {
this.expectValidationError(() => {
commandEncoder.finish();
});
} else {
const cmd = commandEncoder.finish();
this.expectValidationError(() => {
this.device.queue.submit([cmd]);
}, expectation === 'SubmitError');
}
}
}
export const g = makeTestGroup(F);
g.test('buffer_state')
.params(u =>
u //
.combine('srcBufferState', kResourceStates)
.combine('dstBufferState', kResourceStates)
)
.fn(t => {
const { srcBufferState, dstBufferState } = t.params;
const srcBuffer = vtu.createBufferWithState(t, srcBufferState, {
size: 16,
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
});
const dstBuffer = vtu.createBufferWithState(t, dstBufferState, {
size: 16,
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
});
const shouldFinishError = srcBufferState === 'invalid' || dstBufferState === 'invalid';
const shouldSubmitSuccess = srcBufferState === 'valid' && dstBufferState === 'valid';
const expectation = shouldSubmitSuccess
? 'Success'
: shouldFinishError
? 'FinishError'
: 'SubmitError';
t.testCopyBufferToBuffer({
srcBuffer,
srcOffset: 0,
dstBuffer,
dstOffset: 0,
copySize: 8,
expectation,
});
});
g.test('buffer,device_mismatch')
.desc(
'Tests copyBufferToBuffer cannot be called with src buffer or dst buffer created from another device'
)
.paramsSubcasesOnly([
{ srcMismatched: false, dstMismatched: false }, // control case
{ srcMismatched: true, dstMismatched: false },
{ srcMismatched: false, dstMismatched: true },
] as const)
.beforeAllSubcases(t => t.usesMismatchedDevice())
.fn(t => {
const { srcMismatched, dstMismatched } = t.params;
const srcBufferDevice = srcMismatched ? t.mismatchedDevice : t.device;
const srcBuffer = t.trackForCleanup(
srcBufferDevice.createBuffer({
size: 16,
usage: GPUBufferUsage.COPY_SRC,
})
);
const dstBufferDevice = dstMismatched ? t.mismatchedDevice : t.device;
const dstBuffer = t.trackForCleanup(
dstBufferDevice.createBuffer({
size: 16,
usage: GPUBufferUsage.COPY_DST,
})
);
t.testCopyBufferToBuffer({
srcBuffer,
srcOffset: 0,
dstBuffer,
dstOffset: 0,
copySize: 8,
expectation: srcMismatched || dstMismatched ? 'FinishError' : 'Success',
});
});
g.test('buffer_usage')
.paramsSubcasesOnly(u =>
u //
.combine('srcUsage', kBufferUsages)
.combine('dstUsage', kBufferUsages)
)
.fn(t => {
const { srcUsage, dstUsage } = t.params;
const srcBuffer = t.createBufferTracked({
size: 16,
usage: srcUsage,
});
const dstBuffer = t.createBufferTracked({
size: 16,
usage: dstUsage,
});
const isSuccess = srcUsage === GPUBufferUsage.COPY_SRC && dstUsage === GPUBufferUsage.COPY_DST;
const expectation = isSuccess ? 'Success' : 'FinishError';
t.testCopyBufferToBuffer({
srcBuffer,
srcOffset: 0,
dstBuffer,
dstOffset: 0,
copySize: 8,
expectation,
});
});
g.test('copy_size_alignment')
.paramsSubcasesOnly([
{ copySize: 0, _isSuccess: true },
{ copySize: 2, _isSuccess: false },
{ copySize: 4, _isSuccess: true },
{ copySize: 5, _isSuccess: false },
{ copySize: 8, _isSuccess: true },
] as const)
.fn(t => {
const { copySize, _isSuccess: isSuccess } = t.params;
const srcBuffer = t.createBufferTracked({
size: 16,
usage: GPUBufferUsage.COPY_SRC,
});
const dstBuffer = t.createBufferTracked({
size: 16,
usage: GPUBufferUsage.COPY_DST,
});
t.testCopyBufferToBuffer({
srcBuffer,
srcOffset: 0,
dstBuffer,
dstOffset: 0,
copySize,
expectation: isSuccess ? 'Success' : 'FinishError',
});
});
g.test('copy_offset_alignment')
.paramsSubcasesOnly([
{ srcOffset: 0, dstOffset: 0, _isSuccess: true },
{ srcOffset: 2, dstOffset: 0, _isSuccess: false },
{ srcOffset: 4, dstOffset: 0, _isSuccess: true },
{ srcOffset: 5, dstOffset: 0, _isSuccess: false },
{ srcOffset: 8, dstOffset: 0, _isSuccess: true },
{ srcOffset: 0, dstOffset: 2, _isSuccess: false },
{ srcOffset: 0, dstOffset: 4, _isSuccess: true },
{ srcOffset: 0, dstOffset: 5, _isSuccess: false },
{ srcOffset: 0, dstOffset: 8, _isSuccess: true },
{ srcOffset: 4, dstOffset: 4, _isSuccess: true },
] as const)
.fn(t => {
const { srcOffset, dstOffset, _isSuccess: isSuccess } = t.params;
const srcBuffer = t.createBufferTracked({
size: 16,
usage: GPUBufferUsage.COPY_SRC,
});
const dstBuffer = t.createBufferTracked({
size: 16,
usage: GPUBufferUsage.COPY_DST,
});
t.testCopyBufferToBuffer({
srcBuffer,
srcOffset,
dstBuffer,
dstOffset,
copySize: 8,
expectation: isSuccess ? 'Success' : 'FinishError',
});
});
g.test('copy_overflow')
.paramsSubcasesOnly([
{ srcOffset: 0, dstOffset: 0, copySize: kMaxSafeMultipleOf8 },
{ srcOffset: 16, dstOffset: 0, copySize: kMaxSafeMultipleOf8 },
{ srcOffset: 0, dstOffset: 16, copySize: kMaxSafeMultipleOf8 },
{ srcOffset: kMaxSafeMultipleOf8, dstOffset: 0, copySize: 16 },
{ srcOffset: 0, dstOffset: kMaxSafeMultipleOf8, copySize: 16 },
{ srcOffset: kMaxSafeMultipleOf8, dstOffset: 0, copySize: kMaxSafeMultipleOf8 },
{ srcOffset: 0, dstOffset: kMaxSafeMultipleOf8, copySize: kMaxSafeMultipleOf8 },
{
srcOffset: kMaxSafeMultipleOf8,
dstOffset: kMaxSafeMultipleOf8,
copySize: kMaxSafeMultipleOf8,
},
] as const)
.fn(t => {
const { srcOffset, dstOffset, copySize } = t.params;
const srcBuffer = t.createBufferTracked({
size: 16,
usage: GPUBufferUsage.COPY_SRC,
});
const dstBuffer = t.createBufferTracked({
size: 16,
usage: GPUBufferUsage.COPY_DST,
});
t.testCopyBufferToBuffer({
srcBuffer,
srcOffset,
dstBuffer,
dstOffset,
copySize,
expectation: 'FinishError',
});
});
g.test('copy_out_of_bounds')
.paramsSubcasesOnly([
{ srcOffset: 0, dstOffset: 0, copySize: 32, _isSuccess: true },
{ srcOffset: 0, dstOffset: 0, copySize: 36 },
{ srcOffset: 36, dstOffset: 0, copySize: 4 },
{ srcOffset: 0, dstOffset: 36, copySize: 4 },
{ srcOffset: 36, dstOffset: 0, copySize: 0 },
{ srcOffset: 0, dstOffset: 36, copySize: 0 },
{ srcOffset: 20, dstOffset: 0, copySize: 16 },
{ srcOffset: 20, dstOffset: 0, copySize: 12, _isSuccess: true },
{ srcOffset: 0, dstOffset: 20, copySize: 16 },
{ srcOffset: 0, dstOffset: 20, copySize: 12, _isSuccess: true },
] as const)
.fn(t => {
const { srcOffset, dstOffset, copySize, _isSuccess = false } = t.params;
const srcBuffer = t.createBufferTracked({
size: 32,
usage: GPUBufferUsage.COPY_SRC,
});
const dstBuffer = t.createBufferTracked({
size: 32,
usage: GPUBufferUsage.COPY_DST,
});
t.testCopyBufferToBuffer({
srcBuffer,
srcOffset,
dstBuffer,
dstOffset,
copySize,
expectation: _isSuccess ? 'Success' : 'FinishError',
});
});
g.test('copy_within_same_buffer')
.paramsSubcasesOnly([
{ srcOffset: 0, dstOffset: 8, copySize: 4 },
{ srcOffset: 8, dstOffset: 0, copySize: 4 },
{ srcOffset: 0, dstOffset: 4, copySize: 8 },
{ srcOffset: 4, dstOffset: 0, copySize: 8 },
] as const)
.fn(t => {
const { srcOffset, dstOffset, copySize } = t.params;
const buffer = t.createBufferTracked({
size: 16,
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
});
t.testCopyBufferToBuffer({
srcBuffer: buffer,
srcOffset,
dstBuffer: buffer,
dstOffset,
copySize,
expectation: 'FinishError',
});
});