| 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); |
| }); |