gfx-rs / gfx_scene

deprecated
Apache License 2.0
15 stars 1 forks source link

Implement batch hashing on the Phase level #3

Closed kvark closed 9 years ago

kvark commented 9 years ago

Re-creating a batch every time it's requested is heavy, since we've got to do all the same checks again. Instead, we could store compiled batches in a map, defined by Material and Mesh, constraining them to be hashable.

It's not obvious what exactly should be hashed and why. Here are the variants:

kvark commented 9 years ago

If we move the shader parameters out of Material, then the output Batch doesn't depend on it directly, hence we don't even need to hash the material. Moreover, this would allow free modification of material contents without ruining the cache.

kvark commented 9 years ago

This would work, we just need to figure out how (and what) to hash exactly:

use std::collections::hash_map::{HashMap, Entry};

type BatchSource<'a, R> = (&'a gfx::ProgramHandle<R>,
                           &'a gfx::Mesh<R>, gfx::Slice<R>,
                           &'a gfx::DrawState);
//TODO: typedef in gfx::batch
type BatchResult<T: gfx::ShaderParam> =
    Result<gfx::batch::RefBatch<T>, gfx::batch::BatchError>;

trait Memory<T: gfx::ShaderParam> {
    type Error: std::fmt::Debug;
    fn recall(&mut self, BatchSource<T::Resources>,
              &mut gfx::batch::Context<T::Resources>)
              -> BatchResult<T>;
}

impl<T: gfx::ShaderParam> Memory<T> for () {
    type Error = gfx::batch::BatchError;
    fn recall(&mut self, (program, mesh, slice, state): BatchSource<T::Resources>,
              context: &mut gfx::batch::Context<T::Resources>)
              -> BatchResult<T> {
        context.make_batch(program, mesh, slice, state)
    }
}

impl<'a, T: gfx::ShaderParam> Memory<T> for
HashMap<BatchSource<'a, T::Resources>, BatchResult<T>> {
    type Error = gfx::batch::BatchError;
    fn recall(&mut self, (program, mesh, slice, state): BatchSource<T::Resources>,
              context: &mut gfx::batch::Context<T::Resources>)
              -> BatchResult<T> {
        match self.entry((program, mesh, slice, state)) {
            Entry::Occupied(v) => v.clone(),
            Entry::Vacant(v) => {
                let result = context.make_batch(program, mesh, slice, state);
                v.insert(result).clone()
            }
        }
    }
}