const builtin = 'unpack2x16float';
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 { kBit, kValue } from '../../../../../util/constants.js';
import {
kAllScalarsAndVectors,
u32,
i32,
f32,
f16,
bool,
vec2,
vec3,
vec4,
array,
} from '../../../../../util/conversion.js';
import { ShaderValidationTest } from '../../../shader_validation_test.js';
import {
fullRangeForType,
kConstantAndOverrideStages,
stageSupportsType,
validateConstOrOverrideBuiltinEval,
} from './const_override_validation.js';
const kAllValueTypes = objectsToRecord(kAllScalarsAndVectors);
const kValidArgumentTypes = ['u32', 'abstract-int'];
const kReturnType = 'vec2<f32>';
const kArgCases = {
good: [u32(1)],
bad_no_args: [],
bad_more_args: [u32(1), u32(2)],
bad_i32: [i32(1)],
bad_f32: [f32(1)],
bad_f16: [f16(1)],
bad_bool: [bool(false)],
bad_vec2u: [vec2(u32(1), u32(2))],
bad_vec3u: [vec3(u32(1), u32(2), u32(3))],
bad_vec4u: [vec4(u32(1), u32(2), u32(3), u32(4))],
bad_array: [array(u32(1))],
};
const isValidF16AsU16 = (f16AsU16: number) => {
return (
Number.isInteger(f16AsU16) &&
((kBit.f16.positive.zero <= f16AsU16 && f16AsU16 < kBit.f16.positive.infinity) ||
(kBit.f16.negative.zero <= f16AsU16 && f16AsU16 < kBit.f16.negative.infinity))
);
};
const isValidPacked2xF16 = (packed2xF16AsU32: number) => {
return (
Number.isInteger(packed2xF16AsU32) &&
packed2xF16AsU32 >= kValue.u32.min &&
packed2xF16AsU32 <= kValue.u32.max &&
isValidF16AsU16(packed2xF16AsU32 & 0xffff) &&
isValidF16AsU16(packed2xF16AsU32 >>> 16)
);
};
export const g = makeTestGroup(ShaderValidationTest);
g.test('values')
.desc(
`
Validates that constant evaluation and override evaluation of ${builtin} rejects invalid values.
`
)
.params(u =>
u
.combine('stage', kConstantAndOverrideStages)
.combine('type', kValidArgumentTypes)
.filter(u => stageSupportsType(u.stage, kAllValueTypes[u.type]))
.beginSubcases()
.expand('value', u => fullRangeForType(kAllValueTypes[u.type]))
)
.fn(t => {
const type = kAllValueTypes[t.params.type];
const value = t.params.value;
const expectedResult = isValidPacked2xF16(Number(value));
validateConstOrOverrideBuiltinEval(
t,
builtin,
expectedResult,
[type.create(value)],
t.params.stage
);
});
g.test('arguments')
.desc(`Test that ${builtin} is validated correctly when called with different arguments.`)
.params(u =>
u
.combine('args', keysOf(kArgCases))
.beginSubcases()
.expand('returnType', u => (u.args.includes('good') ? keysOf(kAllValueTypes) : [kReturnType]))
)
.fn(t => {
const expectedResult = t.params.args.includes('good') && t.params.returnType === kReturnType;
validateConstOrOverrideBuiltinEval(
t,
builtin,
expectedResult,
kArgCases[t.params.args],
'constant',
kAllValueTypes[t.params.returnType]
);
});
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); }`);
});