From 6303cafefba65f16515c725efde47d350521c802 Mon Sep 17 00:00:00 2001 From: Lokbondo Kung Date: Wed, 3 Apr 2024 15:37:24 -0700 Subject: [PATCH] Add validation tests for *Bit builtin functions (#3565) * Add validation tests for *Bit builtin functions * Address comments --- src/webgpu/listing_meta.json | 12 ++ .../call/builtin/firstLeadingBit.spec.ts | 198 ++++++++++++++++++ .../call/builtin/firstTrailingBit.spec.ts | 198 ++++++++++++++++++ .../call/builtin/reverseBits.spec.ts | 198 ++++++++++++++++++ 4 files changed, 606 insertions(+) create mode 100644 src/webgpu/shader/validation/expression/call/builtin/firstLeadingBit.spec.ts create mode 100644 src/webgpu/shader/validation/expression/call/builtin/firstTrailingBit.spec.ts create mode 100644 src/webgpu/shader/validation/expression/call/builtin/reverseBits.spec.ts diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 820b62a51434..e3902612509e 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1981,6 +1981,14 @@ "webgpu:shader,validation,expression,call,builtin,exp2:values:*": { "subcaseMS": 0.410 }, "webgpu:shader,validation,expression,call,builtin,exp:integer_argument:*": { "subcaseMS": 1.356 }, "webgpu:shader,validation,expression,call,builtin,exp:values:*": { "subcaseMS": 0.311 }, + "webgpu:shader,validation,expression,call,builtin,firstLeadingBit:arguments:*": { "subcaseMS": 210.892 }, + "webgpu:shader,validation,expression,call,builtin,firstLeadingBit:float_argument:*": { "subcaseMS": 29.714 }, + "webgpu:shader,validation,expression,call,builtin,firstLeadingBit:must_use:*": { "subcaseMS": 2.214 }, + "webgpu:shader,validation,expression,call,builtin,firstLeadingBit:values:*": { "subcaseMS": 4227.758 }, + "webgpu:shader,validation,expression,call,builtin,firstTrailingBit:arguments:*": { "subcaseMS": 65.356 }, + "webgpu:shader,validation,expression,call,builtin,firstTrailingBit:float_argument:*": { "subcaseMS": 41.249 }, + "webgpu:shader,validation,expression,call,builtin,firstTrailingBit:must_use:*": { "subcaseMS": 1.982 }, + "webgpu:shader,validation,expression,call,builtin,firstTrailingBit:values:*": { "subcaseMS": 4203.191 }, "webgpu:shader,validation,expression,call,builtin,floor:args:*": { "subcaseMS": 4.221 }, "webgpu:shader,validation,expression,call,builtin,floor:integer_argument:*": { "subcaseMS": 48.400 }, "webgpu:shader,validation,expression,call,builtin,floor:must_use:*": { "subcaseMS": 0.170 }, @@ -2038,6 +2046,10 @@ "webgpu:shader,validation,expression,call,builtin,reflect:args:*": { "subcaseMS": 1.000 }, "webgpu:shader,validation,expression,call,builtin,reflect:must_use:*": { "subcaseMS": 1.000 }, "webgpu:shader,validation,expression,call,builtin,reflect:values:*": { "subcaseMS": 1.000 }, + "webgpu:shader,validation,expression,call,builtin,reverseBits:arguments:*": { "subcaseMS": 77.380 }, + "webgpu:shader,validation,expression,call,builtin,reverseBits:float_argument:*": { "subcaseMS": 28.806 }, + "webgpu:shader,validation,expression,call,builtin,reverseBits:must_use:*": { "subcaseMS": 2.063 }, + "webgpu:shader,validation,expression,call,builtin,reverseBits:values:*": { "subcaseMS": 3140.778 }, "webgpu:shader,validation,expression,call,builtin,round:integer_argument:*": { "subcaseMS": 1.834 }, "webgpu:shader,validation,expression,call,builtin,round:values:*": { "subcaseMS": 0.382 }, "webgpu:shader,validation,expression,call,builtin,saturate:integer_argument:*": { "subcaseMS": 1.878 }, diff --git a/src/webgpu/shader/validation/expression/call/builtin/firstLeadingBit.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/firstLeadingBit.spec.ts new file mode 100644 index 000000000000..4aeb7e8bd2c4 --- /dev/null +++ b/src/webgpu/shader/validation/expression/call/builtin/firstLeadingBit.spec.ts @@ -0,0 +1,198 @@ +const builtin = 'firstLeadingBit'; +export const description = ` +Validation tests for the ${builtin}() builtin. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; +import { + Type, + kConcreteIntegerScalarsAndVectors, + kFloatScalarsAndVectors, +} from '../../../../../util/conversion.js'; +import { ShaderValidationTest } from '../../../shader_validation_test.js'; + +import { + fullRangeForType, + kConstantAndOverrideStages, + stageSupportsType, + validateConstOrOverrideBuiltinEval, +} from './const_override_validation.js'; + +export const g = makeTestGroup(ShaderValidationTest); + +const kValuesTypes = objectsToRecord(kConcreteIntegerScalarsAndVectors); + +g.test('values') + .desc( + ` +Validates that constant evaluation and override evaluation of ${builtin}() never errors +` + ) + .params(u => + u + .combine('stage', kConstantAndOverrideStages) + .combine('type', keysOf(kValuesTypes)) + .filter(u => stageSupportsType(u.stage, kValuesTypes[u.type])) + .beginSubcases() + .expand('value', u => fullRangeForType(kValuesTypes[u.type])) + ) + .fn(t => { + const expectedResult = true; // firstLeadingBit() should never error + validateConstOrOverrideBuiltinEval( + t, + builtin, + expectedResult, + [kValuesTypes[t.params.type].create(t.params.value)], + t.params.stage + ); + }); + +// u32 is included here to confirm that validation is failing due to a type issue and not something else. +const kFloatTypes = objectsToRecord([Type.u32, ...kFloatScalarsAndVectors]); + +g.test('float_argument') + .desc( + ` +Validates that float arguments are rejected by ${builtin}() +` + ) + .params(u => u.combine('type', keysOf(kFloatTypes))) + .fn(t => { + const type = kFloatTypes[t.params.type]; + validateConstOrOverrideBuiltinEval( + t, + builtin, + /* expectedResult */ type === Type.u32, + [type.create(0)], + 'constant' + ); + }); + +const kTests: { + readonly [name: string]: { + /** Arguments to pass to the builtin with parentheses. */ + readonly args: string; + /** Should the test case pass. */ + readonly pass: boolean; + /** Additional setup code in the function scope. */ + readonly preamble?: string; + }; +} = { + valid: { + args: '(1u)', + pass: true, + }, + // Number of arguments. + no_parens: { + args: '', + pass: false, + }, + too_few_args: { + args: '()', + pass: false, + }, + too_many_args: { + args: '(1u,2u)', + pass: false, + }, + // Arguments types (only 1 argument for this builtin). + alias: { + args: '(u32_alias(1))', + pass: true, + }, + bool: { + args: '(false)', + pass: false, + }, + vec_bool: { + args: '(vec2(false,true))', + pass: false, + }, + matrix: { + args: '(mat2x2(1,1,1,1))', + pass: false, + }, + atomic: { + args: '(a)', + pass: false, + }, + array: { + preamble: 'var arry: array;', + args: '(arry)', + pass: false, + }, + array_runtime: { + args: '(k.arry)', + pass: false, + }, + struct: { + preamble: 'var x: A;', + args: '(x)', + pass: false, + }, + enumerant: { + args: '(read_write)', + pass: false, + }, + ptr: { + preamble: `var f = 1u; + let p: ptr = &f;`, + args: '(p)', + pass: false, + }, + ptr_deref: { + preamble: `var f = 1u; + let p: ptr = &f;`, + args: '(*p)', + pass: true, + }, + sampler: { + args: '(s)', + pass: false, + }, + texture: { + args: '(t)', + pass: false, + }, +}; + +g.test('arguments') + .desc(`Test compilation validation of ${builtin} with variously shaped and typed arguments`) + .params(u => u.combine('test', keysOf(kTests))) + .fn(t => { + const test = kTests[t.params.test]; + t.expectCompileResult( + test.pass, + `alias u32_alias = u32; + + @group(0) @binding(0) var s: sampler; + @group(0) @binding(1) var t: texture_2d; + + var a: atomic; + + struct A { + i: u32, + } + struct B { + arry: array, + } + @group(0) @binding(3) var k: B; + + + @vertex + fn main() -> @builtin(position) vec4 { + ${test.preamble ? test.preamble : ''} + _ = ${builtin}${test.args}; + return vec4(.4, .2, .3, .1); + }` + ); + }); + +g.test('must_use') + .desc(`Result of ${builtin} must be used`) + .params(u => u.combine('use', [true, false])) + .fn(t => { + const use_it = t.params.use ? '_ = ' : ''; + t.expectCompileResult(t.params.use, `fn f() { ${use_it}${builtin}(1u); }`); + }); diff --git a/src/webgpu/shader/validation/expression/call/builtin/firstTrailingBit.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/firstTrailingBit.spec.ts new file mode 100644 index 000000000000..897a213fb84e --- /dev/null +++ b/src/webgpu/shader/validation/expression/call/builtin/firstTrailingBit.spec.ts @@ -0,0 +1,198 @@ +const builtin = 'firstTrailingBit'; +export const description = ` +Validation tests for the ${builtin}() builtin. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; +import { + Type, + kConcreteIntegerScalarsAndVectors, + kFloatScalarsAndVectors, +} from '../../../../../util/conversion.js'; +import { ShaderValidationTest } from '../../../shader_validation_test.js'; + +import { + fullRangeForType, + kConstantAndOverrideStages, + stageSupportsType, + validateConstOrOverrideBuiltinEval, +} from './const_override_validation.js'; + +export const g = makeTestGroup(ShaderValidationTest); + +const kValuesTypes = objectsToRecord(kConcreteIntegerScalarsAndVectors); + +g.test('values') + .desc( + ` +Validates that constant evaluation and override evaluation of ${builtin}() never errors +` + ) + .params(u => + u + .combine('stage', kConstantAndOverrideStages) + .combine('type', keysOf(kValuesTypes)) + .filter(u => stageSupportsType(u.stage, kValuesTypes[u.type])) + .beginSubcases() + .expand('value', u => fullRangeForType(kValuesTypes[u.type])) + ) + .fn(t => { + const expectedResult = true; // firstTrailingBit() should never error + validateConstOrOverrideBuiltinEval( + t, + builtin, + expectedResult, + [kValuesTypes[t.params.type].create(t.params.value)], + t.params.stage + ); + }); + +// u32 is included here to confirm that validation is failing due to a type issue and not something else. +const kFloatTypes = objectsToRecord([Type.u32, ...kFloatScalarsAndVectors]); + +g.test('float_argument') + .desc( + ` +Validates that float arguments are rejected by ${builtin}() +` + ) + .params(u => u.combine('type', keysOf(kFloatTypes))) + .fn(t => { + const type = kFloatTypes[t.params.type]; + validateConstOrOverrideBuiltinEval( + t, + builtin, + /* expectedResult */ type === Type.u32, + [type.create(0)], + 'constant' + ); + }); + +const kTests: { + readonly [name: string]: { + /** Arguments to pass to the builtin with parentheses. */ + readonly args: string; + /** Should the test case pass. */ + readonly pass: boolean; + /** Additional setup code in the function scope. */ + readonly preamble?: string; + }; +} = { + valid: { + args: '(1u)', + pass: true, + }, + // Number of arguments. + no_parens: { + args: '', + pass: false, + }, + too_few_args: { + args: '()', + pass: false, + }, + too_many_args: { + args: '(1u,2u)', + pass: false, + }, + // Arguments types (only 1 argument for this builtin). + alias: { + args: '(u32_alias(1))', + pass: true, + }, + bool: { + args: '(false)', + pass: false, + }, + vec_bool: { + args: '(vec2(false,true))', + pass: false, + }, + matrix: { + args: '(mat2x2(1,1,1,1))', + pass: false, + }, + atomic: { + args: '(a)', + pass: false, + }, + array: { + preamble: 'var arry: array;', + args: '(arry)', + pass: false, + }, + array_runtime: { + args: '(k.arry)', + pass: false, + }, + struct: { + preamble: 'var x: A;', + args: '(x)', + pass: false, + }, + enumerant: { + args: '(read_write)', + pass: false, + }, + ptr: { + preamble: `var f = 1u; + let p: ptr = &f;`, + args: '(p)', + pass: false, + }, + ptr_deref: { + preamble: `var f = 1u; + let p: ptr = &f;`, + args: '(*p)', + pass: true, + }, + sampler: { + args: '(s)', + pass: false, + }, + texture: { + args: '(t)', + pass: false, + }, +}; + +g.test('arguments') + .desc(`Test compilation validation of ${builtin} with variously shaped and typed arguments`) + .params(u => u.combine('test', keysOf(kTests))) + .fn(t => { + const test = kTests[t.params.test]; + t.expectCompileResult( + test.pass, + `alias u32_alias = u32; + + @group(0) @binding(0) var s: sampler; + @group(0) @binding(1) var t: texture_2d; + + var a: atomic; + + struct A { + i: u32, + } + struct B { + arry: array, + } + @group(0) @binding(3) var k: B; + + + @vertex + fn main() -> @builtin(position) vec4 { + ${test.preamble ? test.preamble : ''} + _ = ${builtin}${test.args}; + return vec4(.4, .2, .3, .1); + }` + ); + }); + +g.test('must_use') + .desc(`Result of ${builtin} must be used`) + .params(u => u.combine('use', [true, false])) + .fn(t => { + const use_it = t.params.use ? '_ = ' : ''; + t.expectCompileResult(t.params.use, `fn f() { ${use_it}${builtin}(1u); }`); + }); diff --git a/src/webgpu/shader/validation/expression/call/builtin/reverseBits.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/reverseBits.spec.ts new file mode 100644 index 000000000000..8b901bb5958b --- /dev/null +++ b/src/webgpu/shader/validation/expression/call/builtin/reverseBits.spec.ts @@ -0,0 +1,198 @@ +const builtin = 'reverseBits'; +export const description = ` +Validation tests for the ${builtin}() builtin. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; +import { + Type, + kConcreteIntegerScalarsAndVectors, + kFloatScalarsAndVectors, +} from '../../../../../util/conversion.js'; +import { ShaderValidationTest } from '../../../shader_validation_test.js'; + +import { + fullRangeForType, + kConstantAndOverrideStages, + stageSupportsType, + validateConstOrOverrideBuiltinEval, +} from './const_override_validation.js'; + +export const g = makeTestGroup(ShaderValidationTest); + +const kValuesTypes = objectsToRecord(kConcreteIntegerScalarsAndVectors); + +g.test('values') + .desc( + ` +Validates that constant evaluation and override evaluation of ${builtin}() never errors +` + ) + .params(u => + u + .combine('stage', kConstantAndOverrideStages) + .combine('type', keysOf(kValuesTypes)) + .filter(u => stageSupportsType(u.stage, kValuesTypes[u.type])) + .beginSubcases() + .expand('value', u => fullRangeForType(kValuesTypes[u.type])) + ) + .fn(t => { + const expectedResult = true; // reverseBits() should never error + validateConstOrOverrideBuiltinEval( + t, + builtin, + expectedResult, + [kValuesTypes[t.params.type].create(t.params.value)], + t.params.stage + ); + }); + +// u32 is included here to confirm that validation is failing due to a type issue and not something else. +const kFloatTypes = objectsToRecord([Type.u32, ...kFloatScalarsAndVectors]); + +g.test('float_argument') + .desc( + ` +Validates that float arguments are rejected by ${builtin}() +` + ) + .params(u => u.combine('type', keysOf(kFloatTypes))) + .fn(t => { + const type = kFloatTypes[t.params.type]; + validateConstOrOverrideBuiltinEval( + t, + builtin, + /* expectedResult */ type === Type.u32, + [type.create(0)], + 'constant' + ); + }); + +const kTests: { + readonly [name: string]: { + /** Arguments to pass to the builtin with parentheses. */ + readonly args: string; + /** Should the test case pass. */ + readonly pass: boolean; + /** Additional setup code in the function scope. */ + readonly preamble?: string; + }; +} = { + valid: { + args: '(1u)', + pass: true, + }, + // Number of arguments. + no_parens: { + args: '', + pass: false, + }, + too_few_args: { + args: '()', + pass: false, + }, + too_many_args: { + args: '(1u,2u)', + pass: false, + }, + // Arguments types (only 1 argument for this builtin). + alias: { + args: '(u32_alias(1))', + pass: true, + }, + bool: { + args: '(false)', + pass: false, + }, + vec_bool: { + args: '(vec2(false,true))', + pass: false, + }, + matrix: { + args: '(mat2x2(1,1,1,1))', + pass: false, + }, + atomic: { + args: '(a)', + pass: false, + }, + array: { + preamble: 'var arry: array;', + args: '(arry)', + pass: false, + }, + array_runtime: { + args: '(k.arry)', + pass: false, + }, + struct: { + preamble: 'var x: A;', + args: '(x)', + pass: false, + }, + enumerant: { + args: '(read_write)', + pass: false, + }, + ptr: { + preamble: `var f = 1u; + let p: ptr = &f;`, + args: '(p)', + pass: false, + }, + ptr_deref: { + preamble: `var f = 1u; + let p: ptr = &f;`, + args: '(*p)', + pass: true, + }, + sampler: { + args: '(s)', + pass: false, + }, + texture: { + args: '(t)', + pass: false, + }, +}; + +g.test('arguments') + .desc(`Test compilation validation of ${builtin} with variously shaped and typed arguments`) + .params(u => u.combine('test', keysOf(kTests))) + .fn(t => { + const test = kTests[t.params.test]; + t.expectCompileResult( + test.pass, + `alias u32_alias = u32; + + @group(0) @binding(0) var s: sampler; + @group(0) @binding(1) var t: texture_2d; + + var a: atomic; + + struct A { + i: u32, + } + struct B { + arry: array, + } + @group(0) @binding(3) var k: B; + + + @vertex + fn main() -> @builtin(position) vec4 { + ${test.preamble ? test.preamble : ''} + _ = ${builtin}${test.args}; + return vec4(.4, .2, .3, .1); + }` + ); + }); + +g.test('must_use') + .desc(`Result of ${builtin} must be used`) + .params(u => u.combine('use', [true, false])) + .fn(t => { + const use_it = t.params.use ? '_ = ' : ''; + t.expectCompileResult(t.params.use, `fn f() { ${use_it}${builtin}(1u); }`); + });