summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/queue/writeBuffer.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/queue/writeBuffer.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/queue/writeBuffer.spec.ts235
1 files changed, 235 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/queue/writeBuffer.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/queue/writeBuffer.spec.ts
new file mode 100644
index 0000000000..eb42d1a3c3
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/queue/writeBuffer.spec.ts
@@ -0,0 +1,235 @@
+export const description = 'Operation tests for GPUQueue.writeBuffer()';
+
+import { makeTestGroup } from '../../../../common/framework/test_group.js';
+import { memcpy, range } from '../../../../common/util/util.js';
+import { GPUTest } from '../../../gpu_test.js';
+import { align } from '../../../util/math.js';
+
+const kTypedArrays = [
+ 'Uint8Array',
+ 'Uint16Array',
+ 'Uint32Array',
+ 'Int8Array',
+ 'Int16Array',
+ 'Int32Array',
+ 'Float32Array',
+ 'Float64Array',
+] as const;
+
+type WriteBufferSignature = {
+ bufferOffset: number;
+ data: readonly number[];
+ arrayType: typeof kTypedArrays[number];
+ useArrayBuffer: boolean;
+ dataOffset?: number; // In elements when useArrayBuffer === false, bytes otherwise
+ dataSize?: number; // In elements when useArrayBuffer === false, bytes otherwise
+};
+
+class F extends GPUTest {
+ calculateRequiredBufferSize(writes: WriteBufferSignature[]): number {
+ let bufferSize = 0;
+ // Calculate size of final buffer
+ for (const { bufferOffset, data, arrayType, useArrayBuffer, dataOffset, dataSize } of writes) {
+ const TypedArrayConstructor = globalThis[arrayType];
+
+ // When passing data as an ArrayBuffer, dataOffset and dataSize use byte instead of number of
+ // elements. bytesPerElement is used to convert dataOffset and dataSize from elements to bytes
+ // when useArrayBuffer === false.
+ const bytesPerElement = useArrayBuffer ? 1 : TypedArrayConstructor.BYTES_PER_ELEMENT;
+
+ // Calculate the number of bytes written to the buffer. data is always an array of elements.
+ let bytesWritten =
+ data.length * TypedArrayConstructor.BYTES_PER_ELEMENT - (dataOffset || 0) * bytesPerElement;
+
+ if (dataSize) {
+ // When defined, dataSize clamps the number of bytes written
+ bytesWritten = Math.min(bytesWritten, dataSize * bytesPerElement);
+ }
+
+ // The minimum buffer size required for the write to succeed is the number of bytes written +
+ // the bufferOffset
+ const requiredBufferSize = bufferOffset + bytesWritten;
+
+ // Find the largest required size by all writes
+ bufferSize = Math.max(bufferSize, requiredBufferSize);
+ }
+ // writeBuffer requires buffers to be a multiple of 4
+ return align(bufferSize, 4);
+ }
+
+ testWriteBuffer(...writes: WriteBufferSignature[]) {
+ const bufferSize = this.calculateRequiredBufferSize(writes);
+
+ // Initialize buffer to non-zero data (0xff) for easier debug.
+ const expectedData = new Uint8Array(bufferSize).fill(0xff);
+
+ const buffer = this.makeBufferWithContents(
+ expectedData,
+ GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
+ );
+
+ for (const { bufferOffset, data, arrayType, useArrayBuffer, dataOffset, dataSize } of writes) {
+ const TypedArrayConstructor = globalThis[arrayType];
+ const writeData = new TypedArrayConstructor(data);
+ const writeSrc = useArrayBuffer ? writeData.buffer : writeData;
+ this.queue.writeBuffer(buffer, bufferOffset, writeSrc, dataOffset, dataSize);
+ memcpy(
+ { src: writeSrc, start: dataOffset, length: dataSize },
+ { dst: expectedData, start: bufferOffset }
+ );
+ }
+
+ this.debug(`expectedData: [${expectedData.join(', ')}]`);
+ this.expectGPUBufferValuesEqual(buffer, expectedData);
+ }
+}
+
+export const g = makeTestGroup(F);
+
+const kTestData = range<number>(16, i => i);
+
+g.test('array_types')
+ .desc('Tests that writeBuffer correctly handles different TypedArrays and ArrayBuffer.')
+ .params(u =>
+ u //
+ .combine('arrayType', kTypedArrays)
+ .combine('useArrayBuffer', [false, true])
+ )
+ .fn(t => {
+ const { arrayType, useArrayBuffer } = t.params;
+ const dataOffset = 1;
+ const dataSize = 8;
+ t.testWriteBuffer({
+ bufferOffset: 0,
+ arrayType,
+ data: kTestData,
+ dataOffset,
+ dataSize,
+ useArrayBuffer,
+ });
+ });
+
+g.test('multiple_writes_at_different_offsets_and_sizes')
+ .desc(
+ `
+Tests that writeBuffer currently handles different offsets and writes. This includes:
+- Non-overlapping TypedArrays and ArrayLists
+- Overlapping TypedArrays and ArrayLists
+- Writing zero data
+- Writing on zero sized buffers
+- Unaligned source
+- Multiple overlapping writes with decreasing sizes
+ `
+ )
+ .paramsSubcasesOnly([
+ {
+ // Concatenate 2 Uint32Arrays
+ writes: [
+ {
+ bufferOffset: 0,
+ data: kTestData,
+ arrayType: 'Uint32Array',
+ useArrayBuffer: false,
+ dataOffset: 2,
+ dataSize: 2,
+ }, // [2, 3]
+ {
+ bufferOffset: 2 * Uint32Array.BYTES_PER_ELEMENT,
+ data: kTestData,
+ arrayType: 'Uint32Array',
+ useArrayBuffer: false,
+ dataOffset: 0,
+ dataSize: 2,
+ }, // [0, 1]
+ ], // Expected [2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
+ },
+ {
+ // Concatenate 2 Uint8Arrays
+ writes: [
+ { bufferOffset: 0, data: [0, 1, 2, 3], arrayType: 'Uint8Array', useArrayBuffer: false },
+ { bufferOffset: 4, data: [4, 5, 6, 7], arrayType: 'Uint8Array', useArrayBuffer: false },
+ ], // Expected [0, 1, 2, 3, 4, 5, 6, 7]
+ },
+ {
+ // Overlap in the middle
+ writes: [
+ { bufferOffset: 0, data: kTestData, arrayType: 'Uint8Array', useArrayBuffer: false },
+ { bufferOffset: 4, data: [0], arrayType: 'Uint32Array', useArrayBuffer: false },
+ ], // Expected [0, 1, 2, 3, 0, 0 ,0 ,0, 8, 9, 10, 11, 12, 13, 14, 15]
+ },
+ {
+ // Overlapping arrayLists
+ writes: [
+ {
+ bufferOffset: 0,
+ data: kTestData,
+ arrayType: 'Uint32Array',
+ useArrayBuffer: true,
+ dataOffset: 2,
+ dataSize: 4 * Uint32Array.BYTES_PER_ELEMENT,
+ },
+ { bufferOffset: 4, data: [0x04030201], arrayType: 'Uint32Array', useArrayBuffer: true },
+ ], // Expected [0, 0, 1, 0, 1, 2, 3, 4, 0, 0, 3, 0, 0, 0, 4, 0]
+ },
+ {
+ // Write over with empty buffer
+ writes: [
+ { bufferOffset: 0, data: kTestData, arrayType: 'Uint8Array', useArrayBuffer: false },
+ { bufferOffset: 0, data: [], arrayType: 'Uint8Array', useArrayBuffer: false },
+ ], // Expected [0, 1, 2, 3, 4, 5 ,6 ,7, 8, 9, 10, 11, 12, 13, 14, 15]
+ },
+ {
+ // Zero buffer
+ writes: [{ bufferOffset: 0, data: [], arrayType: 'Uint8Array', useArrayBuffer: false }],
+ }, // Expected []
+ {
+ // Unaligned source
+ writes: [
+ {
+ bufferOffset: 0,
+ data: [0x77, ...kTestData],
+ arrayType: 'Uint8Array',
+ useArrayBuffer: false,
+ dataOffset: 1,
+ },
+ ], // Expected [0, 1, 2, 3, 4, 5 ,6 ,7, 8, 9, 10, 11, 12, 13, 14, 15]
+ },
+ {
+ // Multiple overlapping writes
+ writes: [
+ {
+ bufferOffset: 0,
+ data: [0x05050505, 0x05050505, 0x05050505, 0x05050505, 0x05050505],
+ arrayType: 'Uint32Array',
+ useArrayBuffer: false,
+ },
+ {
+ bufferOffset: 0,
+ data: [0x04040404, 0x04040404, 0x04040404, 0x04040404],
+ arrayType: 'Uint32Array',
+ useArrayBuffer: false,
+ },
+ {
+ bufferOffset: 0,
+ data: [0x03030303, 0x03030303, 0x03030303],
+ arrayType: 'Uint32Array',
+ useArrayBuffer: false,
+ },
+ {
+ bufferOffset: 0,
+ data: [0x02020202, 0x02020202],
+ arrayType: 'Uint32Array',
+ useArrayBuffer: false,
+ },
+ {
+ bufferOffset: 0,
+ data: [0x01010101],
+ arrayType: 'Uint32Array',
+ useArrayBuffer: false,
+ },
+ ], // Expected [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5]
+ },
+ ] as const)
+ .fn(t => {
+ t.testWriteBuffer(...t.params.writes);
+ });