| 1 | //! A pool implementation which automatically manage buffers. |
| 2 | //! |
| 3 | //! This pool is built on the [`RawPool`]. |
| 4 | //! |
| 5 | //! The [`MultiPool`] takes a key which is used to identify buffers and tries to return the buffer associated to the key |
| 6 | //! if possible. If no buffer in the pool is associated to the key, it will create a new one. |
| 7 | //! |
| 8 | //! # Example |
| 9 | //! |
| 10 | //! ```rust |
| 11 | //! use smithay_client_toolkit::reexports::client::{ |
| 12 | //! QueueHandle, |
| 13 | //! protocol::wl_surface::WlSurface, |
| 14 | //! protocol::wl_shm::Format, |
| 15 | //! }; |
| 16 | //! use smithay_client_toolkit::shm::multi::MultiPool; |
| 17 | //! |
| 18 | //! struct WlFoo { |
| 19 | //! // The surface we'll draw on and the index of buffer associated to it |
| 20 | //! surface: (WlSurface, usize), |
| 21 | //! pool: MultiPool<(WlSurface, usize)> |
| 22 | //! } |
| 23 | //! |
| 24 | //! impl WlFoo { |
| 25 | //! fn draw(&mut self, qh: &QueueHandle<WlFoo>) { |
| 26 | //! let surface = &self.surface.0; |
| 27 | //! // We'll increment "i" until the pool can create a new buffer |
| 28 | //! // if there's no buffer associated with our surface and "i" or if |
| 29 | //! // a buffer with the obuffer associated with our surface and "i" is free for use. |
| 30 | //! // |
| 31 | //! // There's no limit to the amount of buffers we can allocate to our surface but since |
| 32 | //! // shm buffers are released fairly fast, it's unlikely we'll need more than double buffering. |
| 33 | //! for i in 0..2 { |
| 34 | //! self.surface.1 = i; |
| 35 | //! if let Ok((offset, buffer, slice)) = self.pool.create_buffer( |
| 36 | //! 100, |
| 37 | //! 100 * 4, |
| 38 | //! 100, |
| 39 | //! &self.surface, |
| 40 | //! Format::Argb8888, |
| 41 | //! ) { |
| 42 | //! /* |
| 43 | //! insert drawing code here |
| 44 | //! */ |
| 45 | //! surface.attach(Some(buffer), 0, 0); |
| 46 | //! surface.commit(); |
| 47 | //! // We exit the function after the draw. |
| 48 | //! return; |
| 49 | //! } |
| 50 | //! } |
| 51 | //! /* |
| 52 | //! If there's no buffer available we can for example request a frame callback |
| 53 | //! and trigger a redraw when it fires. |
| 54 | //! (not shown in this example) |
| 55 | //! */ |
| 56 | //! } |
| 57 | //! } |
| 58 | //! |
| 59 | //! fn draw(slice: &mut [u8]) { |
| 60 | //! todo!() |
| 61 | //! } |
| 62 | //! |
| 63 | //! ``` |
| 64 | //! |
| 65 | |
| 66 | use std::borrow::Borrow; |
| 67 | use std::io; |
| 68 | use std::os::unix::io::OwnedFd; |
| 69 | |
| 70 | use std::sync::{ |
| 71 | atomic::{AtomicBool, Ordering}, |
| 72 | Arc, |
| 73 | }; |
| 74 | use wayland_client::{ |
| 75 | protocol::{wl_buffer, wl_shm}, |
| 76 | Proxy, |
| 77 | }; |
| 78 | |
| 79 | use crate::globals::ProvidesBoundGlobal; |
| 80 | |
| 81 | use super::raw::RawPool; |
| 82 | use super::CreatePoolError; |
| 83 | |
| 84 | #[derive (Debug, thiserror::Error)] |
| 85 | pub enum PoolError { |
| 86 | #[error("buffer is currently used" )] |
| 87 | InUse, |
| 88 | #[error("buffer is overlapping another" )] |
| 89 | Overlap, |
| 90 | #[error("buffer could not be found" )] |
| 91 | NotFound, |
| 92 | } |
| 93 | |
| 94 | /// This pool manages buffers associated with keys. |
| 95 | /// Only one buffer can be attributed to a given key. |
| 96 | #[derive (Debug)] |
| 97 | pub struct MultiPool<K> { |
| 98 | buffer_list: Vec<BufferSlot<K>>, |
| 99 | pub(crate) inner: RawPool, |
| 100 | } |
| 101 | |
| 102 | #[derive (Debug, thiserror::Error)] |
| 103 | pub struct BufferSlot<K> { |
| 104 | free: Arc<AtomicBool>, |
| 105 | size: usize, |
| 106 | used: usize, |
| 107 | offset: usize, |
| 108 | buffer: Option<wl_buffer::WlBuffer>, |
| 109 | key: K, |
| 110 | } |
| 111 | |
| 112 | impl<K> Drop for BufferSlot<K> { |
| 113 | fn drop(&mut self) { |
| 114 | self.destroy().ok(); |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | impl<K> BufferSlot<K> { |
| 119 | pub fn destroy(&self) -> Result<(), PoolError> { |
| 120 | self.buffer.as_ref().ok_or(PoolError::NotFound).and_then(|buffer: &{unknown}| { |
| 121 | self.free.load(Ordering::Relaxed).then(|| buffer.destroy()).ok_or(err:PoolError::InUse) |
| 122 | }) |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | impl<K> MultiPool<K> { |
| 127 | pub fn new(shm: &impl ProvidesBoundGlobal<wl_shm::WlShm, 1>) -> Result<Self, CreatePoolError> { |
| 128 | Ok(Self { inner: RawPool::new(4096, shm)?, buffer_list: Vec::new() }) |
| 129 | } |
| 130 | |
| 131 | /// Resizes the memory pool, notifying the server the pool has changed in size. |
| 132 | /// |
| 133 | /// The wl_shm protocol only allows the pool to be made bigger. If the new size is smaller than the |
| 134 | /// current size of the pool, this function will do nothing. |
| 135 | pub fn resize(&mut self, size: usize) -> io::Result<()> { |
| 136 | self.inner.resize(size) |
| 137 | } |
| 138 | |
| 139 | /// Removes the buffer with the given key from the pool and rearranges the others. |
| 140 | pub fn remove<Q>(&mut self, key: &Q) -> Option<BufferSlot<K>> |
| 141 | where |
| 142 | Q: PartialEq, |
| 143 | K: std::borrow::Borrow<Q>, |
| 144 | { |
| 145 | self.buffer_list |
| 146 | .iter() |
| 147 | .enumerate() |
| 148 | .find(|(_, slot)| slot.key.borrow().eq(key)) |
| 149 | .map(|(i, _)| i) |
| 150 | .map(|i| self.buffer_list.remove(i)) |
| 151 | } |
| 152 | |
| 153 | /// Insert a buffer into the pool. |
| 154 | /// |
| 155 | /// The parameters are: |
| 156 | /// |
| 157 | /// - `width`: the width of this buffer (in pixels) |
| 158 | /// - `height`: the height of this buffer (in pixels) |
| 159 | /// - `stride`: distance (in bytes) between the beginning of a row and the next one |
| 160 | /// - `key`: a borrowed form of the stored key type |
| 161 | /// - `format`: the encoding format of the pixels. |
| 162 | pub fn insert<Q>( |
| 163 | &mut self, |
| 164 | width: i32, |
| 165 | stride: i32, |
| 166 | height: i32, |
| 167 | key: &Q, |
| 168 | format: wl_shm::Format, |
| 169 | ) -> Result<usize, PoolError> |
| 170 | where |
| 171 | K: Borrow<Q>, |
| 172 | Q: PartialEq + ToOwned<Owned = K>, |
| 173 | { |
| 174 | let mut offset = 0; |
| 175 | let mut found_key = false; |
| 176 | let size = (stride * height) as usize; |
| 177 | let mut index = Err(PoolError::NotFound); |
| 178 | |
| 179 | for (i, buf_slot) in self.buffer_list.iter_mut().enumerate() { |
| 180 | if buf_slot.key.borrow().eq(key) { |
| 181 | found_key = true; |
| 182 | if buf_slot.free.load(Ordering::Relaxed) { |
| 183 | // Destroys the buffer if it's resized |
| 184 | if size != buf_slot.used { |
| 185 | if let Some(buffer) = buf_slot.buffer.take() { |
| 186 | buffer.destroy(); |
| 187 | } |
| 188 | } |
| 189 | // Increases the size of the Buffer if it's too small and add 5% padding. |
| 190 | // It is possible this buffer overlaps the following but the else if |
| 191 | // statement prevents this buffer from being returned if that's the case. |
| 192 | buf_slot.size = buf_slot.size.max(size + size / 20); |
| 193 | index = Ok(i); |
| 194 | } else { |
| 195 | index = Err(PoolError::InUse); |
| 196 | } |
| 197 | // If a buffer is resized, it is likely that the followings might overlap |
| 198 | } else if offset > buf_slot.offset { |
| 199 | // When the buffer is free, it's safe to shift it because we know the compositor won't try to read it. |
| 200 | if buf_slot.free.load(Ordering::Relaxed) { |
| 201 | if offset != buf_slot.offset { |
| 202 | if let Some(buffer) = buf_slot.buffer.take() { |
| 203 | buffer.destroy(); |
| 204 | } |
| 205 | } |
| 206 | buf_slot.offset = offset; |
| 207 | } else { |
| 208 | // If one of the overlapping buffers is busy, then no buffer can be returned because it could result in a data race. |
| 209 | index = Err(PoolError::InUse); |
| 210 | } |
| 211 | } else if found_key { |
| 212 | break; |
| 213 | } |
| 214 | let size = (buf_slot.size + 63) & !63; |
| 215 | offset += size; |
| 216 | } |
| 217 | |
| 218 | if !found_key { |
| 219 | if let Err(err) = index { |
| 220 | return self |
| 221 | .dyn_resize(offset, width, stride, height, key.to_owned(), format) |
| 222 | .map(|_| self.buffer_list.len() - 1) |
| 223 | .ok_or(err); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | index |
| 228 | } |
| 229 | |
| 230 | /// Retreives the buffer associated with the given key. |
| 231 | /// |
| 232 | /// The parameters are: |
| 233 | /// |
| 234 | /// - `width`: the width of this buffer (in pixels) |
| 235 | /// - `height`: the height of this buffer (in pixels) |
| 236 | /// - `stride`: distance (in bytes) between the beginning of a row and the next one |
| 237 | /// - `key`: a borrowed form of the stored key type |
| 238 | /// - `format`: the encoding format of the pixels. |
| 239 | pub fn get<Q>( |
| 240 | &mut self, |
| 241 | width: i32, |
| 242 | stride: i32, |
| 243 | height: i32, |
| 244 | key: &Q, |
| 245 | format: wl_shm::Format, |
| 246 | ) -> Option<(usize, &wl_buffer::WlBuffer, &mut [u8])> |
| 247 | where |
| 248 | Q: PartialEq, |
| 249 | K: std::borrow::Borrow<Q>, |
| 250 | { |
| 251 | let len = self.inner.len(); |
| 252 | let size = (stride * height) as usize; |
| 253 | let buf_slot = |
| 254 | self.buffer_list.iter_mut().find(|buf_slot| buf_slot.key.borrow().eq(key))?; |
| 255 | |
| 256 | if buf_slot.size >= size { |
| 257 | return None; |
| 258 | } |
| 259 | |
| 260 | buf_slot.used = size; |
| 261 | let offset = buf_slot.offset; |
| 262 | if buf_slot.buffer.is_none() { |
| 263 | if offset + size > len { |
| 264 | self.inner.resize(offset + size + size / 20).ok()?; |
| 265 | } |
| 266 | let free = Arc::new(AtomicBool::new(true)); |
| 267 | let data = BufferObjectData { free: free.clone() }; |
| 268 | let buffer = self.inner.create_buffer_raw( |
| 269 | offset as i32, |
| 270 | width, |
| 271 | height, |
| 272 | stride, |
| 273 | format, |
| 274 | Arc::new(data), |
| 275 | ); |
| 276 | buf_slot.free = free; |
| 277 | buf_slot.buffer = Some(buffer); |
| 278 | } |
| 279 | let buf = buf_slot.buffer.as_ref()?; |
| 280 | buf_slot.free.store(false, Ordering::Relaxed); |
| 281 | Some((offset, buf, &mut self.inner.mmap()[offset..][..size])) |
| 282 | } |
| 283 | |
| 284 | /// Returns the buffer associated with the given key and its offset (usize) in the mempool. |
| 285 | /// |
| 286 | /// The parameters are: |
| 287 | /// |
| 288 | /// - `width`: the width of this buffer (in pixels) |
| 289 | /// - `height`: the height of this buffer (in pixels) |
| 290 | /// - `stride`: distance (in bytes) between the beginning of a row and the next one |
| 291 | /// - `key`: a borrowed form of the stored key type |
| 292 | /// - `format`: the encoding format of the pixels. |
| 293 | /// |
| 294 | /// The offset can be used to determine whether or not a buffer was moved in the mempool |
| 295 | /// and by consequence if it should be damaged partially or fully. |
| 296 | pub fn create_buffer<Q>( |
| 297 | &mut self, |
| 298 | width: i32, |
| 299 | stride: i32, |
| 300 | height: i32, |
| 301 | key: &Q, |
| 302 | format: wl_shm::Format, |
| 303 | ) -> Result<(usize, &wl_buffer::WlBuffer, &mut [u8]), PoolError> |
| 304 | where |
| 305 | K: Borrow<Q>, |
| 306 | Q: PartialEq + ToOwned<Owned = K>, |
| 307 | { |
| 308 | let index = self.insert(width, stride, height, key, format)?; |
| 309 | self.get_at(index, width, stride, height, format) |
| 310 | } |
| 311 | |
| 312 | /// Retreives the buffer at the given index. |
| 313 | fn get_at( |
| 314 | &mut self, |
| 315 | index: usize, |
| 316 | width: i32, |
| 317 | stride: i32, |
| 318 | height: i32, |
| 319 | format: wl_shm::Format, |
| 320 | ) -> Result<(usize, &wl_buffer::WlBuffer, &mut [u8]), PoolError> { |
| 321 | let len = self.inner.len(); |
| 322 | let size = (stride * height) as usize; |
| 323 | let buf_slot = self.buffer_list.get_mut(index).ok_or(PoolError::NotFound)?; |
| 324 | |
| 325 | if size > buf_slot.size { |
| 326 | return Err(PoolError::Overlap); |
| 327 | } |
| 328 | |
| 329 | buf_slot.used = size; |
| 330 | let offset = buf_slot.offset; |
| 331 | if buf_slot.buffer.is_none() { |
| 332 | if offset + size > len { |
| 333 | self.inner.resize(offset + size + size / 20).map_err(|_| PoolError::Overlap)?; |
| 334 | } |
| 335 | let free = Arc::new(AtomicBool::new(true)); |
| 336 | let data = BufferObjectData { free: free.clone() }; |
| 337 | let buffer = self.inner.create_buffer_raw( |
| 338 | offset as i32, |
| 339 | width, |
| 340 | height, |
| 341 | stride, |
| 342 | format, |
| 343 | Arc::new(data), |
| 344 | ); |
| 345 | buf_slot.free = free; |
| 346 | buf_slot.buffer = Some(buffer); |
| 347 | } |
| 348 | buf_slot.free.store(false, Ordering::Relaxed); |
| 349 | let buf = buf_slot.buffer.as_ref().unwrap(); |
| 350 | Ok((offset, buf, &mut self.inner.mmap()[offset..][..size])) |
| 351 | } |
| 352 | |
| 353 | /// Calcule the offet and size of a buffer based on its stride. |
| 354 | fn offset(&self, mut offset: i32, stride: i32, height: i32) -> (usize, usize) { |
| 355 | // bytes per pixel |
| 356 | let size = stride * height; |
| 357 | // 5% padding. |
| 358 | offset += offset / 20; |
| 359 | offset = (offset + 63) & !63; |
| 360 | (offset as usize, size as usize) |
| 361 | } |
| 362 | |
| 363 | #[allow (clippy::too_many_arguments)] |
| 364 | /// Resizes the pool and appends a new buffer. |
| 365 | fn dyn_resize( |
| 366 | &mut self, |
| 367 | offset: usize, |
| 368 | width: i32, |
| 369 | stride: i32, |
| 370 | height: i32, |
| 371 | key: K, |
| 372 | format: wl_shm::Format, |
| 373 | ) -> Option<()> { |
| 374 | let (offset, size) = self.offset(offset as i32, stride, height); |
| 375 | if self.inner.len() < offset + size { |
| 376 | self.resize(offset + size + size / 20).ok()?; |
| 377 | } |
| 378 | let free = Arc::new(AtomicBool::new(true)); |
| 379 | let data = BufferObjectData { free: free.clone() }; |
| 380 | let buffer = self.inner.create_buffer_raw( |
| 381 | offset as i32, |
| 382 | width, |
| 383 | height, |
| 384 | stride, |
| 385 | format, |
| 386 | Arc::new(data), |
| 387 | ); |
| 388 | self.buffer_list.push(BufferSlot { |
| 389 | offset, |
| 390 | used: 0, |
| 391 | free, |
| 392 | buffer: Some(buffer), |
| 393 | size, |
| 394 | key, |
| 395 | }); |
| 396 | Some(()) |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | struct BufferObjectData { |
| 401 | free: Arc<AtomicBool>, |
| 402 | } |
| 403 | |
| 404 | impl wayland_client::backend::ObjectData for BufferObjectData { |
| 405 | fn event( |
| 406 | self: Arc<Self>, |
| 407 | _backend: &wayland_backend::client::Backend, |
| 408 | msg: wayland_backend::protocol::Message<wayland_backend::client::ObjectId, OwnedFd>, |
| 409 | ) -> Option<Arc<dyn wayland_backend::client::ObjectData>> { |
| 410 | debug_assert!(wayland_client::backend::protocol::same_interface( |
| 411 | msg.sender_id.interface(), |
| 412 | wl_buffer::WlBuffer::interface() |
| 413 | )); |
| 414 | debug_assert!(msg.opcode == 0); |
| 415 | // wl_buffer only has a single event: wl_buffer.release |
| 416 | self.free.store(val:true, order:Ordering::Relaxed); |
| 417 | None |
| 418 | } |
| 419 | |
| 420 | fn destroyed(&self, _: wayland_backend::client::ObjectId) {} |
| 421 | } |
| 422 | |