From 45330d1d1cbe2c6afce60fe38ff2366b13e961d9 Mon Sep 17 00:00:00 2001 From: Greg V Date: Mon, 1 Jun 2020 18:00:39 +0300 Subject: [PATCH] [WIP] Implement damage tracking in iced_glutin --- glutin/src/application.rs | 85 ++++++++++++++++++++++++++----------- native/src/program/state.rs | 25 +++++++---- native/src/renderer.rs | 6 +-- winit/src/application.rs | 2 +- 4 files changed, 81 insertions(+), 37 deletions(-) diff --git a/glutin/src/application.rs b/glutin/src/application.rs index c777a13b29..b7bbeb9ad7 100644 --- a/glutin/src/application.rs +++ b/glutin/src/application.rs @@ -2,6 +2,7 @@ use crate::{mouse, Executor, Runtime, Size}; use iced_graphics::window; use iced_graphics::Viewport; +use iced_native::Damage; use iced_winit::application; use iced_winit::conversion; use iced_winit::{Clipboard, Debug, Proxy, Settings}; @@ -93,9 +94,14 @@ pub fn run( ); debug.startup_finished(); + let mut damage = <::Renderer + as iced_native::Renderer>::Output::default().damage(&state.primitive()); + + let mut first = true; + event_loop.run(move |event, _, control_flow| match event { event::Event::MainEventsCleared => { - let command = runtime.enter(|| { + let (command, new_damage) = runtime.enter(|| { state.update( clipboard.as_ref().map(|c| c as _), viewport.logical_size(), @@ -104,6 +110,17 @@ pub fn run( ) }); + eprintln!("{:?}", new_damage); + if !first { + damage = None; + if let Some(dmg) = new_damage { + if dmg.len() > 0 { + damage = Some(dmg); + } + } + } + first = false; + // If the application was updated if let Some(command) = command { runtime.spawn(command); @@ -142,38 +159,58 @@ pub fn run( state.queue_message(message); } event::Event::RedrawRequested(_) => { - debug.render_started(); - - if resized { + if let Some(damage) = damage.take() { + debug.render_started(); let physical_size = viewport.physical_size(); - context.resize(glutin::dpi::PhysicalSize::new( - physical_size.width, - physical_size.height, - )); + if resized { + context.resize(glutin::dpi::PhysicalSize::new( + physical_size.width, + physical_size.height, + )); - compositor.resize_viewport(physical_size); + compositor.resize_viewport(physical_size); - resized = false; - } + resized = false; + } - let new_mouse_interaction = compositor.draw( - &mut renderer, - &viewport, - state.primitive(), - &debug.overlay(), - ); + let new_mouse_interaction = compositor.draw( + &mut renderer, + &viewport, + state.primitive(), + &debug.overlay(), + ); - context.swap_buffers().expect("Swap buffers"); + if context.swap_buffers_with_damage_supported() { + let scale = viewport.scale_factor() as f32; + let rects = damage + .iter() + .map(|dmg| (*dmg * scale).round()) + .map(|dmg| glutin::Rect { + x: dmg.x, + y: physical_size + .height + .saturating_sub(dmg.y + dmg.height), + width: dmg.width.min(physical_size.width), + height: dmg.height.min(physical_size.height), + }) + .collect::>(); + context + .swap_buffers_with_damage(&rects[..]) + .expect("Swap buffers with damage"); + } else { + context.swap_buffers().expect("Swap buffers"); + } - debug.render_finished(); + debug.render_finished(); - if new_mouse_interaction != mouse_interaction { - context.window().set_cursor_icon( - conversion::mouse_interaction(new_mouse_interaction), - ); + if new_mouse_interaction != mouse_interaction { + context.window().set_cursor_icon( + conversion::mouse_interaction(new_mouse_interaction), + ); - mouse_interaction = new_mouse_interaction; + mouse_interaction = new_mouse_interaction; + } } // TODO: Handle animations! diff --git a/native/src/program/state.rs b/native/src/program/state.rs index 8716d8b91c..faaa3681be 100644 --- a/native/src/program/state.rs +++ b/native/src/program/state.rs @@ -1,6 +1,6 @@ use crate::{ - Cache, Clipboard, Command, Debug, Event, Program, Renderer, Size, - UserInterface, + Cache, Clipboard, Command, Damage, Debug, Event, Program, Rectangle, + Renderer, Size, UserInterface, }; /// The execution state of a [`Program`]. It leverages caching, event @@ -92,7 +92,8 @@ where /// the widgets of the linked [`Program`] if necessary. /// /// Returns the [`Command`] obtained from [`Program`] after updating it, - /// only if an update was necessary. + /// only if an update was necessary; and a damage rectangle if any pixels + /// changed from the previous frame. /// /// [`Program`]: trait.Program.html pub fn update( @@ -101,9 +102,9 @@ where bounds: Size, renderer: &mut P::Renderer, debug: &mut Debug, - ) -> Option> { + ) -> (Option>, Option>) { if self.queued_events.is_empty() && self.queued_messages.is_empty() { - return None; + return (None, None); } let mut user_interface = build_user_interface( @@ -125,12 +126,15 @@ where if messages.is_empty() { debug.draw_started(); - self.primitive = user_interface.draw(renderer); + let primitive = user_interface.draw(renderer); debug.draw_finished(); self.cache = Some(user_interface.into_cache()); - None + let damage = self.primitive.damage(&primitive); + self.primitive = primitive; + + (None, damage) } else { // When there are messages, we are forced to rebuild twice // for now :^) @@ -156,12 +160,15 @@ where ); debug.draw_started(); - self.primitive = user_interface.draw(renderer); + let primitive = user_interface.draw(renderer); debug.draw_finished(); self.cache = Some(user_interface.into_cache()); - Some(commands) + let damage = self.primitive.damage(&primitive); + self.primitive = primitive; + + (Some(commands), damage) } } } diff --git a/native/src/renderer.rs b/native/src/renderer.rs index b51bd00c49..0a1e3f8971 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -31,11 +31,11 @@ use crate::{layout, Element, Rectangle}; /// updated between two frames. pub trait Damage { /// Calculates damage between two frames - fn damage(&self, other: &Self) -> Option; + fn damage(&self, other: &Self) -> Option>; } impl Damage for () { - fn damage(&self, _other: &Self) -> Option { + fn damage(&self, _other: &Self) -> Option> { None } } @@ -44,7 +44,7 @@ impl Damage for (T, U) where T: Damage, { - fn damage(&self, other: &Self) -> Option { + fn damage(&self, other: &Self) -> Option> { self.0.damage(&other.0) } } diff --git a/winit/src/application.rs b/winit/src/application.rs index 73ac72b25b..1104ac94c6 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -150,7 +150,7 @@ pub fn run( event_loop.run(move |event, _, control_flow| match event { event::Event::MainEventsCleared => { - let command = runtime.enter(|| { + let (command, _damage) = runtime.enter(|| { state.update( clipboard.as_ref().map(|c| c as _), viewport.logical_size(),