Skip to content
This repository was archived by the owner on Jun 18, 2021. It is now read-only.

Commit 15d784a

Browse files
Merge #715
715: Add pipeline statistics and timeline queries r=kvark a=cwfitzgerald Blocked on gfx-rs/wgpu#1128. Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
2 parents baf6c43 + 6d52bc5 commit 15d784a

File tree

15 files changed

+481
-50
lines changed

15 files changed

+481
-50
lines changed

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,20 @@ webgl = ["wgc"]
2626
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgc]
2727
package = "wgpu-core"
2828
git = "https://github.com/gfx-rs/wgpu"
29-
rev = "c788b1cef630fa5b5f0a86132fe84551d4149fb6"
29+
rev = "9dce0afa746d624533dd086985889a2439750628"
3030
features = ["raw-window-handle"]
3131

3232
[target.'cfg(target_arch = "wasm32")'.dependencies.wgc]
3333
package = "wgpu-core"
3434
git = "https://github.com/gfx-rs/wgpu"
35-
rev = "c788b1cef630fa5b5f0a86132fe84551d4149fb6"
35+
rev = "9dce0afa746d624533dd086985889a2439750628"
3636
features = ["raw-window-handle"]
3737
optional = true
3838

3939
[dependencies.wgt]
4040
package = "wgpu-types"
4141
git = "https://github.com/gfx-rs/wgpu"
42-
rev = "c788b1cef630fa5b5f0a86132fe84551d4149fb6"
42+
rev = "9dce0afa746d624533dd086985889a2439750628"
4343

4444
[dependencies]
4545
arrayvec = "0.5"

examples/README.md

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,35 +10,36 @@ Notably, `capture` example shows rendering without a surface/window. It reads ba
1010
All framework-based examples render to the window.
1111

1212
## Feature matrix
13-
| Feature | boids | cube | mipmap | msaa-line | shadow | skybox | texture-arrays | water |
14-
| ------------------------- | ------ | ------ | ------ | --------- | ------ | ------ | -------------- | ------ |
15-
| vertex attributes | :star: | :star: | :star: | :star: | :star: | | :star: | :star: |
16-
| instancing | :star: | | | | | | | |
17-
| lines and points | | | | :star: | | | | |
18-
| dynamic buffer offsets | | | | | :star: | | | |
19-
| implicit layout | | | :star: | | | | | |
20-
| sampled color textures | :star: | :star: | :star: | | | :star: | :star: | :star: |
21-
| storage textures | :star: | | | | | | | |
22-
| binding array | | | | | | | :star: | |
23-
| comparison samplers | | | | | :star: | | | |
24-
| subresource views | | | :star: | | :star: | | | |
25-
| cubemaps | | | | | | :star: | | |
26-
| multisampling | | | | :star: | | | | |
27-
| off-screen rendering | | | | | :star: | | | :star: |
28-
| stencil testing | | | | | | | | |
29-
| depth testing | | | | | :star: | | | :star: |
30-
| depth biasing | | | | | :star: | | | |
31-
| read-only depth | | | | | | | | :star: |
32-
| blending | | :star: | | | | | | :star: |
33-
| render bundles | | | | :star: | | | | :star: |
34-
| compute passes | :star: | | | | | | | |
35-
| optional extensions | | | | | | | :star: | |
36-
| - binding indexing | | | | | | | :star: | |
37-
| - push constants | | | | | | | :star: | |
38-
| - depth clamping | | | | | :star: | | | |
39-
| - BCn compressed textures | | | | | | :star: | | |
40-
| - polygon mode | | :star: | | | | | | |
41-
| WGSL shaders | | | | | | | | |
13+
| Feature | boids | cube | mipmap | msaa-line | shadow | skybox | texture-arrays | water |
14+
| ---------------------- | ------ | ------ | ------ | --------- | ------ | ------ | -------------- | ------ |
15+
| vertex attributes | :star: | :star: | :star: | :star: | :star: | | :star: | :star: |
16+
| instancing | :star: | | | | | | | |
17+
| lines and points | | | | :star: | | | | |
18+
| dynamic buffer offsets | | | | | :star: | | | |
19+
| implicit layout | | | :star: | | | | | |
20+
| sampled color textures | :star: | :star: | :star: | | | :star: | :star: | :star: |
21+
| storage textures | :star: | | | | | | | |
22+
| binding array | | | | | | | :star: | |
23+
| comparison samplers | | | | | :star: | | | |
24+
| subresource views | | | :star: | | :star: | | | |
25+
| cubemaps | | | | | | :star: | | |
26+
| multisampling | | | | :star: | | | | |
27+
| off-screen rendering | | | | | :star: | | | :star: |
28+
| stencil testing | | | | | | | | |
29+
| depth testing | | | | | :star: | | | :star: |
30+
| depth biasing | | | | | :star: | | | |
31+
| read-only depth | | | | | | | | :star: |
32+
| blending | | :star: | | | | | | :star: |
33+
| render bundles | | | | :star: | | | | :star: |
34+
| compute passes | :star: | | | | | | | |
35+
| optional extensions | | | | | | | :star: | |
36+
| - binding indexing | | | | | | | :star: | |
37+
| - push constants | | | | | | | :star: | |
38+
| - depth clamping | | | | | :star: | | | |
39+
| - compressed textures | | | | | | :star: | | |
40+
| - polygon mode | | :star: | | | | | | |
41+
| - queries | | | :star: | | | | | |
42+
| WGSL shaders | | | | | | | | |
4243

4344
## Hacking
4445

