2
2
mod framework;
3
3
4
4
use bytemuck:: { Pod , Zeroable } ;
5
- use std:: num:: NonZeroU32 ;
5
+ use std:: { mem , num:: NonZeroU32 } ;
6
6
use wgpu:: util:: DeviceExt ;
7
7
8
8
const TEXTURE_FORMAT : wgpu:: TextureFormat = wgpu:: TextureFormat :: Rgba8UnormSrgb ;
9
+ const MIP_LEVEL_COUNT : u32 = 9 ;
10
+ const MIP_PASS_COUNT : u32 = MIP_LEVEL_COUNT - 1 ;
9
11
10
12
#[ repr( C ) ]
11
13
#[ derive( Clone , Copy , Pod , Zeroable ) ]
@@ -54,6 +56,27 @@ fn create_texels(size: usize, cx: f32, cy: f32) -> Vec<u8> {
54
56
. collect ( )
55
57
}
56
58
59
+ struct QuerySets {
60
+ timestamp : wgpu:: QuerySet ,
61
+ timestamp_period : f32 ,
62
+ pipeline_statistics : wgpu:: QuerySet ,
63
+ data_buffer : wgpu:: Buffer ,
64
+ }
65
+
66
+ #[ repr( C ) ]
67
+ #[ derive( Clone , Copy , Pod , Zeroable ) ]
68
+ struct TimestampData {
69
+ start : u64 ,
70
+ end : u64 ,
71
+ }
72
+
73
+ #[ repr( C ) ]
74
+ #[ derive( Clone , Copy , Pod , Zeroable ) ]
75
+ struct QueryData {
76
+ timestamps : [ TimestampData ; MIP_PASS_COUNT as usize ] ,
77
+ pipeline_queries : [ u64 ; MIP_PASS_COUNT as usize ] ,
78
+ }
79
+
57
80
struct Example {
58
81
vertex_buf : wgpu:: Buffer ,
59
82
bind_group : wgpu:: BindGroup ,
@@ -77,6 +100,7 @@ impl Example {
77
100
encoder : & mut wgpu:: CommandEncoder ,
78
101
device : & wgpu:: Device ,
79
102
texture : & wgpu:: Texture ,
103
+ query_sets : & Option < QuerySets > ,
80
104
mip_count : u32 ,
81
105
) {
82
106
let vs_module = device. create_shader_module ( & wgpu:: include_spirv!( "blit.vert.spv" ) ) ;
@@ -154,6 +178,9 @@ impl Example {
154
178
label : None ,
155
179
} ) ;
156
180
181
+ let pipeline_query_index_base = target_mip as u32 - 1 ;
182
+ let timestamp_query_index_base = ( target_mip as u32 - 1 ) * 2 ;
183
+
157
184
let mut rpass = encoder. begin_render_pass ( & wgpu:: RenderPassDescriptor {
158
185
label : None ,
159
186
color_attachments : & [ wgpu:: RenderPassColorAttachmentDescriptor {
@@ -166,21 +193,51 @@ impl Example {
166
193
} ] ,
167
194
depth_stencil_attachment : None ,
168
195
} ) ;
196
+ if let Some ( ref query_sets) = query_sets {
197
+ rpass. write_timestamp ( & query_sets. timestamp , timestamp_query_index_base) ;
198
+ rpass. begin_pipeline_statistics_query (
199
+ & query_sets. pipeline_statistics ,
200
+ pipeline_query_index_base,
201
+ ) ;
202
+ }
169
203
rpass. set_pipeline ( & pipeline) ;
170
204
rpass. set_bind_group ( 0 , & bind_group, & [ ] ) ;
171
205
rpass. draw ( 0 ..4 , 0 ..1 ) ;
206
+ if let Some ( ref query_sets) = query_sets {
207
+ rpass. write_timestamp ( & query_sets. timestamp , timestamp_query_index_base + 1 ) ;
208
+ rpass. end_pipeline_statistics_query ( ) ;
209
+ }
210
+ }
211
+
212
+ if let Some ( ref query_sets) = query_sets {
213
+ let timestamp_query_count = MIP_PASS_COUNT * 2 ;
214
+ encoder. resolve_query_set (
215
+ & query_sets. timestamp ,
216
+ 0 ..timestamp_query_count,
217
+ & query_sets. data_buffer ,
218
+ 0 ,
219
+ ) ;
220
+ encoder. resolve_query_set (
221
+ & query_sets. pipeline_statistics ,
222
+ 0 ..MIP_PASS_COUNT ,
223
+ & query_sets. data_buffer ,
224
+ ( timestamp_query_count * mem:: size_of :: < u64 > ( ) as u32 ) as wgpu:: BufferAddress ,
225
+ ) ;
172
226
}
173
227
}
174
228
}
175
229
176
230
impl framework:: Example for Example {
231
+ fn optional_features ( ) -> wgpu:: Features {
232
+ wgpu:: Features :: TIMESTAMP_QUERY | wgpu:: Features :: PIPELINE_STATISTICS_QUERY
233
+ }
234
+
177
235
fn init (
178
236
sc_desc : & wgpu:: SwapChainDescriptor ,
237
+ adapter : & wgpu:: Adapter ,
179
238
device : & wgpu:: Device ,
180
239
queue : & wgpu:: Queue ,
181
240
) -> Self {
182
- use std:: mem;
183
-
184
241
let mut init_encoder =
185
242
device. create_command_encoder ( & wgpu:: CommandEncoderDescriptor { label : None } ) ;
186
243
@@ -194,8 +251,7 @@ impl framework::Example for Example {
194
251
} ) ;
195
252
196
253
// Create the texture
197
- let mip_level_count = 9 ;
198
- let size = 1 << mip_level_count;
254
+ let size = 1 << MIP_LEVEL_COUNT ;
199
255
let texels = create_texels ( size as usize , -0.8 , 0.156 ) ;
200
256
let texture_extent = wgpu:: Extent3d {
201
257
width : size,
@@ -204,7 +260,7 @@ impl framework::Example for Example {
204
260
} ;
205
261
let texture = device. create_texture ( & wgpu:: TextureDescriptor {
206
262
size : texture_extent,
207
- mip_level_count,
263
+ mip_level_count : MIP_LEVEL_COUNT ,
208
264
sample_count : 1 ,
209
265
dimension : wgpu:: TextureDimension :: D2 ,
210
266
format : TEXTURE_FORMAT ,
@@ -314,9 +370,95 @@ impl framework::Example for Example {
314
370
label : None ,
315
371
} ) ;
316
372
317
- // Done
318
- Self :: generate_mipmaps ( & mut init_encoder, & device, & texture, mip_level_count) ;
373
+ // If both kinds of query are supported, use queries
374
+ let query_sets = if device
375
+ . features ( )
376
+ . contains ( wgpu:: Features :: TIMESTAMP_QUERY | wgpu:: Features :: PIPELINE_STATISTICS_QUERY )
377
+ {
378
+ // For N total mips, it takes N - 1 passes to generate them, and we're measuring those.
379
+ let mip_passes = MIP_LEVEL_COUNT - 1 ;
380
+
381
+ // Create the timestamp query set. We need twice as many queries as we have passes,
382
+ // as we need a query at the beginning and at the end of the operation.
383
+ let timestamp = device. create_query_set ( & wgpu:: QuerySetDescriptor {
384
+ count : mip_passes * 2 ,
385
+ ty : wgpu:: QueryType :: Timestamp ,
386
+ } ) ;
387
+ // Timestamp queries use an device-specific timestamp unit. We need to figure out how many
388
+ // nanoseconds go by for the timestamp to be incremented by one. The period is this value.
389
+ let timestamp_period = adapter. get_timestamp_period ( ) ;
390
+
391
+ // We only need one pipeline statistics query per pass.
392
+ let pipeline_statistics = device. create_query_set ( & wgpu:: QuerySetDescriptor {
393
+ count : mip_passes,
394
+ ty : wgpu:: QueryType :: PipelineStatistics (
395
+ wgpu:: PipelineStatisticsTypes :: FRAGMENT_SHADER_INVOCATIONS ,
396
+ ) ,
397
+ } ) ;
398
+
399
+ // This databuffer has to store all of the query results, 2 * passes timestamp queries
400
+ // and 1 * passes statistics queries. Each query returns a u64 value.
401
+ let data_buffer = device. create_buffer ( & wgpu:: BufferDescriptor {
402
+ label : Some ( "query buffer" ) ,
403
+ size : mip_passes as wgpu:: BufferAddress
404
+ * 3
405
+ * mem:: size_of :: < u64 > ( ) as wgpu:: BufferAddress ,
406
+ usage : wgpu:: BufferUsage :: COPY_DST | wgpu:: BufferUsage :: MAP_READ ,
407
+ mapped_at_creation : false ,
408
+ } ) ;
409
+
410
+ Some ( QuerySets {
411
+ timestamp,
412
+ timestamp_period,
413
+ pipeline_statistics,
414
+ data_buffer,
415
+ } )
416
+ } else {
417
+ None
418
+ } ;
419
+
420
+ Self :: generate_mipmaps (
421
+ & mut init_encoder,
422
+ & device,
423
+ & texture,
424
+ & query_sets,
425
+ MIP_LEVEL_COUNT ,
426
+ ) ;
427
+
319
428
queue. submit ( Some ( init_encoder. finish ( ) ) ) ;
429
+ if let Some ( ref query_sets) = query_sets {
430
+ // We can ignore the future as we're about to wait for the device.
431
+ let _ = query_sets
432
+ . data_buffer
433
+ . slice ( ..)
434
+ . map_async ( wgpu:: MapMode :: Read ) ;
435
+ // Wait for device to be done rendering mipmaps
436
+ device. poll ( wgpu:: Maintain :: Wait ) ;
437
+ // This is guaranteed to be ready.
438
+ let view = query_sets. data_buffer . slice ( ..) . get_mapped_range ( ) ;
439
+ // Convert the raw data into a useful structure
440
+ let data: & QueryData = bytemuck:: from_bytes ( & * view) ;
441
+ // Iterate over the data
442
+ for ( idx, ( timestamp, pipeline) ) in data
443
+ . timestamps
444
+ . iter ( )
445
+ . zip ( data. pipeline_queries . iter ( ) )
446
+ . enumerate ( )
447
+ {
448
+ // Figure out the timestamp differences and multiply by the period to get nanoseconds
449
+ let nanoseconds =
450
+ ( timestamp. end - timestamp. start ) as f32 * query_sets. timestamp_period ;
451
+ // Nanoseconds is a bit small, so lets use microseconds.
452
+ let microseconds = nanoseconds / 1000.0 ;
453
+ // Print the data!
454
+ println ! (
455
+ "Generating mip level {} took {:.3} μs and called the fragment shader {} times" ,
456
+ idx + 1 ,
457
+ microseconds,
458
+ pipeline
459
+ ) ;
460
+ }
461
+ }
320
462
321
463
Example {
322
464
vertex_buf,
0 commit comments