feat: show latest plot frame as soon as it is available

This commit is contained in:
Ilya Zlobintsev
2025-08-21 21:53:16 +03:00
parent f6fbd2f6dd
commit 418b65419b
4 changed files with 30 additions and 3 deletions
+3 -3
View File
@@ -19,11 +19,11 @@ pub struct Plot {
title: RefCell<String>,
#[property(set)]
dirty: Cell<bool>,
render_thread: RenderThread,
pub(super) render_thread: RenderThread,
#[property(get, set)]
time_period_seconds: Cell<i64>,
pub stats: RefCell<Vec<StatType>>,
pub data: RefCell<Arc<RwLock<StatsData>>>,
pub(super) stats: RefCell<Vec<StatType>>,
pub(super) data: RefCell<Arc<RwLock<StatsData>>>,
}
#[glib::object_subclass]
@@ -30,6 +30,15 @@ impl Plot {
pub fn set_stats(&self, stats: Vec<StatType>) {
*self.imp().stats.borrow_mut() = stats;
}
pub fn connect_frame_rendered<F: Fn() + 'static>(&self, f: F) {
let mut rx = self.imp().render_thread.render_notifier();
relm4::spawn_local(async move {
while let Ok(()) = rx.recv().await {
f();
}
});
}
}
#[derive(Debug)]
@@ -9,6 +9,7 @@ use indexmap::IndexSet;
use plotters::prelude::*;
use plotters::style::text_anchor::Pos;
use plotters_cairo::CairoBackend;
use relm4::tokio::sync::broadcast;
use std::cmp::{self, max};
use std::fmt::Write;
use std::ops::{Deref, DerefMut};
@@ -49,6 +50,7 @@ pub struct RenderThread {
/// Shared state is between the main thread and the rendering thread.
state: Arc<RenderThreadState>,
thread_handle: Option<std::thread::JoinHandle<()>>,
render_notifier: broadcast::Sender<()>,
}
/// Ensure the rendering thread is terminated properly when the RenderThread object is dropped.
@@ -69,12 +71,14 @@ impl Drop for RenderThread {
impl RenderThread {
pub fn new() -> Self {
let state = Arc::new(RenderThreadState::default());
let render_tx = broadcast::Sender::new(1);
let thread_handle = std::thread::Builder::new()
.name("Plot-Renderer".to_owned())
// Render thread is very unimportant, skipping frames and rendering slowly is ok
.spawn_with_priority(ThreadPriority::Min, {
let state = state.clone();
let render_tx = render_tx.clone();
move |_| loop {
let RenderThreadState {
request_condition_variable,
@@ -97,6 +101,7 @@ impl RenderThread {
Some(Request::Terminate) => break,
None => {}
}
let _ = render_tx.send(());
}
})
.unwrap();
@@ -104,9 +109,14 @@ impl RenderThread {
Self {
state,
thread_handle: Some(thread_handle),
render_notifier: render_tx,
}
}
pub fn render_notifier(&self) -> broadcast::Receiver<()> {
self.render_notifier.subscribe()
}
/// Replace the current render request with a new one (effectively dropping possible pending frame)
/// Returns dropped request if any
pub fn replace_render_request(&self, request: RenderRequest) -> Option<RenderRequest> {
@@ -39,6 +39,7 @@ pub struct PlotComponentConfig {
#[derive(Debug, Clone)]
pub enum PlotComponentMsg {
Redraw,
FrameRendered,
UpdatedSelection,
}
@@ -67,6 +68,10 @@ impl relm4::factory::FactoryComponent for PlotComponent {
#[watch]
set_time_period_seconds: self.time_period.value() as i64,
connect_frame_rendered[sender] => move || {
sender.input(PlotComponentMsg::FrameRendered);
},
#[wrap(Clone::clone)]
add_controller = &gtk::DragSource {
#[watch]
@@ -190,6 +195,9 @@ impl relm4::factory::FactoryComponent for PlotComponent {
widgets.plot.set_dirty(true);
widgets.plot.queue_draw();
}
PlotComponentMsg::FrameRendered => {
widgets.plot.queue_draw();
}
PlotComponentMsg::UpdatedSelection => {
widgets.plot.set_stats(self.selected_stats());
sender.input(PlotComponentMsg::Redraw);