blob: e5c0a3981eb14afed01d9e318c0c9d5180736bb8 [file]
export const description = `
Validation tests for the WGSL immediate address space.
`;
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { keysOf } from '../../../../common/util/data_tables.js';
import { ShaderValidationTest } from '../shader_validation_test.js';
import { skipIfImmediateDataNotSupported } from './util.js';
export const g = makeTestGroup(ShaderValidationTest);
const kImmediateFeature = 'immediate_address_space' as const;
const kImmediateHeader = `requires ${kImmediateFeature};`;
const kValidStoreTypes = {
u32: { enable: ``, prelude: ``, type: `u32` },
i32: { enable: ``, prelude: ``, type: `i32` },
f32: { enable: ``, prelude: ``, type: `f32` },
f16: { enable: `enable f16;`, prelude: ``, type: `f16` },
vec2u: { enable: ``, prelude: ``, type: `vec2u` },
vec3i: { enable: ``, prelude: ``, type: `vec3i` },
vec4f: { enable: ``, prelude: ``, type: `vec4f` },
vec3h: { enable: `enable f16;`, prelude: ``, type: `vec3h` },
mat2x2f: { enable: ``, prelude: ``, type: `mat2x2f` },
struct_numeric: { enable: ``, prelude: `struct S { a : u32, b : vec4f }`, type: `S` },
} as const;
const kInvalidStoreTypes = {
bool: { enable: ``, prelude: ``, type: `bool` },
vec2_bool: { enable: ``, prelude: ``, type: `vec2<bool>` },
atomic_u32: { enable: ``, prelude: ``, type: `atomic<u32>` },
ptr_function_u32: { enable: ``, prelude: ``, type: `ptr<function, u32>` },
sampler: { enable: ``, prelude: ``, type: `sampler` },
sampler_comparison: { enable: ``, prelude: ``, type: `sampler_comparison` },
texture_2d: { enable: ``, prelude: ``, type: `texture_2d<f32>` },
runtime_array: { enable: ``, prelude: ``, type: `array<u32>` },
fixed_array: { enable: ``, prelude: ``, type: `array<u32, 4>` },
struct_runtime_array: { enable: ``, prelude: `struct S { data : array<u32> }`, type: `S` },
struct_fixed_array: {
enable: ``,
prelude: `struct S { data : array<vec4u, 4> }`,
type: `S`,
},
} as const;
g.test('store_type,valid')
.desc('Validates immediate store types supported by the current WGSL immediate implementation.')
.params(u => u.combine('type', keysOf(kValidStoreTypes)))
.fn(t => {
skipIfImmediateDataNotSupported(t);
const testcase = kValidStoreTypes[t.params.type];
if (testcase.enable.includes('f16')) {
t.skipIfDeviceDoesNotHaveFeature('shader-f16');
}
const wgsl = `
${kImmediateHeader}
${testcase.enable}
${testcase.prelude}
var<immediate> data : ${testcase.type};
@compute @workgroup_size(1)
fn main() {
_ = data;
}`;
t.expectCompileResult(true, wgsl);
});
g.test('store_type,invalid')
.desc('Validates types that cannot be used for immediate variables.')
.params(u => u.combine('type', keysOf(kInvalidStoreTypes)))
.fn(t => {
skipIfImmediateDataNotSupported(t);
const testcase = kInvalidStoreTypes[t.params.type];
const wgsl = `
${kImmediateHeader}
${testcase.enable}
${testcase.prelude}
var<immediate> data : ${testcase.type};
@compute @workgroup_size(1)
fn main() {
_ = data;
}`;
t.expectCompileResult(false, wgsl);
});
g.test('scope')
.desc('Validates that immediate variables cannot be declared at function scope.')
.params(u => u.combine('addressSpace', ['function', 'immediate'] as const))
.fn(t => {
skipIfImmediateDataNotSupported(t);
const wgsl = `
${kImmediateHeader}
@compute @workgroup_size(1)
fn main() {
var<${t.params.addressSpace}> data : u32;
_ = data;
}`;
t.expectCompileResult(t.params.addressSpace === 'function', wgsl);
});
g.test('binding_attributes')
.desc('Validates that @group and @binding are not allowed on immediate variables.')
.params(u =>
u.combine('group', ['', '@group(0)'] as const).combine('binding', ['', '@binding(0)'] as const)
)
.fn(t => {
skipIfImmediateDataNotSupported(t);
const wgsl = `
${kImmediateHeader}
${t.params.group} ${t.params.binding} var<immediate> data : u32;
@compute @workgroup_size(1)
fn main() {
_ = data;
}`;
t.expectCompileResult(t.params.group === '' && t.params.binding === '', wgsl);
});
g.test('access_mode')
.desc('Validates that immediate variables cannot spell an access mode.')
.params(u => u.combine('accessMode', ['', 'read', 'write', 'read_write'] as const))
.fn(t => {
skipIfImmediateDataNotSupported(t);
const suffix = t.params.accessMode === '' ? '' : `, ${t.params.accessMode}`;
const wgsl = `
${kImmediateHeader}
var<immediate${suffix}> data : u32;
@compute @workgroup_size(1)
fn main() {
_ = data;
}`;
t.expectCompileResult(t.params.accessMode === '', wgsl);
});
const kEntryPointCases = {
one_used: {
valid: true,
body: `
var<immediate> a : u32;
@compute @workgroup_size(1)
fn main() {
_ = a;
}`,
},
two_declared_one_used: {
valid: true,
body: `
var<immediate> a : u32;
var<immediate> b : u32;
@compute @workgroup_size(1)
fn main() {
_ = a;
}`,
},
two_entry_points_one_each: {
valid: true,
body: `
var<immediate> a : u32;
var<immediate> b : u32;
@compute @workgroup_size(1)
fn main_a() {
_ = a;
}
@compute @workgroup_size(1)
fn main_b() {
_ = b;
}`,
},
one_entry_point_uses_two_directly: {
valid: false,
body: `
var<immediate> a : u32;
var<immediate> b : u32;
@compute @workgroup_size(1)
fn main() {
_ = a + b;
}`,
},
one_entry_point_uses_two_through_helper: {
valid: false,
body: `
var<immediate> a : u32;
var<immediate> b : u32;
fn read_b() -> u32 {
return b;
}
@compute @workgroup_size(1)
fn main() {
_ = a + read_b();
}`,
},
} as const;
g.test('entry_point_interface')
.desc('Validates one statically used immediate variable per entry point.')
.params(u => u.combine('case', keysOf(kEntryPointCases)))
.fn(t => {
skipIfImmediateDataNotSupported(t);
const testcase = kEntryPointCases[t.params.case];
t.expectCompileResult(testcase.valid, `${kImmediateHeader}\n${testcase.body}`);
});
const kPointerCases = {
alias_module_scope: {
valid: true,
needsUnrestrictedPointerParameters: false,
body: `
alias P = ptr<immediate, u32>;
var<immediate> data : u32;
@compute @workgroup_size(1)
fn main() {
let p : P = &data;
_ = *p;
}`,
},
let_inside_function: {
valid: true,
needsUnrestrictedPointerParameters: false,
body: `
var<immediate> data : u32;
@compute @workgroup_size(1)
fn main() {
let p : ptr<immediate, u32> = &data;
_ = *p;
}`,
},
write_through_pointer: {
valid: false,
needsUnrestrictedPointerParameters: false,
body: `
var<immediate> data : u32;
@compute @workgroup_size(1)
fn main() {
let p : ptr<immediate, u32> = &data;
*p = 1u;
}`,
},
pointer_parameter: {
valid: true,
needsUnrestrictedPointerParameters: true,
body: `
var<immediate> data : u32;
fn read_data(p : ptr<immediate, u32>) -> u32 {
return *p;
}
@compute @workgroup_size(1)
fn main() {
_ = read_data(&data);
}`,
},
explicit_read_access: {
valid: false,
needsUnrestrictedPointerParameters: false,
body: `
alias P = ptr<immediate, u32, read>;`,
},
explicit_write_access: {
valid: false,
needsUnrestrictedPointerParameters: false,
body: `
alias P = ptr<immediate, u32, write>;`,
},
explicit_read_write_access: {
valid: false,
needsUnrestrictedPointerParameters: false,
body: `
alias P = ptr<immediate, u32, read_write>;`,
},
missing_store_type: {
valid: false,
needsUnrestrictedPointerParameters: false,
body: `
alias P = ptr<immediate>;`,
},
} as const;
g.test('pointers')
.desc('Validates ptr<immediate> type creation, use, access modes, and function parameters.')
.params(u => u.combine('case', keysOf(kPointerCases)))
.fn(t => {
skipIfImmediateDataNotSupported(t);
const testcase = kPointerCases[t.params.case];
const unrestrictedHeader =
testcase.needsUnrestrictedPointerParameters &&
t.hasLanguageFeature('unrestricted_pointer_parameters')
? 'requires unrestricted_pointer_parameters;\n'
: '';
const expected =
testcase.valid &&
(!testcase.needsUnrestrictedPointerParameters ||
t.hasLanguageFeature('unrestricted_pointer_parameters'));
const wgsl = `${kImmediateHeader}\n${unrestrictedHeader}${testcase.body}`;
t.expectCompileResult(expected, wgsl);
});