export const description = ` Validation tests for swizzle assignments. `; import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { keysOf } from '../../../../common/util/data_tables.js'; import { ShaderValidationTest } from '../shader_validation_test.js'; export const g = makeTestGroup(ShaderValidationTest); g.test('valid') .desc('Valid swizzle assignments') .params(u => u.combine('elemType', ['f32', 'i32', 'u32'] as const).combine('vecSize', [2, 3, 4] as const) ) .fn(t => { t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); const { elemType, vecSize } = t.params; const swizzle = 'xyzw'.substring(0, vecSize); const vecType = `vec${swizzle.length}<${elemType}>`; const code = ` @fragment fn main() { var v = vec4<${elemType}>(0); v.${swizzle} = ${vecType}(1); } `; t.expectCompileResult(true, code); }); g.test('invalid_lhs_not_reference') .desc('Invalid swizzle assignment where LHS is not a reference') .params(u => u.combine('lhs', ['vec4f()', 'const_vec', 'foo()'])) .fn(t => { t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); const code = ` const const_vec = vec4f(); fn foo() -> vec4f { return vec4f(); } @fragment fn main() { var v = vec4f(); ${t.params.lhs}.xyz = vec3(0.0); } `; t.expectCompileResult(false, code); }); g.test('invalid_duplicate_components') .desc('Invalid swizzle assignment with duplicate LHS components') .fn(t => { t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); const code = ` @fragment fn main() { var v = vec4f(); v.xx = vec2f(); } `; t.expectCompileResult(false, code); }); g.test('invalid_component_mismatch') .desc('Invalid swizzle assignment with mismatched number of components') .fn(t => { t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); const code = ` @fragment fn main() { var v = vec4f(); v.xy = vec3f(); } `; t.expectCompileResult(false, code); }); g.test('invalid_component_oob') .desc('Invalid swizzle assignment with components out of bounds') .fn(t => { t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); const code = ` @fragment fn main() { var v = vec3f(); v.xyzw = vec4f(); } `; t.expectCompileResult(false, code); }); g.test('invalid_type_mismatch') .desc('Invalid swizzle assignment with mismatched types') .fn(t => { t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); const code = ` @fragment fn main() { var v = vec4f(); v.xy = vec2i(); } `; t.expectCompileResult(false, code); }); g.test('invalid_mixed_letter_schemes') .desc('Invalid swizzle assignment with mixed letter schemes (xyzw vs. rgba)') .fn(t => { t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); const code = ` @fragment fn main() { var v = vec4f(); v.xr = vec2i(); } `; t.expectCompileResult(false, code); }); g.test('invalid_address_of_swizzle_view') .desc('Invalid to take the address of a swizzle view') .fn(t => { t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); const code = ` @fragment fn main() { var v = vec4f(); let p = &v.xy; } `; t.expectCompileResult(false, code); }); g.test('index_into_swizzle_view') .desc('Validate constant indexing into a swizzle view on the lhs') .params(u => u .combine('swizzle', ['x', 'xy', 'xx', 'xyz', 'xyzw', 'zxy'] as const) .combine('index', [0, 1, 2, 3, 4] as const) ) .fn(t => { t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); const { swizzle, index } = t.params; const code = ` @fragment fn main() { var v = vec4u(); v.${swizzle}[${index}] = 1u; } `; const isVector = swizzle.length > 1; const inBounds = index < swizzle.length; const expected = isVector && inBounds; t.expectCompileResult(expected, code); }); g.test('dynamic_index_into_swizzle_view') .desc('Validate dynamic indexing into a swizzle view on the lhs') .params(u => u.combine('swizzle', ['x', 'xy', 'xx', 'xyz', 'xyzw', 'zxy'] as const)) .fn(t => { t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); const { swizzle } = t.params; const code = ` @fragment fn main() { var v = vec4u(); var i = 1u; v.${swizzle}[i] = 1u; } `; const isVector = swizzle.length > 1; t.expectCompileResult(isVector, code); }); g.test('pointer_swizzle_assignment') .desc('Validate swizzle assignments on pointers') .params(u => u .combine('use_pointer', [true, false] as const) .combine('requires_pointer_composite_access', [true, false] as const) .combine('use_compound', [true, false] as const) ) .fn(t => { t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); const { use_pointer, requires_pointer_composite_access, use_compound } = t.params; const requires_directive = requires_pointer_composite_access ? 'requires pointer_composite_access;' : ''; const lhs = use_pointer ? 'p.xy' : '(*p).xy'; const op = use_compound ? '+=' : '='; const code = ` ${requires_directive} @fragment fn main() { var v = vec4f(); let p = &v; ${lhs} ${op} vec2f(1.0); } `; const has_feature = t.hasLanguageFeature('pointer_composite_access'); const expected = !use_pointer || has_feature; t.expectCompileResult(expected, code); }); const kChainedSwizzleCases = { // Valid cases: xy_y: { chain: 'xy.y', size: 1, pass: true }, // y xx_x: { chain: 'xx.x', size: 1, pass: true }, // x xyy_xy: { chain: 'xyy.xy', size: 2, pass: true }, // xy xxyy_zxy_xy: { chain: 'xxyy.zxy.xy', size: 2, pass: true }, // yx // Invalid cases: xxyy_xy: { chain: 'xxyy.xy', size: 2, pass: false }, // xx xyy_yy: { chain: 'xyy.yy', size: 2, pass: false }, // yy xxyy_zxy_yz: { chain: 'xxyy.zxy.yz', size: 2, pass: false }, // xx zy_xyx: { chain: 'zy.xyx', size: 3, pass: false }, // zyz }; g.test('chained_swizzle') .desc('Validate chained swizzles on the LHS of an assignment') .params(u => u.combine('case', keysOf(kChainedSwizzleCases))) .fn(t => { const { chain, size, pass } = kChainedSwizzleCases[t.params.case]; const rhs = size === 1 ? '1.0' : `vec${size}f(1.0)`; const code = ` @fragment fn main() { var v = vec4f(); v.${chain} = ${rhs}; } `; const expected = pass && t.hasLanguageFeature('swizzle_assignment'); t.expectCompileResult(expected, code); });