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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use crate::Application;
use crate::EguiWgpuRenderer;
use egui::Context;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;
use wayland_client::Connection;
use wayland_client::QueueHandle;
use wayland_client::protocol::wl_surface::WlSurface;
enum EguiWgpuRendererThreadCommand {
Render,
// ReconfigureSurface { width: u32, height: u32 },
RequestFrame,
}
struct RenderInput {
fulloutput: egui::FullOutput,
width: u32,
height: u32,
pixels_per_point: f32,
}
pub struct EguiWgpuRendererThread {
#[allow(dead_code)]
thread: std::thread::JoinHandle<()>,
render_input: Arc<Mutex<Option<RenderInput>>>,
tx: std::sync::mpsc::SyncSender<EguiWgpuRendererThreadCommand>,
}
impl EguiWgpuRendererThread {
pub fn new(
egui_context: &Context,
wl_surface: &WlSurface,
_qh: &QueueHandle<Application>,
conn: &Connection,
) -> EguiWgpuRendererThread {
let (tx, rx) = std::sync::mpsc::sync_channel(9999);
let render_input = Arc::new(Mutex::new(None));
let tx_ = tx.clone();
egui_context.set_request_repaint_callback(move |_info| {
if _info.delay == Duration::ZERO {
let _ = tx_.send(EguiWgpuRendererThreadCommand::RequestFrame);
} else {
// This is not implemented yet
}
});
// Moved to thread:
let wl_surface_ = wl_surface.clone();
let conn_ = conn.clone();
let egui_context = egui_context.clone();
let render_input_ = render_input.clone();
let thread = std::thread::spawn(move || {
let mut renderer = EguiWgpuRenderer::new(&egui_context, &wl_surface_, &conn_);
let mut last_render = std::time::Instant::now()
.checked_sub(Duration::from_secs(3))
.unwrap();
loop {
// Block until next command arrives
if let Ok(command) = rx.recv() {
match command {
EguiWgpuRendererThreadCommand::Render => {
if last_render.elapsed().as_millis() < 16 {
// Limit to ~60 FPS
println!("Skipping frame to limit FPS");
// This is not exactly working, because it should schedule the next
// frame if render calls stop.
continue;
}
// Get render input
let render_input = {
if let Ok(mut guard) = render_input_.lock() {
guard.take()
} else {
None
}
};
// Render if we have render input available
if let Some(RenderInput {
fulloutput,
width,
height,
pixels_per_point,
}) = render_input
{
renderer.render_to_wgpu(
fulloutput,
width,
height,
pixels_per_point,
);
last_render = std::time::Instant::now();
}
}
// EguiWgpuRendererThreadCommand::ReconfigureSurface { .. } => {
// // renderer.reconfigure_surface(width, height);
// }
EguiWgpuRendererThreadCommand::RequestFrame => {
// renderer.request_frame();
}
}
}
}
});
EguiWgpuRendererThread {
thread,
tx,
render_input,
}
}
/// Resize and reconfigure the WGPU surface
pub fn reconfigure_surface(&mut self, _width: u32, _height: u32) {
// let _ = self
// .tx
// .send(EguiWgpuRendererThreadCommand::ReconfigureSurface { width,
// height });
}
/// Renders EGUI output to the WGPU surface
pub fn render_to_wgpu(
&mut self,
egui_fulloutput: egui::FullOutput,
width: u32,
height: u32,
pixels_per_point: f32,
) {
// If skipping frame rendering is implemented, then fulloutputs need to be
// combined with append:
//
// older_egui_fulloutput.append(egui_fulloutput);
// Append to any existing render input
if let Ok(mut render_input) = self.render_input.lock() {
if let Some(input) = render_input.as_mut() {
input.fulloutput.append(egui_fulloutput.clone());
} else {
*render_input = Some(RenderInput {
fulloutput: egui_fulloutput.clone(),
width,
height,
pixels_per_point,
});
}
}
let _ = self.tx.send(EguiWgpuRendererThreadCommand::Render);
}
/// Request frame callback and commit (must be called after render
/// completes) This is thread-safe as it queues the operation in the
/// renderer thread
pub fn request_frame(&mut self) {
let _ = self.tx.send(EguiWgpuRendererThreadCommand::RequestFrame);
}
}