diff --git a/examples/src/main.rs b/examples/src/main.rs index 0cb7522..e9ab3b5 100644 --- a/examples/src/main.rs +++ b/examples/src/main.rs @@ -1,3 +1,4 @@ +mod plots; mod secondary_frames; mod threads; mod wgpu_frame_images; @@ -30,6 +31,11 @@ const EXAMPLES: &[ExampleDesc] = &[ description: "Demonstrates the use of zones across threads", function: threads::main, }, + ExampleDesc { + name: "plots", + description: "Demonstrates plotting values", + function: plots::main, + }, ]; fn main() { diff --git a/examples/src/plots/mod.rs b/examples/src/plots/mod.rs new file mode 100644 index 0000000..7c97276 --- /dev/null +++ b/examples/src/plots/mod.rs @@ -0,0 +1,43 @@ +use rand::Rng; +use std::thread::sleep; +use std::time::Duration; +use tracy_client::{Client, PlotConfiguration, PlotFormat, PlotLineStyle, PlotName}; + +// Plots you know statically can be defined freely like so +const PLOT_PLAYER_COUNT: PlotName = tracy_client::plot_name!("Player Count"); +const PLOT_DISK_SPACE: PlotName = tracy_client::plot_name!("Disk Space"); + +pub fn main() { + let client = Client::start(); + let mut rng = rand::thread_rng(); + + // Anything at runtime needs to be created via PlotName + let bandwidth = PlotName::new_leak("Bandwidth".to_string()); + + // You can configure how plots display, this only needs to be done once + client.plot_config( + PLOT_DISK_SPACE, + PlotConfiguration::default() + .format(PlotFormat::Memory) + .fill(false), + ); + client.plot_config( + bandwidth, + PlotConfiguration::default() + .format(PlotFormat::Percentage) + .color(Some(0xFF0000)) + .line_style(PlotLineStyle::Stepped), + ); + + for _ in 0..50 { + // You don't need to constantly send a value! + if rng.gen_bool(0.75) { + client.plot(PLOT_PLAYER_COUNT, rng.gen_range(0..10) as f64); + } + + client.plot(PLOT_DISK_SPACE, rng.gen_range(0..1000000) as f64); + client.plot(bandwidth, rng.gen_range(0..100) as f64); + + sleep(Duration::from_millis(20)); + } +} diff --git a/tracy-client/src/lib.rs b/tracy-client/src/lib.rs index 4c0c29d..3de1756 100644 --- a/tracy-client/src/lib.rs +++ b/tracy-client/src/lib.rs @@ -32,7 +32,7 @@ pub use crate::frame::{frame_image, frame_mark, Frame, FrameName}; pub use crate::gpu::{ GpuContext, GpuContextCreationError, GpuContextType, GpuSpan, GpuSpanCreationError, }; -pub use crate::plot::PlotName; +pub use crate::plot::{PlotConfiguration, PlotFormat, PlotLineStyle, PlotName}; pub use crate::span::{Span, SpanLocation}; use std::alloc; use std::ffi::CString; diff --git a/tracy-client/src/plot.rs b/tracy-client/src/plot.rs index d900c88..d7d6039 100644 --- a/tracy-client/src/plot.rs +++ b/tracy-client/src/plot.rs @@ -6,6 +6,89 @@ use crate::Client; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct PlotName(pub(crate) &'static str); +/// The format of a plot to be shown in the Tracy profiler UI. +#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] +#[non_exhaustive] +pub enum PlotFormat { + /// Values will be displayed as plain numbers. This is the default. + #[default] + Number, + + /// Values will be displayed as byte counts, showing as kilobytes, megabytes, etc. + Memory, + + /// Values will be shown as a percentage (with 100 being equal to 100%). + Percentage, + + /// Values will be shown as watts. + Watts, +} + +/// The style of lines of a plot, shown in the Tracy profiler UI. +#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] +#[non_exhaustive] +pub enum PlotLineStyle { + /// Lines will be stepped (ie look like a staircase). + Stepped, + + /// Lines will be smooth (interpolating a line between two values). + #[default] + Smooth, +} + +/// Configuration for how a plot appears in the Tracy profiling UI. +#[derive(Clone, PartialEq, Debug)] +pub struct PlotConfiguration { + /// The format of values on the plot. + format: PlotFormat, + + /// The style of lines on the plot. + line_style: PlotLineStyle, + + /// Whether the plot should be filled with a solid color below the line. + fill: bool, + + /// A custom color of this plot. None means a default color will be generated by Tracy. + color: Option, +} + +impl PlotConfiguration { + /// Sets the format of values on the plot. + pub fn format(mut self, format: PlotFormat) -> Self { + self.format = format; + self + } + + /// Sets the style of lines on the plot. + pub fn line_style(mut self, line_style: PlotLineStyle) -> Self { + self.line_style = line_style; + self + } + + /// Sets whether the plot should be filled with a solid color below the line. + pub fn fill(mut self, fill: bool) -> Self { + self.fill = fill; + self + } + + /// Sets a custom color of the plot. A value of `None` will cause Tracy to create its own color. + pub fn color(mut self, color: Option) -> Self { + self.color = color; + self + } +} + +impl Default for PlotConfiguration { + fn default() -> Self { + Self { + format: Default::default(), + line_style: Default::default(), + fill: true, + color: None, + } + } +} + impl PlotName { /// Construct a `PlotName` dynamically, leaking the provided String. /// @@ -53,6 +136,40 @@ impl Client { let () = sys::___tracy_emit_plot(plot_name.0.as_ptr().cast(), value); } } + + /// Sets the display configuration of the plot named `plot_name`. + /// + /// # Examples + /// + /// ``` + /// use tracy_client::{PlotConfiguration, PlotFormat}; + /// # let client = tracy_client::Client::start(); + /// tracy_client::Client::running() + /// .expect("client must be running") + /// .plot_config(tracy_client::plot_name!("memory"), PlotConfiguration::default().format(PlotFormat::Memory)); + /// ``` + pub fn plot_config(&self, plot_name: PlotName, configuration: PlotConfiguration) { + let format = match configuration.format { + PlotFormat::Number => sys::TracyPlotFormatEnum_TracyPlotFormatNumber, + PlotFormat::Memory => sys::TracyPlotFormatEnum_TracyPlotFormatMemory, + PlotFormat::Percentage => sys::TracyPlotFormatEnum_TracyPlotFormatPercentage, + PlotFormat::Watts => sys::TracyPlotFormatEnum_TracyPlotFormatWatt, + } as std::os::raw::c_int; + let stepped = configuration.line_style == PlotLineStyle::Stepped; + let filled = configuration.fill; + let color = configuration.color.unwrap_or(0); + #[cfg(feature = "enable")] + unsafe { + // SAFE: We made sure the `plot` refers to a null-terminated string. + let () = sys::___tracy_emit_plot_config( + plot_name.0.as_ptr().cast(), + format, + stepped.into(), + filled.into(), + color, + ); + } + } } /// Construct a [`PlotName`]. diff --git a/tracy-client/src/span.rs b/tracy-client/src/span.rs index b7b5d01..4a46ee3 100644 --- a/tracy-client/src/span.rs +++ b/tracy-client/src/span.rs @@ -124,7 +124,7 @@ impl Client { function.len(), name.map_or(std::ptr::null(), |n| n.as_ptr().cast()), name.unwrap_or("").len(), - 0 + 0, ); let zone = if callstack_depth == 0 { sys::___tracy_emit_zone_begin_alloc(loc, 1)