wgpu_profiler/
lib.rs

1/*!
2
3Easy to use profiler scopes for [wgpu](https://github.com/gfx-rs/wgpu) using timer queries.
4
5`wgpu_profiler` manages all the necessary [`wgpu::QuerySet`] and [`wgpu::Buffer`] behind the scenes
6and allows you to create to create timer scopes with minimal overhead!
7
8# How to use
9
10```
11use wgpu_profiler::*;
12
13# async fn wgpu_init() -> (wgpu::Instance, wgpu::Adapter, wgpu::Device, wgpu::Queue) {
14    # let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
15    # let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions::default()).await.unwrap();
16    # let (device, queue) = adapter
17    #     .request_device(
18    #         &wgpu::DeviceDescriptor {
19    #             required_features: wgpu::Features::TIMESTAMP_QUERY,
20    #             ..Default::default()
21    #         },
22    #         None,
23    #     )
24    #     .await
25    #     .unwrap();
26    # (instance, adapter, device, queue)
27# }
28# let (instance, adapter, device, queue) = futures_lite::future::block_on(wgpu_init());
29# let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
30#     label: None,
31#     source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!("../examples/compute_shader.wgsl"))),
32# });
33# let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
34#        label: None,
35#        layout: None,
36#        module: &cs_module,
37#        entry_point: Some("main"),
38#        compilation_options: wgpu::PipelineCompilationOptions::default(),
39#        cache: None,
40#    });
41// ...
42
43let mut profiler = GpuProfiler::new(GpuProfilerSettings::default()).unwrap();
44
45// ...
46
47# let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
48{
49    // You can now open profiling scopes on any encoder or pass:
50    let mut scope = profiler.scope("name of your scope", &mut encoder, &device);
51
52    // Scopes can be nested arbitrarily!
53    let mut nested_scope = scope.scope("nested!", &device);
54
55    // Scopes on encoders can be used to easily create profiled passes!
56    let mut compute_pass = nested_scope.scoped_compute_pass("profiled compute", &device);
57
58
59    // Scopes expose the underlying encoder or pass they wrap:
60    compute_pass.set_pipeline(&pipeline);
61    // ...
62
63    // Scopes created this way are automatically closed when dropped.
64}
65
66// Wgpu-profiler needs to insert buffer copy commands.
67profiler.resolve_queries(&mut encoder);
68# drop(encoder);
69
70// ...
71
72// And finally, to end a profiling frame, call `end_frame`.
73// This does a few checks and will let you know if something is off!
74profiler.end_frame().unwrap();
75
76// Retrieving the oldest available frame and writing it out to a chrome trace file.
77if let Some(profiling_data) = profiler.process_finished_frame(queue.get_timestamp_period()) {
78    # let button_pressed = false;
79    // You usually want to write to disk only under some condition, e.g. press of a key.
80    if button_pressed {
81        wgpu_profiler::chrometrace::write_chrometrace(
82            std::path::Path::new("mytrace.json"), &profiling_data);
83    }
84}
85```
86Check also the [Example](https://github.com/Wumpf/wgpu-profiler/blob/main/examples/demo.rs) where everything can be seen in action.
87
88## Tracy integration
89
90If you want to use [tracy](https://github.com/wolfpld/tracy) for profiling, you can enable the `tracy` feature.
91
92This adds `wgpu_profiler::new_with_tracy_client` which will automatically report profiling data to tracy.
93
94For details check the example code.
95
96## Puffin integration
97
98If you want to use [puffin](https://github.com/EmbarkStudios/puffin) for profiling, you can enable the `puffin` feature.
99
100This adds `wgpu_profiler::puffin::output_frame_to_puffin` which makes it easy to report profiling data to a `puffin::GlobalProfiler`.
101
102You can run the demo example with puffin by running `cargo run --example demo --features puffin`.
103All CPU profiling goes to port `8585`, all GPU profiling goes to port `8586`. You can open puffin viewers for both.
104```sh
105puffin_viewer --url 127.0.0.1:8585
106puffin_viewer --url 127.0.0.1:8586
107```
108
109For details check the example code.
110
111# Internals
112
113For every frame that hasn't completely finished processing yet
114(i.e. hasn't returned results via [`GpuProfiler::process_finished_frame`])
115we keep a `PendingFrame` around.
116
117Whenever a profiling scope is opened, we allocate two queries.
118This is done by either using the most recent `QueryPool` or creating a new one if there's no non-exhausted one ready.
119Ideally, we only ever need a single `QueryPool` per frame! In order to converge to this,
120we allocate new query pools with the size of all previous query pools in a given frame, effectively doubling the size.
121On [`GpuProfiler::end_frame`], we memorize the total size of all `QueryPool`s in the current frame and make this the new minimum pool size.
122
123`QueryPool` from finished frames are re-used, unless they are deemed too small.
124*/
125
126pub mod chrometrace;
127mod errors;
128mod profiler;
129mod profiler_command_recorder;
130mod profiler_query;
131mod profiler_settings;
132#[cfg(feature = "puffin")]
133pub mod puffin;
134mod scope;
135#[cfg(feature = "tracy")]
136mod tracy;
137
138pub use errors::{CreationError, EndFrameError, SettingsError};
139pub use profiler::GpuProfiler;
140pub use profiler_command_recorder::ProfilerCommandRecorder;
141pub use profiler_query::{GpuProfilerQuery, GpuTimerQueryResult};
142pub use profiler_settings::GpuProfilerSettings;
143pub use scope::{ManualOwningScope, OwningScope, Scope};