use {
super::{Lease, Pool},
crate::driver::{
DriverError,
accel_struct::{AccelerationStructure, AccelerationStructureInfo},
buffer::{Buffer, BufferInfo},
cmd_buf::{CommandBuffer, CommandBufferInfo},
descriptor_set::{DescriptorPool, DescriptorPoolInfo},
image::{Image, ImageInfo},
render_pass::{RenderPass, RenderPassInfo},
},
std::{
collections::HashSet,
ops::{Deref, DerefMut},
},
};
#[derive(Default)]
pub(super) struct ResourceRequests {
pub(super) accel_structs: HashSet<AccelerationStructureInfo>,
pub(super) buffers: HashSet<BufferInfo>,
pub(super) images: HashSet<ImageInfo>,
}
impl ResourceRequests {
fn clear(&mut self) {
self.accel_structs.clear();
self.buffers.clear();
self.images.clear();
}
}
pub(super) trait CollectResources {
fn collect_resources(&mut self, requests: &ResourceRequests);
}
pub struct GarbageCollector<T> {
pool: T,
requests: ResourceRequests,
}
impl<T> GarbageCollector<T> {
pub fn new(pool: T) -> Self {
Self {
pool,
requests: Default::default(),
}
}
}
#[allow(private_bounds)]
impl<T> GarbageCollector<T>
where
T: CollectResources,
{
pub fn collect_resources(&mut self) {
self.pool.collect_resources(&self.requests);
self.requests.clear();
}
}
macro_rules! tracked_pool {
($info:ty => $item:ty, $requests:ident) => {
impl<T> Pool<$info, $item> for GarbageCollector<T>
where
T: Pool<$info, $item>,
{
fn resource(&mut self, info: $info) -> Result<Lease<$item>, DriverError> {
let item = self.pool.resource(info)?;
self.requests.$requests.insert(info);
Ok(item)
}
}
};
}
tracked_pool!(AccelerationStructureInfo => AccelerationStructure, accel_structs);
tracked_pool!(BufferInfo => Buffer, buffers);
tracked_pool!(ImageInfo => Image, images);
macro_rules! forwarded_pool {
($info:ty => $item:ty) => {
impl<T> Pool<$info, $item> for GarbageCollector<T>
where
T: Pool<$info, $item>,
{
fn resource(&mut self, info: $info) -> Result<Lease<$item>, DriverError> {
self.pool.resource(info)
}
}
};
}
forwarded_pool!(CommandBufferInfo => CommandBuffer);
forwarded_pool!(DescriptorPoolInfo => DescriptorPool);
forwarded_pool!(RenderPassInfo => RenderPass);
impl<T> Deref for GarbageCollector<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.pool
}
}
impl<T> DerefMut for GarbageCollector<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.pool
}
}
#[cfg(test)]
mod test {
use {
super::*,
crate::{
driver::{
accel_struct::AccelerationStructureInfoBuilder, buffer::BufferInfoBuilder,
image::ImageInfoBuilder,
},
pool::{SubmissionPool, fifo::FifoPool, hash::HashPool, lazy::LazyPool},
},
ash::vk,
};
#[derive(Default)]
struct CollectSpy {
calls: Vec<(usize, usize, usize)>,
}
impl CollectResources for CollectSpy {
fn collect_resources(&mut self, requests: &ResourceRequests) {
self.calls.push((
requests.accel_structs.len(),
requests.buffers.len(),
requests.images.len(),
));
}
}
struct FailingPool;
impl Pool<BufferInfo, Buffer> for FailingPool {
fn resource(&mut self, _: BufferInfo) -> Result<Lease<Buffer>, DriverError> {
Err(DriverError::Unsupported)
}
}
fn assert_pool_capabilities<T>()
where
T: Pool<AccelerationStructureInfo, AccelerationStructure>
+ Pool<AccelerationStructureInfoBuilder, AccelerationStructure>
+ Pool<BufferInfo, Buffer>
+ Pool<BufferInfoBuilder, Buffer>
+ Pool<ImageInfo, Image>
+ Pool<ImageInfoBuilder, Image>
+ Pool<CommandBufferInfo, CommandBuffer>
+ SubmissionPool,
{
}
fn assert_collect_resources<T: CollectResources>() {}
#[test]
fn collect_resources_sweeps_and_resets_observed_requests() {
let mut collector = GarbageCollector::new(CollectSpy::default());
let info = BufferInfo::device_mem(64, vk::BufferUsageFlags::STORAGE_BUFFER);
collector.requests.buffers.insert(info);
collector.requests.buffers.insert(info);
collector.collect_resources();
collector.collect_resources();
assert_eq!(collector.pool.calls, [(0, 1, 0), (0, 0, 0)]);
}
#[test]
fn failed_requests_are_not_observed() {
let mut collector = GarbageCollector::new(FailingPool);
let info = BufferInfo::device_mem(64, vk::BufferUsageFlags::STORAGE_BUFFER);
assert!(matches!(
Pool::<BufferInfo, Buffer>::resource(&mut collector, info),
Err(DriverError::Unsupported)
));
assert!(collector.requests.buffers.is_empty());
}
#[test]
fn built_in_pools_support_collection_and_pool_capabilities() {
assert_collect_resources::<FifoPool>();
assert_collect_resources::<HashPool>();
assert_collect_resources::<LazyPool>();
assert_pool_capabilities::<GarbageCollector<FifoPool>>();
assert_pool_capabilities::<GarbageCollector<HashPool>>();
assert_pool_capabilities::<GarbageCollector<LazyPool>>();
}
}