@@ -8,10 +8,12 @@ there is not much we can test except that there are no errors.
88 - compute pass
99 - render pass
1010 - 64k query objects
11+ - resolving unused slots
1112` ;
1213
1314import { makeTestGroup } from '../../../../../common/framework/test_group.js' ;
14- import { AllFeaturesMaxLimitsGPUTest } from '../../../../gpu_test.js' ;
15+ import { range } from '../../../../../common/util/util.js' ;
16+ import { AllFeaturesMaxLimitsGPUTest , GPUTest } from '../../../../gpu_test.js' ;
1517
1618export const g = makeTestGroup ( AllFeaturesMaxLimitsGPUTest ) ;
1719
@@ -95,6 +97,63 @@ and prevent pages from running.
9597 }
9698 } ) ;
9799
100+ function encoderQueryUsage (
101+ t : GPUTest ,
102+ stage : 'compute' | 'render' ,
103+ numQuerySets : number ,
104+ numSlots : number
105+ ) {
106+ const encoder = t . device . createCommandEncoder ( ) ;
107+
108+ const view = t
109+ . createTextureTracked ( {
110+ size : [ 1 , 1 , 1 ] ,
111+ format : 'rgba8unorm' ,
112+ usage : GPUTextureUsage . RENDER_ATTACHMENT ,
113+ } )
114+ . createView ( ) ;
115+
116+ const querySets = range ( numQuerySets , _ => {
117+ const querySet = t . createQuerySetTracked ( {
118+ type : 'timestamp' ,
119+ count : numSlots ,
120+ } ) ;
121+
122+ switch ( stage ) {
123+ case 'compute' : {
124+ for ( let slot = 0 ; slot < numSlots ; slot += 2 ) {
125+ const pass = encoder . beginComputePass ( {
126+ timestampWrites : {
127+ querySet,
128+ beginningOfPassWriteIndex : slot ,
129+ endOfPassWriteIndex : slot + 1 ,
130+ } ,
131+ } ) ;
132+ pass . end ( ) ;
133+ }
134+ break ;
135+ }
136+ case 'render' : {
137+ for ( let slot = 0 ; slot < numSlots ; slot += 2 ) {
138+ const pass = encoder . beginRenderPass ( {
139+ colorAttachments : [ { view, loadOp : 'load' , storeOp : 'store' } ] ,
140+ timestampWrites : {
141+ querySet,
142+ beginningOfPassWriteIndex : slot ,
143+ endOfPassWriteIndex : slot + 1 ,
144+ } ,
145+ } ) ;
146+ pass . end ( ) ;
147+ }
148+ break ;
149+ }
150+ }
151+
152+ return querySet ;
153+ } ) ;
154+ return { encoder, querySets } ;
155+ }
156+
98157g . test ( 'many_slots' )
99158 . desc (
100159 `
@@ -112,52 +171,49 @@ So, test we can use 4k slots across a few QuerySets
112171 const kNumSlots = 4096 ;
113172 const kNumQuerySets = 4 ;
114173
115- const view = t
116- . createTextureTracked ( {
117- size : [ 1 , 1 , 1 ] ,
118- format : 'rgba8unorm' ,
119- usage : GPUTextureUsage . RENDER_ATTACHMENT ,
120- } )
121- . createView ( ) ;
122- const encoder = t . device . createCommandEncoder ( ) ;
174+ const { encoder } = encoderQueryUsage ( t , stage , kNumQuerySets , kNumSlots ) ;
175+ t . device . queue . submit ( [ encoder . finish ( ) ] ) ;
176+ } ) ;
123177
124- for ( let i = 0 ; i < kNumQuerySets ; ++ i ) {
125- const querySet = t . createQuerySetTracked ( {
126- type : 'timestamp' ,
127- count : kNumSlots ,
128- } ) ;
178+ g . test ( 'resolve_unused_slots' )
179+ . desc (
180+ `
181+ Test resolving query sets with unused slots.
129182
130- switch ( stage ) {
131- case 'compute' : {
132- for ( let slot = 0 ; slot < kNumSlots ; slot += 2 ) {
133- const pass = encoder . beginComputePass ( {
134- timestampWrites : {
135- querySet,
136- beginningOfPassWriteIndex : slot ,
137- endOfPassWriteIndex : slot + 1 ,
138- } ,
139- } ) ;
140- pass . end ( ) ;
141- }
142- break ;
143- }
144- case 'render' : {
145- for ( let slot = 0 ; slot < kNumSlots ; slot += 2 ) {
146- const pass = encoder . beginRenderPass ( {
147- colorAttachments : [ { view, loadOp : 'load' , storeOp : 'store' } ] ,
148- timestampWrites : {
149- querySet,
150- beginningOfPassWriteIndex : slot ,
151- endOfPassWriteIndex : slot + 1 ,
152- } ,
153- } ) ;
154- pass . end ( ) ;
155- }
156- break ;
157- }
158- }
159- }
183+ We create a command buffer that uses the slots but don't actually submit it
184+ to make sure the implementation doesn't mistakenly mark them as used.
185+ `
186+ )
187+ . params ( u => u . combine ( 'stage' , [ 'compute' , 'render' ] as const ) )
188+ . fn ( t => {
189+ const { stage } = t . params ;
160190
161- const shouldError = false ; // just expect no error
162- t . expectValidationError ( ( ) => t . device . queue . submit ( [ encoder . finish ( ) ] ) , shouldError ) ;
191+ t . skipIfDeviceDoesNotHaveFeature ( 'timestamp-query' ) ;
192+
193+ const kNumSlots = 4096 ;
194+ const kNumQuerySets = 2 ;
195+
196+ // Create a encoder and encode usage of every query and slot but do not submit it.
197+ const querySets = ( ( ) => {
198+ const { encoder, querySets } = encoderQueryUsage ( t , stage , kNumQuerySets , kNumSlots ) ;
199+ encoder . finish ( ) ;
200+ return querySets ;
201+ } ) ( ) ;
202+
203+ // Read the slots, they should all be zero.
204+ const encoder = t . device . createCommandEncoder ( ) ;
205+ const buffers = querySets . map ( ( querySet , i ) => {
206+ const resolveBuffer = t . createBufferTracked ( {
207+ size : kNumSlots * 8 ,
208+ usage : GPUBufferUsage . COPY_SRC | GPUBufferUsage . QUERY_RESOLVE ,
209+ } ) ;
210+ encoder . resolveQuerySet ( querySet , 0 , kNumSlots , resolveBuffer , 0 ) ;
211+ return resolveBuffer ;
212+ } ) ;
213+ t . device . queue . submit ( [ encoder . finish ( ) ] ) ;
214+
215+ for ( const buffer of buffers ) {
216+ const expected = new Uint8Array ( buffer . size ) ;
217+ t . expectGPUBufferValuesEqual ( buffer , expected ) ;
218+ }
163219 } ) ;
0 commit comments