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` }, atomic_u32: { enable: ``, prelude: ``, type: `atomic` }, ptr_function_u32: { enable: ``, prelude: ``, type: `ptr` }, sampler: { enable: ``, prelude: ``, type: `sampler` }, sampler_comparison: { enable: ``, prelude: ``, type: `sampler_comparison` }, texture_2d: { enable: ``, prelude: ``, type: `texture_2d` }, runtime_array: { enable: ``, prelude: ``, type: `array` }, fixed_array: { enable: ``, prelude: ``, type: `array` }, struct_runtime_array: { enable: ``, prelude: `struct S { data : array }`, type: `S` }, struct_fixed_array: { enable: ``, prelude: `struct S { data : array }`, 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 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 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 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 data : u32; @compute @workgroup_size(1) fn main() { _ = data; }`; t.expectCompileResult(t.params.accessMode === '', wgsl); }); const kEntryPointCases = { one_used: { valid: true, body: ` var a : u32; @compute @workgroup_size(1) fn main() { _ = a; }`, }, two_declared_one_used: { valid: true, body: ` var a : u32; var b : u32; @compute @workgroup_size(1) fn main() { _ = a; }`, }, two_entry_points_one_each: { valid: true, body: ` var a : u32; var 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 a : u32; var b : u32; @compute @workgroup_size(1) fn main() { _ = a + b; }`, }, one_entry_point_uses_two_through_helper: { valid: false, body: ` var a : u32; var 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; var data : u32; @compute @workgroup_size(1) fn main() { let p : P = &data; _ = *p; }`, }, let_inside_function: { valid: true, needsUnrestrictedPointerParameters: false, body: ` var data : u32; @compute @workgroup_size(1) fn main() { let p : ptr = &data; _ = *p; }`, }, write_through_pointer: { valid: false, needsUnrestrictedPointerParameters: false, body: ` var data : u32; @compute @workgroup_size(1) fn main() { let p : ptr = &data; *p = 1u; }`, }, pointer_parameter: { valid: true, needsUnrestrictedPointerParameters: true, body: ` var data : u32; fn read_data(p : ptr) -> u32 { return *p; } @compute @workgroup_size(1) fn main() { _ = read_data(&data); }`, }, explicit_read_access: { valid: false, needsUnrestrictedPointerParameters: false, body: ` alias P = ptr;`, }, explicit_write_access: { valid: false, needsUnrestrictedPointerParameters: false, body: ` alias P = ptr;`, }, explicit_read_write_access: { valid: false, needsUnrestrictedPointerParameters: false, body: ` alias P = ptr;`, }, missing_store_type: { valid: false, needsUnrestrictedPointerParameters: false, body: ` alias P = ptr;`, }, } as const; g.test('pointers') .desc('Validates ptr 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); });