From 5fb5afd133f01a5daf82197fee817e84663399aa Mon Sep 17 00:00:00 2001 From: Greggman Date: Fri, 31 May 2024 07:42:52 +0900 Subject: [PATCH] Update compat tests for shader module creation (#3769) The spec changed such that several errors that happened at pipeline creation time now happen at shader module creation time. This tests will fail on compat until implementations are updated. They are skipped on core. --- .../render_pipeline/shader_module.spec.ts | 281 ------------------ .../shader_module/shader_module.spec.ts | 156 ++++++++++ src/webgpu/listing_meta.json | 10 +- 3 files changed, 161 insertions(+), 286 deletions(-) delete mode 100644 src/webgpu/compat/api/validation/render_pipeline/shader_module.spec.ts create mode 100644 src/webgpu/compat/api/validation/shader_module/shader_module.spec.ts diff --git a/src/webgpu/compat/api/validation/render_pipeline/shader_module.spec.ts b/src/webgpu/compat/api/validation/render_pipeline/shader_module.spec.ts deleted file mode 100644 index 56da7d355e0f..000000000000 --- a/src/webgpu/compat/api/validation/render_pipeline/shader_module.spec.ts +++ /dev/null @@ -1,281 +0,0 @@ -export const description = ` -Tests limitations of createRenderPipeline related to shader modules in compat mode. -`; - -import { makeTestGroup } from '../../../../../common/framework/test_group.js'; -import { kCompatModeUnsupportedStorageTextureFormats } from '../../../../format_info.js'; -import { CompatibilityTest } from '../../../compatibility_test.js'; - -export const g = makeTestGroup(CompatibilityTest); - -g.test('sample_mask') - .desc( - ` -Tests that you can not create a render pipeline with a shader module that uses sample_mask in compat mode. - -- Test that a pipeline with a shader that uses sample_mask fails. -- Test that a pipeline that references a module that has a shader that uses sample_mask - but the pipeline does not reference that shader succeeds. - ` - ) - .params(u => - u.combine('entryPoint', ['fsWithoutSampleMaskUsage', 'fsWithSampleMaskUsage'] as const) - ) - .fn(t => { - const { entryPoint } = t.params; - - const module = t.device.createShaderModule({ - code: ` - @vertex fn vs() -> @builtin(position) vec4f { - return vec4f(1); - } - struct Output { - @builtin(sample_mask) mask_out: u32, - @location(0) color : vec4f, - } - @fragment fn fsWithoutSampleMaskUsage() -> @location(0) vec4f { - return vec4f(1.0, 1.0, 1.0, 1.0); - } - @fragment fn fsWithSampleMaskUsage() -> Output { - var o: Output; - // We need to make sure this sample_mask isn't optimized out even if its value equals "no op". - o.mask_out = 0xFFFFFFFFu; - o.color = vec4f(1.0, 1.0, 1.0, 1.0); - return o; - } - `, - }); - - const pipelineDescriptor: GPURenderPipelineDescriptor = { - layout: 'auto', - vertex: { - module, - entryPoint: 'vs', - }, - fragment: { - module, - entryPoint, - targets: [ - { - format: 'rgba8unorm', - }, - ], - }, - multisample: { - count: 4, - }, - }; - - const isValid = entryPoint === 'fsWithoutSampleMaskUsage'; - t.expectGPUError( - 'validation', - () => t.device.createRenderPipeline(pipelineDescriptor), - !isValid - ); - }); - -g.test('sample_index') - .desc( - ` -Tests that you can not create a render pipeline with a shader module that uses sample_index in compat mode. - -- Test that a pipeline with a shader that uses sample_index fails. -- Test that a pipeline that references a module that has a shader that uses sample_index - but the pipeline does not reference that shader succeeds. - ` - ) - .params(u => - u.combine('entryPoint', ['fsWithoutSampleIndexUsage', 'fsWithSampleIndexUsage'] as const) - ) - .fn(t => { - const { entryPoint } = t.params; - - const module = t.device.createShaderModule({ - code: ` - @vertex fn vs() -> @builtin(position) vec4f { - return vec4f(1); - } - @fragment fn fsWithoutSampleIndexUsage() -> @location(0) vec4f { - return vec4f(0); - } - @fragment fn fsWithSampleIndexUsage(@builtin(sample_index) sampleIndex: u32) -> @location(0) vec4f { - _ = sampleIndex; - return vec4f(0); - } - `, - }); - - const pipelineDescriptor: GPURenderPipelineDescriptor = { - layout: 'auto', - vertex: { - module, - entryPoint: 'vs', - }, - fragment: { - module, - entryPoint, - targets: [ - { - format: 'rgba8unorm', - }, - ], - }, - multisample: { - count: 4, - }, - }; - - const isValid = entryPoint === 'fsWithoutSampleIndexUsage'; - t.expectGPUError( - 'validation', - () => t.device.createRenderPipeline(pipelineDescriptor), - !isValid - ); - }); - -g.test('interpolate') - .desc( - ` -Tests that you can not create a render pipeline with a shader module that uses interpolate(linear) nor interpolate(...,sample) in compat mode. - -- Test that a pipeline with a shader that uses interpolate(linear) or interpolate(sample) fails. -- Test that a pipeline that references a module that has a shader that uses interpolate(linear/sample) - but the pipeline does not reference that shader succeeds. - ` - ) - .params(u => - u - .combine('interpolate', [ - '', - '@interpolate(linear)', - '@interpolate(linear, sample)', - '@interpolate(perspective, sample)', - ] as const) - .combine('entryPoint', [ - 'fsWithoutInterpolationUsage', - 'fsWithInterpolationUsage1', - 'fsWithInterpolationUsage2', - 'fsWithInterpolationUsage3', - ] as const) - ) - .fn(t => { - const { entryPoint, interpolate } = t.params; - - const module = t.device.createShaderModule({ - code: ` - struct Vertex { - @builtin(position) pos: vec4f, - @location(0) ${interpolate} color : vec4f, - }; - @vertex fn vs() -> Vertex { - var v: Vertex; - v.pos = vec4f(1); - v.color = vec4f(1); - return v; - } - @fragment fn fsWithoutInterpolationUsage() -> @location(0) vec4f { - return vec4f(1); - } - @fragment fn fsWithInterpolationUsage1(v: Vertex) -> @location(0) vec4f { - return vec4f(1); - } - @fragment fn fsWithInterpolationUsage2(v: Vertex) -> @location(0) vec4f { - return v.pos; - } - @fragment fn fsWithInterpolationUsage3(v: Vertex) -> @location(0) vec4f { - return v.color; - } - `, - }); - - const pipelineDescriptor: GPURenderPipelineDescriptor = { - layout: 'auto', - vertex: { - module, - entryPoint: 'vs', - }, - fragment: { - module, - entryPoint, - targets: [ - { - format: 'rgba8unorm', - }, - ], - }, - }; - - const isValid = entryPoint === 'fsWithoutInterpolationUsage' || interpolate === ''; - t.expectGPUError( - 'validation', - () => t.device.createRenderPipeline(pipelineDescriptor), - !isValid - ); - }); - -g.test('unsupportedStorageTextureFormats,computePipeline') - .desc( - ` -Tests that you can not create a compute pipeline with unsupported storage texture formats in compat mode. - ` - ) - .params(u => - u // - .combine('format', kCompatModeUnsupportedStorageTextureFormats) - .combine('async', [false, true] as const) - ) - .fn(t => { - const { format, async } = t.params; - - const module = t.device.createShaderModule({ - code: ` - @group(0) @binding(0) var s: texture_storage_2d<${format}, read>; - @compute @workgroup_size(1) fn cs() { - _ = textureLoad(s, vec2u(0)); - } - `, - }); - - const pipelineDescriptor: GPUComputePipelineDescriptor = { - layout: 'auto', - compute: { - module, - entryPoint: 'cs', - }, - }; - t.doCreateComputePipelineTest(async, false, pipelineDescriptor); - }); - -g.test('unsupportedStorageTextureFormats,renderPipeline') - .desc( - ` -Tests that you can not create a render pipeline with unsupported storage texture formats in compat mode. - ` - ) - .params(u => - u // - .combine('format', kCompatModeUnsupportedStorageTextureFormats) - .combine('async', [false, true] as const) - ) - .fn(t => { - const { format, async } = t.params; - - const module = t.device.createShaderModule({ - code: ` - @group(0) @binding(0) var s: texture_storage_2d<${format}, read>; - @vertex fn vs() -> @builtin(position) vec4f { - _ = textureLoad(s, vec2u(0)); - return vec4f(0); - } - `, - }); - - const pipelineDescriptor: GPURenderPipelineDescriptor = { - layout: 'auto', - vertex: { - module, - entryPoint: 'vs', - }, - }; - t.doCreateRenderPipelineTest(async, false, pipelineDescriptor); - }); diff --git a/src/webgpu/compat/api/validation/shader_module/shader_module.spec.ts b/src/webgpu/compat/api/validation/shader_module/shader_module.spec.ts new file mode 100644 index 000000000000..6242fb8c2cae --- /dev/null +++ b/src/webgpu/compat/api/validation/shader_module/shader_module.spec.ts @@ -0,0 +1,156 @@ +export const description = ` +Tests limitations of createShaderModule in compat mode. +`; + +import { makeTestGroup } from '../../../../../common/framework/test_group.js'; +import { keysOf } from '../../../../../common/util/data_tables.js'; +import { kCompatModeUnsupportedStorageTextureFormats } from '../../../../format_info.js'; +import { CompatibilityTest } from '../../../compatibility_test.js'; + +export const g = makeTestGroup(CompatibilityTest); + +g.test('sample_mask') + .desc(`Tests that you can not create a shader module that uses sample_mask in compat mode.`) + .fn(t => { + t.expectGPUError( + 'validation', + () => + t.device.createShaderModule({ + code: ` + @vertex fn vs() -> @builtin(position) vec4f { + return vec4f(1); + } + struct Output { + @builtin(sample_mask) mask_out: u32, + @location(0) color : vec4f, + } + @fragment fn fsWithSampleMaskUsage() -> Output { + var o: Output; + // We need to make sure this sample_mask isn't optimized out even if its value equals "no op". + o.mask_out = 0xFFFFFFFFu; + o.color = vec4f(1.0, 1.0, 1.0, 1.0); + return o; + } + `, + }), + true + ); + }); + +g.test('sample_index') + .desc(`Tests that you can not create a shader module that uses sample_index in compat mode.`) + .fn(t => { + t.expectGPUError( + 'validation', + () => + t.device.createShaderModule({ + code: ` + @vertex fn vs() -> @builtin(position) vec4f { + return vec4f(1); + } + @fragment fn fsWithSampleIndexUsage(@builtin(sample_index) sampleIndex: u32) -> @location(0) vec4f { + _ = sampleIndex; + return vec4f(0); + } + `, + }), + true + ); + }); + +g.test('interpolate') + .desc( + `Tests that you can not create a shader module that uses interpolate(linear), interpolate(...,sample), + interpolate(flat), nor interpolate(flat, first) in compat mode.` + ) + .params(u => + u.combineWithParams([ + { success: true, interpolate: '' }, + { success: true, interpolate: '@interpolate(linear)' }, + { success: false, interpolate: '@interpolate(linear, sample)' }, + { success: false, interpolate: '@interpolate(perspective, sample)' }, + { success: false, interpolate: '@interpolate(flat)' }, + { success: false, interpolate: '@interpolate(flat, first)' }, + { success: true, interpolate: '@interpolate(flat, either)' }, + ] as const) + ) + .fn(t => { + const { interpolate, success } = t.params; + + t.expectGPUError( + 'validation', + () => + t.device.createShaderModule({ + code: ` + struct Vertex { + @builtin(position) pos: vec4f, + @location(0) ${interpolate} color : vec4f, + }; + @vertex fn vs() -> Vertex { + var v: Vertex; + v.pos = vec4f(1); + v.color = vec4f(1); + return v; + } + @fragment fn fsWithInterpolationUsage(v: Vertex) -> @location(0) vec4f { + return v.color; + } + `, + }), + !success + ); + }); + +g.test('unsupportedStorageTextureFormats') + .desc( + `Tests that you can not create a shader module with unsupported storage texture formats in compat mode.` + ) + .params(u => u.combine('format', kCompatModeUnsupportedStorageTextureFormats)) + .fn(t => { + const { format } = t.params; + + t.expectGPUError( + 'validation', + () => + t.device.createShaderModule({ + code: ` + @group(0) @binding(0) var s: texture_storage_2d<${format}, read>; + @compute @workgroup_size(1) fn cs() { + _ = textureLoad(s, vec2u(0)); + } + `, + }), + true + ); + }); + +const kDepthTextureTypeToParams = { + texture_depth_2d: 'vec2u(0), 0', + texture_depth_2d_array: 'vec2u(0), 0, 0', + texture_depth_multisampled_2d: 'vec2u(0), 0', +} as const; +const kDepthTextureTypes = keysOf(kDepthTextureTypeToParams); + +g.test('textureLoad_with_depth_textures') + .desc( + `Tests that you can not create a shader module that uses textureLoad with a depth texture in compat mode.` + ) + .params(u => u.combine('type', kDepthTextureTypes)) + .fn(t => { + const { type } = t.params; + const params = kDepthTextureTypeToParams[type]; + + t.expectGPUError( + 'validation', + () => + t.device.createShaderModule({ + code: ` + @group(0) @binding(0) var t: ${type}; + @compute @workgroup_size(1) fn cs() { + _ = textureLoad(t, ${params}); + } + `, + }), + true + ); + }); diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index b251aa443fe3..cd9f54d12f49 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -862,12 +862,12 @@ "webgpu:compat,api,validation,encoding,programmable,pipeline_bind_group_compat:twoDifferentTextureViews,render_pass,used:*": { "subcaseMS": 0.000 }, "webgpu:compat,api,validation,render_pipeline,depth_stencil_state:depthBiasClamp:*": { "subcaseMS": 1.604 }, "webgpu:compat,api,validation,render_pipeline,fragment_state:colorState:*": { "subcaseMS": 32.604 }, - "webgpu:compat,api,validation,render_pipeline,shader_module:interpolate:*": { "subcaseMS": 1.502 }, - "webgpu:compat,api,validation,render_pipeline,shader_module:sample_index:*": { "subcaseMS": 1.502 }, - "webgpu:compat,api,validation,render_pipeline,shader_module:sample_mask:*": { "subcaseMS": 14.801 }, - "webgpu:compat,api,validation,render_pipeline,shader_module:unsupportedStorageTextureFormats,computePipeline:*": { "subcaseMS": 0.601 }, - "webgpu:compat,api,validation,render_pipeline,shader_module:unsupportedStorageTextureFormats,renderPipeline:*": { "subcaseMS": 0.601 }, "webgpu:compat,api,validation,render_pipeline,vertex_state:maxVertexAttributesVertexIndexInstanceIndex:*": { "subcaseMS": 3.700 }, + "webgpu:compat,api,validation,shader_module,shader_module:interpolate:*": { "subcaseMS": 3.488 }, + "webgpu:compat,api,validation,shader_module,shader_module:sample_index:*": { "subcaseMS": 0.487 }, + "webgpu:compat,api,validation,shader_module,shader_module:sample_mask:*": { "subcaseMS": 0.408 }, + "webgpu:compat,api,validation,shader_module,shader_module:unsupportedStorageTextureFormats:*": { "subcaseMS": 1.206 }, + "webgpu:compat,api,validation,shader_module,shader_module:textureLoad_with_depth_textures:*": { "subcaseMS": 1.259 }, "webgpu:compat,api,validation,texture,createTexture:depthOrArrayLayers_incompatible_with_textureBindingViewDimension:*": { "subcaseMS": 12.712 }, "webgpu:compat,api,validation,texture,createTexture:format_reinterpretation:*": { "subcaseMS": 7.012 }, "webgpu:compat,api,validation,texture,createTexture:invalidTextureBindingViewDimension:*": { "subcaseMS": 6.022 },