From 576bc30c237afbd15b6be90d1996165070143a55 Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Tue, 17 Dec 2024 17:18:56 +0900 Subject: [PATCH] Compat: skip memory_sync buffer tests when no support At a glance I doesn't seem like there is anything to refactor here. The test that are actually using storage buffers need storage buffers. So, skip them if there are none. --- .../memory_sync/buffer/buffer_sync_test.ts | 49 +++++++++++++++++++ .../buffer/multiple_buffers.spec.ts | 27 +++++++++- .../memory_sync/buffer/single_buffer.spec.ts | 27 +++++++++- 3 files changed, 101 insertions(+), 2 deletions(-) diff --git a/src/webgpu/api/operation/memory_sync/buffer/buffer_sync_test.ts b/src/webgpu/api/operation/memory_sync/buffer/buffer_sync_test.ts index b5ea6c0ec0ba..2126caa362ff 100644 --- a/src/webgpu/api/operation/memory_sync/buffer/buffer_sync_test.ts +++ b/src/webgpu/api/operation/memory_sync/buffer/buffer_sync_test.ts @@ -127,6 +127,21 @@ export function checkOpsValidForContext( return true; } +function readOpUsesStorageBufferInFragmentShader(readOp: ReadOp) { + return ( + readOp === 'storage-read' || + readOp === 'input-vertex' || + readOp === 'input-index' || + readOp === 'input-indirect' || + readOp === 'input-indirect-index' || + readOp === 'constant-uniform' + ); +} + +function writeOpUsesStorageBufferInFragmentShader(writeOp: WriteOp) { + return writeOp === 'storage' || writeOp === 'write-buffer'; +} + const kDummyVertexShader = ` @vertex fn vert_main() -> @builtin(position) vec4 { return vec4(0.5, 0.5, 0.0, 1.0); @@ -145,6 +160,40 @@ export class BufferSyncTest extends GPUTest { tmpValueBuffers: (GPUBuffer | undefined)[] = [undefined, undefined]; tmpValueTextures: (GPUTexture | undefined)[] = [undefined, undefined]; + skipIfNoSupportForStorageBuffersInFragmentStage() { + if (this.isCompatibility) { + this.skipIf( + !(this.device.limits.maxStorageBuffersInFragmentStage! >= 2), + `maxStorageBuffersInFragmentStage(${this.device.limits.maxStorageBuffersInFragmentStage}) < 2` + ); + } + } + + skipIfReadOpsOrWriteOpsUsesStorageBufferInFragmentStageAndNoSupportStorageBuffersInFragmentShaders( + readOp: ReadOp | readonly ReadOp[], + writeOp: WriteOp | readonly WriteOp[] + ) { + if (this.isCompatibility) { + const readOps = Array.isArray(readOp) ? readOp : [readOp]; + const writeOps = Array.isArray(writeOp) ? writeOp : [writeOp]; + const readOpsUseStorageBuffersInFragmentStage = readOps.reduce( + (uses, op) => uses || readOpUsesStorageBufferInFragmentShader(op), + false + ); + const writeOpsUseStorageBuffersInFragmentStage = writeOps.reduce( + (uses, op) => uses || writeOpUsesStorageBufferInFragmentShader(op), + false + ); + const usesStorageBuffersInFragmentStage = + readOpsUseStorageBuffersInFragmentStage || writeOpsUseStorageBuffersInFragmentStage; + this.skipIf( + usesStorageBuffersInFragmentStage && + !(this.device.limits.maxStorageBuffersInFragmentStage! >= 2), + `maxStorageBuffersInFragmentStage(${this.device.limits.maxStorageBuffersInFragmentStage}) < 2` + ); + } + } + // These intermediate buffers/textures are created before any read/write op // to avoid extra memory synchronization between ops introduced by await on buffer/texture creations. // Create extra buffers/textures needed by write operation diff --git a/src/webgpu/api/operation/memory_sync/buffer/multiple_buffers.spec.ts b/src/webgpu/api/operation/memory_sync/buffer/multiple_buffers.spec.ts index 081384cd37af..b10f3e9244d2 100644 --- a/src/webgpu/api/operation/memory_sync/buffer/multiple_buffers.spec.ts +++ b/src/webgpu/api/operation/memory_sync/buffer/multiple_buffers.spec.ts @@ -16,6 +16,7 @@ TODO: Tests with more than one buffer to try to stress implementations a little `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; +import { MaxLimitsTestMixin } from '../../../../gpu_test.js'; import { kOperationBoundaries, kBoundaryInfo, @@ -34,7 +35,7 @@ const kSrcValue = 0; // The op value is what the read/write operation write into the target buffer. const kOpValue = 1; -export const g = makeTestGroup(BufferSyncTest); +export const g = makeTestGroup(MaxLimitsTestMixin(BufferSyncTest)); g.test('rw') .desc( @@ -66,6 +67,12 @@ g.test('rw') ) .fn(async t => { const { readContext, readOp, writeContext, writeOp, boundary } = t.params; + + t.skipIfReadOpsOrWriteOpsUsesStorageBufferInFragmentStageAndNoSupportStorageBuffersInFragmentShaders( + readOp, + writeOp + ); + const helper = new OperationContextHelper(t); const srcBuffers: GPUBuffer[] = []; @@ -131,6 +138,12 @@ g.test('wr') ) .fn(async t => { const { readContext, readOp, writeContext, writeOp, boundary } = t.params; + + t.skipIfReadOpsOrWriteOpsUsesStorageBufferInFragmentStageAndNoSupportStorageBuffersInFragmentShaders( + readOp, + writeOp + ); + const helper = new OperationContextHelper(t); const srcBuffers: GPUBuffer[] = []; @@ -196,6 +209,12 @@ g.test('ww') ) .fn(async t => { const { writeOps, contexts, boundary } = t.params; + + t.skipIfReadOpsOrWriteOpsUsesStorageBufferInFragmentStageAndNoSupportStorageBuffersInFragmentShaders( + [], + writeOps + ); + const helper = new OperationContextHelper(t); const buffers: GPUBuffer[] = []; @@ -245,6 +264,8 @@ g.test('multiple_pairs_of_draws_in_one_render_pass') .fn(async t => { const { firstDrawUseBundle, secondDrawUseBundle } = t.params; + t.skipIfNoSupportForStorageBuffersInFragmentStage(); + const encoder = t.device.createCommandEncoder(); const passEncoder = t.beginSimpleRenderPass(encoder); @@ -294,6 +315,8 @@ g.test('multiple_pairs_of_draws_in_one_render_bundle') colorFormats: ['rgba8unorm'], }); + t.skipIfNoSupportForStorageBuffersInFragmentStage(); + const kBufferCount = 4; const buffers: GPUBuffer[] = []; for (let b = 0; b < kBufferCount; ++b) { @@ -327,6 +350,8 @@ g.test('multiple_pairs_of_dispatches_in_one_compute_pass') ` ) .fn(async t => { + t.skipIfNoSupportForStorageBuffersInFragmentStage(); + const encoder = t.device.createCommandEncoder(); const pass = encoder.beginComputePass(); diff --git a/src/webgpu/api/operation/memory_sync/buffer/single_buffer.spec.ts b/src/webgpu/api/operation/memory_sync/buffer/single_buffer.spec.ts index 817d6465cc23..c471902cd9da 100644 --- a/src/webgpu/api/operation/memory_sync/buffer/single_buffer.spec.ts +++ b/src/webgpu/api/operation/memory_sync/buffer/single_buffer.spec.ts @@ -14,6 +14,7 @@ Wait on another fence, then call expectContents to verify the dst buffer value. `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; +import { MaxLimitsTestMixin } from '../../../../gpu_test.js'; import { kOperationBoundaries, kBoundaryInfo, @@ -32,7 +33,7 @@ const kSrcValue = 0; // The op value is what the read/write operation write into the target buffer. const kOpValue = 1; -export const g = makeTestGroup(BufferSyncTest); +export const g = makeTestGroup(MaxLimitsTestMixin(BufferSyncTest)); g.test('rw') .desc( @@ -65,6 +66,11 @@ g.test('rw') const { readContext, readOp, writeContext, writeOp, boundary } = t.params; const helper = new OperationContextHelper(t); + t.skipIfReadOpsOrWriteOpsUsesStorageBufferInFragmentStageAndNoSupportStorageBuffersInFragmentShaders( + readOp, + writeOp + ); + const { srcBuffer, dstBuffer } = await t.createBuffersForReadOp(readOp, kSrcValue, kOpValue); await t.createIntermediateBuffersAndTexturesForWriteOp(writeOp, 0, kOpValue); @@ -108,6 +114,12 @@ g.test('wr') ) .fn(async t => { const { readContext, readOp, writeContext, writeOp, boundary } = t.params; + + t.skipIfReadOpsOrWriteOpsUsesStorageBufferInFragmentStageAndNoSupportStorageBuffersInFragmentShaders( + readOp, + writeOp + ); + const helper = new OperationContextHelper(t); const { srcBuffer, dstBuffer } = await t.createBuffersForReadOp(readOp, kSrcValue, kOpValue); @@ -151,6 +163,12 @@ g.test('ww') ) .fn(async t => { const { writeOps, contexts, boundary } = t.params; + + t.skipIfReadOpsOrWriteOpsUsesStorageBufferInFragmentStageAndNoSupportStorageBuffersInFragmentShaders( + [], + writeOps + ); + const helper = new OperationContextHelper(t); const buffer = await t.createBufferWithValue(0); @@ -178,7 +196,10 @@ g.test('two_draws_in_the_same_render_pass') .combine('secondDrawUseBundle', [false, true]) ) .fn(async t => { + t.skipIfNoSupportForStorageBuffersInFragmentStage(); + const { firstDrawUseBundle, secondDrawUseBundle } = t.params; + const buffer = await t.createBufferWithValue(0); const encoder = t.device.createCommandEncoder(); const passEncoder = t.beginSimpleRenderPass(encoder); @@ -211,6 +232,8 @@ g.test('two_draws_in_the_same_render_bundle') data in buffer is either 1 or 2.` ) .fn(async t => { + t.skipIfNoSupportForStorageBuffersInFragmentStage(); + const buffer = await t.createBufferWithValue(0); const encoder = t.device.createCommandEncoder(); const passEncoder = t.beginSimpleRenderPass(encoder); @@ -239,6 +262,8 @@ g.test('two_dispatches_in_the_same_compute_pass') data in buffer is 2.` ) .fn(async t => { + t.skipIfNoSupportForStorageBuffersInFragmentStage(); + const buffer = await t.createBufferWithValue(0); const encoder = t.device.createCommandEncoder(); const pass = encoder.beginComputePass();