#wgpu #graphics #profiler #timer #queries #gpu #scope

wgpu-profiler

Simple profiler scopes for wgpu using timer queries

28 releases (17 breaking)

0.19.0 Nov 10, 2024
0.18.0 Aug 1, 2024
0.17.0 May 12, 2024
0.16.2 Mar 24, 2024
0.3.1 Mar 6, 2021

#34 in Graphics APIs

Download history 316/week @ 2024-08-23 403/week @ 2024-08-30 361/week @ 2024-09-06 625/week @ 2024-09-13 1127/week @ 2024-09-20 526/week @ 2024-09-27 377/week @ 2024-10-04 406/week @ 2024-10-11 487/week @ 2024-10-18 598/week @ 2024-10-25 547/week @ 2024-11-01 559/week @ 2024-11-08 482/week @ 2024-11-15 746/week @ 2024-11-22 460/week @ 2024-11-29 446/week @ 2024-12-06

2,188 downloads per month
Used in 11 crates (6 directly)

MIT/Apache

74KB
1K SLoC

wgpu-profiler

Crates.io

Simple profiler scopes for wgpu using timer queries

Features

  • Easy to use profiler scopes
    • Allows nesting!
    • Can be disabled by runtime flag
    • Additionally generates debug markers
    • Thread-safe - can profile several command encoder/buffers in parallel
  • Internally creates pools of timer queries automatically
    • Does not need to know in advance how many queries/profiling scopes are needed
    • Caches up profiler-frames until results are available
      • No stalling of the device at any time!
  • Many profiler instances can live side by side
  • chrome trace flamegraph json export
  • Tracy integration (behind tracy feature flag)

How to use

Create a new profiler object:

use wgpu_profiler::{wgpu_profiler, GpuProfiler, GpuProfilerSettings};
// ...
let mut profiler = GpuProfiler::new(GpuProfilerSettings::default());

Now you can start creating profiler scopes:

// You can now open profiling scopes on any encoder or pass:
let mut scope = profiler.scope("name of your scope", &mut encoder, &device);

// Scopes can be nested arbitrarily!
let mut nested_scope = scope.scope("nested!", &device);

// Scopes on encoders can be used to easily create profiled passes!
let mut compute_pass = nested_scope.scoped_compute_pass("profiled compute", &device);

// Scopes expose the underlying encoder or pass they wrap:
compute_pass.set_pipeline(&pipeline);
// ...

// Scopes created this way are automatically closed when dropped.

GpuProfiler reads the device features on first use:

  • wgpu::Features::TIMESTAMP_QUERY is required to emit any timer queries.
    • Alone, this allows you to use timestamp writes on pass definition as done by Scope::scoped_compute_pass/Scope::scoped_render_pass
  • wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS is required to issue queries at any point within encoders.
  • wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES is required to issue queries at any point within passes.

Wgpu-profiler needs to insert buffer copy commands, so when you're done with an encoder and won't do any more profiling scopes on it, you need to resolve the queries:

profiler.resolve_queries(&mut encoder);

And finally, to end a profiling frame, call end_frame. This does a few checks and will let you know if something is off!

profiler.end_frame().unwrap();

Retrieving the oldest available frame and writing it out to a chrome trace file.

if let Some(profiling_data) = profiler.process_finished_frame(queue.get_timestamp_period()) {
    wgpu_profiler::chrometrace::write_chrometrace(std::path::Path::new("mytrace.json"), &profiling_data);
}

To get a look of it in action, check out the example project!

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~3–34MB
~530K SLoC