diff --git a/src/suites/cts/buffers/createBufferMapped.spec.ts b/src/suites/cts/buffers/createBufferMapped.spec.ts deleted file mode 100644 index 77e4345f3ba4..000000000000 --- a/src/suites/cts/buffers/createBufferMapped.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -export const description = ``; - -import { TestGroup, poptions } from '../../../framework/index.js'; -import { GPUTest } from '../gpu_test.js'; - -export const g = new TestGroup(GPUTest); - -g.test('basic', async t => { - const value = t.params.value; - - const [src, map] = t.device.createBufferMapped({ - size: 12, - usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST, - }); - new Uint32Array(map).set([0, value, 0]); - src.unmap(); - await t.expectContents(src, new Uint32Array([0, value, 0])); -}).params(poptions('value', [0x00000001, 0x01020304])); diff --git a/src/suites/cts/buffers/create_mapped.spec.ts b/src/suites/cts/buffers/create_mapped.spec.ts new file mode 100644 index 000000000000..52294ecfcebe --- /dev/null +++ b/src/suites/cts/buffers/create_mapped.spec.ts @@ -0,0 +1,35 @@ +export const description = ``; + +import { TestGroup, pbool, pcombine, poptions } from '../../../framework/index.js'; + +import { MappingTest } from './mapping_test.js'; + +export const g = new TestGroup(MappingTest); + +g.test('createBufferMapped', async t => { + const size = t.params.size; + const [buffer, arrayBuffer] = t.device.createBufferMapped({ + size, + usage: GPUBufferUsage.COPY_SRC | (t.params.mappable ? GPUBufferUsage.MAP_WRITE : 0), + }); + await t.checkMapWrite(buffer, arrayBuffer, size); +}).params( + pcombine([ + poptions('size', [12, 512 * 1024]), // + pbool('mappable'), + ]) +); + +g.test('createBufferMappedAsync', async t => { + const size = t.params.size; + const [buffer, arrayBuffer] = await t.device.createBufferMappedAsync({ + size, + usage: GPUBufferUsage.COPY_SRC | (t.params.mappable ? GPUBufferUsage.MAP_WRITE : 0), + }); + await t.checkMapWrite(buffer, arrayBuffer, size); +}).params( + pcombine([ + poptions('size', [12, 512 * 1024]), // + pbool('mappable'), + ]) +); diff --git a/src/suites/cts/buffers/map.spec.ts b/src/suites/cts/buffers/map.spec.ts new file mode 100644 index 000000000000..b6f9308e1674 --- /dev/null +++ b/src/suites/cts/buffers/map.spec.ts @@ -0,0 +1,65 @@ +export const description = ``; + +import { TestGroup, pbool, pcombine, poptions } from '../../../framework/index.js'; + +import { MappingTest } from './mapping_test.js'; + +export const g = new TestGroup(MappingTest); + +g.test('mapWriteAsync', async t => { + const size = t.params.size; + const buffer = t.device.createBuffer({ + size, + usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.MAP_WRITE, + }); + + const arrayBuffer = await buffer.mapWriteAsync(); + await t.checkMapWrite(buffer, arrayBuffer, size); +}).params(poptions('size', [12, 512 * 1024])); + +g.test('mapReadAsync', async t => { + const size = t.params.size; + + const [buffer, init] = t.device.createBufferMapped({ + size, + usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ, + }); + + const expected = new Uint32Array(new ArrayBuffer(size)); + const data = new Uint32Array(init); + for (let i = 0; i < data.length; ++i) { + data[i] = expected[i] = i + 1; + } + buffer.unmap(); + + const actual = new Uint8Array(await buffer.mapReadAsync()); + t.expectBuffer(actual, new Uint8Array(expected.buffer)); +}).params(poptions('size', [12, 512 * 1024])); + +g.test('createBufferMapped', async t => { + const size = t.params.size; + const [buffer, arrayBuffer] = t.device.createBufferMapped({ + size, + usage: GPUBufferUsage.COPY_SRC | (t.params.mappable ? GPUBufferUsage.MAP_WRITE : 0), + }); + await t.checkMapWrite(buffer, arrayBuffer, size); +}).params( + pcombine([ + poptions('size', [12, 512 * 1024]), // + pbool('mappable'), + ]) +); + +g.test('createBufferMappedAsync', async t => { + const size = t.params.size; + const [buffer, arrayBuffer] = await t.device.createBufferMappedAsync({ + size, + usage: GPUBufferUsage.COPY_SRC | (t.params.mappable ? GPUBufferUsage.MAP_WRITE : 0), + }); + await t.checkMapWrite(buffer, arrayBuffer, size); +}).params( + pcombine([ + poptions('size', [12, 512 * 1024]), // + pbool('mappable'), + ]) +); diff --git a/src/suites/cts/buffers/mapWriteAsync.spec.ts b/src/suites/cts/buffers/mapWriteAsync.spec.ts deleted file mode 100644 index f856d98a6542..000000000000 --- a/src/suites/cts/buffers/mapWriteAsync.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -export const description = ` -mapWriteAsync tests. -`; - -import { TestGroup, poptions } from '../../../framework/index.js'; -import { GPUTest } from '../gpu_test.js'; - -export const g = new TestGroup(GPUTest); - -g.test('basic', async t => { - const value = t.params.value; - - const buf = t.device.createBuffer({ - size: 12, - usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC, - }); - const mappedBuffer = new Uint32Array(await buf.mapWriteAsync()); - mappedBuffer[1] = value; - buf.unmap(); - await t.expectContents(buf, new Uint32Array([0, value, 0])); -}).params(poptions('value', [0x00000001, 0x01020304])); - -g.test('unmap', async t => { - const buf = t.device.createBuffer({ - size: 12, - usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC, - }); - const mappedBuffer = await buf.mapWriteAsync(); - buf.unmap(); - t.expect(mappedBuffer.byteLength === 0, 'Mapped buffer should be detached.'); -}); diff --git a/src/suites/cts/buffers/map_detach.spec.ts b/src/suites/cts/buffers/map_detach.spec.ts new file mode 100644 index 000000000000..90dd22e51358 --- /dev/null +++ b/src/suites/cts/buffers/map_detach.spec.ts @@ -0,0 +1,68 @@ +export const description = ``; + +import { TestGroup, pbool, pcombine } from '../../../framework/index.js'; +import { GPUTest } from '../gpu_test.js'; + +class F extends GPUTest { + checkDetach(buffer: GPUBuffer, arrayBuffer: ArrayBuffer, unmap: boolean, destroy: boolean): void { + const view = new Uint8Array(arrayBuffer); + this.expect(arrayBuffer.byteLength === 4); + this.expect(view.length === 4); + + if (unmap) buffer.unmap(); + if (destroy) buffer.destroy(); + + this.expect(arrayBuffer.byteLength === 0, 'ArrayBuffer should be detached'); + this.expect(view.byteLength === 0, 'ArrayBufferView should be detached'); + } +} + +export const g = new TestGroup(F); + +g.test('mapWriteAsync', async t => { + const buffer = t.device.createBuffer({ size: 4, usage: GPUBufferUsage.MAP_WRITE }); + const arrayBuffer = await buffer.mapWriteAsync(); + t.checkDetach(buffer, arrayBuffer, t.params.unmap, t.params.destroy); +}).params([ + { unmap: true, destroy: false }, // + { unmap: false, destroy: true }, + { unmap: true, destroy: true }, +]); + +g.test('mapReadAsync', async t => { + const buffer = t.device.createBuffer({ size: 4, usage: GPUBufferUsage.MAP_READ }); + const arrayBuffer = await buffer.mapReadAsync(); + t.checkDetach(buffer, arrayBuffer, t.params.unmap, t.params.destroy); +}).params([ + { unmap: true, destroy: false }, // + { unmap: false, destroy: true }, + { unmap: true, destroy: true }, +]); + +g.test('create mapped', async t => { + const desc = { + size: 4, + usage: GPUBufferUsage.MAP_WRITE, + }; + const [buffer, arrayBuffer] = t.params.async + ? await t.device.createBufferMappedAsync(desc) + : t.device.createBufferMapped(desc); + + const view = new Uint8Array(arrayBuffer); + t.expect(arrayBuffer.byteLength === 4); + t.expect(view.length === 4); + + if (t.params.unmap) buffer.unmap(); + if (t.params.destroy) buffer.destroy(); + t.expect(arrayBuffer.byteLength === 0, 'ArrayBuffer should be detached'); + t.expect(view.byteLength === 0, 'ArrayBufferView should be detached'); +}).params( + pcombine([ + pbool('async'), // + [ + { unmap: true, destroy: false }, + { unmap: false, destroy: true }, + { unmap: true, destroy: true }, + ], + ]) +); diff --git a/src/suites/cts/buffers/map_oom.spec.ts b/src/suites/cts/buffers/map_oom.spec.ts new file mode 100644 index 000000000000..007392f82983 --- /dev/null +++ b/src/suites/cts/buffers/map_oom.spec.ts @@ -0,0 +1,33 @@ +export const description = ``; + +import { TestGroup } from '../../../framework/index.js'; +import { GPUTest } from '../gpu_test.js'; + +function getBufferDesc(): GPUBufferDescriptor { + return { + size: Number.MAX_SAFE_INTEGER, + usage: GPUBufferUsage.MAP_WRITE, + }; +} + +export const g = new TestGroup(GPUTest); + +g.test('mapWriteAsync', async t => { + const buffer = t.device.createBuffer(getBufferDesc()); + await t.shouldReject('RangeError', buffer.mapWriteAsync()); +}); + +g.test('mapReadAsync', async t => { + const buffer = t.device.createBuffer(getBufferDesc()); + await t.shouldReject('RangeError', buffer.mapReadAsync()); +}); + +g.test('createBufferMapped', async t => { + await t.shouldThrow('RangeError', () => { + t.device.createBufferMapped(getBufferDesc()); + }); +}); + +g.test('createBufferAsync', async t => { + await t.shouldReject('RangeError', t.device.createBufferMappedAsync(getBufferDesc())); +}); diff --git a/src/suites/cts/buffers/mapping_test.ts b/src/suites/cts/buffers/mapping_test.ts new file mode 100644 index 000000000000..4a09dc3921b9 --- /dev/null +++ b/src/suites/cts/buffers/mapping_test.ts @@ -0,0 +1,33 @@ +import { GPUTest } from '../gpu_test.js'; + +export class MappingTest extends GPUTest { + checkMapWrite(buffer: GPUBuffer, mappedContents: ArrayBuffer, size: number): Promise { + this.checkMapWriteZeroed(mappedContents, size); + + const mappedView = new Uint32Array(mappedContents); + const expected = new Uint32Array(new ArrayBuffer(size)); + this.expect(mappedView.byteLength === size); + for (let i = 0; i < mappedView.length; ++i) { + mappedView[i] = expected[i] = i + 1; + } + buffer.unmap(); + + return this.expectContents(buffer, expected); + } + + checkMapWriteZeroed(arrayBuffer: ArrayBuffer, expectedSize: number): void { + this.expect(arrayBuffer.byteLength === expectedSize); + const view = new Uint8Array(arrayBuffer); + this.expectZero(view); + } + + expectZero(actual: Uint8Array): void { + const size = actual.byteLength; + for (let i = 0; i < size; ++i) { + if (actual[i] !== 0) { + this.fail(`at [${i}], expected zero, got ${actual[i]}`); + break; + } + } + } +} diff --git a/src/suites/cts/examples.spec.ts b/src/suites/cts/examples.spec.ts index 226d71f05532..3fe14044ee85 100644 --- a/src/suites/cts/examples.spec.ts +++ b/src/suites/cts/examples.spec.ts @@ -12,7 +12,7 @@ import { GPUTest } from './gpu_test.js'; // - http://web-platform.test:8000/webgpu/cts.html?q=cts:examples: // // Tests here can be run individually or in groups: -// - http://localhost:8080/?runnow=1&q=cts:examples:basic/async: +// - http://localhost:8080/?runnow=1&q=cts:examples:basic/async= // - http://localhost:8080/?runnow=1&q=cts:examples:basic/ // - http://localhost:8080/?runnow=1&q=cts:examples: diff --git a/src/suites/cts/gpu_test.ts b/src/suites/cts/gpu_test.ts index ccd8266744a0..35070a8cb099 100644 --- a/src/suites/cts/gpu_test.ts +++ b/src/suites/cts/gpu_test.ts @@ -32,27 +32,36 @@ export class GPUTest extends Fixture { this.queue.submit([c.finish()]); const actual = new Uint8Array(await dst.mapReadAsync()); - let failedPixels = 0; - for (let i = 0; i < size; ++i) { - if (actual[i] !== exp[i]) { - if (failedPixels > 4) { - this.rec.fail('... and more'); - break; - } - failedPixels++; - this.rec.fail(`at [${i}], expected ${exp[i]}, got ${actual[i]}`); + this.expectBuffer(actual, exp); + }); + } + + expectBuffer(actual: Uint8Array, exp: Uint8Array): void { + const size = exp.byteLength; + if (actual.byteLength !== size) { + this.rec.fail('size mismatch'); + return; + } + let failedPixels = 0; + for (let i = 0; i < size; ++i) { + if (actual[i] !== exp[i]) { + if (failedPixels > 4) { + this.rec.fail('... and more'); + break; } + failedPixels++; + this.rec.fail(`at [${i}], expected ${exp[i]}, got ${actual[i]}`); } - if (size <= 256 && failedPixels > 0) { - const expHex = Array.from(exp) - .map(x => x.toString(16).padStart(2, '0')) - .join(''); - const actHex = Array.from(actual) - .map(x => x.toString(16).padStart(2, '0')) - .join(''); - this.rec.log('EXPECT: ' + expHex); - this.rec.log('ACTUAL: ' + actHex); - } - }); + } + if (size <= 256 && failedPixels > 0) { + const expHex = Array.from(exp) + .map(x => x.toString(16).padStart(2, '0')) + .join(''); + const actHex = Array.from(actual) + .map(x => x.toString(16).padStart(2, '0')) + .join(''); + this.rec.log('EXPECT: ' + expHex); + this.rec.log('ACTUAL: ' + actHex); + } } } diff --git a/wpt/cts.html b/wpt/cts.html index 717acbd4e867..b9e417bde20a 100644 --- a/wpt/cts.html +++ b/wpt/cts.html @@ -20,8 +20,12 @@ - - + + + + + +