use std::borrow::Borrow;
use std::io;
use std::os::unix::io::OwnedFd;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use wayland_client::{
protocol::{wl_buffer, wl_shm},
Proxy,
};
use crate::globals::ProvidesBoundGlobal;
use super::raw::RawPool;
use super::CreatePoolError;
#[derive(Debug, thiserror::Error)]
pub enum PoolError {
#[error("buffer is currently used")]
InUse,
#[error("buffer is overlapping another")]
Overlap,
#[error("buffer could not be found")]
NotFound,
}
#[derive(Debug)]
pub struct MultiPool<K> {
buffer_list: Vec<BufferSlot<K>>,
pub(crate) inner: RawPool,
}
#[derive(Debug, thiserror::Error)]
pub struct BufferSlot<K> {
free: Arc<AtomicBool>,
size: usize,
used: usize,
offset: usize,
buffer: Option<wl_buffer::WlBuffer>,
key: K,
}
impl<K> Drop for BufferSlot<K> {
fn drop(&mut self) {
self.destroy().ok();
}
}
impl<K> BufferSlot<K> {
pub fn destroy(&self) -> Result<(), PoolError> {
self.buffer.as_ref().ok_or(PoolError::NotFound).and_then(|buffer| {
self.free.load(Ordering::Relaxed).then(|| buffer.destroy()).ok_or(PoolError::InUse)
})
}
}
impl<K> MultiPool<K> {
pub fn new(shm: &impl ProvidesBoundGlobal<wl_shm::WlShm, 1>) -> Result<Self, CreatePoolError> {
Ok(Self { inner: RawPool::new(4096, shm)?, buffer_list: Vec::new() })
}
pub fn resize(&mut self, size: usize) -> io::Result<()> {
self.inner.resize(size)
}
pub fn remove<Q>(&mut self, key: &Q) -> Option<BufferSlot<K>>
where
Q: PartialEq,
K: std::borrow::Borrow<Q>,
{
self.buffer_list
.iter()
.enumerate()
.find(|(_, slot)| slot.key.borrow().eq(key))
.map(|(i, _)| i)
.map(|i| self.buffer_list.remove(i))
}
pub fn insert<Q>(
&mut self,
width: i32,
stride: i32,
height: i32,
key: &Q,
format: wl_shm::Format,
) -> Result<usize, PoolError>
where
K: Borrow<Q>,
Q: PartialEq + ToOwned<Owned = K>,
{
let mut offset = 0;
let mut found_key = false;
let size = (stride * height) as usize;
let mut index = Err(PoolError::NotFound);
for (i, buf_slot) in self.buffer_list.iter_mut().enumerate() {
if buf_slot.key.borrow().eq(key) {
found_key = true;
if buf_slot.free.load(Ordering::Relaxed) {
if size != buf_slot.used {
if let Some(buffer) = buf_slot.buffer.take() {
buffer.destroy();
}
}
buf_slot.size = buf_slot.size.max(size + size / 20);
index = Ok(i);
} else {
index = Err(PoolError::InUse);
}
} else if offset > buf_slot.offset {
if buf_slot.free.load(Ordering::Relaxed) {
if offset != buf_slot.offset {
if let Some(buffer) = buf_slot.buffer.take() {
buffer.destroy();
}
}
buf_slot.offset = offset;
} else {
index = Err(PoolError::InUse);
}
} else if found_key {
break;
}
let size = (buf_slot.size + 63) & !63;
offset += size;
}
if !found_key {
if let Err(err) = index {
return self
.dyn_resize(offset, width, stride, height, key.to_owned(), format)
.map(|_| self.buffer_list.len() - 1)
.ok_or(err);
}
}
index
}
pub fn get<Q>(
&mut self,
width: i32,
stride: i32,
height: i32,
key: &Q,
format: wl_shm::Format,
) -> Option<(usize, &wl_buffer::WlBuffer, &mut [u8])>
where
Q: PartialEq,
K: std::borrow::Borrow<Q>,
{
let len = self.inner.len();
let size = (stride * height) as usize;
let buf_slot =
self.buffer_list.iter_mut().find(|buf_slot| buf_slot.key.borrow().eq(key))?;
if buf_slot.size >= size {
return None;
}
buf_slot.used = size;
let offset = buf_slot.offset;
if buf_slot.buffer.is_none() {
if offset + size > len {
self.inner.resize(offset + size + size / 20).ok()?;
}
let free = Arc::new(AtomicBool::new(true));
let data = BufferObjectData { free: free.clone() };
let buffer = self.inner.create_buffer_raw(
offset as i32,
width,
height,
stride,
format,
Arc::new(data),
);
buf_slot.free = free;
buf_slot.buffer = Some(buffer);
}
let buf = buf_slot.buffer.as_ref()?;
buf_slot.free.store(false, Ordering::Relaxed);
Some((offset, buf, &mut self.inner.mmap()[offset..][..size]))
}
pub fn create_buffer<Q>(
&mut self,
width: i32,
stride: i32,
height: i32,
key: &Q,
format: wl_shm::Format,
) -> Result<(usize, &wl_buffer::WlBuffer, &mut [u8]), PoolError>
where
K: Borrow<Q>,
Q: PartialEq + ToOwned<Owned = K>,
{
let index = self.insert(width, stride, height, key, format)?;
self.get_at(index, width, stride, height, format)
}
fn get_at(
&mut self,
index: usize,
width: i32,
stride: i32,
height: i32,
format: wl_shm::Format,
) -> Result<(usize, &wl_buffer::WlBuffer, &mut [u8]), PoolError> {
let len = self.inner.len();
let size = (stride * height) as usize;
let buf_slot = self.buffer_list.get_mut(index).ok_or(PoolError::NotFound)?;
if size > buf_slot.size {
return Err(PoolError::Overlap);
}
buf_slot.used = size;
let offset = buf_slot.offset;
if buf_slot.buffer.is_none() {
if offset + size > len {
self.inner.resize(offset + size + size / 20).map_err(|_| PoolError::Overlap)?;
}
let free = Arc::new(AtomicBool::new(true));
let data = BufferObjectData { free: free.clone() };
let buffer = self.inner.create_buffer_raw(
offset as i32,
width,
height,
stride,
format,
Arc::new(data),
);
buf_slot.free = free;
buf_slot.buffer = Some(buffer);
}
buf_slot.free.store(false, Ordering::Relaxed);
let buf = buf_slot.buffer.as_ref().unwrap();
Ok((offset, buf, &mut self.inner.mmap()[offset..][..size]))
}
fn offset(&self, mut offset: i32, stride: i32, height: i32) -> (usize, usize) {
let size = stride * height;
offset += offset / 20;
offset = (offset + 63) & !63;
(offset as usize, size as usize)
}
#[allow(clippy::too_many_arguments)]
fn dyn_resize(
&mut self,
offset: usize,
width: i32,
stride: i32,
height: i32,
key: K,
format: wl_shm::Format,
) -> Option<()> {
let (offset, size) = self.offset(offset as i32, stride, height);
if self.inner.len() < offset + size {
self.resize(offset + size + size / 20).ok()?;
}
let free = Arc::new(AtomicBool::new(true));
let data = BufferObjectData { free: free.clone() };
let buffer = self.inner.create_buffer_raw(
offset as i32,
width,
height,
stride,
format,
Arc::new(data),
);
self.buffer_list.push(BufferSlot {
offset,
used: 0,
free,
buffer: Some(buffer),
size,
key,
});
Some(())
}
}
struct BufferObjectData {
free: Arc<AtomicBool>,
}
impl wayland_client::backend::ObjectData for BufferObjectData {
fn event(
self: Arc<Self>,
_backend: &wayland_backend::client::Backend,
msg: wayland_backend::protocol::Message<wayland_backend::client::ObjectId, OwnedFd>,
) -> Option<Arc<dyn wayland_backend::client::ObjectData>> {
debug_assert!(wayland_client::backend::protocol::same_interface(
msg.sender_id.interface(),
wl_buffer::WlBuffer::interface()
));
debug_assert!(msg.opcode == 0);
self.free.store(true, Ordering::Relaxed);
None
}
fn destroyed(&self, _: wayland_backend::client::ObjectId) {}
}