Skip to content
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

Owned pixel buffer for no-copy presentation #65

Merged
merged 26 commits into from
Apr 6, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c12cba8
wayland: Use `mmap`
ids1024 Jan 10, 2023
8577275
WIP no-copy in public API
ids1024 Jan 11, 2023
7a5f61d
win32: Use owned buffer and `Gdi:BitBlt`
ids1024 Jan 11, 2023
de2f37d
Implement X11 owned buffer
notgull Jan 11, 2023
ce59528
fmt
notgull Jan 11, 2023
579656a
Add owned buffer for web
notgull Jan 11, 2023
256ff9e
cg: Port to owned buffer
ids1024 Jan 13, 2023
8c3275c
win32: Fix length of buffer slice
ids1024 Jan 14, 2023
b16e00e
Port more examples to owned buffers
ids1024 Jan 14, 2023
39cad4e
Merge remote-tracking branch 'origin/master' into owned-buffer
ids1024 Jan 18, 2023
5e8a09d
orbital: Implement new API (but with copy)
ids1024 Jan 18, 2023
1c08169
cg: Allocate correct buffer size
ids1024 Jan 18, 2023
b963c43
examples: Use owned buffer API for `fruit` and `winit_wrong_sized_buf…
ids1024 Jan 18, 2023
a922a39
Remove `set_buffer`, and move its documentation to the new methods
ids1024 Jan 19, 2023
1bb74dd
Make `Context`/`Surface` consistently `!Send`/`!Sync`
ids1024 Jan 19, 2023
34a2796
Don't use thread-safe types
ids1024 Jan 20, 2023
82ca6be
Make `Surface::present()` return a `Result`
ids1024 Jan 20, 2023
9ab6807
Merge remote-tracking branch 'origin/master' into owned-buffer
ids1024 Jan 25, 2023
bb8b1e5
examples: Unwrap present errors
ids1024 Jan 25, 2023
527e170
Make `resize` and `buffer_mut` return `Result`s
ids1024 Jan 25, 2023
e38809e
Separate `Buffer` type, with a `present` method
ids1024 Jan 26, 2023
a4931bf
x11: Use `swbuf_err` instead of `map_err`
ids1024 Feb 23, 2023
109e884
Make `deref` and `deref_mut` as `#[inline]`
ids1024 Feb 23, 2023
8d35f0e
Update documentation of public API
ids1024 Feb 23, 2023
20ab5fd
Updates to documentation for `Surface`
ids1024 Apr 1, 2023
58e277d
Use `NonZeroU32` for arguments to `Surface::resize`
ids1024 Apr 4, 2023
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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ rust-version = "1.60.0"

[features]
default = ["x11", "wayland", "wayland-dlopen"]
wayland = ["wayland-backend", "wayland-client", "nix", "fastrand"]
wayland = ["wayland-backend", "wayland-client", "memmap2", "nix", "fastrand"]
wayland-dlopen = ["wayland-sys/dlopen"]
x11 = ["bytemuck", "nix", "x11rb", "x11-dl"]

Expand All @@ -25,6 +25,7 @@ thiserror = "1.0.30"

[target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))))'.dependencies]
bytemuck = { version = "1.12.3", optional = true }
memmap2 = { version = "0.5.8", optional = true }
nix = { version = "0.26.1", optional = true }
wayland-backend = { version = "0.1.0", features = ["client_system"], optional = true }
wayland-client = { version = "0.30.0", optional = true }
Expand Down
24 changes: 12 additions & 12 deletions examples/winit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,21 @@ fn main() {
let size = window.inner_size();
(size.width, size.height)
};
let buffer = (0..((width * height) as usize))
.map(|index| {
let y = index / (width as usize);
let x = index % (width as usize);
let red = x % 255;
let green = y % 255;
let blue = (x * y) % 255;

let color = blue | (green << 8) | (red << 16);
surface.resize(width, height);

color as u32
})
.collect::<Vec<_>>();
let buffer = surface.buffer_mut();
for index in 0..(width * height) {
let y = index as u32 / width;
let x = index as u32 % width;
let red = x % 255;
let green = y % 255;
let blue = (x * y) % 255;

surface.set_buffer(&buffer, width as u16, height as u16);
buffer[index as usize] = blue | (green << 8) | (red << 16);
}

