From 717ad74436477085c799a0e67cf3ba8ff33d235a Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Thu, 14 Mar 2024 16:19:48 -0700 Subject: [PATCH] Cleanup texture builtin WGSL validation tests Also add in_fragment_only tests that tests these builtins only work in the fragment shader. --- src/webgpu/listing_meta.json | 2 + .../call/builtin/shader_stage_utils.ts | 64 ++++++++++++++++ .../call/builtin/textureSample.spec.ts | 54 ++++++++++++- .../call/builtin/textureSampleCompare.spec.ts | 76 ++++++++++++++++--- 4 files changed, 183 insertions(+), 13 deletions(-) create mode 100644 src/webgpu/shader/validation/expression/call/builtin/shader_stage_utils.ts diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index ea3f8b3397bb..682d8283ba63 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1944,11 +1944,13 @@ "webgpu:shader,validation,expression,call,builtin,textureSample:coords_argument:*": { "subcaseMS": 1.342 }, "webgpu:shader,validation,expression,call,builtin,textureSample:offset_argument,non_const:*": { "subcaseMS": 1.604 }, "webgpu:shader,validation,expression,call,builtin,textureSample:offset_argument:*": { "subcaseMS": 1.401 }, + "webgpu:shader,validation,expression,call,builtin,textureSample:only_in_fragment:*": { "subcaseMS": 1.121 }, "webgpu:shader,validation,expression,call,builtin,textureSampleCompare:array_index_argument:*": { "subcaseMS": 1.932 }, "webgpu:shader,validation,expression,call,builtin,textureSampleCompare:coords_argument:*": { "subcaseMS": 1.282 }, "webgpu:shader,validation,expression,call,builtin,textureSampleCompare:depth_ref_argument:*": { "subcaseMS": 1.563 }, "webgpu:shader,validation,expression,call,builtin,textureSampleCompare:offset_argument,non_const:*": { "subcaseMS": 1.720 }, "webgpu:shader,validation,expression,call,builtin,textureSampleCompare:offset_argument:*": { "subcaseMS": 1.540 }, + "webgpu:shader,validation,expression,call,builtin,textureSampleCompare:only_in_fragment:*": { "subcaseMS": 1.121 }, "webgpu:shader,validation,expression,call,builtin,unpack4xI8:bad_args:*": { "subcaseMS": 121.263 }, "webgpu:shader,validation,expression,call,builtin,unpack4xI8:must_use:*": { "subcaseMS": 35.200 }, "webgpu:shader,validation,expression,call,builtin,unpack4xI8:supported:*": { "subcaseMS": 24.150 }, diff --git a/src/webgpu/shader/validation/expression/call/builtin/shader_stage_utils.ts b/src/webgpu/shader/validation/expression/call/builtin/shader_stage_utils.ts new file mode 100644 index 000000000000..34e5bc530d9d --- /dev/null +++ b/src/webgpu/shader/validation/expression/call/builtin/shader_stage_utils.ts @@ -0,0 +1,64 @@ +/** + * Use to test that certain WGSL builtins are only available in the fragment stage. + * Create WGSL that defines a function "foo" and its required variables that uses + * the builtin being tested. Append it to these code strings then compile. It should + * succeed or fail based on the value `expectSuccess`. + * + * See ./textureSample.spec.ts was one example + */ +export const kEntryPointsToValidateFragmentOnlyBuiltins = { + none: { + expectSuccess: true, + code: ``, + }, + fragment: { + expectSuccess: true, + code: ` + @fragment + fn main() { + foo(); + } + `, + }, + vertex: { + expectSuccess: false, + code: ` + @vertex + fn main() -> @builtin(position) vec4f { + foo(); + return vec4f(); + } + `, + }, + compute: { + expectSuccess: false, + code: ` + @compute @workgroup_size(1) + fn main() { + foo(); + } + `, + }, + fragment_and_compute: { + expectSuccess: false, + code: ` + @fragment + fn main1() { + foo(); + } + + @compute @workgroup_size(1) + fn main2() { + foo(); + } + `, + }, + compute_without_call: { + expectSuccess: true, + code: ` + @compute @workgroup_size(1) + fn main() { + } + `, + }, +}; diff --git a/src/webgpu/shader/validation/expression/call/builtin/textureSample.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/textureSample.spec.ts index f4cf696de029..4bdafdef417d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/textureSample.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/textureSample.spec.ts @@ -8,6 +8,8 @@ Validation tests for the ${builtin}() builtin. * test textureSample offset parameter must be correct type * test textureSample offset parameter must be a const-expression * test textureSample offset parameter must be between -8 and +7 inclusive + +note: uniformity validation is covered in src/webgpu/shader/validation/uniformity/uniformity.spec.ts `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; @@ -22,6 +24,8 @@ import { } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; +import { kEntryPointsToValidateFragmentOnlyBuiltins } from './shader_stage_utils.js'; + type TextureSampleArguments = { coordsArgType: ScalarType | VectorType; hasArrayIndexArg?: boolean; @@ -55,6 +59,7 @@ const kValuesTypes = objectsToRecord(kAllScalarsAndVectors); export const g = makeTestGroup(ShaderValidationTest); g.test('coords_argument') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#texturesample') .desc( ` Validates that only incorrect coords arguments are rejected by ${builtin} @@ -68,10 +73,9 @@ Validates that only incorrect coords arguments are rejected by ${builtin} .combine('value', [-1, 0, 1] as const) // filter out unsigned types with negative values .filter(t => !isUnsignedType(kValuesTypes[t.coordType]) || t.value >= 0) - .expand('offset', ({ textureType }) => { - const offset = kValidTextureSampleParameterTypes[textureType].offsetArgType; - return offset ? [false, true] : [false]; - }) + .expand('offset', t => + kValidTextureSampleParameterTypes[t.textureType].offsetArgType ? [false, true] : [false] + ) ) .fn(t => { const { textureType, coordType, offset, value } = t.params; @@ -99,6 +103,7 @@ Validates that only incorrect coords arguments are rejected by ${builtin} }); g.test('array_index_argument') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#texturesample') .desc( ` Validates that only incorrect array_index arguments are rejected by ${builtin} @@ -114,6 +119,9 @@ Validates that only incorrect array_index arguments are rejected by ${builtin} .combine('value', [-9, -8, 0, 7, 8]) // filter out unsigned types with negative values .filter(t => !isUnsignedType(kValuesTypes[t.arrayIndexType]) || t.value >= 0) + .expand('offset', t => + kValidTextureSampleParameterTypes[t.textureType].offsetArgType ? [false, true] : [false] + ) ) .fn(t => { const { textureType, arrayIndexType, value } = t.params; @@ -139,6 +147,7 @@ Validates that only incorrect array_index arguments are rejected by ${builtin} }); g.test('offset_argument') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#texturesample') .desc( ` Validates that only incorrect offset arguments are rejected by ${builtin} @@ -183,6 +192,7 @@ Validates that only incorrect offset arguments are rejected by ${builtin} }); g.test('offset_argument,non_const') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#texturesample') .desc( ` Validates that only non-const offset arguments are rejected by ${builtin} @@ -218,3 +228,39 @@ Validates that only non-const offset arguments are rejected by ${builtin} const expectSuccess = varType === 'c'; t.expectCompileResult(expectSuccess, code); }); + +g.test('only_in_fragment') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#texturesample') + .desc( + ` +Validates that ${builtin} must not be used in a compute or vertex shader. +` + ) + .params(u => + u + .combine('textureType', kTextureTypes) + .combine('entryPoint', keysOf(kEntryPointsToValidateFragmentOnlyBuiltins)) + .expand('offset', t => + kValidTextureSampleParameterTypes[t.textureType].offsetArgType ? [false, true] : [false] + ) + ) + .fn(t => { + const { textureType, entryPoint, offset } = t.params; + const { coordsArgType, hasArrayIndexArg, offsetArgType } = + kValidTextureSampleParameterTypes[textureType]; + + const coordWGSL = coordsArgType.create(0).wgsl(); + const arrayWGSL = hasArrayIndexArg ? ', 0' : ''; + const offsetWGSL = offset ? `, ${offsetArgType?.create(0).wgsl()}` : ''; + + const config = kEntryPointsToValidateFragmentOnlyBuiltins[entryPoint]; + const code = ` +${config.code} +@group(0) @binding(0) var s: sampler; +@group(0) @binding(1) var t: ${textureType}; + +fn foo() { + _ = textureSample(t, s, ${coordWGSL}${arrayWGSL}${offsetWGSL}); +}`; + t.expectCompileResult(config.expectSuccess, code); + }); diff --git a/src/webgpu/shader/validation/expression/call/builtin/textureSampleCompare.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/textureSampleCompare.spec.ts index aa7e769b94f2..adbd6111684c 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/textureSampleCompare.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/textureSampleCompare.spec.ts @@ -8,6 +8,8 @@ Validation tests for the ${builtin}() builtin. * test textureSampleCompare offset parameter must be correct type * test textureSampleCompare offset parameter must be a const-expression * test textureSampleCompare offset parameter must be between -8 and +7 inclusive + +note: uniformity validation is covered in src/webgpu/shader/validation/uniformity/uniformity.spec.ts `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; @@ -22,6 +24,8 @@ import { } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; +import { kEntryPointsToValidateFragmentOnlyBuiltins } from './shader_stage_utils.js'; + type TextureSampleCompareArguments = { coordsArgType: ScalarType | VectorType; hasArrayIndexArg?: boolean; @@ -45,6 +49,7 @@ const kValuesTypes = objectsToRecord(kAllScalarsAndVectors); export const g = makeTestGroup(ShaderValidationTest); g.test('coords_argument') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#texturesamplecompare') .desc( ` Validates that only incorrect coords arguments are rejected by ${builtin} @@ -58,10 +63,11 @@ Validates that only incorrect coords arguments are rejected by ${builtin} .combine('value', [-1, 0, 1] as const) // filter out unsigned types with negative values .filter(t => !isUnsignedType(kValuesTypes[t.coordType]) || t.value >= 0) - .expand('offset', ({ textureType }) => { - const offset = kValidTextureSampleCompareParameterTypes[textureType].offsetArgType; - return offset ? [false, true] : [false]; - }) + .expand('offset', t => + kValidTextureSampleCompareParameterTypes[t.textureType].offsetArgType + ? [false, true] + : [false] + ) ) .fn(t => { const { textureType, coordType, offset, value } = t.params; @@ -80,7 +86,7 @@ Validates that only incorrect coords arguments are rejected by ${builtin} @group(0) @binding(0) var s: sampler_comparison; @group(0) @binding(1) var t: ${textureType}; @fragment fn fs() -> @location(0) vec4f { - let v = textureSampleCompare(t, s, ${coordWGSL}${arrayWGSL}, 0${offsetWGSL}); + _ = textureSampleCompare(t, s, ${coordWGSL}${arrayWGSL}, 0${offsetWGSL}); return vec4f(0); } `; @@ -89,6 +95,7 @@ Validates that only incorrect coords arguments are rejected by ${builtin} }); g.test('array_index_argument') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#texturesamplecompare') .desc( ` Validates that only incorrect array_index arguments are rejected by ${builtin} @@ -104,6 +111,11 @@ Validates that only incorrect array_index arguments are rejected by ${builtin} .combine('value', [-9, -8, 0, 7, 8]) // filter out unsigned types with negative values .filter(t => !isUnsignedType(kValuesTypes[t.arrayIndexType]) || t.value >= 0) + .expand('offset', t => + kValidTextureSampleCompareParameterTypes[t.textureType].offsetArgType + ? [false, true] + : [false] + ) ) .fn(t => { const { textureType, arrayIndexType, value } = t.params; @@ -119,7 +131,7 @@ Validates that only incorrect array_index arguments are rejected by ${builtin} @group(0) @binding(0) var s: sampler_comparison; @group(0) @binding(1) var t: ${textureType}; @fragment fn fs() -> @location(0) vec4f { - let v = textureSampleCompare(t, s, ${coordWGSL}, ${arrayWGSL}, 0${offsetWGSL}); + _ = textureSampleCompare(t, s, ${coordWGSL}, ${arrayWGSL}, 0${offsetWGSL}); return vec4f(0); } `; @@ -129,6 +141,7 @@ Validates that only incorrect array_index arguments are rejected by ${builtin} }); g.test('depth_ref_argument') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#texturesamplecompare') .desc( ` Validates that only incorrect depth_ref arguments are rejected by ${builtin} @@ -142,6 +155,11 @@ Validates that only incorrect depth_ref arguments are rejected by ${builtin} .combine('value', [-1, 0, 1]) // filter out unsigned types with negative values .filter(t => !isUnsignedType(kValuesTypes[t.depthRefType]) || t.value >= 0) + .expand('offset', t => + kValidTextureSampleCompareParameterTypes[t.textureType].offsetArgType + ? [false, true] + : [false] + ) ) .fn(t => { const { textureType, depthRefType, value } = t.params; @@ -159,7 +177,7 @@ Validates that only incorrect depth_ref arguments are rejected by ${builtin} @group(0) @binding(0) var s: sampler_comparison; @group(0) @binding(1) var t: ${textureType}; @fragment fn fs() -> @location(0) vec4f { - let v = textureSampleCompare(t, s, ${coordWGSL}${arrayWGSL}, ${depthRefWGSL}${offsetWGSL}); + _ = textureSampleCompare(t, s, ${coordWGSL}${arrayWGSL}, ${depthRefWGSL}${offsetWGSL}); return vec4f(0); } `; @@ -168,6 +186,7 @@ Validates that only incorrect depth_ref arguments are rejected by ${builtin} }); g.test('offset_argument') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#texturesamplecompare') .desc( ` Validates that only incorrect offset arguments are rejected by ${builtin} @@ -202,7 +221,7 @@ Validates that only incorrect offset arguments are rejected by ${builtin} @group(0) @binding(0) var s: sampler_comparison; @group(0) @binding(1) var t: ${textureType}; @fragment fn fs() -> @location(0) vec4f { - let v = textureSampleCompare(t, s, ${coordWGSL}${arrayWGSL}, 0, ${offsetWGSL}); + _ = textureSampleCompare(t, s, ${coordWGSL}${arrayWGSL}, 0, ${offsetWGSL}); return vec4f(0); } `; @@ -212,6 +231,7 @@ Validates that only incorrect offset arguments are rejected by ${builtin} }); g.test('offset_argument,non_const') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#texturesamplecompare') .desc( ` Validates that only non-const offset arguments are rejected by ${builtin} @@ -240,10 +260,48 @@ Validates that only non-const offset arguments are rejected by ${builtin} @fragment fn fs() -> @location(0) vec4f { const c = 1; let l = ${offsetArgType?.create(0).wgsl()}; - let v = textureSampleCompare(t, s, ${coordWGSL}${arrayWGSL}, 0, ${offsetWGSL}); + _ = textureSampleCompare(t, s, ${coordWGSL}${arrayWGSL}, 0, ${offsetWGSL}); return vec4f(0); } `; const expectSuccess = varType === 'c'; t.expectCompileResult(expectSuccess, code); }); + +g.test('only_in_fragment') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#texturesample') + .desc( + ` +Validates that ${builtin} must not be used in a compute or vertex shader. +` + ) + .params(u => + u + .combine('textureType', kTextureTypes) + .combine('entryPoint', keysOf(kEntryPointsToValidateFragmentOnlyBuiltins)) + .expand('offset', t => + kValidTextureSampleCompareParameterTypes[t.textureType].offsetArgType + ? [false, true] + : [false] + ) + ) + .fn(t => { + const { textureType, entryPoint, offset } = t.params; + const { coordsArgType, hasArrayIndexArg, offsetArgType } = + kValidTextureSampleCompareParameterTypes[textureType]; + + const coordWGSL = coordsArgType.create(0).wgsl(); + const arrayWGSL = hasArrayIndexArg ? ', 0' : ''; + const offsetWGSL = offset ? `, ${offsetArgType?.create(0).wgsl()}` : ''; + + const config = kEntryPointsToValidateFragmentOnlyBuiltins[entryPoint]; + const code = ` +${config.code} +@group(0) @binding(0) var s: sampler_comparison; +@group(0) @binding(1) var t: ${textureType}; + +fn foo() { + _ = textureSampleCompare(t, s, ${coordWGSL}${arrayWGSL}, 0${offsetWGSL}); +}`; + t.expectCompileResult(config.expectSuccess, code); + });