@@ -3,6 +3,8 @@ misc createRenderPipeline and createRenderPipelineAsync validation tests.
33` ;
44
55import { makeTestGroup } from '../../../../common/framework/test_group.js' ;
6+ import { getGPU } from '../../../../common/util/navigator_gpu.js' ;
7+ import { assert , supportsImmediateData } from '../../../../common/util/util.js' ;
68import {
79 isTextureFormatUsableWithStorageAccessMode ,
810 kPossibleStorageTextureFormats ,
@@ -119,8 +121,10 @@ g.test('pipeline_layout,device_mismatch')
119121 } ) ;
120122
121123g . test ( 'external_texture' )
122- . desc ( 'Tests createRenderPipeline() with an external_texture' )
124+ . desc ( 'Tests createRenderPipeline(Async) with an external_texture' )
125+ . params ( u => u . combine ( 'isAsync' , [ false , true ] ) )
123126 . fn ( t => {
127+ const { isAsync } = t . params ;
124128 const shader = t . device . createShaderModule ( {
125129 code : `
126130 @vertex
@@ -149,7 +153,7 @@ g.test('external_texture')
149153 } ,
150154 } ;
151155
152- vtu . doCreateRenderPipelineTest ( t , false , true , descriptor ) ;
156+ vtu . doCreateRenderPipelineTest ( t , isAsync , true , descriptor ) ;
153157 } ) ;
154158
155159g . test ( 'storage_texture,format' )
@@ -192,3 +196,122 @@ generates a validation error at createComputePipeline(Async)
192196 } ;
193197 vtu . doCreateRenderPipelineTest ( t , isAsync , success , descriptor ) ;
194198 } ) ;
199+
200+ g . test ( 'pipeline_creation_immediate_size_mismatch' )
201+ . desc (
202+ `
203+ Validate that creating a pipeline fails if the shader uses immediate data
204+ larger than the immediateSize specified in the pipeline layout, or larger than
205+ maxImmediateSize if layout is 'auto'.
206+ Also validates that using less or equal size is allowed.
207+ `
208+ )
209+ . params ( u => {
210+ const kNumericCases = [
211+ { vertexSize : 16 , fragmentSize : 16 , layoutSize : 16 } , // Equal
212+ { vertexSize : 12 , fragmentSize : 12 , layoutSize : 16 } , // Shader smaller
213+ { vertexSize : 20 , fragmentSize : 20 , layoutSize : 16 } , // Shader larger (small diff)
214+ { vertexSize : 32 , fragmentSize : 32 , layoutSize : 16 } , // Shader larger
215+ ] as const ;
216+ const kMaxLimitsCases = [
217+ { vertexSize : 'max' , fragmentSize : 0 , layoutSize : 'auto' } , // Vertex = Limit (Control)
218+ { vertexSize : 0 , fragmentSize : 'max' , layoutSize : 'auto' } , // Fragment = Limit (Control)
219+ { vertexSize : 'max' , fragmentSize : 'max' , layoutSize : 'auto' } , // Both at Limit (Control)
220+ { vertexSize : 'exceedLimits' , fragmentSize : 0 , layoutSize : 'auto' } , // Vertex > Limit
221+ { vertexSize : 0 , fragmentSize : 'exceedLimits' , layoutSize : 'auto' } , // Fragment > Limit
222+ ] as const ;
223+ return u
224+ . combine ( 'isAsync' , [ true , false ] )
225+ . combineWithParams ( [ ...kNumericCases , ...kMaxLimitsCases ] as const ) ;
226+ } )
227+ . fn ( t => {
228+ t . skipIf ( ! supportsImmediateData ( getGPU ( t . rec ) ) , 'Immediate data not supported' ) ;
229+
230+ const { isAsync, vertexSize, fragmentSize, layoutSize } = t . params ;
231+
232+ assert ( t . device . limits . maxImmediateSize !== undefined ) ;
233+ const maxImmediateSize = t . device . limits . maxImmediateSize ;
234+
235+ const resolveSize = ( sizeDescriptor : number | string ) => {
236+ if ( typeof sizeDescriptor === 'number' ) return sizeDescriptor ;
237+ if ( sizeDescriptor === 'max' ) return maxImmediateSize ;
238+ if ( sizeDescriptor === 'exceedLimits' ) return maxImmediateSize + 4 ;
239+ return 0 ;
240+ } ;
241+
242+ const resolvedVertexImmediateSize = resolveSize ( vertexSize ) ;
243+ const resolvedFragmentImmediateSize = resolveSize ( fragmentSize ) ;
244+
245+ // Helper to generate a stage-specific shader module with the given immediate data size.
246+ const makeShaderCode = ( size : number , stage : 'vertex' | 'fragment' ) => {
247+ if ( size === 0 ) {
248+ if ( stage === 'vertex' ) {
249+ return `@vertex fn main_vertex() -> @builtin(position) vec4<f32> { return vec4<f32>(0.0, 0.0, 0.0, 1.0); }` ;
250+ }
251+ return `@fragment fn main_fragment() -> @location(0) vec4<f32> { return vec4<f32>(0.0, 1.0, 0.0, 1.0); }` ;
252+ }
253+ const numFields = size / 4 ;
254+ const fields = Array . from ( { length : numFields } , ( _ , i ) => `m${ i } : u32` ) . join ( ', ' ) ;
255+ if ( stage === 'vertex' ) {
256+ return `
257+ struct Immediates { ${ fields } }
258+ var<immediate> data: Immediates;
259+ @vertex fn main_vertex() -> @builtin(position) vec4<f32> { _ = data.m0; return vec4<f32>(0.0, 0.0, 0.0, 1.0); }
260+ ` ;
261+ }
262+ return `
263+ struct Immediates { ${ fields } }
264+ var<immediate> data: Immediates;
265+ @fragment fn main_fragment() -> @location(0) vec4<f32> { _ = data.m0; return vec4<f32>(0.0, 1.0, 0.0, 1.0); }
266+ ` ;
267+ } ;
268+
269+ let layout : GPUPipelineLayout | 'auto' ;
270+ let validSize : number ;
271+
272+ if ( layoutSize === 'auto' ) {
273+ layout = 'auto' ;
274+ validSize = maxImmediateSize ;
275+ } else {
276+ layout = t . device . createPipelineLayout ( {
277+ bindGroupLayouts : [ ] ,
278+ immediateSize : layoutSize as number ,
279+ } ) ;
280+ validSize = layoutSize as number ;
281+ }
282+
283+ const vertexExceedsLimit = resolvedVertexImmediateSize > validSize ;
284+ const fragmentExceedsLimit = resolvedFragmentImmediateSize > validSize ;
285+ const shouldError = vertexExceedsLimit || fragmentExceedsLimit ;
286+
287+ // When the shader exceeds the device's maxImmediateSize, the error occurs
288+ // at shader module creation time, not pipeline creation time.
289+ // Create each shader module separately so the correct one gets the error.
290+ const vertexCode = makeShaderCode ( resolvedVertexImmediateSize , 'vertex' ) ;
291+ const fragmentCode = makeShaderCode ( resolvedFragmentImmediateSize , 'fragment' ) ;
292+
293+ if ( layoutSize === 'auto' && vertexExceedsLimit ) {
294+ t . expectValidationError ( ( ) => {
295+ t . device . createShaderModule ( { code : vertexCode } ) ;
296+ } ) ;
297+ }
298+ if ( layoutSize === 'auto' && fragmentExceedsLimit ) {
299+ t . expectValidationError ( ( ) => {
300+ t . device . createShaderModule ( { code : fragmentCode } ) ;
301+ } ) ;
302+ }
303+ if ( layoutSize === 'auto' && shouldError ) {
304+ return ;
305+ }
306+
307+ vtu . doCreateRenderPipelineTest ( t , isAsync , ! shouldError , {
308+ layout,
309+ vertex : {
310+ module : t . device . createShaderModule ( { code : vertexCode } ) ,
311+ } ,
312+ fragment : {
313+ module : t . device . createShaderModule ( { code : fragmentCode } ) ,
314+ targets : [ { format : 'rgba8unorm' } ] ,
315+ } ,
316+ } ) ;
317+ } ) ;
0 commit comments