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 = {
xy_y: { chain: 'xy.y', size: 1, pass: true },
xx_x: { chain: 'xx.x', size: 1, pass: true },
xyy_xy: { chain: 'xyy.xy', size: 2, pass: true },
xxyy_zxy_xy: { chain: 'xxyy.zxy.xy', size: 2, pass: true },
xxyy_xy: { chain: 'xxyy.xy', size: 2, pass: false },
xyy_yy: { chain: 'xyy.yy', size: 2, pass: false },
xxyy_zxy_yz: { chain: 'xxyy.zxy.yz', size: 2, pass: false },
zy_xyx: { chain: 'zy.xyx', size: 3, pass: false },
};
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);
});