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