1
- import { range } from '../../../../../common/util/util.js' ;
1
+ import { assert } from '../../../../../common/util/util.js' ;
2
2
3
3
import {
4
4
kCreatePipelineTypes ,
@@ -10,30 +10,152 @@ import {
10
10
const limit = 'maxBindGroups' ;
11
11
export const { g, description } = makeLimitTestGroup ( limit ) ;
12
12
13
+ type BindingLayout = {
14
+ buffer ?: GPUBufferBindingLayout ;
15
+ sampler ?: GPUSamplerBindingLayout ;
16
+ texture ?: GPUTextureBindingLayout ;
17
+ storageTexture ?: GPUStorageTextureBindingLayout ;
18
+ externalTexture ?: GPUExternalTextureBindingLayout ;
19
+ } ;
20
+
21
+ type LimitToBindingLayout = {
22
+ name : keyof GPUSupportedLimits ;
23
+ entry : BindingLayout ;
24
+ } ;
25
+
26
+ const kLimitToBindingLayout : LimitToBindingLayout [ ] = [
27
+ {
28
+ name : 'maxSampledTexturesPerShaderStage' ,
29
+ entry : {
30
+ texture : { } ,
31
+ } ,
32
+ } ,
33
+ {
34
+ name : 'maxSamplersPerShaderStage' ,
35
+ entry : {
36
+ sampler : { } ,
37
+ } ,
38
+ } ,
39
+ {
40
+ name : 'maxUniformBuffersPerShaderStage' ,
41
+ entry : {
42
+ buffer : { } ,
43
+ } ,
44
+ } ,
45
+ {
46
+ name : 'maxStorageBuffersPerShaderStage' ,
47
+ entry : {
48
+ buffer : {
49
+ type : 'read-only-storage' ,
50
+ } ,
51
+ } ,
52
+ } ,
53
+ {
54
+ name : 'maxStorageTexturesPerShaderStage' ,
55
+ entry : {
56
+ storageTexture : {
57
+ access : 'write-only' ,
58
+ format : 'rgba8unorm' ,
59
+ viewDimension : '2d' ,
60
+ } ,
61
+ } ,
62
+ } ,
63
+ ] ;
64
+
65
+ /**
66
+ * Yields all possible binding layout entries for a stage.
67
+ */
68
+ function * getBindingLayoutEntriesForStage ( device : GPUDevice ) {
69
+ for ( const { name, entry } of kLimitToBindingLayout ) {
70
+ const limit = device . limits [ name ] as number ;
71
+ for ( let i = 0 ; i < limit ; ++ i ) {
72
+ yield entry ;
73
+ }
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Yields all of the possible BindingLayoutEntryAndVisibility entries for a render pipeline
79
+ */
80
+ function * getBindingLayoutEntriesForRenderPipeline (
81
+ device : GPUDevice
82
+ ) : Generator < GPUBindGroupLayoutEntry > {
83
+ const visibilities = [ GPUShaderStage . VERTEX , GPUShaderStage . FRAGMENT ] ;
84
+ for ( const visibility of visibilities ) {
85
+ for ( const bindEntryResourceType of getBindingLayoutEntriesForStage ( device ) ) {
86
+ const entry : GPUBindGroupLayoutEntry = {
87
+ binding : 0 ,
88
+ visibility,
89
+ ...bindEntryResourceType ,
90
+ } ;
91
+ yield entry ;
92
+ }
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Returns the total possible bindings per render pipeline
98
+ */
99
+ function getTotalPossibleBindingsPerRenderPipeline ( device : GPUDevice ) {
100
+ const totalPossibleBindingsPerStage =
101
+ device . limits . maxSampledTexturesPerShaderStage +
102
+ device . limits . maxSamplersPerShaderStage +
103
+ device . limits . maxUniformBuffersPerShaderStage +
104
+ device . limits . maxStorageBuffersPerShaderStage +
105
+ device . limits . maxStorageTexturesPerShaderStage ;
106
+ return totalPossibleBindingsPerStage * 2 ;
107
+ }
108
+
109
+ /**
110
+ * Yields count GPUBindGroupLayoutEntries
111
+ */
112
+ function * getBindingLayoutEntries (
113
+ device : GPUDevice ,
114
+ count : number
115
+ ) : Generator < GPUBindGroupLayoutEntry > {
116
+ assert ( count < getTotalPossibleBindingsPerRenderPipeline ( device ) ) ;
117
+ const iter = getBindingLayoutEntriesForRenderPipeline ( device ) ;
118
+ for ( ; count > 0 ; -- count ) {
119
+ yield iter . next ( ) . value ;
120
+ }
121
+ }
122
+
13
123
g . test ( 'createPipelineLayout,at_over' )
14
124
. desc ( `Test using createPipelineLayout at and over ${ limit } limit` )
15
125
. params ( kMaximumLimitBaseParams )
16
126
. fn ( async t => {
17
127
const { limitTest, testValueName } = t . params ;
128
+
18
129
await t . testDeviceWithRequestedMaximumLimits (
19
130
limitTest ,
20
131
testValueName ,
21
- async ( { device, testValue, shouldError } ) => {
22
- const bindGroupLayouts = range ( testValue , _i =>
23
- device . createBindGroupLayout ( {
24
- entries : [
25
- {
26
- binding : 0 ,
27
- visibility : GPUShaderStage . VERTEX ,
28
- buffer : { } ,
29
- } ,
30
- ] ,
31
- } )
132
+ async ( { device, testValue, shouldError, actualLimit } ) => {
133
+ const totalPossibleBindingsPerPipeline = getTotalPossibleBindingsPerRenderPipeline ( device ) ;
134
+ // Not sure what to do if we ever hit this but I think it's better to assert than silently skip.
135
+ assert (
136
+ testValue < totalPossibleBindingsPerPipeline ,
137
+ `not enough possible bindings(${ totalPossibleBindingsPerPipeline } ) to test ${ testValue } bindGroups`
32
138
) ;
33
139
34
- await t . expectValidationError ( ( ) => {
35
- device . createPipelineLayout ( { bindGroupLayouts } ) ;
36
- } , shouldError ) ;
140
+ const bindingDescriptions : string [ ] = [ ] ;
141
+ const bindGroupLayouts = [ ...getBindingLayoutEntries ( device , testValue ) ] . map ( entry => {
142
+ bindingDescriptions . push (
143
+ `${ JSON . stringify ( entry ) } // group(${ bindingDescriptions . length } )`
144
+ ) ;
145
+ return device . createBindGroupLayout ( {
146
+ entries : [ entry ] ,
147
+ } ) ;
148
+ } ) ;
149
+
150
+ await t . expectValidationError (
151
+ ( ) => {
152
+ device . createPipelineLayout ( { bindGroupLayouts } ) ;
153
+ } ,
154
+ shouldError ,
155
+ `testing ${ testValue } bindGroups on maxBindGroups = ${ actualLimit } with \n${ bindingDescriptions . join (
156
+ '\n'
157
+ ) } `
158
+ ) ;
37
159
}
38
160
) ;
39
161
} ) ;
0 commit comments