Add a Buffer::age() method

Fixes https://github.com/rust-windowing/softbuffer/issues/90.
This commit is contained in:
Ian Douglas Scott 2023-04-20 18:52:34 -07:00
parent c1d6716eec
commit 29b3f4a978
8 changed files with 126 additions and 20 deletions

View file

@ -87,6 +87,10 @@ impl<'a> BufferImpl<'a> {
&mut self.buffer &mut self.buffer
} }
pub fn age(&self) -> u8 {
0
}
pub fn present(self) -> Result<(), SoftBufferError> { pub fn present(self) -> Result<(), SoftBufferError> {
let data_provider = CGDataProvider::from_buffer(Arc::new(Buffer(self.buffer))); let data_provider = CGDataProvider::from_buffer(Arc::new(Buffer(self.buffer)));
let image = CGImage::new( let image = CGImage::new(

View file

@ -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> { pub fn present(self) -> Result<(), SoftBufferError> {
match self { match self {
$( $(
@ -326,7 +335,7 @@ impl Surface {
/// Return a [`Buffer`] that the next frame should be rendered into. The size must /// 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 /// 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<Buffer, SoftBufferError> { pub fn buffer_mut(&mut self) -> Result<Buffer, SoftBufferError> {
Ok(Buffer { Ok(Buffer {
buffer_impl: self.surface_impl.buffer_mut()?, buffer_impl: self.surface_impl.buffer_mut()?,
@ -377,6 +386,16 @@ pub struct Buffer<'a> {
} }
impl<'a> 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. /// Presents buffer to the window.
/// ///
/// # Platform dependent behavior /// # Platform dependent behavior

View file

@ -57,6 +57,7 @@ pub struct OrbitalImpl {
handle: OrbitalWindowHandle, handle: OrbitalWindowHandle,
width: u32, width: u32,
height: u32, height: u32,
presented: bool,
} }
impl OrbitalImpl { impl OrbitalImpl {
@ -65,12 +66,18 @@ impl OrbitalImpl {
handle, handle,
width: 0, width: 0,
height: 0, height: 0,
presented: false,
}) })
} }
pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
self.width = width.get(); let width = width.get();
self.height = height.get(); let height = height.get();
if width != self.width && height != self.height {
self.presented = false;
self.width = width;
self.height = height;
}
Ok(()) 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> { pub fn present(self) -> Result<(), SoftBufferError> {
match self.pixels { match self.pixels {
Pixels::Mapping(mapping) => { Pixels::Mapping(mapping) => {
drop(mapping); drop(mapping);
syscall::fsync(self.imp.window_fd()).expect("failed to sync orbital window"); syscall::fsync(self.imp.window_fd()).expect("failed to sync orbital window");
self.imp.presented = true;
} }
Pixels::Buffer(buffer) => { Pixels::Buffer(buffer) => {
self.imp self.imp

View file

@ -90,6 +90,7 @@ pub(super) struct WaylandBuffer {
width: i32, width: i32,
height: i32, height: i32,
released: Arc<AtomicBool>, released: Arc<AtomicBool>,
pub age: u8,
} }
impl WaylandBuffer { impl WaylandBuffer {
@ -125,6 +126,7 @@ impl WaylandBuffer {
width, width,
height, height,
released, released,
age: 0,
} }
} }

View file

@ -125,9 +125,14 @@ impl WaylandImpl {
)); ));
}; };
Ok(BufferImpl(util::BorrowStack::new(self, |buffer| { let age = self.buffers.as_mut().unwrap().1.age;
Ok(unsafe { buffer.buffers.as_mut().unwrap().1.mapped_mut() })
})?)) 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> { fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> {
@ -138,6 +143,11 @@ impl WaylandImpl {
.dispatch_pending(&mut State); .dispatch_pending(&mut State);
if let Some((front, back)) = &mut self.buffers { if let Some((front, back)) = &mut self.buffers {
front.age = 1;
if back.age != 0 {
back.age += 1;
}
// Swap front and back buffer // Swap front and back buffer
std::mem::swap(front, back); 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> { impl<'a> BufferImpl<'a> {
#[inline] #[inline]
pub fn pixels(&self) -> &[u32] { pub fn pixels(&self) -> &[u32] {
self.0.member() self.stack.member()
} }
#[inline] #[inline]
pub fn pixels_mut(&mut self) -> &mut [u32] { 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> { 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> { pub fn present(self) -> Result<(), SoftBufferError> {
let imp = self.0.into_container(); let imp = self.stack.into_container();
let (width, height) = imp let (width, height) = imp
.size .size
.expect("Must set size of surface before calling `present()`"); .expect("Must set size of surface before calling `present()`");

View file

@ -41,8 +41,14 @@ pub struct WebImpl {
/// The buffer that we're drawing to. /// The buffer that we're drawing to.
buffer: Vec<u32>, buffer: Vec<u32>,
/// Buffer has been presented.
buffer_presented: bool,
/// The current width of the canvas. /// The current width of the canvas.
width: u32, width: u32,
/// The current height of the canvas.
height: u32,
} }
impl WebImpl { impl WebImpl {
@ -70,7 +76,9 @@ impl WebImpl {
canvas, canvas,
ctx, ctx,
buffer: Vec::new(), buffer: Vec::new(),
buffer_presented: false,
width: 0, width: 0,
height: 0,
}) })
} }
@ -83,10 +91,15 @@ impl WebImpl {
let width = width.get(); let width = width.get();
let height = height.get(); let height = height.get();
self.buffer.resize(total_len(width, height), 0); if width != self.width || height != self.height {
self.canvas.set_width(width); self.buffer_presented = false;
self.canvas.set_height(height); self.buffer.resize(total_len(width, height), 0);
self.width = width; self.canvas.set_width(width);
self.canvas.set_height(height);
self.width = width;
self.height = height;
}
Ok(()) Ok(())
} }
@ -109,6 +122,14 @@ impl<'a> BufferImpl<'a> {
&mut self.imp.buffer &mut self.imp.buffer
} }
pub fn age(&self) -> u8 {
if self.imp.buffer_presented {
1
} else {
0
}
}
/// Push the buffer to the canvas. /// Push the buffer to the canvas.
pub fn present(self) -> Result<(), SoftBufferError> { pub fn present(self) -> Result<(), SoftBufferError> {
// Create a bitmap from the buffer. // 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. // 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.ctx.put_image_data(&image_data, 0.0, 0.0).unwrap();
self.imp.buffer_presented = true;
Ok(()) Ok(())
} }

View file

@ -27,6 +27,7 @@ struct Buffer {
pixels: NonNull<u32>, pixels: NonNull<u32>,
width: NonZeroI32, width: NonZeroI32,
height: NonZeroI32, height: NonZeroI32,
presented: bool,
} }
impl Drop for Buffer { impl Drop for Buffer {
@ -101,6 +102,7 @@ impl Buffer {
width, width,
height, height,
pixels, pixels,
presented: false,
} }
} }
@ -203,8 +205,8 @@ impl Win32Impl {
Ok(BufferImpl(self)) Ok(BufferImpl(self))
} }
fn present_with_damage(&self, damage: &[Rect]) -> Result<(), SoftBufferError> { fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> {
let buffer = self.buffer.as_ref().unwrap(); let buffer = self.buffer.as_mut().unwrap();
unsafe { unsafe {
for Rect { for Rect {
x, x,
@ -229,6 +231,7 @@ impl Win32Impl {
// Validate the window. // Validate the window.
Gdi::ValidateRect(self.window, ptr::null_mut()); Gdi::ValidateRect(self.window, ptr::null_mut());
} }
buffer.presented = true;
Ok(()) Ok(())
} }
@ -247,6 +250,13 @@ impl<'a> BufferImpl<'a> {
self.0.buffer.as_mut().unwrap().pixels_mut() 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> { pub fn present(self) -> Result<(), SoftBufferError> {
let imp = self.0; let imp = self.0;
let buffer = imp.buffer.as_ref().unwrap(); let buffer = imp.buffer.as_ref().unwrap();

View file

@ -107,6 +107,9 @@ pub struct X11Impl {
/// The buffer we draw to. /// The buffer we draw to.
buffer: Buffer, buffer: Buffer,
/// Buffer has been presented.
buffer_presented: bool,
/// The current buffer width. /// The current buffer width.
width: u16, width: u16,
@ -223,6 +226,7 @@ impl X11Impl {
gc, gc,
depth: geometry_reply.depth, depth: geometry_reply.depth,
buffer, buffer,
buffer_presented: false,
width: 0, width: 0,
height: 0, height: 0,
}) })
@ -255,6 +259,7 @@ impl X11Impl {
}))?; }))?;
if width != self.width || height != self.height { if width != self.width || height != self.height {
self.buffer_presented = false;
self.buffer self.buffer
.resize(&self.display.connection, width, height) .resize(&self.display.connection, width, height)
.swbuf_err("Failed to resize X11 buffer")?; .swbuf_err("Failed to resize X11 buffer")?;
@ -294,13 +299,21 @@ impl<'a> BufferImpl<'a> {
unsafe { self.0.buffer.buffer_mut() } 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. /// Push the buffer to the window.
pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
let imp = self.0; let imp = self.0;
log::trace!("present: window={:X}", imp.window); log::trace!("present: window={:X}", imp.window);
let result = match imp.buffer { match imp.buffer {
Buffer::Wire(ref wire) => { Buffer::Wire(ref wire) => {
// This is a suboptimal strategy, raise a stink in the debug logs. // This is a suboptimal strategy, raise a stink in the debug logs.
log::debug!("Falling back to non-SHM method for window drawing."); log::debug!("Falling back to non-SHM method for window drawing.");
@ -368,9 +381,12 @@ impl<'a> BufferImpl<'a> {
Ok(()) 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> { pub fn present(self) -> Result<(), SoftBufferError> {