1
1
import { unreachable } from '../../../../../common/util/util.js' ;
2
- import { GPUTest } from '../../../../gpu_test.js' ;
2
+ import { GPUTest , GPUTestBase } from '../../../../gpu_test.js' ;
3
3
import { EncoderType } from '../../../../util/command_buffer_maker.js' ;
4
4
5
5
interface BindGroupIndices {
@@ -8,38 +8,81 @@ interface BindGroupIndices {
8
8
out : number ;
9
9
}
10
10
11
+ type CreateEncoderType = ReturnType <
12
+ typeof GPUTestBase . prototype . createEncoder < 'compute pass' | 'render pass' | 'render bundle' >
13
+ > [ 'encoder' ] ;
14
+
11
15
export class ProgrammableStateTest extends GPUTest {
12
16
private commonBindGroupLayouts : Map < string , GPUBindGroupLayout > = new Map ( ) ;
13
17
14
- getBindGroupLayout ( type : GPUBufferBindingType ) : GPUBindGroupLayout {
15
- if ( ! this . commonBindGroupLayouts . has ( type ) ) {
18
+ skipIfNeedsStorageBuffersInFragmentStageAndHaveNone (
19
+ type : GPUBufferBindingType ,
20
+ encoderType : EncoderType
21
+ ) {
22
+ if ( ! this . isCompatibility ) {
23
+ return ;
24
+ }
25
+
26
+ const needsStorageBuffersInFragmentStage =
27
+ type === 'storage' && ( encoderType === 'render bundle' || encoderType === 'render pass' ) ;
28
+
29
+ this . skipIf (
30
+ needsStorageBuffersInFragmentStage &&
31
+ ! ( this . device . limits . maxStorageBuffersInFragmentStage ! >= 3 ) ,
32
+ `maxStorageBuffersInFragmentStage(${ this . device . limits . maxStorageBuffersInFragmentStage } ) < 3`
33
+ ) ;
34
+ }
35
+
36
+ getBindGroupLayout (
37
+ type : GPUBufferBindingType ,
38
+ visibility : GPUShaderStageFlags
39
+ ) : GPUBindGroupLayout {
40
+ const id = `${ type } :${ visibility } ` ;
41
+ if ( ! this . commonBindGroupLayouts . has ( id ) ) {
16
42
this . commonBindGroupLayouts . set (
17
- type ,
43
+ id ,
18
44
this . device . createBindGroupLayout ( {
19
45
entries : [
20
46
{
21
47
binding : 0 ,
22
- visibility : GPUShaderStage . COMPUTE | GPUShaderStage . FRAGMENT ,
48
+ visibility,
23
49
buffer : { type } ,
24
50
} ,
25
51
] ,
26
52
} )
27
53
) ;
28
54
}
29
- return this . commonBindGroupLayouts . get ( type ) ! ;
55
+ return this . commonBindGroupLayouts . get ( id ) ! ;
30
56
}
31
57
32
- getBindGroupLayouts ( indices : BindGroupIndices ) : GPUBindGroupLayout [ ] {
58
+ getVisibilityForEncoderType ( encoderType : EncoderType ) {
59
+ return encoderType === 'compute pass' ? GPUShaderStage . COMPUTE : GPUShaderStage . FRAGMENT ;
60
+ }
61
+
62
+ getBindGroupLayouts (
63
+ indices : BindGroupIndices ,
64
+ type : GPUBufferBindingType ,
65
+ encoderType : EncoderType
66
+ ) : GPUBindGroupLayout [ ] {
33
67
const bindGroupLayouts : GPUBindGroupLayout [ ] = [ ] ;
34
- bindGroupLayouts [ indices . a ] = this . getBindGroupLayout ( 'read-only-storage' ) ;
35
- bindGroupLayouts [ indices . b ] = this . getBindGroupLayout ( 'read-only-storage' ) ;
36
- bindGroupLayouts [ indices . out ] = this . getBindGroupLayout ( 'storage' ) ;
68
+ const inputType = type === 'storage' ? 'read-only-storage' : 'uniform' ;
69
+ const visibility = this . getVisibilityForEncoderType ( encoderType ) ;
70
+ bindGroupLayouts [ indices . a ] = this . getBindGroupLayout ( inputType , visibility ) ;
71
+ bindGroupLayouts [ indices . b ] = this . getBindGroupLayout ( inputType , visibility ) ;
72
+ if ( type === 'storage' || encoderType === 'compute pass' ) {
73
+ bindGroupLayouts [ indices . out ] = this . getBindGroupLayout ( 'storage' , visibility ) ;
74
+ }
37
75
return bindGroupLayouts ;
38
76
}
39
77
40
- createBindGroup ( buffer : GPUBuffer , type : GPUBufferBindingType ) : GPUBindGroup {
78
+ createBindGroup (
79
+ buffer : GPUBuffer ,
80
+ type : GPUBufferBindingType ,
81
+ encoderType : EncoderType
82
+ ) : GPUBindGroup {
83
+ const visibility = this . getVisibilityForEncoderType ( encoderType ) ;
41
84
return this . device . createBindGroup ( {
42
- layout : this . getBindGroupLayout ( type ) ,
85
+ layout : this . getBindGroupLayout ( type , visibility ) ,
43
86
entries : [ { binding : 0 , resource : { buffer } } ] ,
44
87
} ) ;
45
88
}
@@ -57,6 +100,7 @@ export class ProgrammableStateTest extends GPUTest {
57
100
createBindingStatePipeline < T extends EncoderType > (
58
101
encoderType : T ,
59
102
groups : BindGroupIndices ,
103
+ type : GPUBufferBindingType ,
60
104
algorithm : string = 'a.value - b.value'
61
105
) : GPUComputePipeline | GPURenderPipeline {
62
106
switch ( encoderType ) {
@@ -65,8 +109,8 @@ export class ProgrammableStateTest extends GPUTest {
65
109
value : i32
66
110
};
67
111
68
- @group(${ groups . a } ) @binding(0) var<storage > a : Data;
69
- @group(${ groups . b } ) @binding(0) var<storage > b : Data;
112
+ @group(${ groups . a } ) @binding(0) var<${ type } > a : Data;
113
+ @group(${ groups . b } ) @binding(0) var<${ type } > b : Data;
70
114
@group(${ groups . out } ) @binding(0) var<storage, read_write> out : Data;
71
115
72
116
@compute @workgroup_size(1) fn main() {
@@ -77,7 +121,7 @@ export class ProgrammableStateTest extends GPUTest {
77
121
78
122
return this . device . createComputePipeline ( {
79
123
layout : this . device . createPipelineLayout ( {
80
- bindGroupLayouts : this . getBindGroupLayouts ( groups ) ,
124
+ bindGroupLayouts : this . getBindGroupLayouts ( groups , type , encoderType ) ,
81
125
} ) ,
82
126
compute : {
83
127
module : this . device . createShaderModule ( {
@@ -92,7 +136,7 @@ export class ProgrammableStateTest extends GPUTest {
92
136
const wgslShaders = {
93
137
vertex : `
94
138
@vertex fn vert_main() -> @builtin(position) vec4<f32> {
95
- return vec4<f32>(0.5 , 0.5 , 0.0 , 1.0 );
139
+ return vec4<f32>(0, 0, 0, 1);
96
140
}
97
141
` ,
98
142
@@ -101,20 +145,23 @@ export class ProgrammableStateTest extends GPUTest {
101
145
value : i32
102
146
};
103
147
104
- @group(${ groups . a } ) @binding(0) var<storage > a : Data;
105
- @group(${ groups . b } ) @binding(0) var<storage > b : Data;
148
+ @group(${ groups . a } ) @binding(0) var<${ type } > a : Data;
149
+ @group(${ groups . b } ) @binding(0) var<${ type } > b : Data;
106
150
@group(${ groups . out } ) @binding(0) var<storage, read_write> out : Data;
107
151
108
- @fragment fn frag_main () -> @location(0) vec4<f32 > {
152
+ @fragment fn frag_main_storage () -> @location(0) vec4<i32 > {
109
153
out.value = ${ algorithm } ;
110
- return vec4<f32>(1.0, 0.0, 0.0, 1.0);
154
+ return vec4<i32>(1, 0, 0, 1);
155
+ }
156
+ @fragment fn frag_main_uniform() -> @location(0) vec4<i32> {
157
+ return vec4<i32>(${ algorithm } );
111
158
}
112
159
` ,
113
160
} ;
114
161
115
162
return this . device . createRenderPipeline ( {
116
163
layout : this . device . createPipelineLayout ( {
117
- bindGroupLayouts : this . getBindGroupLayouts ( groups ) ,
164
+ bindGroupLayouts : this . getBindGroupLayouts ( groups , type , encoderType ) ,
118
165
} ) ,
119
166
vertex : {
120
167
module : this . device . createShaderModule ( {
@@ -126,8 +173,8 @@ export class ProgrammableStateTest extends GPUTest {
126
173
module : this . device . createShaderModule ( {
127
174
code : wgslShaders . fragment ,
128
175
} ) ,
129
- entryPoint : 'frag_main ',
130
- targets : [ { format : 'rgba8unorm ' } ] ,
176
+ entryPoint : type === 'uniform' ? 'frag_main_uniform' : 'frag_main_storage ',
177
+ targets : [ { format : 'r32sint ' } ] ,
131
178
} ,
132
179
primitive : { topology : 'point-list' } ,
133
180
} ) ;
@@ -137,6 +184,57 @@ export class ProgrammableStateTest extends GPUTest {
137
184
}
138
185
}
139
186
187
+ createEncoderForStateTest (
188
+ type : GPUBufferBindingType ,
189
+ out : GPUBuffer ,
190
+ ...params : Parameters < typeof GPUTestBase . prototype . createEncoder >
191
+ ) : {
192
+ encoder : CreateEncoderType ;
193
+ validateFinishAndSubmit : ( shouldBeValid : boolean , submitShouldSucceedIfValid : boolean ) => void ;
194
+ } {
195
+ const encoderType = params [ 0 ] ;
196
+ const renderTarget = this . createTextureTracked ( {
197
+ size : [ 1 , 1 ] ,
198
+ format : 'r32sint' ,
199
+ usage : GPUTextureUsage . RENDER_ATTACHMENT | GPUTextureUsage . COPY_SRC ,
200
+ } ) ;
201
+
202
+ // Note: This nightmare of gibberish is trying the result of 2 hours of
203
+ // trying to get typescript to accept the code. Originally the code was
204
+ // effectively just
205
+ //
206
+ // const { encoder, validateFinishAndSubmit } = this.createEncoder(...);
207
+ // const fn = (b0, b1) => { validateFinishAndSubmit(b1, b1); if (...) { ... copyT2B ... } }
208
+ // return { encoder: e__, validateFinishAndSubmit: fn };
209
+ //
210
+ // But TS didn't like it. I couldn't figure out why.
211
+ const encoderAndFinish = this . createEncoder ( encoderType , {
212
+ attachmentInfo : { colorFormats : [ 'r32sint' ] } ,
213
+ targets : [ renderTarget . createView ( ) ] ,
214
+ } ) ;
215
+
216
+ const validateFinishAndSubmit = (
217
+ shouldBeValid : boolean ,
218
+ submitShouldSucceedIfValid : boolean
219
+ ) => {
220
+ encoderAndFinish . validateFinishAndSubmit ( shouldBeValid , submitShouldSucceedIfValid ) ;
221
+
222
+ if (
223
+ type === 'uniform' &&
224
+ ( encoderType === 'render pass' || encoderType === 'render bundle' )
225
+ ) {
226
+ const encoder = this . device . createCommandEncoder ( ) ;
227
+ encoder . copyTextureToBuffer ( { texture : renderTarget } , { buffer : out } , [ 1 , 1 ] ) ;
228
+ this . device . queue . submit ( [ encoder . finish ( ) ] ) ;
229
+ }
230
+ } ;
231
+
232
+ return {
233
+ encoder : encoderAndFinish . encoder as CreateEncoderType ,
234
+ validateFinishAndSubmit,
235
+ } ;
236
+ }
237
+
140
238
setPipeline ( pass : GPUBindingCommandsMixin , pipeline : GPUComputePipeline | GPURenderPipeline ) {
141
239
if ( pass instanceof GPUComputePassEncoder ) {
142
240
pass . setPipeline ( pipeline as GPUComputePipeline ) ;
0 commit comments