13 releases
0.3.12 | Jan 8, 2023 |
---|---|
0.3.11 | Feb 20, 2021 |
0.3.10 | Nov 17, 2020 |
0.0.0 |
|
#55 in Visualization
22 downloads per month
95KB
2K
SLoC
plotters-conrod
This is an implementation of a Conrod backend for Plotters. This is more efficient than using the default Bitmap backend when plotting in Conrod, as it has been observed that Conrod was quite inefficient at re-rendering images at high FPS (eg. for real-time plotting).
This backend has been optimized as for speed, and as to render plots that look very similar to the default Bitmap backend, if not indistinguishable. Note that some specific plotting features supported in the Bitmap backend may not be implemented there, though.
đĢđˇ Crafted in Nantes, France.
Who uses it?
MakAir |
- đ You use
plotters-conrod
and you want to be listed there? Contact me. - âšī¸ The MakAir open-source medical ventilator uses
plotters-conrod
on its Rust-based MakAir Control UI.
What is Plotters?
Plotters is an extensible Rust drawing library that can be used to plot data on nice-looking graphs, rendering them through a plotting backend (eg. to a Bitmap image raw buffer, to your GUI backend, to an SVG file, etc.).
For more details on Plotters, please check the following links:
- For an introduction of Plotters, see: Plotters on Crates.io;
- Check the main repository on GitHub;
- You can also visit the Plotters homepage;
How to install?
Include plotters-conrod
in your Cargo.toml
dependencies:
[dependencies]
plotters-conrod = "0.3"
The plotters-conrod
version used should match your plotters
version. If there is no such plotters-conrod
version yet, using an older plotters-conrod
version than your plotters
should usually work.
How to use?
First, import ConrodBackend
and ConrodBackendReusableGraph
:
use plotters_conrod::{ConrodBackend, ConrodBackendReusableGraph};
Then, build the re-usable graph instance (outside of your drawing loop):
let mut conrod_graph = ConrodBackendReusableGraph::build();
â ī¸ This should be put outside of your loop and called once; failing to do so will result in heavy CPU usage due to the graph being rebuilt for every frame!
Finally, for each frame you draw (ie. your main loop), call:
// Where:
// - 'ui' is the UiCell that was derived from Ui for this frame;
// - '(plot_width, plot_height)' is the size of your plot in pixels (make sure it matches its parent canvas size);
// - 'ids.parent' is the widget::Id of the canvas that contains your plot (of the same size than the plot itself);
// - 'fonts.regular' is the font::Id of the font to use to draw text (ie. a Conrod font identifier);
// - 'conrod_graph' is a mutable reference to the graph instance you built outside of the drawing loop (pass it as a mutable reference);
let drawing = ConrodBackend::new(
ui,
(plot_width, plot_height),
ids.parent,
fonts.regular,
&mut conrod_graph,
).into_drawing_area();
//-
// Build your chart as usual here, using the regular Plotters syntax
//-
If you are looking for a full example of an implementation, please check cpu-monitor.rs.
How to run the examples?
Example #1: cpu-monitor
This example samples your CPU load every second, and renders it in a real-time chart:
cargo run --release --example cpu-monitor
The first plot uses plotters-conrod
, while the second plot uses the default Bitmap backend as a reference. This can be used to compare the output and performance of both plotting backends. The Bitmap reference plot can be disabled by setting REFERENCE_BITMAP_ENABLED
to false
.
How lightweight is it compared to other backends?
The plotters-conrod
backend was designed to perform all expensive computational work on the GPU, rather than on the CPU. This is a much more efficient, especially for large plot draw areas (in pixels).
While the default Bitmap backend rasterizer would only use the CPU (quite heavily at high FPS on real-time plots), this Conrod backend sends most of its work over to the GPU, as it uses OpenGL primitives to draw shapes.
Measurements have been made by running this example comparing the Bitmap backend with the Conrod backend, on a 2019 MacBook Pro laptop, running a 2,3GHz 8-Core Intel Core i9 CPU with a Radeon Pro 560X 4GB GPU. The example was compiled in --release
mode, with all CPU optimizations enabled.
Those are the measurements results for a 800x480 pixels plot at 30 FPS:
- âĄī¸ Bitmap backend: CPU
~31%
stable, GPU~10%
mean (the CPU draws, then the GPU renders the bitmap on its framebuffer); - âĄī¸ Conrod backend: CPU
~4%
stable, GPU~15%
mean (the CPU passes data, the GPU draws);
Memory usage is about the same for both backends.
Are there any limitations?
Limitation #1: No pixel-by-pixel rendering
As Conrod is known to be quite inefficient at rendering image widgets at any high-enough FPS (the likely cause is that it bypasses the GPU and does heavy CPU processing work), it was chosen to ignore the rendering of pixel primitives.
The default Plotters rasterizer has been disabled in that case, as to avoid rendering performance to be degraded without the library user noticing. This guarantees that the GPU is used for rendering, while the CPU does minimal work.
It means that, some complex plot types may not render well. Though, rest assured that common plot types have been tested to render exactly as expected, eg. LineSeries
or Histogram
.
There are plans to implement those pixel-based rendering methods in the future. If you already have an implementation, feel free to PR this library!
Limitation #2: Limited text rendering
Only a single font family (ie. serif
, sans-serif
, etc.) and a single font style (ie. regular
, bold
, etc.) are supported for text rendering. The reason is that Conrod makes it quite tedious to load fonts and pass them over, so we better off limit the backend API to a single font for simplicity's sake. As well, font transforms are not supported due to the underlying Conrod renderer, which does not seem to support text rotations.
Dependencies
~6MB
~111K SLoC