diff --git a/src/cg.rs b/src/cg.rs index f88db20..f47e639 100644 --- a/src/cg.rs +++ b/src/cg.rs @@ -87,6 +87,10 @@ impl<'a> BufferImpl<'a> { &mut self.buffer } + pub fn age(&self) -> u8 { + 0 + } + pub fn present(self) -> Result<(), SoftBufferError> { let data_provider = CGDataProvider::from_buffer(Arc::new(Buffer(self.buffer))); let image = CGImage::new( diff --git a/src/lib.rs b/src/lib.rs index 053482e..aab88cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,6 +124,15 @@ macro_rules! make_dispatch { } } + pub fn age(&self) -> u8 { + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.age(), + )* + } + } + pub fn present(self) -> Result<(), SoftBufferError> { match self { $( @@ -326,7 +335,7 @@ impl Surface { /// Return a [`Buffer`] that the next frame should be rendered into. The size must /// be set with [`Surface::resize`] first. The initial contents of the buffer may be zeroed, or - /// may contain a previous frame. + /// may contain a previous frame. Call [`Buffer::age`] to determine this. pub fn buffer_mut(&mut self) -> Result { Ok(Buffer { buffer_impl: self.surface_impl.buffer_mut()?, @@ -377,6 +386,16 @@ pub struct Buffer<'a> { } impl<'a> Buffer<'a> { + /// Is age is the number of frames ago this buffer was last presented. So if the value is + /// `1`, it is the same as the last frame, and if it is `2`, it is the same as the frame + /// before that (for backends using double buffering). If the value is `0`, it is a new + /// buffer that has unspecified contents. + /// + /// This can be used to update only a portion of the buffer. + pub fn age(&self) -> u8 { + self.buffer_impl.age() + } + /// Presents buffer to the window. /// /// # Platform dependent behavior diff --git a/src/orbital.rs b/src/orbital.rs index a38d0c3..7ad792b 100644 --- a/src/orbital.rs +++ b/src/orbital.rs @@ -57,6 +57,7 @@ pub struct OrbitalImpl { handle: OrbitalWindowHandle, width: u32, height: u32, + presented: bool, } impl OrbitalImpl { @@ -65,12 +66,18 @@ impl OrbitalImpl { handle, width: 0, height: 0, + presented: false, }) } pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - self.width = width.get(); - self.height = height.get(); + let width = width.get(); + let height = height.get(); + if width != self.width && height != self.height { + self.presented = false; + self.width = width; + self.height = height; + } Ok(()) } @@ -172,11 +179,19 @@ impl<'a> BufferImpl<'a> { } } + pub fn age(&self) -> u8 { + match self.pixels { + Pixels::Mapping(_) if self.imp.presented => 1, + _ => 0, + } + } + pub fn present(self) -> Result<(), SoftBufferError> { match self.pixels { Pixels::Mapping(mapping) => { drop(mapping); syscall::fsync(self.imp.window_fd()).expect("failed to sync orbital window"); + self.imp.presented = true; } Pixels::Buffer(buffer) => { self.imp diff --git a/src/wayland/buffer.rs b/src/wayland/buffer.rs index e17961a..ed8855f 100644 --- a/src/wayland/buffer.rs +++ b/src/wayland/buffer.rs @@ -90,6 +90,7 @@ pub(super) struct WaylandBuffer { width: i32, height: i32, released: Arc, + pub age: u8, } impl WaylandBuffer { @@ -125,6 +126,7 @@ impl WaylandBuffer { width, height, released, + age: 0, } } diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 5922379..596907c 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -125,9 +125,14 @@ impl WaylandImpl { )); }; - Ok(BufferImpl(util::BorrowStack::new(self, |buffer| { - Ok(unsafe { buffer.buffers.as_mut().unwrap().1.mapped_mut() }) - })?)) + let age = self.buffers.as_mut().unwrap().1.age; + + Ok(BufferImpl { + stack: util::BorrowStack::new(self, |buffer| { + Ok(unsafe { buffer.buffers.as_mut().unwrap().1.mapped_mut() }) + })?, + age, + }) } fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { @@ -138,6 +143,11 @@ impl WaylandImpl { .dispatch_pending(&mut State); if let Some((front, back)) = &mut self.buffers { + front.age = 1; + if back.age != 0 { + back.age += 1; + } + // Swap front and back buffer std::mem::swap(front, back); @@ -170,25 +180,32 @@ impl WaylandImpl { } } -pub struct BufferImpl<'a>(util::BorrowStack<'a, WaylandImpl, [u32]>); +pub struct BufferImpl<'a> { + stack: util::BorrowStack<'a, WaylandImpl, [u32]>, + age: u8, +} impl<'a> BufferImpl<'a> { #[inline] pub fn pixels(&self) -> &[u32] { - self.0.member() + self.stack.member() } #[inline] pub fn pixels_mut(&mut self) -> &mut [u32] { - self.0.member_mut() + self.stack.member_mut() + } + + pub fn age(&self) -> u8 { + self.age } pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { - self.0.into_container().present_with_damage(damage) + self.stack.into_container().present_with_damage(damage) } pub fn present(self) -> Result<(), SoftBufferError> { - let imp = self.0.into_container(); + let imp = self.stack.into_container(); let (width, height) = imp .size .expect("Must set size of surface before calling `present()`"); diff --git a/src/web.rs b/src/web.rs index 76f7c19..48ceac3 100644 --- a/src/web.rs +++ b/src/web.rs @@ -41,8 +41,14 @@ pub struct WebImpl { /// The buffer that we're drawing to. buffer: Vec, + /// Buffer has been presented. + buffer_presented: bool, + /// The current width of the canvas. width: u32, + + /// The current height of the canvas. + height: u32, } impl WebImpl { @@ -70,7 +76,9 @@ impl WebImpl { canvas, ctx, buffer: Vec::new(), + buffer_presented: false, width: 0, + height: 0, }) } @@ -83,10 +91,15 @@ impl WebImpl { let width = width.get(); let height = height.get(); - self.buffer.resize(total_len(width, height), 0); - self.canvas.set_width(width); - self.canvas.set_height(height); - self.width = width; + if width != self.width || height != self.height { + self.buffer_presented = false; + self.buffer.resize(total_len(width, height), 0); + self.canvas.set_width(width); + self.canvas.set_height(height); + self.width = width; + self.height = height; + } + Ok(()) } @@ -109,6 +122,14 @@ impl<'a> BufferImpl<'a> { &mut self.imp.buffer } + pub fn age(&self) -> u8 { + if self.imp.buffer_presented { + 1 + } else { + 0 + } + } + /// Push the buffer to the canvas. pub fn present(self) -> Result<(), SoftBufferError> { // Create a bitmap from the buffer. @@ -151,6 +172,8 @@ impl<'a> BufferImpl<'a> { // This can only throw an error if `data` is detached, which is impossible. self.imp.ctx.put_image_data(&image_data, 0.0, 0.0).unwrap(); + self.imp.buffer_presented = true; + Ok(()) } diff --git a/src/win32.rs b/src/win32.rs index 1e02174..ba6a829 100644 --- a/src/win32.rs +++ b/src/win32.rs @@ -27,6 +27,7 @@ struct Buffer { pixels: NonNull, width: NonZeroI32, height: NonZeroI32, + presented: bool, } impl Drop for Buffer { @@ -101,6 +102,7 @@ impl Buffer { width, height, pixels, + presented: false, } } @@ -203,8 +205,8 @@ impl Win32Impl { Ok(BufferImpl(self)) } - fn present_with_damage(&self, damage: &[Rect]) -> Result<(), SoftBufferError> { - let buffer = self.buffer.as_ref().unwrap(); + fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { + let buffer = self.buffer.as_mut().unwrap(); unsafe { for Rect { x, @@ -229,6 +231,7 @@ impl Win32Impl { // Validate the window. Gdi::ValidateRect(self.window, ptr::null_mut()); } + buffer.presented = true; Ok(()) } @@ -247,6 +250,13 @@ impl<'a> BufferImpl<'a> { self.0.buffer.as_mut().unwrap().pixels_mut() } + pub fn age(&self) -> u8 { + match self.0.buffer.as_ref() { + Some(buffer) if buffer.presented => 1, + _ => 0, + } + } + pub fn present(self) -> Result<(), SoftBufferError> { let imp = self.0; let buffer = imp.buffer.as_ref().unwrap(); diff --git a/src/x11.rs b/src/x11.rs index fd70727..4c1abc7 100644 --- a/src/x11.rs +++ b/src/x11.rs @@ -107,6 +107,9 @@ pub struct X11Impl { /// The buffer we draw to. buffer: Buffer, + /// Buffer has been presented. + buffer_presented: bool, + /// The current buffer width. width: u16, @@ -223,6 +226,7 @@ impl X11Impl { gc, depth: geometry_reply.depth, buffer, + buffer_presented: false, width: 0, height: 0, }) @@ -255,6 +259,7 @@ impl X11Impl { }))?; if width != self.width || height != self.height { + self.buffer_presented = false; self.buffer .resize(&self.display.connection, width, height) .swbuf_err("Failed to resize X11 buffer")?; @@ -294,13 +299,21 @@ impl<'a> BufferImpl<'a> { unsafe { self.0.buffer.buffer_mut() } } + pub fn age(&self) -> u8 { + if self.0.buffer_presented { + 1 + } else { + 0 + } + } + /// Push the buffer to the window. pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { let imp = self.0; log::trace!("present: window={:X}", imp.window); - let result = match imp.buffer { + match imp.buffer { Buffer::Wire(ref wire) => { // This is a suboptimal strategy, raise a stink in the debug logs. log::debug!("Falling back to non-SHM method for window drawing."); @@ -368,9 +381,12 @@ impl<'a> BufferImpl<'a> { Ok(()) } } - }; + } + .swbuf_err("Failed to draw image to window")?; - result.swbuf_err("Failed to draw image to window") + imp.buffer_presented = true; + + Ok(()) } pub fn present(self) -> Result<(), SoftBufferError> {