Complete Rust bindings for the Tracy profiler.
Getting Started
Just add the following to your Cargo.toml
:
[dependencies.tracy]
package = "tracy_full"
version = "1.3.0"
To enable profiling for a build, add the enable
feature:
[dependencies.tracy]
...
features = ["enable"]
Features
Allocation Tracking
#[global_allocator]
static ALLOC: tracy::GlobalAllocator = tracy::GlobalAllocator::new();
This tracks all allocations, using the default System
allocator for allocations.
For a custom allocator:
#[global_allocator]
static ALLOC: tracy::GlobalAllocator<MyAlloc> = tracy::GlobalAllocator::new_with(MyAlloc::new());
Tracy also supports tracking custom allocators using the allocator_api
feature:
[dependencies.tracy]
...
features = ["allocator_api"]
let alloc = TrackedAllocator::new(alloc, tracy::c_str!("TrackedAllocator"));
This creates a memory pool named TrackedAllocator
in Tracy.
All the allocators have a *Sampled
variant that samples the callstack on each allocation.
Frame Marks
Mark the end of the main frame using:
use tracy::frame;
frame!();
Mark the end of a sub-frame using:
frame!("Name");
Mark the scope of a discontinuous frame using:
frame!(discontinuous "Name");
The difference between frame types
The main frame what is usually thought of as a 'frame'. It is usually placed after the swapchain present call on the main thread.
Sub-frames are parts of the main frame, for example, input gathering, physics, and rendering:
loop {
// Gather input
frame!("Input");
// Process input
frame!("Processing");
// Render
frame!("Render");
swapchain.present();
frame!();
}
Discontinuous frames are frames that are not in sync with the frame on the main thread. This can be things like async asset loading on different threads.
Plotting
You can plot graphs in Tracy:
use tracy::plotter;
let plotter = plotter!("MyGraph");
plotter.value(1.0);
plotter.value(2.0);
Zones
use tracy::zone;
zone!(); // Zone with no name
zone!("MyZone"); // Zone with name "MyZone"
zone!(tracy::color::RED); // Zone with color red
zone!("MyZone", true); // Zone with name "MyZone", and enabled with a runtime expression.
zone!(tracy::color::RED, true); // Zone with color red, and enabled with a runtime expression.
zone!("MyZone", tracy::color::RED, true); // Zone with name "MyZone", color red, and enabled with a runtime expression.
All zones profile from creation to the end of the enclosed scope.
Extra features
Future support
Futures can be represented as fibers in Tracy. The futures
feature must be enabled.
[dependencies.tracy]
...
features = ["enable", "futures"]
use tracy::future;
trace_future!(async_function(), "Async Function").await;
Unstable
The unstable
feature allows for optimizations that require a nightly compiler.
[dependencies.tracy]
...
features = ["enable", "unstable"]
External Library Integration
bevy
Enable the bevy
feature to be able to profile Bevy systems.
[dependencies.tracy]
...
features = ["enable", "bevy"]
use tracy::bevy::timeline;
App::new().add_system(timeline(my_system)).run();
This creates a separate fiber for the system in the tracy timeline.
tracing
Enable the tracing
feature to be able to profile tracing spans.
[dependencies.tracy]
...
features = ["enable", "tracing"]
use tracy::tracing::TracyLayer;
tracing::subscriber::set_global_default(
tracing_subscriber::registry().with(TracyLayer)
);
wgpu
Enable the wgpu
feature to be able to profile wgpu command encoders and render/compute passes.
[dependencies.tracy]
...
features = ["enable", "wgpu"]
use tracy::wgpu::ProfileContext;
let mut profile_context = ProfileContext::with_name("Name", &adapter, &device, &queue, buffered_frames);
buffered_frames
: the number of frames of profiling data you want the profiler to buffer. Note that you must synchronize the host and device accordingly, or else the call to end_frame
will panic.
You also need to have one ProfileContext
per host thread.
You can create a profiled command encoder:
use tracy::{wgpu_command_encoder, wgpu_render_pass, wgpu_compute_pass};
let mut command_encoder = wgpu_command_encoder!(device, profile_context, desc);
{
let render_pass = wgpu_render_pass!(command_encoder, desc)
}
{
let compute_pass = wgpu_compute_pass!(command_encoder, desc)
}
At the end of each frame, you must call end_frame
:
profile_context.end_frame(&device, &queue);
This uploads the profiling data to the Tracy.