tracy_client/
frame.rs

1use crate::Client;
2
3/// A non-continuous frame region.
4///
5/// Create with the [`Client::non_continuous_frame`] function.
6pub struct Frame(Client, FrameName);
7
8/// A name for secondary and non-continuous frames.
9///
10/// Create with the [`frame_name!`](crate::frame_name) macro.
11#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
12pub struct FrameName(pub(crate) &'static str);
13
14impl FrameName {
15    /// Construct a `FrameName` dynamically, leaking the provided String.
16    ///
17    /// You should call this function once for a given name, and store the returned `FrameName` for
18    /// continued use, to avoid rapid memory use growth. Whenever possible, prefer the
19    /// [`frame_name!`](crate::frame_name) macro, which takes a literal name and doesn't leak
20    /// memory.
21    ///
22    /// The resulting value may be used as an argument for the the [`Client::secondary_frame_mark`]
23    /// and [`Client::non_continuous_frame`] methods.
24    #[must_use]
25    pub fn new_leak(name: String) -> Self {
26        #[cfg(feature = "enable")]
27        {
28            // Ensure the name is null-terminated.
29            let mut name = name;
30            name.push('\0');
31            // Drop excess capacity by converting into a boxed str, then leak.
32            let name = Box::leak(name.into_boxed_str());
33            Self(name)
34        }
35        #[cfg(not(feature = "enable"))]
36        {
37            drop(name);
38            Self("\0")
39        }
40    }
41}
42
43/// Instrumentation for global frame indicators.
44impl Client {
45    /// Indicate that rendering of a continuous frame has ended.
46    ///
47    /// # Examples
48    ///
49    /// In a traditional rendering scenarios a frame mark should be inserted after a buffer swap.
50    ///
51    /// ```
52    /// use tracy_client::Client;
53    /// # fn swap_buffers() {}
54    /// # let client = tracy_client::Client::start();
55    /// // loop {
56    /// //     ...
57    ///        swap_buffers();
58    ///        Client::running().expect("client must be running").frame_mark();
59    /// // }
60    /// ```
61    pub fn frame_mark(&self) {
62        #[cfg(feature = "enable")]
63        unsafe {
64            let () = sys::___tracy_emit_frame_mark(std::ptr::null());
65        }
66    }
67
68    /// Indicate that rendering of a secondary (named) continuous frame has ended.
69    ///
70    /// # Examples
71    ///
72    /// Much like with the primary frame mark, the secondary (named) frame mark should be inserted
73    /// after some continuously repeating operation finishes one iteration of its processing.
74    ///
75    /// ```
76    /// use tracy_client::frame_name;
77    /// # fn physics_tick() {}
78    /// # let client = tracy_client::Client::start();
79    /// // loop {
80    /// //     ...
81    ///        physics_tick();
82    ///        tracy_client::Client::running()
83    ///            .expect("client must be running")
84    ///            .secondary_frame_mark(frame_name!("physics"));
85    /// // }
86    /// ```
87    pub fn secondary_frame_mark(&self, name: FrameName) {
88        #[cfg(feature = "enable")]
89        unsafe {
90            // SAFE: We ensured that the name would be null-terminated.
91            let () = sys::___tracy_emit_frame_mark(name.0.as_ptr().cast());
92        }
93    }
94
95    /// Indicate that a processing of a non-continuous frame has begun.
96    ///
97    /// Dropping the returned [`Frame`] will terminate the non-continuous frame.
98    ///
99    /// # Examples
100    ///
101    /// ```
102    /// use tracy_client::frame_name;
103    /// # let client = tracy_client::Client::start();
104    /// let _guard = tracy_client::Client::running()
105    ///     .expect("client must be running")
106    ///     .non_continuous_frame(frame_name!("a frame"));
107    /// ```
108    #[must_use]
109    pub fn non_continuous_frame(&self, name: FrameName) -> Frame {
110        #[cfg(feature = "enable")]
111        unsafe {
112            // SAFE: We ensure that the name would be null-terminated.
113            let () = sys::___tracy_emit_frame_mark_start(name.0.as_ptr().cast());
114        }
115        Frame(self.clone(), name)
116    }
117
118    /// Emits an image of a frame.
119    ///
120    /// The following restrictions apply:
121    /// - The image must be in RGBA format.
122    /// - The image width and height must be divisible by four.
123    /// - The image data must be less than 256 KB, and should be as small as
124    ///   possible.
125    ///
126    /// The offset indicates how many frames in the past the image was captured.
127    /// For example, an offset of 1 would associate the image with the previous
128    /// call to [`frame_mark`](Client::frame_mark).
129    ///
130    /// The `flip` parameter indicates that the image should be flipped before
131    /// displaying, for example if the image is an OpenGL texture.
132    pub fn frame_image(&self, image: &[u8], width: u16, height: u16, offset: u8, flip: bool) {
133        #[cfg(feature = "enable")]
134        unsafe {
135            // SAFE: Tracy copies the data before returning.
136            let ptr = image.as_ptr();
137
138            let () = sys::___tracy_emit_frame_image(ptr.cast(), width, height, offset, flip as i32);
139        }
140    }
141}
142
143/// Construct a [`FrameName`].
144///
145/// The resulting value may be used as an argument for the the [`Client::secondary_frame_mark`] and
146/// [`Client::non_continuous_frame`] methods. The macro can be used in a `const` context.
147#[macro_export]
148macro_rules! frame_name {
149    ($name: literal) => {{
150        unsafe { $crate::internal::create_frame_name(concat!($name, "\0")) }
151    }};
152}
153
154impl Drop for Frame {
155    fn drop(&mut self) {
156        #[cfg(feature = "enable")]
157        unsafe {
158            // SAFE: We ensure that thena me would be null-terminated. We also still have an owned
159            // Client handle.
160            let () = sys::___tracy_emit_frame_mark_end(self.1 .0.as_ptr().cast());
161            std::convert::identity(&self.0);
162        }
163    }
164}
165
166/// Convenience shortcut for [`Client::frame_mark`] on the current client.
167///
168/// # Panics
169///
170/// - If a `Client` isn't currently running.
171pub fn frame_mark() {
172    Client::running()
173        .expect("frame_mark! without a running Client")
174        .frame_mark();
175}
176
177/// Convenience shortcut for [`Client::frame_image`] on the current client.
178pub fn frame_image(image: &[u8], width: u16, height: u16, offset: u8, flip: bool) {
179    Client::running()
180        .expect("frame_image without a running Client")
181        .frame_image(image, width, height, offset, flip);
182}
183
184/// Convenience macro for [`Client::secondary_frame_mark`] on the current client.
185///
186/// # Panics
187///
188/// - If a `Client` isn't currently running.
189#[macro_export]
190macro_rules! secondary_frame_mark {
191    ($name: literal) => {{
192        $crate::Client::running()
193            .expect("secondary_frame_mark! without a running Client")
194            .secondary_frame_mark($crate::frame_name!($name))
195    }};
196}
197
198/// Convenience macro for [`Client::non_continuous_frame`] on the current client.
199///
200/// # Panics
201///
202/// - If a `Client` isn't currently running.
203#[macro_export]
204macro_rules! non_continuous_frame {
205    ($name: literal) => {{
206        $crate::Client::running()
207            .expect("non_continuous_frame! without a running Client")
208            .non_continuous_frame($crate::frame_name!($name))
209    }};
210}