#gpu #frame #full #tracy


Fully featured bindings for the Tracy profiler

14 stable releases

new 1.6.1 Nov 20, 2023
1.5.0 Jul 31, 2023
1.4.0 Mar 9, 2023
1.3.0 Nov 27, 2022
1.1.1 May 26, 2022

#66 in Game dev

Download history 8/week @ 2023-08-06 12/week @ 2023-08-13 17/week @ 2023-08-20 17/week @ 2023-08-27 5/week @ 2023-09-03 29/week @ 2023-09-10 16/week @ 2023-09-17 7/week @ 2023-09-24 12/week @ 2023-10-01 7/week @ 2023-10-08 10/week @ 2023-10-15 13/week @ 2023-10-22 25/week @ 2023-10-29 6/week @ 2023-11-05 18/week @ 2023-11-12 66/week @ 2023-11-19

116 downloads per month

MIT license

1.5K SLoC

Complete Rust bindings for the Tracy profiler.

Getting Started

Just add the following to your Cargo.toml:

package = "tracy_full"
version = "1.3.0"

To enable profiling for a build, add the enable feature:

features = ["enable"]


Allocation Tracking

static ALLOC: tracy::GlobalAllocator = tracy::GlobalAllocator::new();

This tracks all allocations, using the default System allocator for allocations.

For a custom allocator:

static ALLOC: tracy::GlobalAllocator<MyAlloc> = tracy::GlobalAllocator::new_with(MyAlloc::new());

Tracy also supports tracking custom allocators using the allocator_api feature:

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;


Mark the end of a sub-frame using:


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
    // Process input
    // Render


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.


You can plot graphs in Tracy:

use tracy::plotter;

let plotter = plotter!("MyGraph");


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.

features = ["enable", "futures"]
use tracy::future;

trace_future!(async_function(), "Async Function").await;


The unstable feature allows for optimizations that require a nightly compiler.

features = ["enable", "unstable"]

External Library Integration


Enable the bevy feature to be able to profile Bevy systems.

features = ["enable", "bevy"]
use tracy::bevy::timeline;


This creates a separate fiber for the system in the tracy timeline.


Enable the tracing feature to be able to profile tracing spans.

features = ["enable", "tracing"]
use tracy::tracing::TracyLayer;



Enable the wgpu feature to be able to profile wgpu command encoders and render/compute passes.

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.


~588K SLoC