Performance

Measure drawing cost and reduce dense Cartesian geometry without changing source data.

Retain data and measure the workload

Keep immutable datasets and series in the owner and clone handles into builders. Series samples use shared storage and cache ordering/finite-count information at construction. Rebuilding a Series from the same samples each render repeats that work.

Measure representative data, viewport sizes, layers, markers, tooltips, and redraw frequency. Counts and timings are workload-specific; no universal frame-rate guarantee follows from the chart type.

Dense lines and decimation

Decimation::None draws accepted samples without reduction. MinMax retains first/last/minimum/maximum observations per logical pixel column in input order, processing each contiguous run separately. Reduction adapts to plot width and leaves stored samples unchanged.

Runs whose X coordinates reverse direction fall back to full resolution. Non-finite observations preserve gaps. Markers represent retained observations only, so decimation also affects marker picking. Min/max reduction is a line-layer policy, not a general optimization switch for pie, radar, radial, or all area/bar geometry.

Interpolation::Monotone is a visual connection policy applied after decimation, not a performance shortcut. Compare raw and reduced output for your data; use linear interpolation when appropriate.

Render statistics

Opt in with .on_render_stats(...) on Cartesian charts. The callback runs after canvas painting. Defer owner updates, and avoid notifying on every timing fluctuation: otherwise displaying diagnostics can create a redraw feedback loop.

Rust
use gpui::{App, Entity};
use xgpui_luma_plot::{
    CartesianChartBuilder, CartesianChartView, ChartRenderStats, Dataset,
    Decimation, LineLayer,
};

#[derive(Default)]
struct Diagnostics {
    stats: Option<ChartRenderStats>,
}

fn measured_chart(data: Dataset, owner: &Entity<Diagnostics>) -> CartesianChartView {
    let weak = owner.downgrade();
    CartesianChartBuilder::new(data)
        .id("measured-signal")
        .line(LineLayer::new("signal").decimation(Decimation::MinMax))
        .on_render_stats(move |stats, cx: &mut App| {
            let weak = weak.clone();
            cx.defer(move |cx| {
                let _ = weak.update(cx, |owner, cx| {
                    let changed = owner.stats.is_none_or(|previous| {
                        previous.source_points != stats.source_points
                            || previous.visible_points != stats.visible_points
                            || previous.retained_points != stats.retained_points
                            || previous.plot_width != stats.plot_width
                            || previous.plot_height != stats.plot_height
                            || previous.x_domain != stats.x_domain
                    });
                    if changed {
                        owner.stats = Some(stats);
                        cx.notify();
                    }
                });
            });
        })
        .build()
}

Supply a dataset containing signal and an owner created by your app. This example snapshots timings when its selected geometry/count fields change; it is not continuous timing telemetry. For continuous measurements, collect reports without driving an owner redraw for each report.

StatisticMeaning
source_pointsStored observations, including invalid/unmapped coordinates.
valid_pointsAccepted observations before decimation.
visible_pointsViewport-selected observations before decimation, including edge neighbors.
retained_pointsDrawing points after decimation or projected bar rectangles.
prepare_timeCPU scale scans, axis measurement, projection, and decimation.
paint_timeCPU construction and paint submission; excludes GPU execution, host layout, and legend.

Counts include each selected layer; drawing the same series as both line and area can count observations twice. Use plot dimensions, X domain, and run counts to interpret reports. These statistics are not complete application frame timings.

Viewport and rendering costs

A narrow numeric viewport can reduce projected geometry. The implementation has optimized paths for sorted X data; do not assume every dataset uses the same selection path or that clipping removes all preprocessing cost. Preserve intended line order rather than sorting solely for speed.

Limit unnecessary markers, repeated dataset construction, expensive style/tooltip factories, and redundant owner notifications. Keep style providers side-effect-free and request animation frames only while transitions are active. Profile host layout and GPU work separately if CPU chart timings do not explain performance.

Source

Statistics contract · Decimation policy · Gallery diagnostics · Reduced-line example