surface.present();
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
Expand Down
39 changes: 39 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,33 @@ macro_rules! make_dispatch {
}

impl SurfaceDispatch {
pub fn resize(&mut self, width: u32, height: u32) {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.resize(width, height),
)*
}
}

pub fn buffer_mut(&mut self) -> &mut [u32] {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.buffer_mut(),
)*
}
}

pub fn present(&mut self) {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.present(),
)*
}
}

unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) {
match self {
$(
Expand Down Expand Up @@ -235,6 +262,18 @@ impl Surface {
})
}

pub fn resize(&mut self, width: u32, height: u32) {
self.surface_impl.resize(width, height);
}

pub fn buffer_mut(&mut self) -> &mut [u32] {
self.surface_impl.buffer_mut()
}

pub fn present(&mut self) {
self.surface_impl.present();
}

/// Shows the given buffer with the given width and height on the window corresponding to this
/// graphics context. Panics if buffer.len() ≠ width*height. If the size of the buffer does
/// not match the size of the window, the buffer is drawn in the upper-left corner of the window.
Expand Down
45 changes: 35 additions & 10 deletions src/wayland/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use memmap2::MmapMut;
use std::{
ffi::CStr,
fs::File,
os::unix::prelude::{AsRawFd, FileExt, FromRawFd},
os::unix::prelude::{AsRawFd, FromRawFd},
slice,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
Expand Down Expand Up @@ -69,9 +71,19 @@ fn create_memfile() -> File {
panic!("Failed to generate non-existant shm name")
}

// Round size to use for pool for given dimentions, rounding up to power of 2
fn get_pool_size(width: i32, height: i32) -> i32 {
((width * height * 4) as u32).next_power_of_two() as i32
}

unsafe fn map_file(file: &File) -> MmapMut {
unsafe { MmapMut::map_mut(file.as_raw_fd()).expect("Failed to map shared memory") }
}

pub(super) struct WaylandBuffer {
qh: QueueHandle<State>,
tempfile: File,
map: MmapMut,
pool: wl_shm_pool::WlShmPool,
pool_size: i32,
buffer: wl_buffer::WlBuffer,
Expand All @@ -82,8 +94,15 @@ pub(super) struct WaylandBuffer {

impl WaylandBuffer {
pub fn new(shm: &wl_shm::WlShm, width: i32, height: i32, qh: &QueueHandle<State>) -> Self {
// Calculate size to use for shm pool
let pool_size = get_pool_size(width, height);

// Create an `mmap` shared memory
let tempfile = create_memfile();
let pool_size = width * height * 4;
let _ = tempfile.set_len(pool_size as u64);
let map = unsafe { map_file(&tempfile) };

// Create wayland shm pool and buffer
let pool = shm.create_pool(tempfile.as_raw_fd(), pool_size, qh, ());
let released = Arc::new(AtomicBool::new(true));
let buffer = pool.create_buffer(
Expand All @@ -95,8 +114,10 @@ impl WaylandBuffer {
qh,
released.clone(),
);

Self {
qh: qh.clone(),
map,
tempfile,
pool,
pool_size,
Expand All @@ -119,6 +140,7 @@ impl WaylandBuffer {
let _ = self.tempfile.set_len(size as u64);
self.pool.resize(size);
self.pool_size = size;
self.map = unsafe { map_file(&self.tempfile) };
}

// Create buffer with correct size
Expand All @@ -131,15 +153,10 @@ impl WaylandBuffer {
&self.qh,
self.released.clone(),
);
}
}

pub fn write(&self, buffer: &[u32]) {
let buffer =
unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len() * 4) };
self.tempfile
.write_all_at(buffer, 0)
.expect("Failed to write buffer to temporary file.");
self.width = width;
self.height = height;
}
}

pub fn attach(&self, surface: &wl_surface::WlSurface) {
Expand All @@ -150,6 +167,14 @@ impl WaylandBuffer {
pub fn released(&self) -> bool {
self.released.load(Ordering::SeqCst)
}

fn len(&self) -> usize {
self.width as usize * self.height as usize
}

pub unsafe fn mapped_mut(&mut self) -> &mut [u32] {
unsafe { slice::from_raw_parts_mut(self.map.as_mut_ptr() as *mut u32, self.len()) }
}
}

impl Drop for WaylandBuffer {
Expand Down
85 changes: 54 additions & 31 deletions src/wayland/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ pub struct WaylandImpl {
display: Arc<WaylandDisplayImpl>,
surface: wl_surface::WlSurface,
buffers: Option<(WaylandBuffer, WaylandBuffer)>,
width: i32,
height: i32,
}

impl WaylandImpl {
Expand All @@ -72,61 +74,82 @@ impl WaylandImpl {
display,
surface,
buffers: Default::default(),
width: 0,
height: 0,
})
}

fn buffer(&mut self, width: i32, height: i32) -> &WaylandBuffer {
self.buffers = Some(if let Some((front, mut back)) = self.buffers.take() {
// Swap buffers; block if back buffer not released yet
// Allocate front and back buffer
fn alloc_buffers(&mut self) {
self.buffers = Some((
WaylandBuffer::new(&self.display.shm, self.width, self.height, &self.display.qh),
WaylandBuffer::new(&self.display.shm, self.width, self.height, &self.display.qh),
));
}

pub fn resize(&mut self, width: u32, height: u32) {
self.width = width as i32;
self.height = height as i32;
}

pub fn buffer_mut(&mut self) -> &mut [u32] {
if let Some((_front, back)) = &mut self.buffers {
// Block if back buffer not released yet
if !back.released() {
let mut event_queue = self.display.event_queue.lock().unwrap();
while !back.released() {
event_queue.blocking_dispatch(&mut State).unwrap();
}
}
back.resize(width, height);
(back, front)

// Resize, if buffer isn't large enough
back.resize(self.width, self.height);
} else {
// Allocate front and back buffer
(
WaylandBuffer::new(&self.display.shm, width, height, &self.display.qh),
WaylandBuffer::new(&self.display.shm, width, height, &self.display.qh),
)
});
&self.buffers.as_ref().unwrap().0
self.alloc_buffers();
};

unsafe { self.buffers.as_mut().unwrap().1.mapped_mut() }
}

pub(super) unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) {
pub fn present(&mut self) {
let _ = self
.display
.event_queue
.lock()
.unwrap()
.dispatch_pending(&mut State);

let surface = self.surface.clone();
let wayland_buffer = self.buffer(width.into(), height.into());
wayland_buffer.write(buffer);
wayland_buffer.attach(&surface);

// FIXME: Proper damaging mechanism.
//
// In order to propagate changes on compositors which track damage, for now damage the entire surface.
if self.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.
if let Some((front, back)) = &mut self.buffers {
// Swap front and back buffer
std::mem::swap(front, back);

front.attach(&self.surface);

// FIXME: Proper damaging mechanism.
//
// i32::MAX is a valid damage box (most compositors interpret the damage box as "the entire surface")
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.
self.surface
.damage_buffer(0, 0, width as i32, height as i32);
// In order to propagate changes on compositors which track damage, for now damage the entire surface.
if self.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")
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.
self.surface.damage_buffer(0, 0, self.width, self.height);
}

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

let _ = self.display.event_queue.lock().unwrap().flush();
}

pub unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) {
self.resize(width.into(), height.into());
self.buffer_mut().copy_from_slice(buffer);
self.present();
}
}

impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {
Expand Down
Loading