← luna

Commit 9154b90

3
files
+118
added
-9
deleted
@@ -44,6 +44,8 @@ fn main() -> Result<(), Box<dyn Error>> {
44 44 let mut timer = 0.0f64;
45 45 // Lista okien z kompozytora (foreign-toplevel) — własne połączenie + wątek.
46 46 let toplevels = luna::toplevels::Toplevels::spawn();
47 + // Sygnatura stanu — przerysowujemy tylko, gdy coś się zmieniło.
48 + let mut last_sig: (bool, String, u64) = (false, String::new(), u64::MAX);
47 49
48 50 run(cfg, move |frame: &mut Frame| {
49 51 let dt = frame.delta_time();
@@ -206,7 +208,20 @@ fn main() -> Result<(), Box<dyn Error>> {
206 208 Rect::from_xywh(0.0, THICKNESS + 8.0, size.width, size.height - THICKNESS),
207 209 );
208 210
209 - frame.request_frame();
211 + // Przerysowujemy tylko, gdy coś się zmieniło — inaczej powłoka nie commituje
212 + // bufora 1920x680 przy każdej klatce (mniej pracy dla kompozytora i CPU).
213 + let mut th: u64 = state.tasks.len() as u64;
214 + for t in &state.tasks {
215 + th = th
216 + .wrapping_mul(1_099_511_628_211)
217 + .wrapping_add(u64::from(t.active) ^ t.title.len() as u64);
218 + }
219 + let sig = (open, state.clock.clone(), th);
220 + let animating = osd.visible() || notes.visible();
221 + if !frame.events.is_empty() || animating || open || sig != last_sig {
222 + frame.request_frame();
223 + }
224 + last_sig = sig;
210 225 })
211 226 }
212 227
@@ -58,6 +58,11 @@ impl Notifications {
58 58 Self::default()
59 59 }
60 60
61 + /// Czy stos powiadomień nie jest pusty (potrzebna animacja/przerysowanie).
62 + pub fn visible(&self) -> bool {
63 + !self.items.is_empty()
64 + }
65 +
61 66 /// Dodaje powiadomienie na wierzch stosu.
62 67 pub fn push(&mut self, n: Notification) {
63 68 self.items.push((n, 0.0));
@@ -139,11 +139,21 @@ pub fn spawn_drm(model: WindowModel, cursor: CursorModel) {
139 139 let mut frames = 0u64;
140 140 let (mut ms_render, mut ms_present, mut ms_wait) = (0.0f64, 0.0f64, 0.0f64);
141 141 let mut report = std::time::Instant::now();
142 + let mut last_sig: Option<u64> = None;
142 143 loop {
144 + // Przerysowujemy tylko, gdy coś się zmieniło (okna albo kursor). Inaczej
145 + // śpimy — mniej CPU i brak zbędnych page-flipów, przy płynnym kursorze.
146 + let cur = *cursor.lock_or_recover();
147 + let sig = scene_signature(&model, cur);
148 + if last_sig == Some(sig) {
149 + std::thread::sleep(Duration::from_millis(4));
150 + continue;
151 + }
152 + last_sig = Some(sig);
153 +
143 154 for i in 0..sizes.len() {
144 155 let (w, h) = sizes[i];
145 156 let (ox, oy) = origins[i];
146 - let cur = *cursor.lock_or_recover();
147 157
148 158 let t = std::time::Instant::now();
149 159 let mut scene = Scene::new();
@@ -159,7 +169,8 @@ pub fn spawn_drm(model: WindowModel, cursor: CursorModel) {
159 169 ms_render += t.elapsed().as_secs_f64() * 1000.0;
160 170
161 171 let t = std::time::Instant::now();
162 - if !backend.present(i, renderers[i].data()) && !flip_error_logged {
172 + let flipped = backend.present(i, renderers[i].data());
173 + if !flipped && !flip_error_logged {
163 174 eprintln!(
164 175 "{}",
165 176 libluna_i18n::tr("DRM page-flip failed (screen may stay blank)")
@@ -168,14 +179,15 @@ pub fn spawn_drm(model: WindowModel, cursor: CursorModel) {
168 179 }
169 180 ms_present += t.elapsed().as_secs_f64() * 1000.0;
170 181
171 - let t = std::time::Instant::now();
172 - backend.wait_flip();
173 - ms_wait += t.elapsed().as_secs_f64() * 1000.0;
182 + // Czekamy na flip tylko, gdy został zakolejkowany (inaczej `read`
183 + // mógłby zablokować się na zawsze).
184 + if flipped {
185 + let t = std::time::Instant::now();
186 + backend.wait_flip();
187 + ms_wait += t.elapsed().as_secs_f64() * 1000.0;
188 + }
174 189 }
175 - // `wait_flip` i tak synchronizuje z vblankiem, więc nie dosypujemy snu
176 - // (wcześniejsze 16 ms samo ograniczało do ~20 fps).
177 190 frames += 1;
178 -
179 191 if stats && report.elapsed().as_secs_f64() >= 2.0 {
180 192 let secs = report.elapsed().as_secs_f64();
181 193 let per_frame = (frames * sizes.len() as u64).max(1) as f64;
@@ -196,6 +208,33 @@ pub fn spawn_drm(model: WindowModel, cursor: CursorModel) {
196 208 });
197 209 }
198 210
211 +/// Sygnatura sceny (okna + kursor) — do wykrywania, że nic się nie zmieniło.
212 +fn scene_signature(model: &WindowModel, cursor: Option<Point>) -> u64 {
213 + let mut h: u64 = 0xcbf2_9ce4_8422_2325;
214 + let mut mix = |v: u64| {
215 + h ^= v;
216 + h = h.wrapping_mul(0x0000_0100_0000_01b3);
217 + };
218 + {
219 + let windows = model.lock_or_recover();
220 + mix(windows.len() as u64);
221 + for w in windows.iter() {
222 + mix(w.x.to_bits());
223 + mix(w.y.to_bits());
224 + mix(w.z as u64);
225 + mix(w.width as u64);
226 + mix(w.height as u64);
227 + mix(u64::from(w.mapped) | (u64::from(w.minimized) << 1) | (u64::from(w.layer) << 2));
228 + mix(std::sync::Arc::as_ptr(&w.data) as u64);
229 + }
230 + }
231 + if let Some(c) = cursor {
232 + mix(c.x.to_bits());
233 + mix(c.y.to_bits());
234 + }
235 + h
236 +}
237 +
199 238 /// Buduje scenę okien w rozmiarze wyjścia, przesuniętą o `(ox, oy)` (origin monitora).
200 239 fn build_scene(
201 240 scene: &mut Scene,
@@ -357,4 +396,54 @@ mod tests {
357 396 t.elapsed().as_secs_f64() * 1000.0 / n as f64
358 397 );
359 398 }
399 +
400 + #[test]
401 + #[ignore = "benchmark reczny"]
402 + fn bench_1080p_with_panel() {
403 + use std::sync::{Arc, Mutex};
404 + use std::time::Instant;
405 + let data = Arc::new(libluna_render::ImageData::new(
406 + 1920,
407 + 680,
408 + vec![128u8; 1920 * 680 * 4],
409 + ));
410 + let windows = vec![Window {
411 + x: 0.0,
412 + y: 0.0,
413 + width: 1920,
414 + height: 680,
415 + z: 1_000_000,
416 + title: String::new(),
417 + popup: false,
418 + layer: true,
419 + mapped: true,
420 + minimized: false,
421 + maximized: false,
422 + fullscreen: false,
423 + saved: None,
424 + data,
425 + }];
426 + let model: WindowModel = Arc::new(Mutex::new(windows));
427 + let bg = Color::from_rgba8(0x1e, 0x20, 0x28, 0xff);
428 + let mut renderer = SoftwareRenderer::new(1920, 1080);
429 + let n = 20;
430 + // Sam render sceny:
431 + let t = Instant::now();
432 + for _ in 0..n {
433 + let mut scene = Scene::new();
434 + build_scene(
435 + &mut scene,
436 + Size::new(1920.0, 1080.0),
437 + 0.0,
438 + 0.0,
439 + &model,
440 + Some(Point::new(100.0, 100.0)),
441 + );
442 + renderer.draw(&scene, 1.0, bg);
443 + }
444 + println!(
445 + "render 1080p + panel 1920x680: {:.2} ms/klatke",
446 + t.elapsed().as_secs_f64() * 1000.0 / n as f64
447 + );
448 + }
360 449 }