summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_oom.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_oom.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_oom.spec.ts120
1 files changed, 120 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_oom.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_oom.spec.ts
new file mode 100644
index 0000000000..ba4ddfb5c3
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_oom.spec.ts
@@ -0,0 +1,120 @@
+export const description =
+ 'Test out-of-memory conditions creating large mappable/mappedAtCreation buffers.';
+
+import { kUnitCaseParamsBuilder } from '../../../../common/framework/params_builder.js';
+import { makeTestGroup } from '../../../../common/framework/test_group.js';
+import { kBufferUsages } from '../../../capability_info.js';
+import { GPUTest } from '../../../gpu_test.js';
+import { kMaxSafeMultipleOf8 } from '../../../util/math.js';
+
+const oomAndSizeParams = kUnitCaseParamsBuilder
+ .combine('oom', [false, true])
+ .expand('size', ({ oom }) => {
+ return oom
+ ? [
+ kMaxSafeMultipleOf8,
+ 0x20_0000_0000, // 128 GB
+ ]
+ : [16];
+ });
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('mapAsync')
+ .desc(
+ `Test creating a large mappable buffer should produce an out-of-memory error if allocation fails.
+ - The resulting buffer is an error buffer, so mapAsync rejects and produces a validation error.
+ - Calling getMappedRange should throw an OperationError because the buffer is not in the mapped state.
+ - unmap() doesn't throw an error even if mapping failed, and otherwise should detach the ArrayBuffer.
+`
+ )
+ .params(
+ oomAndSizeParams //
+ .beginSubcases()
+ .combine('write', [false, true])
+ .combine('unmapBeforeResolve', [false, true])
+ )
+ .fn(async t => {
+ const { oom, write, size, unmapBeforeResolve } = t.params;
+
+ const buffer = t.expectGPUError(
+ 'out-of-memory',
+ () =>
+ t.device.createBuffer({
+ size,
+ usage: write ? GPUBufferUsage.MAP_WRITE : GPUBufferUsage.MAP_READ,
+ }),
+ oom
+ );
+
+ let promise: Promise<void>;
+ // Should be a validation error since the buffer is invalid.
+ // Unmap abort error shouldn't cause a validation error.
+ t.expectValidationError(() => {
+ promise = buffer.mapAsync(write ? GPUMapMode.WRITE : GPUMapMode.READ);
+ }, oom);
+
+ if (oom) {
+ if (unmapBeforeResolve) {
+ // Should reject with abort error because buffer will be unmapped
+ // before validation check finishes.
+ t.shouldReject('AbortError', promise!);
+ } else {
+ // Should also reject in addition to the validation error.
+ t.shouldReject('OperationError', promise!);
+
+ // Wait for validation error before unmap to ensure validation check
+ // ends before unmap.
+ try {
+ await promise!;
+ throw new Error('The promise should be rejected.');
+ } catch {
+ // Should cause an exception because the promise should be rejected.
+ }
+ }
+
+ // Should throw an OperationError because the buffer is not mapped.
+ // Note: not a RangeError because the state of the buffer is checked first.
+ t.shouldThrow('OperationError', () => {
+ buffer.getMappedRange();
+ });
+
+ // Should't be a validation error even if the buffer failed to be mapped.
+ buffer.unmap();
+ } else {
+ await promise!;
+ const arraybuffer = buffer.getMappedRange();
+ t.expect(arraybuffer.byteLength === size);
+ buffer.unmap();
+ t.expect(arraybuffer.byteLength === 0, 'Mapping should be detached');
+ }
+ });
+
+g.test('mappedAtCreation')
+ .desc(
+ `Test creating a very large buffer mappedAtCreation buffer should throw a RangeError only
+ because such a large allocation cannot be created when we initialize an active buffer mapping.
+`
+ )
+ .params(
+ oomAndSizeParams //
+ .beginSubcases()
+ .combine('usage', kBufferUsages)
+ )
+ .fn(async t => {
+ const { oom, usage, size } = t.params;
+
+ const f = () => t.device.createBuffer({ mappedAtCreation: true, size, usage });
+
+ if (oom) {
+ // getMappedRange is normally valid on OOM buffers, but this one fails because the
+ // (default) range is too large to create the returned ArrayBuffer.
+ t.shouldThrow('RangeError', f);
+ } else {
+ const buffer = f();
+ const mapping = buffer.getMappedRange();
+ t.expect(mapping.byteLength === size, 'Mapping should be successful');
+ buffer.unmap();
+ t.expect(mapping.byteLength === 0, 'Mapping should be detached');
+ }
+ });