Skip to content

Add Buffer::present_with_damage() and Buffer::age() #99

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/cg.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::SoftBufferError;
use crate::{Rect, SoftBufferError};
use core_graphics::base::{
kCGBitmapByteOrder32Little, kCGImageAlphaNoneSkipFirst, kCGRenderingIntentDefault,
};
Expand Down Expand Up @@ -92,6 +92,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(
Expand Down Expand Up @@ -124,6 +128,10 @@ impl<'a> BufferImpl<'a> {

Ok(())
}

pub fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> {
self.present()
}
}

impl Drop for CGImpl {
Expand Down
5 changes: 5 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ pub enum SoftBufferError {
height: NonZeroU32,
},

#[error(
"Damage rect {}x{} at ({}, {}) out of range for backend.", .rect.width, .rect.height, .rect.x, .rect.y,
)]
DamageOutOfRange { rect: crate::Rect },

#[error("Platform error")]
PlatformError(Option<String>, Option<Box<dyn Error>>),

Expand Down
57 changes: 56 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,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 {
$(
Expand All @@ -145,6 +154,15 @@ macro_rules! make_dispatch {
)*
}
}

pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.present_with_damage(damage),
)*
}
}
}
};
}
Expand Down Expand Up @@ -220,6 +238,19 @@ impl Context {
}
}

/// A rectangular region of the buffer coordinate space.
#[derive(Clone, Copy, Debug)]
pub struct Rect {
/// x coordinate of top left corner
pub x: u32,
/// y coordinate of top left corner
pub y: u32,
/// width
pub width: NonZeroU32,
/// height
pub height: NonZeroU32,
}

/// A surface for drawing to a window with software buffers.
pub struct Surface {
/// This is boxed so that `Surface` is the same size on every platform.
Expand Down Expand Up @@ -329,7 +360,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<Buffer, SoftBufferError> {
Ok(Buffer {
buffer_impl: self.surface_impl.buffer_mut()?,
Expand Down Expand Up @@ -380,6 +411,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
Expand All @@ -395,6 +436,20 @@ impl<'a> Buffer<'a> {
pub fn present(self) -> Result<(), SoftBufferError> {
self.buffer_impl.present()
}

/// Presents buffer to the window, with damage regions.
///
/// # Platform dependent behavior
///
/// Supported on:
/// - Wayland
/// - X, when XShm is available
/// - Win32
///
/// Otherwise this is equivalent to [`Self::present`].
pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
self.buffer_impl.present_with_damage(damage)
}
}

impl<'a> ops::Deref for Buffer<'a> {
Expand Down
25 changes: 22 additions & 3 deletions src/orbital.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use raw_window_handle::OrbitalWindowHandle;
use std::{cmp, num::NonZeroU32, slice, str};

use crate::SoftBufferError;
use crate::{Rect, SoftBufferError};

struct OrbitalMap {
address: usize,
Expand Down Expand Up @@ -57,6 +57,7 @@ pub struct OrbitalImpl {
handle: OrbitalWindowHandle,
width: u32,
height: u32,
presented: bool,
}

impl OrbitalImpl {
Expand All @@ -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(())
}

Expand Down Expand Up @@ -177,11 +184,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
Expand All @@ -191,4 +206,8 @@ impl<'a> BufferImpl<'a> {

Ok(())
}

pub fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> {
self.present()
}
}
2 changes: 2 additions & 0 deletions src/wayland/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ pub(super) struct WaylandBuffer {
width: i32,
height: i32,
released: Arc<AtomicBool>,
pub age: u8,
}

impl WaylandBuffer {
Expand Down Expand Up @@ -125,6 +126,7 @@ impl WaylandBuffer {
width,
height,
released,
age: 0,
}
}

Expand Down
119 changes: 77 additions & 42 deletions src/wayland/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{error::SwResultExt, util, SoftBufferError};
use crate::{error::SwResultExt, util, Rect, SoftBufferError};
use raw_window_handle::{WaylandDisplayHandle, WaylandWindowHandle};
use std::{
cell::RefCell,
Expand Down Expand Up @@ -125,72 +125,107 @@ 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,
})
}

/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
Err(SoftBufferError::Unimplemented)
}
}

pub struct BufferImpl<'a>(util::BorrowStack<'a, WaylandImpl, [u32]>);

impl<'a> BufferImpl<'a> {
#[inline]
pub fn pixels(&self) -> &[u32] {
self.0.member()
}

#[inline]
pub fn pixels_mut(&mut self) -> &mut [u32] {
self.0.member_mut()
}

pub fn present(self) -> Result<(), SoftBufferError> {
let imp = self.0.into_container();

let (width, height) = imp
.size
.expect("Must set size of surface before calling `present()`");

let _ = imp
fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> {
let _ = self
.display
.event_queue
.borrow_mut()
.dispatch_pending(&mut State);

if let Some((front, back)) = &mut imp.buffers {
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);

front.attach(&imp.surface);

// FIXME: Proper damaging mechanism.
//
// In order to propagate changes on compositors which track damage, for now damage the entire surface.
if imp.surface.version() < 4 {
// FIXME: Accommodate scale factor since wl_surface::damage is in terms of surface coordinates while
// wl_surface::damage_buffer is in buffer coordinates.
//
// i32::MAX is a valid damage box (most compositors interpret the damage box as "the entire surface")
imp.surface.damage(0, 0, i32::MAX, i32::MAX);
front.attach(&self.surface);

// Like Mesa's EGL/WSI implementation, we damage the whole buffer with `i32::MAX` if
// the compositor doesn't support `damage_buffer`.
// https://bugs.freedesktop.org/show_bug.cgi?id=78190
if self.surface.version() < 4 {
self.surface.damage(0, 0, i32::MAX, i32::MAX);
} else {
// Introduced in version 4, it is an error to use this request in version 3 or lower.
imp.surface.damage_buffer(0, 0, width.get(), height.get());
for rect in damage {
// Introduced in version 4, it is an error to use this request in version 3 or lower.
let (x, y, width, height) = (|| {
Some((
i32::try_from(rect.x).ok()?,
i32::try_from(rect.y).ok()?,
i32::try_from(rect.width.get()).ok()?,
i32::try_from(rect.height.get()).ok()?,
))
})()
.ok_or(SoftBufferError::DamageOutOfRange { rect: *rect })?;
self.surface.damage_buffer(x, y, width, height);
}
}

imp.surface.commit();
self.surface.commit();
}

let _ = imp.display.event_queue.borrow_mut().flush();
let _ = self.display.event_queue.borrow_mut().flush();

Ok(())
}
}

pub struct BufferImpl<'a> {
stack: util::BorrowStack<'a, WaylandImpl, [u32]>,
age: u8,
}

impl<'a> BufferImpl<'a> {
#[inline]
pub fn pixels(&self) -> &[u32] {
self.stack.member()
}

#[inline]
pub fn pixels_mut(&mut self) -> &mut [u32] {
self.stack.member_mut()
}

pub fn age(&self) -> u8 {
self.age
}

pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
self.stack.into_container().present_with_damage(damage)
}

pub fn present(self) -> Result<(), SoftBufferError> {
let imp = self.stack.into_container();
let (width, height) = imp
.size
.expect("Must set size of surface before calling `present()`");
imp.present_with_damage(&[Rect {
x: 0,
y: 0,
// We know width/height will be non-negative
width: width.try_into().unwrap(),
height: height.try_into().unwrap(),
}])
}
}

impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {
fn event(
_: &mut State,
Expand Down
Loading