Skip to content

Commit cdfae58

Browse files
ids1024jackpot51
authored andcommitted
wayland: Reuse buffers and pools; check buffer release
Also updates `winit` example to redraw on resize, which seems to be necessary. With this resizing seems to be entirely smooth, without visual corruption from it overwriting the buffer the server is displaying.
1 parent 9b8641f commit cdfae58

File tree

3 files changed

+165
-98
lines changed

3 files changed

+165
-98
lines changed

examples/winit.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ fn main() {
5454
} if window_id == window.id() => {
5555
*control_flow = ControlFlow::Exit;
5656
}
57+
Event::WindowEvent {
58+
event: WindowEvent::Resized(_),
59+
window_id,
60+
} if window_id == window.id() => {
61+
window.request_redraw();
62+
}
5763
_ => {}
5864
}
5965
});

src/wayland/buffer.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
use nix::sys::memfd::{memfd_create, MemFdCreateFlag};
2+
use std::{
3+
ffi::CStr,
4+
fs::File,
5+
os::unix::prelude::{AsRawFd, FileExt, FromRawFd},
6+
sync::{
7+
atomic::{AtomicBool, Ordering},
8+
Arc,
9+
},
10+
};
11+
use wayland_client::{
12+
protocol::{wl_buffer, wl_shm, wl_shm_pool, wl_surface},
13+
Connection, Dispatch, QueueHandle,
14+
};
15+
16+
use super::State;
17+
18+
pub(super) struct WaylandBuffer {
19+
qh: QueueHandle<State>,
20+
tempfile: File,
21+
pool: wl_shm_pool::WlShmPool,
22+
pool_size: i32,
23+
buffer: wl_buffer::WlBuffer,
24+
width: i32,
25+
height: i32,
26+
released: Arc<AtomicBool>,
27+
}
28+
29+
impl WaylandBuffer {
30+
pub fn new(shm: &wl_shm::WlShm, width: i32, height: i32, qh: &QueueHandle<State>) -> Self {
31+
let name = unsafe { CStr::from_bytes_with_nul_unchecked("swbuf\0".as_bytes()) };
32+
let tempfile_fd = memfd_create(name, MemFdCreateFlag::MFD_CLOEXEC)
33+
.expect("Failed to create memfd to store buffer.");
34+
let tempfile = unsafe { File::from_raw_fd(tempfile_fd) };
35+
let pool_size = width * height * 4;
36+
let pool = shm.create_pool(tempfile.as_raw_fd(), pool_size, &qh, ());
37+
let released = Arc::new(AtomicBool::new(true));
38+
let buffer = pool.create_buffer(
39+
0,
40+
width,
41+
height,
42+
width * 4,
43+
wl_shm::Format::Xrgb8888,
44+
&qh,
45+
released.clone(),
46+
);
47+
Self {
48+
qh: qh.clone(),
49+
tempfile,
50+
pool,
51+
pool_size,
52+
buffer,
53+
width,
54+
height,
55+
released,
56+
}
57+
}
58+
59+
pub fn resize(&mut self, width: i32, height: i32) {
60+
// If size is the same, there's nothing to do
61+
if self.width != width || self.height != height {
62+
// Destroy old buffer
63+
self.buffer.destroy();
64+
65+
// Grow pool, if needed
66+
let size = ((width * height * 4) as u32).next_power_of_two() as i32;
67+
if size > self.pool_size {
68+
let _ = self.tempfile.set_len(size as u64);
69+
self.pool.resize(size);
70+
self.pool_size = size;
71+
}
72+
73+
// Create buffer with correct size
74+
self.buffer = self.pool.create_buffer(
75+
0,
76+
width,
77+
height,
78+
width * 4,
79+
wl_shm::Format::Xrgb8888,
80+
&self.qh,
81+
self.released.clone(),
82+
);
83+
}
84+
}
85+
86+
pub fn write(&self, buffer: &[u32]) {
87+
let buffer =
88+
unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len() * 4) };
89+
self.tempfile
90+
.write_all_at(buffer, 0)
91+
.expect("Failed to write buffer to temporary file.");
92+
}
93+
94+
pub fn attach(&self, surface: &wl_surface::WlSurface) {
95+
self.released.store(false, Ordering::SeqCst);
96+
surface.attach(Some(&self.buffer), 0, 0);
97+
}
98+
99+
pub fn released(&self) -> bool {
100+
self.released.load(Ordering::SeqCst)
101+
}
102+
}
103+
104+
impl Drop for WaylandBuffer {
105+
fn drop(&mut self) {
106+
self.buffer.destroy();
107+
self.pool.destroy();
108+
}
109+
}
110+
111+
impl Dispatch<wl_shm_pool::WlShmPool, ()> for State {
112+
fn event(
113+
_: &mut State,
114+
_: &wl_shm_pool::WlShmPool,
115+
_: wl_shm_pool::Event,
116+
_: &(),
117+
_: &Connection,
118+
_: &QueueHandle<State>,
119+
) {
120+
}
121+
}
122+
123+
impl Dispatch<wl_buffer::WlBuffer, Arc<AtomicBool>> for State {
124+
fn event(
125+
_: &mut State,
126+
_: &wl_buffer::WlBuffer,
127+
event: wl_buffer::Event,
128+
released: &Arc<AtomicBool>,
129+
_: &Connection,
130+
_: &QueueHandle<State>,
131+
) {
132+
match event {
133+
wl_buffer::Event::Release => released.store(true, Ordering::SeqCst),
134+
_ => {}
135+
}
136+
}
137+
}
Lines changed: 22 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,23 @@
11
use crate::{error::unwrap, GraphicsContextImpl, SwBufError};
2-
use nix::sys::memfd::{memfd_create, MemFdCreateFlag};
32
use raw_window_handle::{WaylandDisplayHandle, WaylandWindowHandle};
4-
use std::{
5-
ffi::CStr,
6-
fs::File,
7-
io::Write,
8-
os::unix::prelude::{AsRawFd, FileExt, FromRawFd},
9-
};
103
use wayland_client::{
114
backend::{Backend, ObjectId},
125
globals::{registry_queue_init, GlobalListContents},
13-
protocol::{wl_buffer, wl_registry, wl_shm, wl_shm_pool, wl_surface},
6+
protocol::{wl_registry, wl_shm, wl_surface},
147
Connection, Dispatch, EventQueue, Proxy, QueueHandle,
158
};
169

