diff --git a/src/webgpu/api/operation/adapter/requestAdapter.spec.ts b/src/webgpu/api/operation/adapter/requestAdapter.spec.ts index 629b8213c16d..28b735f477b7 100644 --- a/src/webgpu/api/operation/adapter/requestAdapter.spec.ts +++ b/src/webgpu/api/operation/adapter/requestAdapter.spec.ts @@ -17,6 +17,7 @@ import { Fixture } from '../../../../common/framework/fixture.js'; import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { getGPU } from '../../../../common/util/navigator_gpu.js'; import { assert, objectEquals, iterRange } from '../../../../common/util/util.js'; +import { getDefaultLimitsForAdapter, kLimits } from '../../../capability_info.js'; export const g = makeTestGroup(Fixture); @@ -122,3 +123,110 @@ g.test('requestAdapter_no_parameters') const adapter = await getGPU(t.rec).requestAdapter(); await testAdapter(adapter); }); + +g.test('requestAdapter_check_capability_guarantees') + .desc(`request adapter and check adapter capability guarantees`) + .fn(async t => { + const adapter = await getGPU(t.rec).requestAdapter(); + assert(adapter !== null); + + const features = adapter.features; + t.expect( + features.has('texture-compression-bc') || + (features.has('texture-compression-etc2') && features.has('texture-compression-astc')) + ); + + const limits = adapter.limits; + const limitInfo = getDefaultLimitsForAdapter(adapter); + for (const limit of kLimits) { + t.expect( + limits[limit] >= limitInfo[limit].default, + `Expected ${limit} >= default: ${limits[limit]} < ${limitInfo[limit].default}` + ); + if (limitInfo[limit].class === 'alignment') { + t.expect( + Math.log2(limits[limit]) % 1 === 0, + `Expected alignment-class ${limit} to be a power of 2: ${limits[limit]} is not a power of 2` + ); + } + } + + const maxBindingsPerBindGroup = limits['maxBindingsPerBindGroup']; + const maxBindingsPerShaderStage = + limits['maxSampledTexturesPerShaderStage'] + + limits['maxSamplersPerShaderStage'] + + limits['maxStorageBuffersPerShaderStage'] + + limits['maxStorageTexturesPerShaderStage'] + + limits['maxUniformBuffersPerShaderStage']; + const maxShaderStagesPerPipeline = 2; + t.expect( + maxBindingsPerBindGroup >= maxBindingsPerShaderStage * maxShaderStagesPerPipeline, + `Expected maxBindingsPerBindGroup >= max bindings per shader stage × max shader stages per pipeline: ${maxBindingsPerBindGroup} < ${ + maxBindingsPerShaderStage * maxShaderStagesPerPipeline + }` + ); + + t.expect( + limits['maxBindGroups'] <= limits['maxBindGroupsPlusVertexBuffers'], + `Expected maxBindGroups <= maxBindGroupsPlusVertexBuffers: ${limits['maxBindGroups']} > ${limits['maxBindGroupsPlusVertexBuffers']}` + ); + + t.expect( + limits['maxVertexBuffers'] <= limits['maxBindGroupsPlusVertexBuffers'], + `Expected maxVertexBuffers <= maxBindGroupsPlusVertexBuffers: ${limits['maxVertexBuffers']} > ${limits['maxBindGroupsPlusVertexBuffers']}` + ); + + t.expect( + limits['minUniformBufferOffsetAlignment'] >= 32, + `Expected minUniformBufferOffsetAlignment >= 32: ${limits['minUniformBufferOffsetAlignment']} < 32` + ); + + t.expect( + limits['minStorageBufferOffsetAlignment'] >= 32, + `Expected minStorageBufferOffsetAlignment >= 32: ${limits['minStorageBufferOffsetAlignment']} < 32` + ); + + t.expect( + limits['maxUniformBufferBindingSize'] <= limits['maxBufferSize'], + `Expected maxUniformBufferBindingSize <= maxBufferSize: ${limits['maxUniformBufferBindingSize']} > ${limits['maxBufferSize']}` + ); + + t.expect( + limits['maxStorageBufferBindingSize'] <= limits['maxBufferSize'], + `Expected maxStorageBufferBindingSize <= maxBufferSize: ${limits['maxStorageBufferBindingSize']} > ${limits['maxBufferSize']}` + ); + + t.expect( + limits['maxStorageBufferBindingSize'] % 4 === 0, + `Expected maxStorageBufferBindingSize to be a multiple of 4 bytes: ${limits['maxStorageBufferBindingSize']} is not a multiple of 4` + ); + + t.expect( + limits['maxVertexBufferArrayStride'] % 4 === 0, + `Expected maxStorageBufferBindingSize to be a multiple of 4 bytes: ${limits['maxVertexBufferArrayStride']} is not a multiple of 4` + ); + + t.expect( + limits['maxComputeWorkgroupSizeX'] <= limits['maxComputeInvocationsPerWorkgroup'], + `Expected maxComputeWorkgroupSizeX <= maxComputeInvocationsPerWorkgroup: ${limits['maxComputeWorkgroupSizeX']} > ${limits['maxComputeInvocationsPerWorkgroup']}` + ); + + t.expect( + limits['maxComputeWorkgroupSizeY'] <= limits['maxComputeInvocationsPerWorkgroup'], + `Expected maxComputeWorkgroupSizeY <= maxComputeInvocationsPerWorkgroup: ${limits['maxComputeWorkgroupSizeY']} > ${limits['maxComputeInvocationsPerWorkgroup']}` + ); + + t.expect( + limits['maxComputeWorkgroupSizeZ'] <= limits['maxComputeInvocationsPerWorkgroup'], + `Expected maxComputeWorkgroupSizeZ <= maxComputeInvocationsPerWorkgroup: ${limits['maxComputeWorkgroupSizeZ']} > ${limits['maxComputeInvocationsPerWorkgroup']}` + ); + + const maxComputeWorkgroupSizeProduct = + limits['maxComputeWorkgroupSizeX'] * + limits['maxComputeWorkgroupSizeY'] * + limits['maxComputeWorkgroupSizeZ']; + t.expect( + limits['maxComputeInvocationsPerWorkgroup'] <= maxComputeWorkgroupSizeProduct, + `Expected maxComputeInvocationsPerWorkgroup <= maxComputeWorkgroupSizeX x maxComputeWorkgroupSizeY x maxComputeWorkgroupSizeZ: ${limits['maxComputeInvocationsPerWorkgroup']} > ${maxComputeWorkgroupSizeProduct}` + ); + }); diff --git a/src/webgpu/capability_info.ts b/src/webgpu/capability_info.ts index 3626877544af..f0b5e938a134 100644 --- a/src/webgpu/capability_info.ts +++ b/src/webgpu/capability_info.ts @@ -667,6 +667,7 @@ const [ 'maxTextureArrayLayers': [ , 256, 256, ], 'maxBindGroups': [ , 4, 4, ], + 'maxBindGroupsPlusVertexBuffers': [ , 24, 24, ], 'maxBindingsPerBindGroup': [ , 1000, 1000, ], 'maxDynamicUniformBuffersPerPipelineLayout': [ , 8, 8, ], 'maxDynamicStorageBuffersPerPipelineLayout': [ , 4, 4, ], diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index fa57ff978139..61f6c3ac680d 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -2,6 +2,7 @@ "_comment": "SEMI AUTO-GENERATED: Please read docs/adding_timing_metadata.md.", "webgpu:api,operation,adapter,requestAdapter:requestAdapter:*": { "subcaseMS": 152.083 }, "webgpu:api,operation,adapter,requestAdapter:requestAdapter_no_parameters:*": { "subcaseMS": 384.601 }, + "webgpu:api,operation,adapter,requestAdapter:requestAdapter_check_capability_guarantees:*": { "subcaseMS": 30.1 }, "webgpu:api,operation,adapter,requestAdapterInfo:adapter_info:*": { "subcaseMS": 136.601 }, "webgpu:api,operation,adapter,requestAdapterInfo:adapter_info_with_hints:*": { "subcaseMS": 0.101 }, "webgpu:api,operation,adapter,requestDevice:default:*": { "subcaseMS": 19.450 },