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}