From 0b21a605eaf03e81f08624e13a7b1f20e7717239 Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Fri, 5 Jun 2026 14:09:37 +0800 Subject: [PATCH 1/3] Add unary expression tests for WGSL immediate pointers This patch extends address-of and indirection validation to `var`. The new cases verify that shaders can take the address of immediate variables, read through `ptr` values, and reject writes through those pointers. The generated matrix is limited to store types that are valid for `immediate`; invalid store types are covered by the declaration and pointer type suites. --- .../unary/address_of_and_indirection.spec.ts | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/webgpu/shader/validation/expression/unary/address_of_and_indirection.spec.ts b/src/webgpu/shader/validation/expression/unary/address_of_and_indirection.spec.ts index 97ab7e8e4750..32f826c48816 100644 --- a/src/webgpu/shader/validation/expression/unary/address_of_and_indirection.spec.ts +++ b/src/webgpu/shader/validation/expression/unary/address_of_and_indirection.spec.ts @@ -4,11 +4,12 @@ Validation tests for unary address-of and indirection (dereference) import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { keysOf } from '../../../../../common/util/data_tables.js'; +import { skipIfImmediateDataNotSupported } from '../../decl/util.js'; import { ShaderValidationTest } from '../../shader_validation_test.js'; export const g = makeTestGroup(ShaderValidationTest); -const kAddressSpaces = ['function', 'private', 'workgroup', 'uniform', 'storage']; +const kAddressSpaces = ['function', 'private', 'workgroup', 'uniform', 'storage', 'immediate']; const kAccessModes = ['read', 'read_write']; const kStorageTypes = ['bool', 'u32', 'i32', 'f32', 'f16']; const kCompositeTypes = ['array', 'struct', 'vec', 'mat']; @@ -49,16 +50,25 @@ g.test('basic') } return true; }) + .filter(t => { + if (t.addressSpace !== 'immediate') return true; + return t.storageType !== 'bool'; + }) .filter(t => { // This test does not test composite access return !kDerefTypes[t.derefType].requires_pointer_composite_access; }) ) .fn(t => { + if (t.params.addressSpace === 'immediate') { + skipIfImmediateDataNotSupported(t); + } const isLocal = t.params.addressSpace === 'function'; const deref = kDerefTypes[t.params.derefType]; // Only specify access mode for storage buffers const commaAccessMode = t.params.addressSpace === 'storage' ? `, ${t.params.accessMode}` : ''; + const header = + t.params.addressSpace === 'immediate' ? 'requires immediate_address_space;\n' : ''; let varDecl = ''; if (t.params.addressSpace === 'uniform' || t.params.addressSpace === 'storage') { @@ -66,7 +76,7 @@ g.test('basic') } varDecl += `var<${t.params.addressSpace}${commaAccessMode}> a : VarType;`; - const wgsl = ` + const wgsl = `${header} ${t.params.storageType === 'f16' ? 'enable f16;' : ''} alias VarType = ${t.params.storageType}; @@ -111,12 +121,21 @@ g.test('composite') } return true; }) + .filter(t => { + if (t.addressSpace !== 'immediate') return true; + return t.compositeType !== 'array' && t.storageType !== 'bool'; + }) ) .fn(t => { + if (t.params.addressSpace === 'immediate') { + skipIfImmediateDataNotSupported(t); + } const isLocal = t.params.addressSpace === 'function'; const deref = kDerefTypes[t.params.derefType]; // Only specify access mode for storage buffers const commaAccessMode = t.params.addressSpace === 'storage' ? `, ${t.params.accessMode}` : ''; + const header = + t.params.addressSpace === 'immediate' ? 'requires immediate_address_space;\n' : ''; let varDecl = ''; if (t.params.addressSpace === 'uniform' || t.params.addressSpace === 'storage') { @@ -124,7 +143,7 @@ g.test('composite') } varDecl += `var<${t.params.addressSpace}${commaAccessMode}> a : VarType;`; - let wgsl = ` + let wgsl = `${header} ${t.params.storageType === 'f16' ? 'enable f16;' : ''}`; switch (t.params.compositeType) { From e997f0aa65b4acbeee7cb32564a08d570f6529f7 Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Fri, 5 Jun 2026 14:09:47 +0800 Subject: [PATCH 2/3] Add function tests for WGSL immediate pointers This patch extends function restriction tests for `ptr` parameters. The new cases verify that immediate pointer parameters follow the `unrestricted_pointer_parameters` language feature gate, that calls can pass the address of a `var` to a matching parameter, and that explicit read and write access modes on immediate pointer parameters are rejected. --- .../validation/functions/restrictions.spec.ts | 49 +++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/webgpu/shader/validation/functions/restrictions.spec.ts b/src/webgpu/shader/validation/functions/restrictions.spec.ts index 73c3a376765e..84eef391760d 100644 --- a/src/webgpu/shader/validation/functions/restrictions.spec.ts +++ b/src/webgpu/shader/validation/functions/restrictions.spec.ts @@ -2,6 +2,7 @@ export const description = `Validation tests for function restrictions`; import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { keysOf } from '../../../../common/util/data_tables.js'; +import { skipIfImmediateDataNotSupported } from '../decl/util.js'; import { ShaderValidationTest } from '../shader_validation_test.js'; export const g = makeTestGroup(ShaderValidationTest); @@ -274,6 +275,10 @@ const kFunctionParamTypeCases: Record = { name: `ptr`, valid: 'with_unrestricted_pointer_parameters', }, + ptrImmediate: { + name: `ptr`, + valid: 'with_unrestricted_pointer_parameters', + }, ptrWorkgroupAtomic: { name: `ptr>`, valid: 'with_unrestricted_pointer_parameters', @@ -304,6 +309,12 @@ const kFunctionParamTypeCases: Record = { invalid_ptr6: { name: `ptr`, valid: false }, // Can't specify access mode invalid_ptr7: { name: `ptr`, valid: false }, // Invalid store type invalid_ptr8: { name: `ptr`, valid: false }, // non-constructible pointer type + invalid_ptr_immediate_access_read: { name: `ptr`, valid: false }, + invalid_ptr_immediate_access_write: { name: `ptr`, valid: false }, + invalid_ptr_immediate_access_read_write: { + name: `ptr`, + valid: false, + }, }; g.test('function_parameter_types') @@ -313,8 +324,20 @@ g.test('function_parameter_types') .fn(t => { const testcase = kFunctionParamTypeCases[t.params.case]; const enable = testcase.name === 'f16' ? 'enable f16;' : ''; + const needsImmediate = testcase.name.includes('immediate'); + if (needsImmediate) { + skipIfImmediateDataNotSupported(t); + } + const immediateRequires = needsImmediate ? 'requires immediate_address_space;' : ''; + const unrestrictedRequires = + testcase.valid === 'with_unrestricted_pointer_parameters' && + t.hasLanguageFeature('unrestricted_pointer_parameters') + ? 'requires unrestricted_pointer_parameters;' + : ''; const code = ` ${enable} +${immediateRequires} +${unrestrictedRequires} ${kCCommonTypeDecls} @@ -504,6 +527,11 @@ const kFunctionParamValueCases: Record = { matches: ['ptr12'], needsUnrestrictedPointerParameters: true, }, + ptrImmediate: { + value: `&immediate_u32`, + matches: ['ptrImmediate'], + needsUnrestrictedPointerParameters: true, + }, ptrWorkgroupOverrideNoDefault: { value: `&wg_override_no_default`, matches: ['ptrWorkgroupOverrideNoDefault'], @@ -548,8 +576,23 @@ g.test('function_parameter_matching') const param = kFunctionParamTypeCases[t.params.decl]; const arg = kFunctionParamValueCases[t.params.arg]; const enable = param.name === 'f16' ? 'enable f16;' : ''; + const needsUnrestrictedPointerParameters = + (kFunctionParamTypeCases[t.params.decl].valid === 'with_unrestricted_pointer_parameters' || + arg.needsUnrestrictedPointerParameters) ?? + false; + const needsImmediate = param.name.includes('immediate') || t.params.arg === 'ptrImmediate'; + if (needsImmediate) { + skipIfImmediateDataNotSupported(t); + } + const immediateRequires = needsImmediate ? 'requires immediate_address_space;' : ''; + const unrestrictedRequires = + needsUnrestrictedPointerParameters && t.hasLanguageFeature('unrestricted_pointer_parameters') + ? 'requires unrestricted_pointer_parameters;' + : ''; const code = ` ${enable} +${immediateRequires} +${unrestrictedRequires} ${kCCommonTypeDecls} @group(0) @binding(0) @@ -573,6 +616,7 @@ var ro_host_shareable : host_shareable; var rw_host_shareable : host_shareable; @group(1) @binding(2) var uniform_host_shareable : host_shareable; +${needsImmediate ? 'var immediate_u32 : u32;' : ''} fn bar(param : ${param.name}) { } @@ -638,11 +682,6 @@ fn foo() { } `; - const needsUnrestrictedPointerParameters = - (kFunctionParamTypeCases[t.params.decl].valid === 'with_unrestricted_pointer_parameters' || - arg.needsUnrestrictedPointerParameters) ?? - false; - let isValid = parameterMatches(t.params.decl, arg.matches); if (isValid && needsUnrestrictedPointerParameters) { isValid = t.hasLanguageFeature('unrestricted_pointer_parameters'); From 05a63dda759a577234e780ee46102e83e257cb81 Mon Sep 17 00:00:00 2001 From: shaoboyan Date: Fri, 5 Jun 2026 14:09:54 +0800 Subject: [PATCH 3/3] Add uniformity tests for WGSL immediate reads This patch adds `var` reads as a uniform condition source in the uniformity suite. The coverage verifies that control flow depending on immediate data is treated as uniform in the baseline uniformity cases and in the subgroup variants. The cases are skipped unless immediate data support is exposed by the browser. --- .../validation/uniformity/uniformity.spec.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/webgpu/shader/validation/uniformity/uniformity.spec.ts b/src/webgpu/shader/validation/uniformity/uniformity.spec.ts index cbc8476d466e..f3a3bddb5215 100644 --- a/src/webgpu/shader/validation/uniformity/uniformity.spec.ts +++ b/src/webgpu/shader/validation/uniformity/uniformity.spec.ts @@ -4,6 +4,7 @@ import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { keysOf } from '../../../../common/util/data_tables.js'; import { unreachable } from '../../../../common/util/util.js'; import { WGSLLanguageFeature } from '../../../capability_info.js'; +import { skipIfImmediateDataNotSupported } from '../decl/util.js'; import { ShaderValidationTest } from '../shader_validation_test.js'; import { Snippet, LoopCase, compileShouldSucceed } from './snippet.js'; @@ -41,6 +42,7 @@ const kConditions = [ { cond: 'uniform_override', expectation: true }, { cond: 'uniform_let', expectation: true }, { cond: 'nonuniform_let', expectation: false }, + { cond: 'uniform_immediate', expectation: true }, { cond: 'uniform_or', expectation: true }, { cond: 'nonuniform_or1', expectation: false }, { cond: 'nonuniform_or2', expectation: false }, @@ -82,6 +84,9 @@ function generateCondition(condition: string): string { case 'nonuniform_let': { return `n_let == 0`; } + case 'uniform_immediate': { + return `immediate_value == 0u`; + } case 'uniform_or': { return `u_let == 0 || uniform_buffer.y > 1`; } @@ -468,8 +473,13 @@ g.test('basics') if (t.params.op === 'textureBarrier' || t.params.cond.startsWith('storage_texture')) { t.skipIfLanguageFeatureNotSupported('readonly_and_readwrite_storage_textures'); } + const needsImmediate = t.params.cond === 'uniform_immediate'; + if (needsImmediate) { + skipIfImmediateDataNotSupported(t); + } let code = ` + ${needsImmediate ? 'requires immediate_address_space;' : ''} @group(0) @binding(0) var s : sampler; @group(0) @binding(1) var s_comp : sampler_comparison; @group(0) @binding(2) var tex : texture_2d; @@ -478,6 +488,7 @@ g.test('basics') @group(1) @binding(0) var ro_buffer : array; @group(1) @binding(1) var rw_buffer : array; @group(1) @binding(2) var uniform_buffer : vec4; + ${needsImmediate ? 'var immediate_value : u32;' : ''} @group(2) @binding(0) var ro_storage_texture : texture_storage_2d; @group(2) @binding(1) var rw_storage_texture : texture_storage_2d; @@ -566,8 +577,14 @@ g.test('basics,subgroups') .combine('stage', ['compute', 'fragment'] as const) ) .fn(t => { + const needsImmediate = t.params.cond === 'uniform_immediate'; + if (needsImmediate) { + skipIfImmediateDataNotSupported(t); + } + let code = ` enable subgroups; + ${needsImmediate ? 'requires immediate_address_space;' : ''} @group(0) @binding(0) var s : sampler; @group(0) @binding(1) var s_comp : sampler_comparison; @@ -577,6 +594,7 @@ g.test('basics,subgroups') @group(1) @binding(0) var ro_buffer : array; @group(1) @binding(1) var rw_buffer : array; @group(1) @binding(2) var uniform_buffer : vec4; + ${needsImmediate ? 'var immediate_value : u32;' : ''} @group(2) @binding(0) var ro_storage_texture : texture_storage_2d; @group(2) @binding(1) var rw_storage_texture : texture_storage_2d;