irox_egui_extras/
repainting.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// SPDX-License-Identifier: MIT
// Copyright 2025 IROX Contributors
//

use egui::Context;
use irox_time::{epoch::UnixTimestamp, Duration};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::Arc;
use std::thread::JoinHandle;

pub struct RepaintManager {
    repaint_requested: RepaintRequest,
    running: Arc<AtomicBool>,
    measured_fps: Arc<AtomicU32>,
    hndl: Option<JoinHandle<()>>,
}

impl RepaintManager {
    pub fn new(max_repaint_rate: Duration, context: Context) -> RepaintManager {
        let running = Arc::new(AtomicBool::new(true));
        let repaint_requested = RepaintRequest {
            requested: Arc::new(AtomicBool::new(false)),
        };
        let measured_fps = Arc::new(AtomicU32::new(0));
        let hndl = {
            let running = running.clone();
            let requested = repaint_requested.clone();
            let measured_fps = measured_fps.clone();
            std::thread::spawn(move || {
                let mut last_repaint = UnixTimestamp::now();
                let mut last_metrics = UnixTimestamp::now();
                let mut per_second = 0;
                while running.load(Ordering::Relaxed) {
                    let next = last_repaint + max_repaint_rate;
                    let now = UnixTimestamp::now();
                    let remaining = next - now;
                    if remaining.as_seconds_f64() <= 0.0 {
                        if requested.take() {
                            context.request_repaint();
                        }
                        last_repaint = now;
                        per_second += 1;
                    } else {
                        std::thread::sleep(remaining.into());
                    }
                    if now - last_metrics >= Duration::from_seconds(1) {
                        // println!("{per_second}");
                        per_second = 0;
                        measured_fps.store(per_second, Ordering::Relaxed);
                        last_metrics = now;
                    }
                }
            })
        };
        Self {
            repaint_requested,
            running,
            measured_fps,
            hndl: Some(hndl),
        }
    }

    pub fn requester(&self) -> RepaintRequest {
        self.repaint_requested.clone()
    }

    pub fn measured_fps(&self) -> Arc<AtomicU32> {
        self.measured_fps.clone()
    }
}
impl Drop for RepaintManager {
    fn drop(&mut self) {
        self.running.store(false, Ordering::Relaxed);
        if let Some(hndl) = self.hndl.take() {
            let _ = hndl.join();
        }
    }
}

#[derive(Clone)]
pub struct RepaintRequest {
    requested: Arc<AtomicBool>,
}
impl RepaintRequest {
    pub fn request(&self) {
        self.requested.store(true, Ordering::SeqCst);
    }
    pub fn is_requested(&self) -> bool {
        self.requested.load(Ordering::SeqCst)
    }
    pub fn take(&self) -> bool {
        self.requested.swap(false, Ordering::SeqCst)
    }
}