diff --git a/src/webgpu/api/validation/capability_checks/limits/maxStorageBuffersPerShaderStage.spec.ts b/src/webgpu/api/validation/capability_checks/limits/maxStorageBuffersPerShaderStage.spec.ts index ee7c3a024630..60be6d30c1d2 100644 --- a/src/webgpu/api/validation/capability_checks/limits/maxStorageBuffersPerShaderStage.spec.ts +++ b/src/webgpu/api/validation/capability_checks/limits/maxStorageBuffersPerShaderStage.spec.ts @@ -21,6 +21,8 @@ import { const kExtraLimits: LimitsRequest = { maxBindingsPerBindGroup: 'adapterLimit', maxBindGroups: 'adapterLimit', + maxStorageBuffersInFragmentStage: 'adapterLimit', + maxStorageBuffersInVertexStage: 'adapterLimit', }; const limit = 'maxStorageBuffersPerShaderStage'; @@ -165,6 +167,24 @@ g.test('createPipeline,at_over') `can not test ${testValue} bindings in same group because maxBindingsPerBindGroup = ${device.limits.maxBindingsPerBindGroup}` ); + if (t.isCompatibility) { + t.skipIf( + (bindingCombination === 'fragment' || + bindingCombination === 'vertexAndFragmentWithPossibleVertexStageOverflow' || + bindingCombination === 'vertexAndFragmentWithPossibleFragmentStageOverflow') && + testValue > device.limits.maxStorageBuffersInFragmentStage!, + `can not test ${testValue} bindings as it is more than maxStorageBuffersInFragmentStage(${device.limits.maxStorageBuffersInFragmentStage})` + ); + + t.skipIf( + (bindingCombination === 'vertex' || + bindingCombination === 'vertexAndFragmentWithPossibleVertexStageOverflow' || + bindingCombination === 'vertexAndFragmentWithPossibleFragmentStageOverflow') && + testValue > device.limits.maxStorageBuffersInVertexStage!, + `can not test ${testValue} bindings as it is more than maxStorageBuffersInVertexStage(${device.limits.maxStorageBuffersInVertexStage})` + ); + } + const code = getPerStageWGSLForBindingCombination( bindingCombination, order, diff --git a/src/webgpu/api/validation/layout_shader_compat.spec.ts b/src/webgpu/api/validation/layout_shader_compat.spec.ts index 5ee16510c77a..79e4f7b2926e 100644 --- a/src/webgpu/api/validation/layout_shader_compat.spec.ts +++ b/src/webgpu/api/validation/layout_shader_compat.spec.ts @@ -12,6 +12,7 @@ import { ValidBindableResource, } from '../../capability_info.js'; import { GPUConst } from '../../constants.js'; +import { MaxLimitsTestMixin } from '../../gpu_test.js'; import { ValidationTest } from './validation_test.js'; @@ -156,7 +157,7 @@ const BindingResourceCompatibleWithShaderStages = function ( return true; }; -export const g = makeTestGroup(F); +export const g = makeTestGroup(MaxLimitsTestMixin(F)); g.test('pipeline_layout_shader_exact_match') .desc( @@ -195,6 +196,46 @@ g.test('pipeline_layout_shader_exact_match') isBindingStaticallyUsed, } = t.params; + if (t.isCompatibility) { + const bindingUsedWithVertexStage = + (shaderStageWithBinding & GPUShaderStage.VERTEX) !== 0 || + (pipelineLayoutVisibility & GPUShaderStage.VERTEX) !== 0; + const bindingUsedWithFragmentStage = + (shaderStageWithBinding & GPUShaderStage.FRAGMENT) !== 0 || + (pipelineLayoutVisibility & GPUShaderStage.FRAGMENT) !== 0; + const bindingIsStorageBuffer = + bindingInPipelineLayout === 'readonlyStorageBuf' || + bindingInPipelineLayout === 'storageBuf'; + const bindingIsStorageTexture = + bindingInPipelineLayout === 'readonlyStorageTex' || + bindingInPipelineLayout === 'readwriteStorageTex' || + bindingInPipelineLayout === 'writeonlyStorageTex'; + t.skipIf( + bindingUsedWithVertexStage && + bindingIsStorageBuffer && + t.device.limits.maxStorageBuffersInVertexStage === 0, + 'Storage buffers can not be used in vertex shaders because maxStorageBuffersInVertexStage === 0' + ); + t.skipIf( + bindingUsedWithVertexStage && + bindingIsStorageTexture && + t.device.limits.maxStorageTexturesInVertexStage === 0, + 'Storage textures can not be used in vertex shaders because maxStorageTexturesInVertexStage === 0' + ); + t.skipIf( + bindingUsedWithFragmentStage && + bindingIsStorageBuffer && + t.device.limits.maxStorageBuffersInFragmentStage === 0, + 'Storage buffers can not be used in fragment shaders because maxStorageBuffersInFragmentStage === 0' + ); + t.skipIf( + bindingUsedWithFragmentStage && + bindingIsStorageTexture && + t.device.limits.maxStorageTexturesInFragmentStage === 0, + 'Storage textures can not be used in fragment shaders because maxStorageTexturesInFragmentStage === 0' + ); + } + const layout = t.createPipelineLayout(bindingInPipelineLayout, pipelineLayoutVisibility); const bindResourceDeclaration = `@group(0) @binding(0) ${t.GetBindableResourceShaderDeclaration( bindingInShader diff --git a/src/webgpu/api/validation/render_pipeline/resource_compatibility.spec.ts b/src/webgpu/api/validation/render_pipeline/resource_compatibility.spec.ts index f14211482164..9406d30f9493 100644 --- a/src/webgpu/api/validation/render_pipeline/resource_compatibility.spec.ts +++ b/src/webgpu/api/validation/render_pipeline/resource_compatibility.spec.ts @@ -4,6 +4,7 @@ Tests for resource compatibility between pipeline layout and shader modules import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { keysOf } from '../../../../common/util/data_tables.js'; +import { MaxLimitsTestMixin } from '../../../gpu_test.js'; import { kAPIResources, getWGSLShaderForResource, @@ -13,7 +14,7 @@ import { import { CreateRenderPipelineValidationTest } from './common.js'; -export const g = makeTestGroup(CreateRenderPipelineValidationTest); +export const g = makeTestGroup(MaxLimitsTestMixin(CreateRenderPipelineValidationTest)); g.test('resource_compatibility') .desc( @@ -53,8 +54,36 @@ g.test('resource_compatibility') ((wgslResource.buffer !== undefined && wgslResource.buffer.type === 'storage') || (wgslResource.storageTexture !== undefined && wgslResource.storageTexture.access !== 'read-only')), - 'Storage buffers and textures cannot be used in vertex shaders' + 'Read-Write Storage buffers and textures cannot be used in vertex shaders' ); + if (t.isCompatibility) { + t.skipIf( + t.params.stage === 'vertex' && + (apiResource.buffer?.type === 'storage' || + apiResource.buffer?.type === 'read-only-storage') && + t.device.limits.maxStorageBuffersInVertexStage === 0, + 'Storage buffers can not be used in vertex shaders because maxStorageBuffersInVertexStage === 0' + ); + t.skipIf( + t.params.stage === 'vertex' && + apiResource.storageTexture !== undefined && + t.device.limits.maxStorageTexturesInVertexStage === 0, + 'Storage textures can not be used in vertex shaders because maxStorageTexturesInVertexStage === 0' + ); + t.skipIf( + t.params.stage === 'fragment' && + (apiResource.buffer?.type === 'storage' || + apiResource.buffer?.type === 'read-only-storage') && + t.device.limits.maxStorageBuffersInFragmentStage === 0, + 'Storage buffers can not be used in fragment shaders because maxStorageBuffersInFragmentStage === 0' + ); + t.skipIf( + t.params.stage === 'fragment' && + apiResource.storageTexture !== undefined && + t.device.limits.maxStorageTexturesInFragmentStage === 0, + 'Storage textures can not be used in fragment shaders because maxStorageTexturesInFragmentStage === 0' + ); + } t.skipIfTextureViewDimensionNotSupported(wgslResource.texture?.viewDimension); const emptyVS = ` @vertex diff --git a/src/webgpu/gpu_test.ts b/src/webgpu/gpu_test.ts index ffc20f5f2c22..53e4a1481791 100644 --- a/src/webgpu/gpu_test.ts +++ b/src/webgpu/gpu_test.ts @@ -67,6 +67,17 @@ import { import { createTextureFromTexelViews } from './util/texture.js'; import { reifyExtent3D, reifyOrigin3D } from './util/unions.js'; +// Declarations for WebGPU items we want tests for that are not yet officially part of the spec. +declare global { + // MAINTENANCE_TODO: remove once added to @webgpu/types + interface GPUSupportedLimits { + readonly maxStorageBuffersInFragmentStage?: number; + readonly maxStorageTexturesInFragmentStage?: number; + readonly maxStorageBuffersInVertexStage?: number; + readonly maxStorageTexturesInVertexStage?: number; + } +} + const devicePool = new DevicePool(); // MAINTENANCE_TODO: When DevicePool becomes able to provide multiple devices at once, use the