blob: 32e09f3540981d339be08bf6e1a6a91c7ef0df0f [file] [log] [blame]
export const description = `
Validation tests for matrix access expressions
* Index type
* Result type
* Early-evaluation errors
`;
import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { keysOf } from '../../../../../common/util/data_tables.js';
import { Type } from '../../../../util/conversion.js';
import { ShaderValidationTest } from '../../shader_validation_test.js';
export const g = makeTestGroup(ShaderValidationTest);
g.test('index_type')
.desc('Tests valid index types for array access expressions')
.params(u =>
u.combine('type', [
'bool',
'u32',
'i32',
'abstract-int',
'f32',
'f16',
'abstract-float',
'vec2i',
] as const)
)
.fn(t => {
const ty = Type[t.params.type];
const enable = ty.requiresF16() ? 'enable f16;' : '';
const code = `${enable}
fn foo() {
var x = mat2x2(1,2,3,4);
let tmp = x[${ty.create(0).wgsl()}];
}`;
const expect =
t.params.type === 'i32' || t.params.type === 'u32' || t.params.type === 'abstract-int';
t.expectCompileResult(expect, code);
});
g.test('result_type')
.desc('Tests that correct result type is produced for an access expression')
.params(u =>
u
.combine('element', ['f16', 'f32'] as const)
.combine('columns', [2, 3, 4] as const)
.beginSubcases()
.combine('rows', [2, 3, 4] as const)
.combine('decl', ['function', 'module'] as const)
)
.fn(t => {
const enable = t.params.element === 'f16' ? 'enable f16;' : '';
const scalarTy = Type[t.params.element];
const vectorTy = Type['vec'](t.params.rows, scalarTy);
const matrixTy = Type['mat'](t.params.columns, t.params.rows, scalarTy);
const module_decl =
t.params.decl === 'module'
? `@group(0) @binding(0) var<storage> x : ${matrixTy.toString()};`
: ``;
const function_decl = t.params.decl === 'module' ? `` : `var x : ${matrixTy.toString()};`;
const code = `${enable}
${module_decl}
fn foo() {
${function_decl}
let tmp1 : ${vectorTy.toString()} = x[0];
let tmp2 : ${vectorTy.toString()} = x[1];
}`;
t.expectCompileResult(true, code);
});
interface OutOfBoundsCase {
code: string;
result: boolean;
pipeline?: boolean;
value?: number;
}
const kOutOfBoundsCases: Record<string, OutOfBoundsCase> = {
const_module_in_bounds: {
code: `const x = mat2x2(1,2,3,4)[0];`,
result: true,
},
const_module_oob_neg: {
code: `const x = mat2x2(1,2,3,4)[-1];`,
result: false,
},
const_module_oob_pos: {
code: `const x = mat2x2(1,2,3,4)[2];`,
result: false,
},
const_func_in_bounds: {
code: `fn foo() {
const x = mat2x2(1,2,3,4)[0];
}`,
result: true,
},
const_func_oob_neg: {
code: `fn foo {
const x = mat2x2(1,2,3,4)[-1];
}`,
result: false,
},
const_func_oob_pos: {
code: `fn foo {
const x = mat2x2(1,2,3,4)[2];
}`,
result: false,
},
override_in_bounds: {
code: `override x : i32;
fn y() -> u32 {
let tmp = mat2x2(1,2,3,4)[x];
return 0;
}`,
result: true,
pipeline: true,
value: 0,
},
override_oob_neg: {
code: `override x : i32;
fn y() -> u32 {
let tmp = mat2x2(1,2,3,4)[x];
return 0;
}`,
result: false,
pipeline: true,
value: -1,
},
override_oob_pos: {
code: `override x : i32;
fn y() -> u32 {
let tmp = mat2x2(1,2,3,4)[x];
return 0;
}`,
result: false,
pipeline: true,
value: 2,
},
runtime_in_bounds: {
code: `fn foo() {
let idx = 0;
let x = mat2x2(1,2,3,4)[idx];
}`,
result: true,
},
runtime_oob_neg: {
code: `fn foo() {
let idx = -1;
let x = mat2x2(1,2,3,4)[idx];
}`,
result: true,
},
runtime_oob_pos: {
code: `fn foo() {
let idx = 3;
let x = mat2x2(1,2,3,4)[idx];
}`,
result: true,
},
runtime_array_const_oob_neg: {
code: `@group(0) @binding(0) var<storage> x : mat2x2<f32>;
fn y() -> u32 {
let tmp = x[-1];
return 0;
}`,
result: false,
},
runtime_array_override_oob_neg: {
code: `@group(0) @binding(0) var<storage> v : mat2x2<f32>;
override x : i32;
fn y() -> u32 {
let tmp = v[x];
return 0;
}`,
result: false,
pipeline: true,
value: -1,
},
};
g.test('early_eval_errors')
.desc('Tests early evaluation errors for out-of-bounds indexing')
.params(u => u.combine('case', keysOf(kOutOfBoundsCases)))
.fn(t => {
const testcase = kOutOfBoundsCases[t.params.case];
if (testcase.pipeline) {
const v: number = testcase.value ?? 0;
t.expectPipelineResult({
expectedResult: testcase.result,
code: testcase.code,
constants: { x: v },
reference: ['y()'],
});
} else {
t.expectCompileResult(testcase.result, testcase.code);
}
});
g.test('abstract_matrix_concrete_index')
.desc('Tests that a concrete index type on an abstract array remains abstract')
.fn(t => {
const code = `
const idx = 0i;
const_assert mat2x2(1.11001100110011008404,1,1,1)[0i][0i] == 1.11001100110011008404;`;
t.expectCompileResult(true, code);
});