summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/queue/buffer_mapped.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/queue/buffer_mapped.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/queue/buffer_mapped.spec.ts280
1 files changed, 280 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/queue/buffer_mapped.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/queue/buffer_mapped.spec.ts
new file mode 100644
index 0000000000..f979dc3146
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/queue/buffer_mapped.spec.ts
@@ -0,0 +1,280 @@
+export const description = `
+Validation tests for the map-state of mappable buffers used in submitted command buffers.
+
+Tests every operation that has a dependency on a buffer
+ - writeBuffer
+ - copyB2B {src,dst}
+ - copyB2T
+ - copyT2B
+
+Test those operations against buffers in the following states:
+ - Unmapped
+ - In the process of mapping
+ - mapped
+ - mapped with a mapped range queried
+ - unmapped after mapping
+ - mapped at creation
+
+Also tests every order of operations combination of mapping operations and command recording
+operations to ensure the mapping state is only considered when a command buffer is submitted.
+`;
+
+import { makeTestGroup } from '../../../../common/framework/test_group.js';
+import { ValidationTest } from '../validation_test.js';
+
+class F extends ValidationTest {
+ async runBufferDependencyTest(usage: number, callback: Function): Promise<void> {
+ const bufferDesc = {
+ size: 8,
+ usage,
+ mappedAtCreation: false,
+ };
+
+ const mapMode = usage & GPUBufferUsage.MAP_READ ? GPUMapMode.READ : GPUMapMode.WRITE;
+
+ // Create a mappable buffer, and one that will remain unmapped for comparison.
+ const mappableBuffer = this.device.createBuffer(bufferDesc);
+ const unmappedBuffer = this.device.createBuffer(bufferDesc);
+
+ // Run the given operation before the buffer is mapped. Should succeed.
+ callback(mappableBuffer);
+
+ // Map the buffer
+ const mapPromise = mappableBuffer.mapAsync(mapMode);
+
+ // Run the given operation while the buffer is in the process of mapping. Should fail.
+ this.expectValidationError(() => {
+ callback(mappableBuffer);
+ });
+
+ // Run on a different, unmapped buffer. Should succeed.
+ callback(unmappedBuffer);
+
+ await mapPromise;
+
+ // Run the given operation when the buffer is finished mapping with no getMappedRange. Should fail.
+ this.expectValidationError(() => {
+ callback(mappableBuffer);
+ });
+
+ // Run on a different, unmapped buffer. Should succeed.
+ callback(unmappedBuffer);
+
+ // Run the given operation when the buffer is mapped with getMappedRange. Should fail.
+ mappableBuffer.getMappedRange();
+ this.expectValidationError(() => {
+ callback(mappableBuffer);
+ });
+
+ // Unmap the buffer and run the operation. Should succeed.
+ mappableBuffer.unmap();
+ callback(mappableBuffer);
+
+ // Create a buffer that's mappedAtCreation.
+ bufferDesc.mappedAtCreation = true;
+ const mappedBuffer = this.device.createBuffer(bufferDesc);
+
+ // Run the operation with the mappedAtCreation buffer. Should fail.
+ this.expectValidationError(() => {
+ callback(mappedBuffer);
+ });
+
+ // Run on a different, unmapped buffer. Should succeed.
+ callback(unmappedBuffer);
+
+ // Unmap the mappedAtCreation buffer and run the operation. Should succeed.
+ mappedBuffer.unmap();
+ callback(mappedBuffer);
+ }
+}
+
+export const g = makeTestGroup(F);
+
+g.test('writeBuffer')
+ .desc(`Test that an outstanding mapping will prevent writeBuffer calls.`)
+ .fn(async t => {
+ const data = new Uint32Array([42]);
+
+ await t.runBufferDependencyTest(
+ GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
+ (buffer: GPUBuffer) => {
+ t.queue.writeBuffer(buffer, 0, data);
+ }
+ );
+ });
+
+g.test('copyBufferToBuffer')
+ .desc(
+ `
+ Test that an outstanding mapping will prevent copyBufferToTexture commands from submitting,
+ both when used as the source and destination.`
+ )
+ .fn(async t => {
+ const sourceBuffer = t.device.createBuffer({
+ size: 8,
+ usage: GPUBufferUsage.COPY_SRC,
+ });
+
+ const destBuffer = t.device.createBuffer({
+ size: 8,
+ usage: GPUBufferUsage.COPY_DST,
+ });
+
+ await t.runBufferDependencyTest(
+ GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
+ (buffer: GPUBuffer) => {
+ const commandEncoder = t.device.createCommandEncoder();
+ commandEncoder.copyBufferToBuffer(buffer, 0, destBuffer, 0, 4);
+ t.queue.submit([commandEncoder.finish()]);
+ }
+ );
+
+ await t.runBufferDependencyTest(
+ GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
+ (buffer: GPUBuffer) => {
+ const commandEncoder = t.device.createCommandEncoder();
+ commandEncoder.copyBufferToBuffer(sourceBuffer, 0, buffer, 0, 4);
+ t.queue.submit([commandEncoder.finish()]);
+ }
+ );
+ });
+
+g.test('copyBufferToTexture')
+ .desc(
+ `Test that an outstanding mapping will prevent copyBufferToTexture commands from submitting.`
+ )
+ .fn(async t => {
+ const size = { width: 1, height: 1 };
+
+ const texture = t.device.createTexture({
+ size,
+ format: 'rgba8unorm',
+ usage: GPUTextureUsage.COPY_DST,
+ });
+
+ await t.runBufferDependencyTest(
+ GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
+ (buffer: GPUBuffer) => {
+ const commandEncoder = t.device.createCommandEncoder();
+ commandEncoder.copyBufferToTexture({ buffer }, { texture }, size);
+ t.queue.submit([commandEncoder.finish()]);
+ }
+ );
+ });
+
+g.test('copyTextureToBuffer')
+ .desc(
+ `Test that an outstanding mapping will prevent copyTextureToBuffer commands from submitting.`
+ )
+ .fn(async t => {
+ const size = { width: 1, height: 1 };
+
+ const texture = t.device.createTexture({
+ size,
+ format: 'rgba8unorm',
+ usage: GPUTextureUsage.COPY_SRC,
+ });
+
+ await t.runBufferDependencyTest(
+ GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
+ (buffer: GPUBuffer) => {
+ const commandEncoder = t.device.createCommandEncoder();
+ commandEncoder.copyTextureToBuffer({ texture }, { buffer }, size);
+ t.queue.submit([commandEncoder.finish()]);
+ }
+ );
+ });
+
+g.test('map_command_recording_order')
+ .desc(
+ `
+Test that the order of mapping a buffer relative to when commands are recorded that use it
+ does not matter, as long as the buffer is unmapped when the commands are submitted.
+ `
+ )
+ .paramsSubcasesOnly([
+ {
+ order: ['record', 'map', 'unmap', 'finish', 'submit'],
+ mappedAtCreation: false,
+ _shouldError: false,
+ },
+ {
+ order: ['record', 'map', 'finish', 'unmap', 'submit'],
+ mappedAtCreation: false,
+ _shouldError: false,
+ },
+ {
+ order: ['record', 'finish', 'map', 'unmap', 'submit'],
+ mappedAtCreation: false,
+ _shouldError: false,
+ },
+ {
+ order: ['map', 'record', 'unmap', 'finish', 'submit'],
+ mappedAtCreation: false,
+ _shouldError: false,
+ },
+ {
+ order: ['map', 'record', 'finish', 'unmap', 'submit'],
+ mappedAtCreation: false,
+ _shouldError: false,
+ },
+ {
+ order: ['map', 'record', 'finish', 'submit', 'unmap'],
+ mappedAtCreation: false,
+ _shouldError: true,
+ },
+ {
+ order: ['record', 'map', 'finish', 'submit', 'unmap'],
+ mappedAtCreation: false,
+ _shouldError: true,
+ },
+ {
+ order: ['record', 'finish', 'map', 'submit', 'unmap'],
+ mappedAtCreation: false,
+ _shouldError: true,
+ },
+ { order: ['record', 'unmap', 'finish', 'submit'], mappedAtCreation: true, _shouldError: false },
+ { order: ['record', 'finish', 'unmap', 'submit'], mappedAtCreation: true, _shouldError: false },
+ { order: ['record', 'finish', 'submit', 'unmap'], mappedAtCreation: true, _shouldError: true },
+ ] as const)
+ .fn(async t => {
+ const { order, mappedAtCreation, _shouldError: shouldError } = t.params;
+
+ const buffer = t.device.createBuffer({
+ size: 4,
+ usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
+ mappedAtCreation,
+ });
+
+ const targetBuffer = t.device.createBuffer({
+ size: 4,
+ usage: GPUBufferUsage.COPY_DST,
+ });
+
+ const commandEncoder = t.device.createCommandEncoder();
+ let commandBuffer: GPUCommandBuffer;
+
+ const steps = {
+ record: async () => {
+ commandEncoder.copyBufferToBuffer(buffer, 0, targetBuffer, 0, 4);
+ },
+ map: async () => {
+ await buffer.mapAsync(GPUMapMode.WRITE);
+ },
+ unmap: async () => {
+ buffer.unmap();
+ },
+ finish: async () => {
+ commandBuffer = commandEncoder.finish();
+ },
+ submit: async () => {
+ t.expectValidationError(() => {
+ t.queue.submit([commandBuffer]);
+ }, shouldError);
+ },
+ };
+
+ for (const op of order) {
+ await steps[op]();
+ }
+ });