1export const description = `
2copyBufferToBuffer tests.
3
4Test Plan:
5* Buffer is valid/invalid
6  - the source buffer is invalid
7  - the destination buffer is invalid
8* Buffer usages
9  - the source buffer is created without GPUBufferUsage::COPY_SRC
10  - the destination buffer is created without GPUBufferUsage::COPY_DEST
11* CopySize
12  - copySize is not a multiple of 4
13  - copySize is 0
14* copy offsets
15  - sourceOffset is not a multiple of 4
16  - destinationOffset is not a multiple of 4
17* Arthimetic overflow
18  - (sourceOffset + copySize) is overflow
19  - (destinationOffset + copySize) is overflow
20* Out of bounds
21  - (sourceOffset + copySize) > size of source buffer
22  - (destinationOffset + copySize) > size of destination buffer
23* Source buffer and destination buffer are the same buffer
24`;
25
26import { poptions, params } from '../../../common/framework/params_builder.js';
27import { makeTestGroup } from '../../../common/framework/test_group.js';
28import { kBufferUsages } from '../../capability_info.js';
29import { kMaxSafeMultipleOf8 } from '../../util/math.js';
30
31import { ValidationTest } from './validation_test.js';
32
33class F extends ValidationTest {
34  TestCopyBufferToBuffer(options: {
35    srcBuffer: GPUBuffer;
36    srcOffset: number;
37    dstBuffer: GPUBuffer;
38    dstOffset: number;
39    copySize: number;
40    isSuccess: boolean;
41  }): void {
42    const { srcBuffer, srcOffset, dstBuffer, dstOffset, copySize, isSuccess } = options;
43
44    const commandEncoder = this.device.createCommandEncoder();
45    commandEncoder.copyBufferToBuffer(srcBuffer, srcOffset, dstBuffer, dstOffset, copySize);
46
47    this.expectValidationError(() => {
48      commandEncoder.finish();
49    }, !isSuccess);
50  }
51}
52
53export const g = makeTestGroup(F);
54
55g.test('copy_with_invalid_buffer').fn(async t => {
56  const validBuffer = t.device.createBuffer({
57    size: 16,
58    usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
59  });
60
61  const errorBuffer = t.getErrorBuffer();
62
63  t.TestCopyBufferToBuffer({
64    srcBuffer: errorBuffer,
65    srcOffset: 0,
66    dstBuffer: validBuffer,
67    dstOffset: 0,
68    copySize: 8,
69    isSuccess: false,
70  });
71
72  t.TestCopyBufferToBuffer({
73    srcBuffer: validBuffer,
74    srcOffset: 0,
75    dstBuffer: errorBuffer,
76    dstOffset: 0,
77    copySize: 8,
78    isSuccess: false,
79  });
80});
81
82g.test('buffer_usage')
83  .params(
84    params()
85      .combine(poptions('srcUsage', kBufferUsages))
86      .combine(poptions('dstUsage', kBufferUsages))
87  )
88  .fn(async t => {
89    const { srcUsage, dstUsage } = t.params;
90
91    const srcBuffer = t.device.createBuffer({
92      size: 16,
93      usage: srcUsage,
94    });
95    const dstBuffer = t.device.createBuffer({
96      size: 16,
97      usage: dstUsage,
98    });
99
100    const isSuccess = srcUsage === GPUBufferUsage.COPY_SRC && dstUsage === GPUBufferUsage.COPY_DST;
101
102    t.TestCopyBufferToBuffer({
103      srcBuffer,
104      srcOffset: 0,
105      dstBuffer,
106      dstOffset: 0,
107      copySize: 8,
108      isSuccess,
109    });
110  });
111
112g.test('copy_size_alignment')
113  .params([
114    { copySize: 0, _isSuccess: true },
115    { copySize: 2, _isSuccess: false },
116    { copySize: 4, _isSuccess: true },
117    { copySize: 5, _isSuccess: false },
118    { copySize: 8, _isSuccess: true },
119  ] as const)
120  .fn(async t => {
121    const { copySize, _isSuccess: isSuccess } = t.params;
122
123    const srcBuffer = t.device.createBuffer({
124      size: 16,
125      usage: GPUBufferUsage.COPY_SRC,
126    });
127    const dstBuffer = t.device.createBuffer({
128      size: 16,
129      usage: GPUBufferUsage.COPY_DST,
130    });
131
132    t.TestCopyBufferToBuffer({
133      srcBuffer,
134      srcOffset: 0,
135      dstBuffer,
136      dstOffset: 0,
137      copySize,
138      isSuccess,
139    });
140  });
141
142g.test('copy_offset_alignment')
143  .params([
144    { srcOffset: 0, dstOffset: 0, _isSuccess: true },
145    { srcOffset: 2, dstOffset: 0, _isSuccess: false },
146    { srcOffset: 4, dstOffset: 0, _isSuccess: true },
147    { srcOffset: 5, dstOffset: 0, _isSuccess: false },
148    { srcOffset: 8, dstOffset: 0, _isSuccess: true },
149    { srcOffset: 0, dstOffset: 2, _isSuccess: false },
150    { srcOffset: 0, dstOffset: 4, _isSuccess: true },
151    { srcOffset: 0, dstOffset: 5, _isSuccess: false },
152    { srcOffset: 0, dstOffset: 8, _isSuccess: true },
153    { srcOffset: 4, dstOffset: 4, _isSuccess: true },
154  ] as const)
155  .fn(async t => {
156    const { srcOffset, dstOffset, _isSuccess: isSuccess } = t.params;
157
158    const srcBuffer = t.device.createBuffer({
159      size: 16,
160      usage: GPUBufferUsage.COPY_SRC,
161    });
162    const dstBuffer = t.device.createBuffer({
163      size: 16,
164      usage: GPUBufferUsage.COPY_DST,
165    });
166
167    t.TestCopyBufferToBuffer({
168      srcBuffer,
169      srcOffset,
170      dstBuffer,
171      dstOffset,
172      copySize: 8,
173      isSuccess,
174    });
175  });
176
177g.test('copy_overflow')
178  .params([
179    { srcOffset: 0, dstOffset: 0, copySize: kMaxSafeMultipleOf8 },
180    { srcOffset: 16, dstOffset: 0, copySize: kMaxSafeMultipleOf8 },
181    { srcOffset: 0, dstOffset: 16, copySize: kMaxSafeMultipleOf8 },
182    { srcOffset: kMaxSafeMultipleOf8, dstOffset: 0, copySize: 16 },
183    { srcOffset: 0, dstOffset: kMaxSafeMultipleOf8, copySize: 16 },
184    { srcOffset: kMaxSafeMultipleOf8, dstOffset: 0, copySize: kMaxSafeMultipleOf8 },
185    { srcOffset: 0, dstOffset: kMaxSafeMultipleOf8, copySize: kMaxSafeMultipleOf8 },
186    {
187      srcOffset: kMaxSafeMultipleOf8,
188      dstOffset: kMaxSafeMultipleOf8,
189      copySize: kMaxSafeMultipleOf8,
190    },
191  ] as const)
192  .fn(async t => {
193    const { srcOffset, dstOffset, copySize } = t.params;
194
195    const srcBuffer = t.device.createBuffer({
196      size: 16,
197      usage: GPUBufferUsage.COPY_SRC,
198    });
199    const dstBuffer = t.device.createBuffer({
200      size: 16,
201      usage: GPUBufferUsage.COPY_DST,
202    });
203
204    t.TestCopyBufferToBuffer({
205      srcBuffer,
206      srcOffset,
207      dstBuffer,
208      dstOffset,
209      copySize,
210      isSuccess: false,
211    });
212  });
213
214g.test('copy_out_of_bounds')
215  .params([
216    { srcOffset: 0, dstOffset: 0, copySize: 32, _isSuccess: true },
217    { srcOffset: 0, dstOffset: 0, copySize: 36 },
218    { srcOffset: 36, dstOffset: 0, copySize: 4 },
219    { srcOffset: 0, dstOffset: 36, copySize: 4 },
220    { srcOffset: 36, dstOffset: 0, copySize: 0 },
221    { srcOffset: 0, dstOffset: 36, copySize: 0 },
222    { srcOffset: 20, dstOffset: 0, copySize: 16 },
223    { srcOffset: 20, dstOffset: 0, copySize: 12, _isSuccess: true },
224    { srcOffset: 0, dstOffset: 20, copySize: 16 },
225    { srcOffset: 0, dstOffset: 20, copySize: 12, _isSuccess: true },
226  ] as const)
227  .fn(async t => {
228    const { srcOffset, dstOffset, copySize, _isSuccess = false } = t.params;
229
230    const srcBuffer = t.device.createBuffer({
231      size: 32,
232      usage: GPUBufferUsage.COPY_SRC,
233    });
234    const dstBuffer = t.device.createBuffer({
235      size: 32,
236      usage: GPUBufferUsage.COPY_DST,
237    });
238
239    t.TestCopyBufferToBuffer({
240      srcBuffer,
241      srcOffset,
242      dstBuffer,
243      dstOffset,
244      copySize,
245      isSuccess: _isSuccess,
246    });
247  });
248
249g.test('copy_within_same_buffer')
250  .params([
251    { srcOffset: 0, dstOffset: 8, copySize: 4 },
252    { srcOffset: 8, dstOffset: 0, copySize: 4 },
253    { srcOffset: 0, dstOffset: 4, copySize: 8 },
254    { srcOffset: 4, dstOffset: 0, copySize: 8 },
255  ] as const)
256  .fn(async t => {
257    const { srcOffset, dstOffset, copySize } = t.params;
258
259    const buffer = t.device.createBuffer({
260      size: 16,
261      usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
262    });
263
264    t.TestCopyBufferToBuffer({
265      srcBuffer: buffer,
266      srcOffset,
267      dstBuffer: buffer,
268      dstOffset,
269      copySize,
270      isSuccess: false,
271    });
272  });
273