tracy_client/
plot.rs

1use crate::Client;
2
3/// Name of a plot.
4///
5/// Create with the [`plot_name!`](crate::plot_name) macro.
6#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
7pub struct PlotName(pub(crate) &'static str);
8
9/// The format of a plot to be shown in the Tracy profiler UI.
10#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
11#[non_exhaustive]
12pub enum PlotFormat {
13    /// Values will be displayed as plain numbers. This is the default.
14    #[default]
15    Number,
16
17    /// Values will be displayed as byte counts, showing as kilobytes, megabytes, etc.
18    Memory,
19
20    /// Values will be shown as a percentage (with 100 being equal to 100%).
21    Percentage,
22
23    /// Values will be shown as watts.
24    Watts,
25}
26
27/// The style of lines of a plot, shown in the Tracy profiler UI.
28#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
29#[non_exhaustive]
30pub enum PlotLineStyle {
31    /// Lines will be stepped (ie look like a staircase).
32    Stepped,
33
34    /// Lines will be smooth (interpolating a line between two values).
35    #[default]
36    Smooth,
37}
38
39/// Configuration for how a plot appears in the Tracy profiling UI.
40///
41/// # Examples
42///
43/// ```
44/// # use tracy_client::PlotConfiguration;
45/// // Create a red plot line.
46/// let configuration = PlotConfiguration::default().fill(false).color(Some(0xFF0000));
47/// ```
48#[derive(Clone, PartialEq, Debug)]
49pub struct PlotConfiguration {
50    /// The format of values on the plot.
51    format: PlotFormat,
52
53    /// The style of lines on the plot.
54    line_style: PlotLineStyle,
55
56    /// Whether the plot should be filled with a solid color below the line.
57    fill: bool,
58
59    /// A custom color of this plot. None means a default color will be generated by Tracy.
60    color: Option<u32>,
61}
62
63impl PlotConfiguration {
64    /// Sets the format of values on the plot.
65    pub fn format(mut self, format: PlotFormat) -> Self {
66        self.format = format;
67        self
68    }
69
70    /// Sets the style of lines on the plot.
71    pub fn line_style(mut self, line_style: PlotLineStyle) -> Self {
72        self.line_style = line_style;
73        self
74    }
75
76    /// Sets whether the plot should be filled with a solid color below the line.
77    pub fn fill(mut self, fill: bool) -> Self {
78        self.fill = fill;
79        self
80    }
81
82    /// Sets a custom color of the plot. A value of `None` will cause Tracy to create its own color.
83    ///
84    /// # Examples
85    ///
86    /// ```
87    /// # use tracy_client::PlotConfiguration;
88    /// // Configure the plot to be red.
89    /// let red = PlotConfiguration::default().color(Some(0xFF0000));
90    /// // Configure the plot to be green.
91    /// let green = PlotConfiguration::default().color(Some(0x00FF00));
92    /// // Configure the plot to be blue.
93    /// let blue = PlotConfiguration::default().color(Some(0x0000FF));
94    /// ```
95    pub fn color(mut self, color: Option<u32>) -> Self {
96        self.color = color;
97        self
98    }
99}
100
101impl Default for PlotConfiguration {
102    fn default() -> Self {
103        Self {
104            format: Default::default(),
105            line_style: Default::default(),
106            fill: true,
107            color: None,
108        }
109    }
110}
111
112impl PlotName {
113    /// Construct a `PlotName` dynamically, leaking the provided String.
114    ///
115    /// You should call this function once for a given name, and store the returned `PlotName` for
116    /// continued use, to avoid rapid memory use growth. Whenever possible, prefer the
117    /// [`plot_name!`](crate::plot_name) macro, which takes a literal name and doesn't leak memory.
118    ///
119    /// The resulting value may be used as an argument for the the [`Client::secondary_frame_mark`]
120    /// and [`Client::non_continuous_frame`] methods.
121    #[must_use]
122    pub fn new_leak(name: String) -> Self {
123        #[cfg(feature = "enable")]
124        {
125            // Ensure the name is null-terminated.
126            let mut name = name;
127            name.push('\0');
128            // Drop excess capacity by converting into a boxed str, then leak.
129            let name = Box::leak(name.into_boxed_str());
130            Self(name)
131        }
132        #[cfg(not(feature = "enable"))]
133        {
134            drop(name);
135            Self("\0")
136        }
137    }
138}
139
140/// Instrumentation for drawing 2D plots.
141impl Client {
142    /// Add a point with an y-axis value of `value` to the plot named `plot_name`.
143    ///
144    /// # Examples
145    ///
146    /// ```
147    /// # let client = tracy_client::Client::start();
148    /// tracy_client::Client::running()
149    ///     .expect("client must be running")
150    ///     .plot(tracy_client::plot_name!("temperature"), 37.0);
151    /// ```
152    pub fn plot(&self, plot_name: PlotName, value: f64) {
153        #[cfg(feature = "enable")]
154        unsafe {
155            // SAFE: We made sure the `plot` refers to a null-terminated string.
156            let () = sys::___tracy_emit_plot(plot_name.0.as_ptr().cast(), value);
157        }
158    }
159
160    /// Sets the display configuration of the plot named `plot_name`.
161    ///
162    /// # Examples
163    ///
164    /// ```
165    /// use tracy_client::{PlotConfiguration, PlotFormat};
166    /// # let client = tracy_client::Client::start();
167    /// tracy_client::Client::running()
168    ///     .expect("client must be running")
169    ///     .plot_config(tracy_client::plot_name!("memory"), PlotConfiguration::default().format(PlotFormat::Memory));
170    /// ```
171    pub fn plot_config(&self, plot_name: PlotName, configuration: PlotConfiguration) {
172        #[cfg(feature = "enable")]
173        {
174            let format = match configuration.format {
175                PlotFormat::Number => sys::TracyPlotFormatEnum_TracyPlotFormatNumber,
176                PlotFormat::Memory => sys::TracyPlotFormatEnum_TracyPlotFormatMemory,
177                PlotFormat::Percentage => sys::TracyPlotFormatEnum_TracyPlotFormatPercentage,
178                PlotFormat::Watts => sys::TracyPlotFormatEnum_TracyPlotFormatWatt,
179            } as std::os::raw::c_int;
180            let stepped = configuration.line_style == PlotLineStyle::Stepped;
181            let filled = configuration.fill;
182            let color = configuration.color.unwrap_or(0);
183            unsafe {
184                // SAFE: We made sure the `plot` refers to a null-terminated string.
185                let () = sys::___tracy_emit_plot_config(
186                    plot_name.0.as_ptr().cast(),
187                    format,
188                    stepped.into(),
189                    filled.into(),
190                    color,
191                );
192            }
193        }
194    }
195}
196
197/// Construct a [`PlotName`].
198///
199/// The resulting value may be used as an argument for the [`Client::plot`] method. The macro can
200/// be used in a `const` context.
201#[macro_export]
202macro_rules! plot_name {
203    ($name: expr) => {
204        unsafe { $crate::internal::create_plot(concat!($name, "\0")) }
205    };
206}
207
208/// Convenience macro for [`Client::plot`] on the current client.
209///
210/// # Panics
211///
212/// - If a `Client` isn't currently running.
213#[macro_export]
214macro_rules! plot {
215    ($name: expr, $value: expr) => {{
216        $crate::Client::running()
217            .expect("plot! without a running Client")
218            .plot($crate::plot_name!($name), $value)
219    }};
220}