examples/boids/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ impl framework::Example for Example {
2929
/// constructs initial instance of Example struct
3030
fn init(
3131
sc_desc: &wgpu::SwapChainDescriptor,
32+
_adapter: &wgpu::Adapter,
3233
device: &wgpu::Device,
3334
_queue: &wgpu::Queue,
3435
) -> Self {

examples/cube/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ impl framework::Example for Example {
118118

119119
fn init(
120120
sc_desc: &wgpu::SwapChainDescriptor,
121+
_adapter: &wgpu::Adapter,
121122
device: &wgpu::Device,
122123
queue: &wgpu::Queue,
123124
) -> Self {

examples/framework.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub trait Example: 'static + Sized {
4141
}
4242
fn init(
4343
sc_desc: &wgpu::SwapChainDescriptor,
44+
adapter: &wgpu::Adapter,
4445
device: &wgpu::Device,
4546
queue: &wgpu::Queue,
4647
) -> Self;
@@ -209,7 +210,7 @@ fn start<E: Example>(
209210
let mut swap_chain = device.create_swap_chain(&surface, &sc_desc);
210211

211212
log::info!("Initializing the example...");
212-
let mut example = E::init(&sc_desc, &device, &queue);
213+
let mut example = E::init(&sc_desc, &adapter, &device, &queue);
213214

214215
#[cfg(not(target_arch = "wasm32"))]
215216
let mut last_update_inst = Instant::now();

examples/hello-compute/shader.comp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ layout(set = 0, binding = 0) buffer PrimeIndices {
1313
// This function returns how many times this recurrence needs to be applied to reach 1.
1414
uint collatz_iterations(uint n) {
1515
uint i = 0;
16-
while(n != 1) {
16+
while(n > 1) {
1717
if (mod(n, 2) == 0) {
1818
n = n / 2;
1919
}
40 Bytes
Binary file not shown.

examples/mipmap/main.rs

Lines changed: 150 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
mod framework;
33

44
use bytemuck::{Pod, Zeroable};
5-
use std::num::NonZeroU32;
5+
use std::{mem, num::NonZeroU32};
66
use wgpu::util::DeviceExt;
77

88
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;
911

1012
#[repr(C)]
1113
#[derive(Clone, Copy, Pod, Zeroable)]
@@ -54,6 +56,27 @@ fn create_texels(size: usize, cx: f32, cy: f32) -> Vec<u8> {
5456
.collect()
5557
}
5658

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+
5780
struct Example {
5881
vertex_buf: wgpu::Buffer,
5982
bind_group: wgpu::BindGroup,
@@ -77,6 +100,7 @@ impl Example {
77100
encoder: &mut wgpu::CommandEncoder,
78101
device: &wgpu::Device,
79102
texture: &wgpu::Texture,
103+
query_sets: &Option<QuerySets>,
80104
mip_count: u32,
81105
) {
82106
let vs_module = device.create_shader_module(&wgpu::include_spirv!("blit.vert.spv"));
@@ -154,6 +178,9 @@ impl Example {
154178
label: None,
155179
});
156180

181+
let pipeline_query_index_base = target_mip as u32 - 1;
182+
let timestamp_query_index_base = (target_mip as u32 - 1) * 2;
183+
157184
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
158185
label: None,
159186
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
@@ -166,21 +193,51 @@ impl Example {
166193
}],
167194
depth_stencil_attachment: None,
168195
});
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+
}
169203
rpass.set_pipeline(&pipeline);
170204
rpass.set_bind_group(0, &bind_group, &[]);
171205
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+
);
172226
}
173227
}
174228
}
175229

176230
impl framework::Example for Example {
231+
fn optional_features() -> wgpu::Features {
232+
wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::PIPELINE_STATISTICS_QUERY
233+
}
234+
177235
fn init(
178236
sc_desc: &wgpu::SwapChainDescriptor,
237+
adapter: &wgpu::Adapter,
179238
device: &wgpu::Device,
180239
queue: &wgpu::Queue,
181240
) -> Self {
182-
use std::mem;
183-
184241
let mut init_encoder =
185242
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
186243

@@ -194,8 +251,7 @@ impl framework::Example for Example {
194251
});
195252

196253
// Create the texture
197-
let mip_level_count = 9;
198-
let size = 1 << mip_level_count;
254+
let size = 1 << MIP_LEVEL_COUNT;
199255
let texels = create_texels(size as usize, -0.8, 0.156);
200256
let texture_extent = wgpu::Extent3d {
201257
width: size,
@@ -204,7 +260,7 @@ impl framework::Example for Example {
204260
};
205261
let texture = device.create_texture(&wgpu::TextureDescriptor {
206262
size: texture_extent,
207-
mip_level_count,
263+
mip_level_count: MIP_LEVEL_COUNT,
208264
sample_count: 1,
209265
dimension: wgpu::TextureDimension::D2,
210266
format: TEXTURE_FORMAT,
@@ -314,9 +370,95 @@ impl framework::Example for Example {
314370
label: None,
315371
});
316372

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+
319428
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+
}
320462

321463
Example {
322464
vertex_buf,

examples/msaa-line/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ impl Example {
122122
impl framework::Example for Example {
123123
fn init(
124124
sc_desc: &wgpu::SwapChainDescriptor,
125+
_adapter: &wgpu::Adapter,
125126
device: &wgpu::Device,
126127
_queue: &wgpu::Queue,
127128
) -> Self {

examples/shadow/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ impl framework::Example for Example {
199199

200200
fn init(
201201
sc_desc: &wgpu::SwapChainDescriptor,
202+
_adapter: &wgpu::Adapter,
202203
device: &wgpu::Device,
203204
_queue: &wgpu::Queue,
204205
) -> Self {

0 commit comments

Comments
 (0)