use std::hash::Hash;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Poll, Context};
use anyhow::Result;
use ash::vk;
use futures::Future;
use multimap::{Entry, MultiMap};
use crate::{
Allocator, BufferView, DefaultAllocator, DescriptorCache, Device, Fence, PipelineCache,
ScratchAllocator,
};
pub trait Poolable {
type Key: Clone + Hash + PartialEq + Eq;
fn on_release(&mut self);
fn new_in_pool(pool: &Pool<Self>, key: &Self::Key) -> Result<Pooled<Self>>
where
Self: Sized, {
let item = pool.with(|pool| pool.get(key))?;
Ok(Pooled::from_pool(pool.clone(), key.clone(), item))
}
fn into_pooled(self, pool: &Pool<Self>, key: Self::Key) -> Pooled<Self>
where
Self: Sized, {
Pooled::from_pool(pool.clone(), key, self)
}
}
pub struct Pooled<P: Poolable> {
item: Option<P>,
pool: Pool<P>,
key: Option<P::Key>,
}
type BoxedCreateFunc<P> = Box<dyn FnMut(&<P as Poolable>::Key) -> Result<P>>;
struct PoolInner<P: Poolable> {
items: MultiMap<P::Key, P>,
create_fn: BoxedCreateFunc<P>,
}
pub struct Pool<P: Poolable> {
inner: Arc<Mutex<PoolInner<P>>>,
}
#[derive(Clone, Derivative)]
#[derivative(Debug)]
pub struct ResourcePool<A: Allocator = DefaultAllocator> {
pub pipelines: PipelineCache<A>,
pub descriptors: DescriptorCache,
#[derivative(Debug = "ignore")]
pub allocators: Pool<ScratchAllocator<A>>,
#[derivative(Debug = "ignore")]
pub fences: Pool<Fence<()>>,
}
pub struct ResourcePoolCreateInfo<A: Allocator = DefaultAllocator> {
pub device: Device,
pub allocator: A,
pub scratch_size: u64,
}
pub struct LocalPool<A: Allocator = DefaultAllocator> {
#[allow(dead_code)]
pool: ResourcePool<A>,
scratch_allocator: Pooled<ScratchAllocator<A>>,
}
impl<P: Poolable> Clone for Pool<P> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<P: Poolable> Pooled<P> {
fn from_pool(pool: Pool<P>, key: P::Key, item: P) -> Self {
Self {
item: Some(item),
pool,
key: Some(key),
}
}
pub fn replace<F: FnOnce(P) -> P>(&mut self, f: F) {
let item = self.item.take().unwrap();
self.item = Some(f(item));
}
}
impl<P: Poolable + Future<Output = T>, T> Future for Pooled<P> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let item = unsafe { self.map_unchecked_mut(|poolable| poolable.item.as_mut().unwrap()) };
item.poll(cx)
}
}
impl<P: Poolable> Deref for Pooled<P> {
type Target = P;
fn deref(&self) -> &Self::Target {
self.item.as_ref().unwrap()
}
}
impl<P: Poolable> DerefMut for Pooled<P> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.item.as_mut().unwrap()
}
}
impl<P: Poolable> Drop for Pooled<P> {
fn drop(&mut self) {
let mut item = self.item.take().unwrap();
let key = self.key.take().unwrap();
item.on_release();
self.pool.with(|pool| pool.take(item, key));
}
}
impl<P: Poolable> PoolInner<P> {
fn take(&mut self, item: P, key: P::Key) {
self.items.insert(key, item);
}
fn get(&mut self, key: &P::Key) -> Result<P> {
match self.items.entry(key.clone()) {
Entry::Occupied(mut entry) => {
let vec = entry.get_vec_mut();
match vec.pop() {
None => (self.create_fn)(key),
Some(item) => Ok(item),
}
}
Entry::Vacant(_) => (self.create_fn)(key),
}
}
}
impl<P: Poolable> Pool<P> {
fn with<F: FnOnce(&mut PoolInner<P>) -> R, R>(&self, f: F) -> R {
let mut inner = self.inner.lock().unwrap();
f(&mut inner)
}
pub fn new(create_fn: impl FnMut(&P::Key) -> Result<P> + 'static) -> Result<Self> {
let inner = PoolInner {
items: MultiMap::new(),
create_fn: Box::new(create_fn),
};
Ok(Self {
inner: Arc::new(Mutex::new(inner)),
})
}
}
impl<A: Allocator + 'static> ResourcePool<A> {
pub fn new(info: ResourcePoolCreateInfo<A>) -> Result<Self> {
let pipelines = PipelineCache::new(info.device.clone(), info.allocator.clone())?;
let descriptors = DescriptorCache::new(info.device.clone())?;
let device = info.device.clone();
let mut alloc = info.allocator.clone();
let allocators = Pool::new(move |_| {
ScratchAllocator::new(device.clone(), &mut alloc, info.scratch_size)
})?;
let device = info.device.clone();
let fences = Pool::new(move |_| Ok(Fence::new(device.clone(), false)?))?;
Ok(Self {
pipelines,
descriptors,
allocators,
fences,
})
}
}
impl<A: Allocator> ResourcePool<A> {
pub fn get_scratch_allocator(
&self,
) -> Result<Pooled<ScratchAllocator<A>>> {
ScratchAllocator::new_in_pool(
&self.allocators,
&()
)
}
pub fn next_frame(&self) {
self.pipelines.next_frame();
self.descriptors.next_frame();
}
}
impl<A: Allocator> LocalPool<A> {
pub fn new(pool: ResourcePool<A>) -> Result<Self> {
let alloc = pool.get_scratch_allocator()?;
Ok(Self {
pool,
scratch_allocator: alloc,
})
}
pub fn allocate_scratch_buffer(&mut self, size: vk::DeviceSize) -> Result<BufferView> {
self.scratch_allocator.allocate(size)
}
}