diff --git a/glutin/src/application.rs b/glutin/src/application.rs
index 4f36114c2a..7e8fc0a1d2 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,13 +94,18 @@ 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 => {
if state.is_queue_empty() {
return;
}
- let command = runtime.enter(|| {
+ let (command, new_damage) = runtime.enter(|| {
state.update(
clipboard.as_ref().map(|c| c as _),
viewport.logical_size(),
@@ -108,6 +114,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);
@@ -146,38 +163,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).snap())
+ .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 bb4281988b..2eb75ad266 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
@@ -99,7 +99,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(
@@ -108,7 +109,7 @@ where
bounds: Size,
renderer: &mut P::Renderer,
debug: &mut Debug,
- ) -> Option> {
+ ) -> (Option>, Option>) {
let mut user_interface = build_user_interface(
&mut self.program,
self.cache.take().unwrap(),
@@ -128,12 +129,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 :^)
@@ -159,12 +163,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 a5d004078f..5849ebddd9 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -154,7 +154,7 @@ pub fn run(
return;
}
- let command = runtime.enter(|| {
+ let (command, _damage) = runtime.enter(|| {
state.update(
clipboard.as_ref().map(|c| c as _),
viewport.logical_size(),