export const description = `
Execution Tests for value constructors from components
`;
import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../../gpu_test.js';
import {
ArrayValue,
MatrixType,
ScalarKind,
Type,
Value,
VectorType,
VectorValue,
scalarTypeOf,
vec2,
vec3,
} from '../../../../util/conversion.js';
import { FP } from '../../../../util/floating_point.js';
import {
ShaderBuilderParams,
allInputSources,
basicExpressionBuilder,
run,
} from '../expression.js';
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
function isMinOrMax(v: number | 'min' | 'max') {
return v === 'min' || v === 'max';
}
const kConcreteTypesForAbstractType = {
'abstract-float': ['f32', 'f16'] as const,
'abstract-int': ['f32', 'f16', 'i32', 'u32'] as const,
'vec3<abstract-int>': ['vec3f', 'vec3h', 'vec3i', 'vec3u'] as const,
'vec4<abstract-float>': ['vec4f', 'vec4h'] as const,
'mat2x3<abstract-float>': ['mat2x3f', 'mat2x3h'] as const,
};
* @returns the lowest finite value for 'kind' if 'v' is 'min',
* the highest finite value for 'kind' if 'v' is 'max',
* otherwise returns 'v'
*/
function valueFor(v: number | 'min' | 'max', kind: 'bool' | 'i32' | 'u32' | 'f32' | 'f16') {
if (!isMinOrMax(v)) {
return v as number;
}
switch (kind) {
case 'bool':
return v === 'min' ? 0 : 1;
case 'i32':
return v === 'min' ? -0x80000000 : 0x7fffffff;
case 'u32':
return v === 'min' ? 0 : 0xffffffff;
case 'f32':
return v === 'min' ? FP['f32'].constants().negative.min : FP['f32'].constants().positive.max;
case 'f16':
return v === 'min' ? FP['f16'].constants().negative.min : FP['f16'].constants().positive.max;
}
}
g.test('scalar_identity')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that a scalar constructed from a value of the same type produces the expected value`)
.params(u =>
u
.combine('inputSource', allInputSources)
.combine('type', ['bool', 'i32', 'u32', 'f32', 'f16'] as const)
.combine('value', ['min', 'max', 1, 2, 5, 100] as const)
)
.beforeAllSubcases(t => {
t.skipIf(t.params.type === 'bool' && !isMinOrMax(t.params.value));
})
.fn(async t => {
if (t.params.type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const type = Type[t.params.type];
const value = valueFor(t.params.value, t.params.type);
await run(
t,
basicExpressionBuilder(ops => `${type}(${ops[0]})`),
[type],
type,
t.params,
[{ input: [type.create(value)], expected: type.create(value) }]
);
});
g.test('vector_identity')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that a vector constructed from a value of the same type produces the expected value`)
.params(u =>
u
.combine('inputSource', allInputSources)
.combine('type', ['bool', 'i32', 'u32', 'f32', 'f16'] as const)
.combine('width', [2, 3, 4] as const)
.combine('infer_type', [false, true] as const)
)
.fn(async t => {
if (t.params.type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const elementType = Type[t.params.type];
const vectorType = Type.vec(t.params.width, elementType);
const elements: number[] = [];
const fn = t.params.infer_type ? `vec${t.params.width}` : `${vectorType}`;
for (let i = 0; i < t.params.width; i++) {
if (t.params.type === 'bool') {
elements.push(i & 1);
} else {
elements.push((i + 1) * 10);
}
}
await run(
t,
basicExpressionBuilder(ops => `${fn}(${ops[0]})`),
[vectorType],
vectorType,
t.params,
[
{
input: vectorType.create(elements),
expected: vectorType.create(elements),
},
]
);
});
g.test('concrete_vector_splat')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that a vector constructed from a single concrete scalar produces the expected value`)
.params(u =>
u
.combine('inputSource', allInputSources)
.combine('type', ['bool', 'i32', 'u32', 'f32', 'f16'] as const)
.combine('value', ['min', 'max', 1, 2, 5, 100] as const)
.combine('width', [2, 3, 4] as const)
.combine('infer_type', [false, true] as const)
)
.beforeAllSubcases(t => {
t.skipIf(t.params.type === 'bool' && !isMinOrMax(t.params.value));
})
.fn(async t => {
if (t.params.type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const value = valueFor(t.params.value, t.params.type);
const elementType = Type[t.params.type];
const vectorType = Type.vec(t.params.width, elementType);
const fn = t.params.infer_type ? `vec${t.params.width}` : `${vectorType}`;
await run(
t,
basicExpressionBuilder(ops => `${fn}(${ops[0]})`),
[elementType],
vectorType,
t.params,
[{ input: [elementType.create(value)], expected: vectorType.create(value) }]
);
});
g.test('abstract_vector_splat')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that a vector constructed from a single abstract scalar produces the expected value`)
.params(u =>
u
.combine('abstract_type', ['abstract-int', 'abstract-float'] as const)
.expand('concrete_type', t => kConcreteTypesForAbstractType[t.abstract_type])
.combine('value', [1, 2, 5, 100] as const)
.combine('width', [2, 3, 4] as const)
)
.fn(async t => {
if (t.params.concrete_type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const suffix = t.params.abstract_type === 'abstract-float' ? '.0' : '';
const concreteElementType = Type[t.params.concrete_type];
const concreteVectorType = Type.vec(t.params.width, concreteElementType);
const fn = `vec${t.params.width}`;
await run(
t,
basicExpressionBuilder(_ => `${fn}(${t.params.value * 0x100000000}${suffix}) / 0x100000000`),
[],
concreteVectorType,
{ inputSource: 'const', constEvaluationMode: 'direct' },
[{ input: [], expected: concreteVectorType.create(t.params.value) }]
);
});
g.test('concrete_vector_elements')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that a vector constructed from concrete element values produces the expected value`)
.params(u =>
u
.combine('inputSource', allInputSources)
.combine('type', ['bool', 'i32', 'u32', 'f32', 'f16'] as const)
.combine('width', [2, 3, 4] as const)
.combine('infer_type', [false, true] as const)
)
.fn(async t => {
if (t.params.type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const elementType = Type[t.params.type];
const vectorType = Type.vec(t.params.width, elementType);
const elements: number[] = [];
const fn = t.params.infer_type ? `vec${t.params.width}` : `${vectorType}`;
for (let i = 0; i < t.params.width; i++) {
if (t.params.type === 'bool') {
elements.push(i & 1);
} else {
elements.push((i + 1) * 10);
}
}
await run(
t,
basicExpressionBuilder(ops => `${fn}(${ops.join(', ')})`),
elements.map(e => elementType),
vectorType,
t.params,
[
{
input: elements.map(v => elementType.create(v)),
expected: vectorType.create(elements),
},
]
);
});
g.test('abstract_vector_elements')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that a vector constructed from abstract element values produces the expected value`)
.params(u =>
u
.combine('abstract_type', ['abstract-int', 'abstract-float'] as const)
.expand('concrete_type', t => kConcreteTypesForAbstractType[t.abstract_type])
.combine('width', [2, 3, 4] as const)
)
.fn(async t => {
if (t.params.concrete_type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const suffix = t.params.abstract_type === 'abstract-float' ? '.0' : '';
const concreteElementType = Type[t.params.concrete_type];
const concreteVectorType = Type.vec(t.params.width, concreteElementType);
const fn = `vec${t.params.width}`;
const elements: number[] = [];
for (let i = 0; i < t.params.width; i++) {
elements.push((i + 1) * 10);
}
await run(
t,
basicExpressionBuilder(
_ => `${fn}(${elements.map(v => `${v * 0x100000000}${suffix}`).join(', ')}) / 0x100000000`
),
[],
concreteVectorType,
{ inputSource: 'const', constEvaluationMode: 'direct' },
[{ input: [], expected: concreteVectorType.create(elements) }]
);
});
const kMixSignatures = [
'2s',
's2',
'2ss',
's2s',
'ss2',
'22',
'3s',
's3',
] as const;
g.test('concrete_vector_mix')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(
`Test that a vector constructed from a mix of concrete element values and sub-vectors produces the expected value`
)
.params(u =>
u
.combine('inputSource', allInputSources)
.combine('type', ['bool', 'i32', 'u32', 'f32', 'f16'] as const)
.combine('signature', kMixSignatures)
.combine('infer_type', [false, true] as const)
)
.fn(async t => {
if (t.params.type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const elementType = Type[t.params.type];
let width = 0;
const elementValue = (i: number) => (t.params.type === 'bool' ? i & 1 : (i + 1) * 10);
const elements: number[] = [];
const nextValue = () => {
const value = elementValue(width++);
elements.push(value);
return elementType.create(value);
};
const args: Value[] = [];
for (const c of t.params.signature) {
switch (c) {
case '2':
args.push(vec2(nextValue(), nextValue()));
break;
case '3':
args.push(vec3(nextValue(), nextValue(), nextValue()));
break;
case 's':
args.push(nextValue());
break;
}
}
const vectorType = Type.vec(width, elementType);
const fn = t.params.infer_type ? `vec${width}` : `${vectorType}`;
await run(
t,
basicExpressionBuilder(ops => `${fn}(${ops.join(', ')})`),
args.map(e => e.type),
vectorType,
t.params,
[
{
input: args,
expected: vectorType.create(elements),
},
]
);
});
g.test('abstract_vector_mix')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(
`Test that a vector constructed from a mix of abstract element values and sub-vectors produces the expected value`
)
.params(u =>
u
.combine('abstract_type', ['abstract-int', 'abstract-float'] as const)
.expand('concrete_type', t => kConcreteTypesForAbstractType[t.abstract_type])
.combine('signature', kMixSignatures)
)
.fn(async t => {
if (t.params.concrete_type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
let width = 0;
const suffix = t.params.abstract_type === 'abstract-float' ? '.0' : '';
const concreteElementType = Type[t.params.concrete_type];
const elementValue = (i: number) => (i + 1) * 10;
const elements: number[] = [];
const nextValue = () => {
const value = elementValue(width++);
elements.push(value);
return `${value * 0x100000000}${suffix}`;
};
const args: string[] = [];
for (const c of t.params.signature) {
switch (c) {
case '2':
args.push(`vec2(${nextValue()}, ${nextValue()})`);
break;
case '3':
args.push(`vec3(${nextValue()}, ${nextValue()}, ${nextValue()})`);
break;
case 's':
args.push(`${nextValue()}`);
break;
}
}
const concreteVectorType = Type.vec(width, concreteElementType);
const fn = `vec${width}`;
await run(
t,
basicExpressionBuilder(_ => `${fn}(${args.join(', ')}) / 0x100000000`),
[],
concreteVectorType,
{ inputSource: 'const', constEvaluationMode: 'direct' },
[
{
input: [],
expected: concreteVectorType.create(elements),
},
]
);
});
g.test('matrix_identity')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that a matrix constructed from a value of the same type produces the expected value`)
.params(u =>
u
.combine('inputSource', allInputSources)
.combine('type', ['f32', 'f16'] as const)
.combine('columns', [2, 3, 4] as const)
.combine('rows', [2, 3, 4] as const)
.combine('infer_type', [false, true] as const)
)
.fn(async t => {
if (t.params.type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const elementType = Type[t.params.type];
const matrixType = Type.mat(t.params.columns, t.params.rows, elementType);
const elements: number[] = [];
for (let column = 0; column < t.params.columns; column++) {
for (let row = 0; row < t.params.rows; row++) {
elements.push((column + 1) * 10 + (row + 1));
}
}
const fn = t.params.infer_type ? `mat${t.params.columns}x${t.params.rows}` : `${matrixType}`;
await run(
t,
basicExpressionBuilder(ops => `${fn}(${ops[0]})`),
[matrixType],
matrixType,
t.params,
[
{
input: matrixType.create(elements),
expected: matrixType.create(elements),
},
]
);
});
g.test('concrete_matrix_elements')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that a matrix constructed from concrete element values produces the expected value`)
.params(u =>
u
.combine('inputSource', allInputSources)
.combine('type', ['f32', 'f16'] as const)
.combine('columns', [2, 3, 4] as const)
.combine('rows', [2, 3, 4] as const)
.combine('infer_type', [false, true] as const)
)
.fn(async t => {
if (t.params.type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const elementType = Type[t.params.type];
const matrixType = Type.mat(t.params.columns, t.params.rows, elementType);
const elements: number[] = [];
for (let column = 0; column < t.params.columns; column++) {
for (let row = 0; row < t.params.rows; row++) {
elements.push((column + 1) * 10 + (row + 1));
}
}
const fn = t.params.infer_type ? `mat${t.params.columns}x${t.params.rows}` : `${matrixType}`;
await run(
t,
basicExpressionBuilder(ops => `${fn}(${ops.join(', ')})`),
elements.map(e => elementType),
matrixType,
t.params,
[
{
input: elements.map(e => elementType.create(e)),
expected: matrixType.create(elements),
},
]
);
});
g.test('abstract_matrix_elements')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that a matrix constructed from concrete element values produces the expected value`)
.params(u =>
u
.combine('concrete_type', ['f32', 'f16'] as const)
.combine('columns', [2, 3, 4] as const)
.combine('rows', [2, 3, 4] as const)
)
.fn(async t => {
if (t.params.concrete_type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const concreteElementType = Type[t.params.concrete_type];
const concreteMatrixType = Type.mat(t.params.columns, t.params.rows, concreteElementType);
const elements: number[] = [];
for (let column = 0; column < t.params.columns; column++) {
for (let row = 0; row < t.params.rows; row++) {
elements.push((column + 1) * 10 + (row + 1));
}
}
const fn = `mat${t.params.columns}x${t.params.rows}`;
await run(
t,
basicExpressionBuilder(
_ => `${fn}(${elements.map(v => `${v * 0x100000000}.0`).join(', ')}) * (1.0 / 0x100000000)`
),
[],
concreteMatrixType,
{ inputSource: 'const', constEvaluationMode: 'direct' },
[
{
input: [],
expected: concreteMatrixType.create(elements),
},
]
);
});
g.test('concrete_matrix_column_vectors')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that a matrix constructed from concrete column vectors produces the expected value`)
.params(u =>
u
.combine('inputSource', allInputSources)
.combine('type', ['f32', 'f16'] as const)
.combine('columns', [2, 3, 4] as const)
.combine('rows', [2, 3, 4] as const)
.combine('infer_type', [false, true] as const)
)
.fn(async t => {
if (t.params.type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const elementType = Type[t.params.type];
const columnType = Type.vec(t.params.rows, elementType);
const matrixType = Type.mat(t.params.columns, t.params.rows, elementType);
const elements: number[] = [];
const columnVectors: VectorValue[] = [];
for (let column = 0; column < t.params.columns; column++) {
const columnElements: number[] = [];
for (let row = 0; row < t.params.rows; row++) {
const v = (column + 1) * 10 + (row + 1);
elements.push(v);
columnElements.push(v);
}
columnVectors.push(columnType.create(columnElements));
}
const fn = t.params.infer_type ? `mat${t.params.columns}x${t.params.rows}` : `${matrixType}`;
await run(
t,
basicExpressionBuilder(ops => `${fn}(${ops.join(', ')})`),
columnVectors.map(v => v.type),
matrixType,
t.params,
[
{
input: columnVectors,
expected: matrixType.create(elements),
},
]
);
});
g.test('abstract_matrix_column_vectors')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that a matrix constructed from abstract column vectors produces the expected value`)
.params(u =>
u
.combine('concrete_type', ['f32', 'f16'] as const)
.combine('columns', [2, 3, 4] as const)
.combine('rows', [2, 3, 4] as const)
)
.fn(async t => {
if (t.params.concrete_type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const concreteElementType = Type[t.params.concrete_type];
const concreteMatrixType = Type.mat(t.params.columns, t.params.rows, concreteElementType);
const elements: number[] = [];
const columnVectors: string[] = [];
for (let column = 0; column < t.params.columns; column++) {
const columnElements: string[] = [];
for (let row = 0; row < t.params.rows; row++) {
const v = (column + 1) * 10 + (row + 1);
elements.push(v);
columnElements.push(`${v * 0x100000000}`);
}
columnVectors.push(`vec${t.params.rows}(${columnElements.join(', ')})`);
}
const fn = `mat${t.params.columns}x${t.params.rows}`;
await run(
t,
basicExpressionBuilder(_ => `${fn}(${columnVectors.join(', ')}) * (1.0 / 0x100000000)`),
[],
concreteMatrixType,
{ inputSource: 'const', constEvaluationMode: 'direct' },
[
{
input: [],
expected: concreteMatrixType.create(elements),
},
]
);
});
g.test('concrete_array_elements')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that an array constructed from concrete element values produces the expected value`)
.params(u =>
u
.combine('inputSource', allInputSources)
.combine('type', ['bool', 'i32', 'u32', 'f32', 'f16', 'vec3f', 'vec4i'] as const)
.combine('length', [1, 5, 10] as const)
.combine('infer_type', [false, true] as const)
)
.fn(async t => {
if (t.params.type === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const elementType = Type[t.params.type];
const arrayType = Type.array(t.params.length, elementType);
const elements: number[] = [];
for (let i = 0; i < t.params.length; i++) {
elements.push((i + 1) * 10);
}
const fn = t.params.infer_type ? `array` : `${arrayType}`;
await run(
t,
basicExpressionBuilder(ops => `${fn}(${ops.join(', ')})`),
elements.map(e => elementType),
arrayType,
t.params,
[
{
input: elements.map(e => elementType.create(e)),
expected: arrayType.create(elements),
},
]
);
});
g.test('abstract_array_elements')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that an array constructed from element values produces the expected value`)
.params(u =>
u
.combine('abstract_type', [
'abstract-int',
'abstract-float',
'vec3<abstract-int>',
'vec4<abstract-float>',
'mat2x3<abstract-float>',
] as const)
.expand('concrete_type', t => kConcreteTypesForAbstractType[t.abstract_type])
.combine('length', [1, 5, 10] as const)
)
.fn(async t => {
if (scalarTypeOf(Type[t.params.concrete_type]).kind === 'f16') {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const count = t.params.length;
const concreteElementType = Type[t.params.concrete_type];
const concreteArrayType = Type.array(count, concreteElementType);
const elements: { args: string; value: Value }[] = [];
let i = 0;
const nextValue = () => ++i * 10;
for (let i = 0; i < count; i++) {
switch (t.params.abstract_type) {
case 'abstract-int': {
const value = nextValue();
elements.push({ args: `${value}`, value: concreteElementType.create(value) });
break;
}
case 'abstract-float': {
const value = nextValue();
elements.push({ args: `${value}.0`, value: concreteElementType.create(value) });
break;
}
case 'vec3<abstract-int>': {
const x = nextValue();
const y = nextValue();
const z = nextValue();
elements.push({
args: `vec3(${x}, ${y}, ${z})`,
value: (concreteElementType as VectorType).create([x, y, z]),
});
break;
}
case 'vec4<abstract-float>': {
const x = nextValue();
const y = nextValue();
const z = nextValue();
const w = nextValue();
elements.push({
args: `vec4(${x}.0, ${y}.0, ${z}.0, ${w}.0)`,
value: (concreteElementType as VectorType).create([x, y, z, w]),
});
break;
}
case 'mat2x3<abstract-float>': {
const e00 = nextValue();
const e01 = nextValue();
const e02 = nextValue();
const e10 = nextValue();
const e11 = nextValue();
const e12 = nextValue();
elements.push({
args: `mat2x3(vec3(${e00}.0, ${e01}.0, ${e02}.0), vec3(${e10}.0, ${e11}.0, ${e12}.0))`,
value: (concreteElementType as MatrixType).create([e00, e01, e02, e10, e11, e12]),
});
break;
}
}
}
const fn = `array`;
await run(
t,
basicExpressionBuilder(_ => `${fn}(${elements.map(e => e.args).join(', ')})`),
[],
concreteArrayType,
{ inputSource: 'const', constEvaluationMode: 'direct' },
[
{
input: [],
expected: new ArrayValue(elements.map(e => e.value)),
},
]
);
});
g.test('structure')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(`Test that an structure constructed from element values produces the expected value`)
.params(u =>
u
.combine('member_types', [
['bool'],
['u32'],
['vec3f'],
['i32', 'u32'],
['i32', 'f16', 'vec4i', 'mat3x2f'],
['bool', 'u32', 'f16', 'vec3f', 'vec2i'],
['i32', 'u32', 'f32', 'f16', 'vec3f', 'vec4i'],
] as readonly ScalarKind[][])
.combine('nested', [false, true])
.beginSubcases()
.expand('member_index', t => t.member_types.map((_, i) => i))
)
.fn(async t => {
if (t.params.member_types.includes('f16')) {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const memberType = Type[t.params.member_types[t.params.member_index]];
const values = t.params.member_types.map((ty, i) => Type[ty].create(i));
const builder = basicExpressionBuilder(ops =>
t.params.nested
? `OuterStruct(10, MyStruct(${ops.join(', ')}), 20).inner.member_${t.params.member_index}`
: `MyStruct(${ops.join(', ')}).member_${t.params.member_index}`
);
await run(
t,
(params: ShaderBuilderParams) => {
return `
${t.params.member_types.includes('f16') ? 'enable f16;' : ''}
${builder(params)}
struct MyStruct {
${t.params.member_types.map((ty, i) => ` member_${i} : ${ty},`).join('\n')}
};
struct OuterStruct {
pre : i32,
inner : MyStruct,
post : i32,
};
`;
},
t.params.member_types.map(ty => Type[ty]),
memberType,
{ inputSource: 'const' },
[{ input: values, expected: values[t.params.member_index] }]
);
});