feat: wip plotters-gtk4

This commit is contained in:
Ilya Zlobintsev 2025-02-08 21:29:04 +02:00
parent 0894f9ae9d
commit 4f1074526e
6 changed files with 102 additions and 39 deletions

38
Cargo.lock generated
View File

@ -1283,6 +1283,7 @@ version = "0.7.1"
dependencies = [
"anyhow",
"divan",
"gtk4",
"lact-cli",
"lact-daemon",
"lact-gui",
@ -1366,7 +1367,7 @@ dependencies = [
"lact-schema",
"libadwaita",
"plotters",
"plotters-cairo",
"plotters-gtk4",
"pretty_assertions",
"relm4",
"relm4-components",
@ -1776,6 +1777,32 @@ dependencies = [
"system-deps",
]
[[package]]
name = "pangocairo"
version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4690509a2fea2a6552a0ef8aa3e5f790c1365365ee0712afa1aedb39af3997b6"
dependencies = [
"cairo-rs",
"glib",
"libc",
"pango",
"pangocairo-sys",
]
[[package]]
name = "pangocairo-sys"
version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be6ac24147911a6a46783922fc288cf02f67570bc0d360e563b5b26aead6767"
dependencies = [
"cairo-sys-rs",
"glib-sys",
"libc",
"pango-sys",
"system-deps",
]
[[package]]
name = "parking"
version = "2.2.1"
@ -1857,12 +1884,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
[[package]]
name = "plotters-cairo"
version = "0.7.0"
name = "plotters-gtk4"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e7a3a2567b691ed2f0670ea3cc39988add29c968228b474ec5fe8261b1ff2a5"
checksum = "2f18ee173b0c8e6d8cb01c1e32a9b41bf41d74b277a69f22277bab05870e13d7"
dependencies = [
"cairo-rs",
"gtk4",
"pangocairo",
"plotters-backend",
]

View File

@ -32,13 +32,14 @@ plotters = { version = "0.3.5", default-features = false, features = [
"line_series",
"full_palette",
] }
plotters-cairo = "0.7.0"
# plotters-cairo = "0.7.0"
cairo-rs = { version = "0.20", default-features = false }
itertools = "0.13.0"
thread-priority = "1.1.0"
divan = { workspace = true, optional = true }
plotters-gtk4 = "0.5.0"
[dev-dependencies]
pretty_assertions = "1.4.0"

View File

@ -2,6 +2,7 @@ use chrono::NaiveDateTime;
use glib::Properties;
use gtk::{glib, prelude::*, subclass::prelude::*};
use plotters_gtk4::SnapshotBackend;
use std::cell::Cell;
use std::cell::RefCell;
@ -24,7 +25,7 @@ pub struct Plot {
secondary_y_label_area_relative_size: Cell<f64>,
pub(super) data: RefCell<PlotData>,
pub(super) dirty: Cell<bool>,
render_thread: RenderThread,
// render_thread: RenderThread,
#[property(get, set)]
time_period_seconds: Cell<i64>,
}
@ -50,42 +51,58 @@ impl ObjectImpl for Plot {
impl WidgetImpl for Plot {
fn snapshot(&self, snapshot: &gtk::Snapshot) {
snapshot.scale(0.25, 0.25);
let width = self.obj().width() as u32;
let height = self.obj().height() as u32;
if width == 0 || height == 0 {
return;
}
let request = RenderRequest {
data: self.data.borrow().clone(),
width,
height,
title: self.title.borrow().clone(),
value_suffix: self.value_suffix.borrow().clone(),
secondary_value_suffix: self.secondary_value_suffix.borrow().clone(),
y_label_area_relative_size: self.y_label_area_relative_size.get(),
secondary_y_label_relative_area_size: self.secondary_y_label_area_relative_size.get(),
supersample_factor: 4,
time_period_seconds: self.time_period_seconds.get(),
};
let backend = SnapshotBackend::new(snapshot, (width * 4, height * 4));
request.draw(backend).unwrap();
let last_texture = self.render_thread.get_last_texture();
let size_changed = last_texture
.as_ref()
.map(|texture| (texture.width() as u32, texture.height() as u32) != (width, height))
.unwrap_or(true);
// let last_texture = self.render_thread.get_last_texture();
// let size_changed = last_texture
// .as_ref()
// .map(|texture| (texture.width() as u32, texture.height() as u32) != (width, height))
// .unwrap_or(true);
if self.dirty.replace(false) || size_changed {
self.render_thread.replace_render_request(RenderRequest {
data: self.data.borrow().clone(),
width,
height,
title: self.title.borrow().clone(),
value_suffix: self.value_suffix.borrow().clone(),
secondary_value_suffix: self.secondary_value_suffix.borrow().clone(),
y_label_area_relative_size: self.y_label_area_relative_size.get(),
secondary_y_label_relative_area_size: self
.secondary_y_label_area_relative_size
.get(),
supersample_factor: 4,
time_period_seconds: self.time_period_seconds.get(),
});
}
// if self.dirty.replace(false) || size_changed {
// self.render_thread.replace_render_request(RenderRequest {
// data: self.data.borrow().clone(),
// width,
// height,
// title: self.title.borrow().clone(),
// value_suffix: self.value_suffix.borrow().clone(),
// secondary_value_suffix: self.secondary_value_suffix.borrow().clone(),
// y_label_area_relative_size: self.y_label_area_relative_size.get(),
// secondary_y_label_relative_area_size: self
// .secondary_y_label_area_relative_size
// .get(),
// supersample_factor: 4,
// time_period_seconds: self.time_period_seconds.get(),
// });
// }
// Rendering is always behind by at least one frame, but it's not an issue
if let Some(texture) = last_texture {
let bounds = gtk::graphene::Rect::new(0.0, 0.0, width as f32, height as f32);
// Uses by default Trillinear texture filtering, which is quite good at 4x supersampling
snapshot.append_texture(&texture, &bounds);
}
// // Rendering is always behind by at least one frame, but it's not an issue
// if let Some(texture) = last_texture {
// let bounds = gtk::graphene::Rect::new(0.0, 0.0, width as f32, height as f32);
// // Uses by default Trillinear texture filtering, which is quite good at 4x supersampling
// snapshot.append_texture(&texture, &bounds);
// }
}
}

View File

@ -9,7 +9,6 @@ use itertools::Itertools;
use plotters::prelude::*;
use plotters::style::colors::full_palette::DEEPORANGE_100;
use plotters::style::RelativeSize;
use plotters_cairo::CairoBackend;
use std::cmp::{max, min};
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex};
@ -131,7 +130,7 @@ impl RenderThread {
}
fn process_request(render_request: RenderRequest, last_texture: &Mutex<Option<MemoryTexture>>) {
// Create a new ImageSurface for Cairo rendering.
/*// Create a new ImageSurface for Cairo rendering.
let mut surface = ImageSurface::create(
cairo::Format::ARgb32,
(render_request.width * render_request.supersample_factor) as i32,
@ -178,7 +177,7 @@ fn process_request(render_request: RenderRequest, last_texture: &Mutex<Option<Me
(result, last_texture) => {
*last_texture = result;
}
};
};*/
}
// Implement the default constructor for RenderThread using the `new` method.
@ -400,11 +399,13 @@ mod benches {
use crate::app::graphs_window::plot::PlotData;
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
use divan::{counter::ItemsCount, Bencher};
use gtk::prelude::SnapshotExt;
use plotters_gtk4::SnapshotBackend;
use std::sync::Mutex;
#[divan::bench]
fn render_plot(bencher: Bencher) {
let last_texture = &Mutex::new(None);
// let last_texture = &Mutex::new(None);
bencher
.with_inputs(sample_plot_data)
@ -423,7 +424,20 @@ mod benches {
time_period_seconds: 60,
};
process_request(request, last_texture)
let snapshot = gtk::Snapshot::new();
snapshot.scale(
1.0 / request.supersample_factor as f32,
1.0 / request.supersample_factor as f32,
);
let backend = SnapshotBackend::new(
&snapshot,
(
request.width * request.supersample_factor,
request.height * request.supersample_factor,
),
);
request.draw(backend).unwrap();
// process_request(request, last_texture)
});
}

View File

@ -18,6 +18,7 @@ anyhow = { workspace = true }
divan = { workspace = true }
lact-daemon = { path = "../lact-daemon", features = ["bench"] }
lact-gui = { path = "../lact-gui", features = ["bench"] }
gtk = { version = "0.9", package = "gtk4" }
[[bench]]
name = "bench"

View File

@ -3,5 +3,7 @@ fn main() {
let _ = lact_daemon::run;
let _ = lact_gui::run;
let _ = gtk::init();
divan::main();
}