use std::hash::Hash;
use std::marker::PhantomData;
use std::ops::{Index, Range};
use fnv::FnvHashMap;
use gfx_hal::Backend;
use crate::factory::Factory;
#[derive(derivative::Derivative)]
#[derivative(Hash(bound = ""), Clone(bound = ""))]
pub struct ResourceId<B: Backend, R: Resource<B>>(R::Plan, u16);
pub trait Resource<B: Backend> {
type Plan: Hash + Eq + Clone;
fn create(factory: &mut Factory<B>, plan: &Self::Plan) -> Self;
fn destroy(self, factory: &mut Factory<B>);
}
#[derive(derivative::Derivative)]
#[derivative(Default(bound = ""))]
pub struct ResourceGroup<B: Backend, R: Resource<B>> {
#[derivative(Default(value = "0..0"))]
visit_range: Range<usize>,
resources: Vec<R>,
_marker: PhantomData<B>,
}
#[derive(derivative::Derivative)]
#[derivative(Default(bound = ""))]
pub struct Resources<B: Backend, R: Resource<B>> {
groups: FnvHashMap<R::Plan, ResourceGroup<B, R>>,
}
impl<B: Backend, R: Resource<B>> Resources<B, R> {
pub fn add(&mut self, factory: &mut Factory<B>, plan: R::Plan) -> ResourceId<B, R> {
let group = self
.groups
.entry(plan.clone())
.or_insert_with(Default::default);
if group.visit_range.start != group.visit_range.end {
group.visit_range.start += 1;
ResourceId(plan, (group.visit_range.start - 1) as _)
} else {
let new = R::create(factory, &plan);
group.resources.push(new);
ResourceId(plan, (group.resources.len() - 1) as _)
}
}
pub fn reset(&mut self, factory: &mut Factory<B>) {
for group in self.groups.values_mut() {
for node in group.resources.drain(group.visit_range.start..) {
node.destroy(factory);
}
group.visit_range = 0..group.resources.len();
}
}
pub fn get(&self, id: &ResourceId<B, R>) -> Option<&R> {
self.groups
.get(&id.0)
.and_then(|group| group.resources.get(id.1 as usize))
}
pub fn destroy(mut self, factory: &mut Factory<B>) {
for (_, mut group) in self.groups.drain() {
for resource in group.resources.drain(..) {
resource.destroy(factory);
}
}
}
}
impl<'a, B: Backend, R: Resource<B>> Index<&'a ResourceId<B, R>> for Resources<B, R> {
type Output = R;
fn index(&self, id: &'a ResourceId<B, R>) -> &R {
self.get(id).unwrap()
}
}