10+
mod buffer;
11+
use buffer::WaylandBuffer;
12+
1713
struct State;
1814

1915
pub struct WaylandImpl {
2016
event_queue: EventQueue<State>,
2117
qh: QueueHandle<State>,
2218
surface: wl_surface::WlSurface,
2319
shm: wl_shm::WlShm,
24-
tempfile: File,
25-
buffer: Option<WaylandBuffer>,
26-
}
27-
28-
struct WaylandBuffer {
29-
width: i32,
30-
height: i32,
31-
pool: wl_shm_pool::WlShmPool,
32-
buffer: wl_buffer::WlBuffer,
33-
}
34-
35-
impl Drop for WaylandBuffer {
36-
fn drop(&mut self) {
37-
self.buffer.destroy();
38-
self.pool.destroy();
39-
}
20+
buffers: Vec<WaylandBuffer>,
4021
}
4122

4223
impl WaylandImpl {
@@ -56,12 +37,6 @@ impl WaylandImpl {
5637
globals.bind(&qh, 1..=1, ()),
5738
"Failed to instantiate Wayland Shm",
5839
)?;
59-
let name = CStr::from_bytes_with_nul_unchecked("swbuf\0".as_bytes());
60-
let tempfile_fd = unwrap(
61-
memfd_create(name, MemFdCreateFlag::MFD_CLOEXEC),
62-
"Failed to create temporary file to store buffer.",
63-
)?;
64-
let tempfile = File::from_raw_fd(tempfile_fd);
6540
let surface_id = unwrap(
6641
ObjectId::from_ptr(
6742
wl_surface::WlSurface::interface(),
@@ -78,58 +53,31 @@ impl WaylandImpl {
7853
qh,
7954
surface,
8055
shm,
81-
tempfile,
82-
buffer: None,
56+
buffers: Vec::new(),
8357
})
8458
}
8559

86-
fn ensure_buffer_size(&mut self, width: i32, height: i32) {
87-
if !self.check_buffer_size_equals(width, height) {
88-
let pool =
89-
self.shm
90-
.create_pool(self.tempfile.as_raw_fd(), width * height * 4, &self.qh, ());
91-
let buffer = pool.create_buffer(
92-
0,
93-
width,
94-
height,
95-
width * 4,
96-
wayland_client::protocol::wl_shm::Format::Xrgb8888,
97-
&self.qh,
98-
(),
99-
);
100-
self.buffer = Some(WaylandBuffer {
101-
width,
102-
height,
103-
pool,
104-
buffer,
105-
});
106-
}
107-
}
108-
109-
fn check_buffer_size_equals(&self, width: i32, height: i32) -> bool {
110-
match &self.buffer {
111-
Some(buffer) => buffer.width == width && buffer.height == height,
112-
None => false,
60+
// Allocate or reuse a buffer of the given size
61+
fn buffer(&mut self, width: i32, height: i32) -> &WaylandBuffer {
62+
if let Some(idx) = self.buffers.iter().position(|i| i.released()) {
63+
self.buffers[idx].resize(width, height);
64+
&mut self.buffers[idx]
65+
} else {
66+
self.buffers
67+
.push(WaylandBuffer::new(&self.shm, width, height, &self.qh));
68+
self.buffers.last().unwrap()
11369
}
11470
}
11571
}
11672

11773
impl GraphicsContextImpl for WaylandImpl {
11874
unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) {
119-
self.ensure_buffer_size(width as i32, height as i32);
120-
let wayland_buffer = self.buffer.as_mut().unwrap();
121-
self.tempfile.set_len(buffer.len() as u64 * 4)
122-
.expect("Failed to truncate temporary file.");
123-
self.tempfile
124-
.write_at(
125-
std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len() * 4),
126-
0,
127-
)
128-
.expect("Failed to write buffer to temporary file.");
129-
self.tempfile
130-
.flush()
131-
.expect("Failed to flush buffer to temporary file.");
132-
self.surface.attach(Some(&wayland_buffer.buffer), 0, 0);
75+
let _ = self.event_queue.dispatch_pending(&mut State);
76+
77+
let surface = self.surface.clone();
78+
let wayland_buffer = self.buffer(width.into(), height.into());
79+
wayland_buffer.write(buffer);
80+
wayland_buffer.attach(&surface);
13381

13482
// FIXME: Proper damaging mechanism.
13583
//
@@ -145,8 +93,8 @@ impl GraphicsContextImpl for WaylandImpl {
14593
self.surface
14694
.damage_buffer(0, 0, width as i32, height as i32);
14795
}
148-
14996
self.surface.commit();
97+
15098
let _ = self.event_queue.flush();
15199
}
152100
}
@@ -175,27 +123,3 @@ impl Dispatch<wl_shm::WlShm, ()> for State {
175123
) {
176124
}
177125
}
178-
179-
impl Dispatch<wl_shm_pool::WlShmPool, ()> for State {
180-
fn event(
181-
_: &mut State,
182-
_: &wl_shm_pool::WlShmPool,
183-
_: wl_shm_pool::Event,
184-
_: &(),
185-
_: &Connection,
186-
_: &QueueHandle<State>,
187-
) {
188-
}
189-
}
190-
191-
impl Dispatch<wl_buffer::WlBuffer, ()> for State {
192-
fn event(
193-
_: &mut State,
194-
_: &wl_buffer::WlBuffer,
195-
_: wl_buffer::Event,
196-
_: &(),
197-
_: &Connection,
198-
_: &QueueHandle<State>,
199-
) {
200-
}
201-
}

0 commit comments

Comments
 (0)