From 647ce0036583918557c9ac18f4b60bea7fd8fdc0 Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Mon, 5 Aug 2024 14:54:33 -0700 Subject: [PATCH] Switch compat WGSL errors to pipeline errors https://github.com/gpuweb/cts/issues/3893 --- .../render_pipeline/unsupported_wgsl.spec.ts | 309 ++++++++++++++++++ .../shader_module/shader_module.spec.ts | 156 --------- src/webgpu/listing_meta.json | 12 +- 3 files changed, 316 insertions(+), 161 deletions(-) create mode 100644 src/webgpu/compat/api/validation/render_pipeline/unsupported_wgsl.spec.ts delete mode 100644 src/webgpu/compat/api/validation/shader_module/shader_module.spec.ts diff --git a/src/webgpu/compat/api/validation/render_pipeline/unsupported_wgsl.spec.ts b/src/webgpu/compat/api/validation/render_pipeline/unsupported_wgsl.spec.ts new file mode 100644 index 000000000000..805203870903 --- /dev/null +++ b/src/webgpu/compat/api/validation/render_pipeline/unsupported_wgsl.spec.ts @@ -0,0 +1,309 @@ +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 render pipeline 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) + .combine('async', [false, true] as const) + ) + .fn(t => { + const { entryPoint, async } = 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 isValid = !t.isCompatibility || entryPoint === 'fsWithoutSampleMaskUsage'; + t.doCreateRenderPipelineTest(async, isValid, { + layout: 'auto', + vertex: { module }, + fragment: { + module, + entryPoint, + targets: [{ format: 'rgba8unorm' }], + }, + }); + }); + +g.test('sample_index') + .desc( + ` +Tests that you can not create a render pipeline 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) + .combine('async', [false, true] as const) + ) + .fn(t => { + const { entryPoint, async } = 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 isValid = !t.isCompatibility || entryPoint === 'fsWithoutSampleIndexUsage'; + t.doCreateRenderPipelineTest(async, isValid, { + layout: 'auto', + vertex: { module }, + fragment: { + module, + entryPoint, + targets: [{ format: 'rgba8unorm' }], + }, + }); + }); + +g.test('interpolate') + .desc( + `Tests that you can not create a render pipeline that uses interpolate(linear), interpolate(...,sample), + interpolate(flat), nor interpolate(flat, first) in compat mode.` + ) + .params(u => + u + .combineWithParams([ + { success: false, 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) + .combine('entryPoint', [ + 'fsWithoutInterpolationUsage', + 'fsWithInterpolationUsage1', + 'fsWithInterpolationUsage2', + 'fsWithInterpolationUsage3', + ] as const) + .combine('async', [false, true] as const) + ) + .fn(t => { + const { interpolate, success, entryPoint, async } = 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 isValid = success || !t.isCompatibility || entryPoint === 'fsWithoutInterpolationUsage'; + t.doCreateRenderPipelineTest(async, isValid, { + layout: 'auto', + vertex: { module }, + fragment: { + entryPoint, + module, + targets: [{ format: 'rgba8unorm' }], + }, + }); + }); + +g.test('unsupportedStorageTextureFormats,computePipeline') + .desc( + `Tests that you can not create a compute pipeline that uses an + unsupported storage texture format in compat mode.` + ) + .params(u => + u + .combine('format', kCompatModeUnsupportedStorageTextureFormats) + .combine('entryPoint', ['csWithoutStorageUsage', 'csWithStorageUsage'] as const) + .combine('async', [false, true] as const) + ) + .fn(t => { + const { format, entryPoint, 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 csWithoutStorageUsage() { + } + @compute @workgroup_size(1) fn csWithStorageUsage() { + _ = textureLoad(s, vec2u(0)); + } + `, + }); + + const isValid = !t.isCompatibility || entryPoint === 'csWithoutStorageUsage'; + t.doCreateComputePipelineTest(async, isValid, { + layout: 'auto', + compute: { module, entryPoint }, + }); + }); + +g.test('unsupportedStorageTextureFormats,renderPipeline') + .desc( + `Tests that you can not create a render pipeline that uses an + unsupported storage texture format in compat mode.` + ) + .params(u => + u + .combine('format', kCompatModeUnsupportedStorageTextureFormats) + .combine('entryPoint', ['vsWithoutStorageUsage', 'vsWithStorageUsage'] as const) + .combine('async', [false, true] as const) + ) + .fn(t => { + const { format, entryPoint, async } = t.params; + + const module = t.device.createShaderModule({ + code: ` + @group(0) @binding(0) var s: texture_storage_2d<${format}, read>; + @vertex fn vsWithoutStorageUsage() -> @builtin(position) vec4f { + return vec4f(0); + } + @vertex fn vsWithStorageUsage() -> @builtin(position) vec4f { + _ = textureLoad(s, vec2u(0)); + return vec4f(0); + } + `, + }); + + const isValid = !t.isCompatibility || entryPoint === 'vsWithoutStorageUsage'; + t.doCreateRenderPipelineTest(async, isValid, { + layout: 'auto', + vertex: { module, entryPoint }, + }); + }); + +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,computePipeline') + .desc( + `Tests that you can not create a compute pipeline that uses textureLoad with a depth texture in compat mode.` + ) + .params(u => + u + .combine('type', kDepthTextureTypes) + .combine('entryPoint', ['csWithoutDepthUsage', 'csWithDepthUsage'] as const) + .combine('async', [false, true] as const) + ) + .fn(t => { + const { type, entryPoint, async } = t.params; + const params = kDepthTextureTypeToParams[type]; + + const module = t.device.createShaderModule({ + code: ` + @group(0) @binding(0) var t: ${type}; + @compute @workgroup_size(1) fn csWithoutDepthUsage() { + } + @compute @workgroup_size(1) fn csWithDepthUsage() { + _ = textureLoad(t, ${params}); + } + `, + }); + + const isValid = !t.isCompatibility; + t.doCreateComputePipelineTest(async, isValid, { + layout: 'auto', + compute: { module, entryPoint }, + }); + }); + +g.test('textureLoad_with_depth_textures,renderPipeline') + .desc( + `Tests that you can not create a render pipeline that uses textureLoad with a depth texture in compat mode.` + ) + .params(u => + u + .combine('type', kDepthTextureTypes) + .combine('entryPoint', ['vsWithoutDepthUsage', 'vsWithDepthUsage'] as const) + .combine('async', [false, true] as const) + ) + .fn(t => { + const { type, entryPoint, async } = t.params; + const params = kDepthTextureTypeToParams[type]; + + const module = t.device.createShaderModule({ + code: ` + @group(0) @binding(0) var t: ${type}; + @vertex fn vsWithoutDepthUsage() -> @builtin(position) vec4f { + return vec4f(0); + } + @vertex fn vsWithDepthUsage() -> @builtin(position) vec4f { + _ = textureLoad(t, ${params}); + return vec4f(0); + } + `, + }); + + const isValid = !t.isCompatibility; + t.doCreateRenderPipelineTest(async, isValid, { + layout: 'auto', + vertex: { module, entryPoint }, + }); + }); 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 deleted file mode 100644 index d52dc20f34ce..000000000000 --- a/src/webgpu/compat/api/validation/shader_module/shader_module.spec.ts +++ /dev/null @@ -1,156 +0,0 @@ -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.expectGPUErrorInCompatibilityMode( - '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.expectGPUErrorInCompatibilityMode( - '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: false, 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.expectGPUErrorInCompatibilityMode( - '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.expectGPUErrorInCompatibilityMode( - '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.expectGPUErrorInCompatibilityMode( - '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 2d100ac6b6a7..f4929c57d35f 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -863,11 +863,13 @@ "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,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:textureLoad_with_depth_textures:*": { "subcaseMS": 1.259 }, - "webgpu:compat,api,validation,shader_module,shader_module:unsupportedStorageTextureFormats:*": { "subcaseMS": 1.206 }, + "webgpu:compat,api,validation,render_pipeline,unsupported_wgsl:interpolate:*": { "subcaseMS": 3.488 }, + "webgpu:compat,api,validation,render_pipeline,unsupported_wgsl:sample_index:*": { "subcaseMS": 0.487 }, + "webgpu:compat,api,validation,render_pipeline,unsupported_wgsl:sample_mask:*": { "subcaseMS": 0.408 }, + "webgpu:compat,api,validation,render_pipeline,unsupported_wgsl:textureLoad_with_depth_textures,computePipeline:*": { "subcaseMS": 1.259 }, + "webgpu:compat,api,validation,render_pipeline,unsupported_wgsl:textureLoad_with_depth_textures,renderPipeline:*": { "subcaseMS": 1.259 }, + "webgpu:compat,api,validation,render_pipeline,unsupported_wgsl:unsupportedStorageTextureFormats,computePipeline:*": { "subcaseMS": 1.206 }, + "webgpu:compat,api,validation,render_pipeline,unsupported_wgsl:unsupportedStorageTextureFormats,renderPipeline:*": { "subcaseMS": 1.206 }, "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 },