use rafx_api::{RafxDescriptorSetArray, RafxDeviceContext, RafxResult};
use std::collections::VecDeque;
use std::num::Wrapping;
pub type PoolResourceAllocatorAllocFn<T> =
dyn Fn(&RafxDeviceContext, u32) -> RafxResult<T> + Send + Sync;
pub trait PooledResourceImpl {
fn reset(&mut self) -> RafxResult<()>;
}
struct PoolResourceInFlight<T: PooledResourceImpl> {
pool: T,
live_until_frame: Wrapping<u32>,
}
pub struct PooledResourceAllocator<T: PooledResourceImpl> {
device_context: RafxDeviceContext,
allocate_fn: Box<PoolResourceAllocatorAllocFn<T>>,
in_flight_pools: VecDeque<PoolResourceInFlight<T>>,
reset_pools: Vec<T>,
max_in_flight_frames: Wrapping<u32>,
frame_index: Wrapping<u32>,
created_pool_count: u32,
max_pool_count: u32,
}
impl<T: PooledResourceImpl> PooledResourceAllocator<T> {
pub fn new<F: Fn(&RafxDeviceContext, u32) -> RafxResult<T> + Send + Sync + 'static>(
device_context: &RafxDeviceContext,
max_in_flight_frames: u32,
max_pool_count: u32,
allocate_fn: F,
) -> Self {
PooledResourceAllocator {
device_context: device_context.clone(),
allocate_fn: Box::new(allocate_fn),
in_flight_pools: Default::default(),
reset_pools: Default::default(),
max_in_flight_frames: Wrapping(max_in_flight_frames),
frame_index: Wrapping(0),
created_pool_count: 0,
max_pool_count,
}
}
pub fn allocate_pool(&mut self) -> RafxResult<T> {
self.reset_pools.pop().map(Ok).unwrap_or_else(|| {
let pool_index = self.created_pool_count;
self.created_pool_count += 1;
assert!(self.created_pool_count <= self.max_pool_count);
(self.allocate_fn)(&self.device_context, pool_index)
})
}
pub fn retire_pool(
&mut self,
pool: T,
) {
self.in_flight_pools.push_back(PoolResourceInFlight {
pool,
live_until_frame: self.frame_index + self.max_in_flight_frames,
});
}
pub fn update(&mut self) -> RafxResult<()> {
self.frame_index += Wrapping(1);
let mut pools_to_drain = 0;
for in_flight_pool in &self.in_flight_pools {
if in_flight_pool.live_until_frame - self.frame_index > Wrapping(std::u32::MAX / 2) {
pools_to_drain += 1;
} else {
break;
}
}
let pools_to_reset: Vec<_> = self.in_flight_pools.drain(0..pools_to_drain).collect();
for mut pool_to_reset in pools_to_reset {
T::reset(&mut pool_to_reset.pool)?;
self.reset_pools.push(pool_to_reset.pool);
}
Ok(())
}
pub fn destroy(&mut self) -> RafxResult<()> {
for pool in self.in_flight_pools.drain(..) {
std::mem::drop(pool.pool);
}
for pool in self.reset_pools.drain(..) {
std::mem::drop(pool);
}
Ok(())
}
}
impl<T: PooledResourceImpl> Drop for PooledResourceAllocator<T> {
fn drop(&mut self) {
assert!(self.in_flight_pools.is_empty());
assert!(self.reset_pools.is_empty())
}
}
impl PooledResourceImpl for RafxDescriptorSetArray {
fn reset(&mut self) -> RafxResult<()> {
Ok(())
}
}
pub type DescriptorSetArrayPoolAllocator = PooledResourceAllocator<RafxDescriptorSetArray>;