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