use std::cmp::Ordering;
use std::collections::HashMap;
use draw_queue;
use gfx;
use mem;
use hprof;
pub type FlushError = gfx::DrawError<gfx::batch::Error>;
pub trait AbstractPhase<R: gfx::Resources, M, V: ::ToDepth> {
fn enqueue(&mut self, &gfx::Mesh<R>, &gfx::Slice<R>, &M, &V)
-> Result<bool, gfx::batch::Error>;
fn flush<S: gfx::Stream<R>>(&mut self, stream: &mut S)
-> Result<(), FlushError>;
}
#[allow(missing_docs)]
pub struct Object<S, K, P: gfx::shade::ShaderParam> {
pub batch: gfx::batch::Core<P>,
pub params: P,
pub slice: gfx::Slice<P::Resources>,
pub instances: Option<gfx::InstanceCount>,
pub depth: S,
pub kernel: K,
pub state: gfx::DrawState,
}
impl<S: Copy, K: Copy, P: gfx::shade::ShaderParam + Clone> Clone
for Object<S, K, P> where
P::Link: Clone,
{
fn clone(&self) -> Object<S, K, P> {
Object {
batch: self.batch.clone(),
params: self.params.clone(),
slice: self.slice.clone(),
instances: self.instances.clone(),
depth: self.depth,
kernel: self.kernel,
state: self.state
}
}
}
impl<'a, S, K, P: gfx::shade::ShaderParam> Object<S, K, P> {
fn draw<X>(&self, stream: &mut X)
-> Result<(), gfx::DrawError<gfx::batch::Error>> where
X: gfx::Stream<P::Resources>,
{
let batch = self.batch.with(&self.slice, &self.params, &self.state);
match self.instances {
Some(num) => stream.draw_instanced(&batch, num, 0),
None => stream.draw(&batch),
}
}
}
impl<S: PartialOrd, K, P: gfx::shade::ShaderParam> Object<S, K, P> {
pub fn cmp_depth(&self, other: &Object<S, K, P>) -> Ordering {
self.depth.partial_cmp(&other.depth)
.unwrap_or(Ordering::Equal)
}
}
pub mod sort {
use std::cmp::Ordering;
use gfx::shade::ShaderParam;
use super::Object;
pub fn front_to_back<S: PartialOrd, K, P: ShaderParam>(
a: &Object<S, K, P>, b: &Object<S, K, P>) -> Ordering
{
a.cmp_depth(b)
}
pub fn back_to_front<S: PartialOrd, K, P: ShaderParam>(
a: &Object<S, K, P>, b: &Object<S, K, P>) -> Ordering
{
b.cmp_depth(a)
}
pub fn program<S, K, P: ShaderParam>(a: &Object<S, K, P>, b: &Object<S, K, P>)
-> Ordering
{
a.batch.program().cmp_ref(&b.batch.program())
}
pub fn mesh<S, K, P: ShaderParam>(a: &Object<S, K, P>, b: &Object<S, K, P>)
-> Ordering
{
for (a, b) in a.batch.mesh().attributes.iter().zip(b.batch.mesh().attributes.iter()) {
match a.buffer.cmp_ref(&b.buffer) {
Ordering::Equal => continue,
x => return x
}
}
Ordering::Equal
}
}
pub type OrderFun<S, K, P> = fn(&Object<S, K, P>, &Object<S, K, P>) -> Ordering;
pub struct Phase<
R: gfx::Resources,
M: ::Material,
V: ::ToDepth,
T: ::Technique<R, M, V>,
Y, >{
pub name: String,
pub technique: T,
pub sort: Option<OrderFun<V::Depth, T::Kernel, T::Params>>,
memory: Y,
queue: draw_queue::Queue<Object<V::Depth, T::Kernel, T::Params>>,
}
pub type CacheMap<
R: gfx::Resources,
M: ::Material,
V: ::ToDepth,
T: ::Technique<R, M, V>,
> = HashMap<(T::Kernel, gfx::Mesh<R>),
mem::MemResult<Object<V::Depth, T::Kernel, T::Params>>,
>;
pub type CachedPhase<
R: gfx::Resources,
M: ::Material,
V: ::ToDepth,
T: ::Technique<R, M, V>,
> = Phase<R, M, V, T, CacheMap<R, M, V, T>>;
impl<
R: gfx::Resources,
M: ::Material,
V: ::ToDepth,
T: ::Technique<R, M, V>,
> Phase<R, M, V, T, ()> {
pub fn new(name: &str, tech: T) -> Phase<R, M, V, T, ()> {
Phase {
name: name.to_string(),
technique: tech,
sort: None,
memory: (),
queue: draw_queue::Queue::new(),
}
}
pub fn with_sort(self, fun: OrderFun<V::Depth, T::Kernel, T::Params>)
-> Phase<R, M, V, T, ()> {
Phase {
sort: Some(fun),
.. self
}
}
pub fn with_cache(self) -> CachedPhase<R, M, V, T> {
Phase {
name: self.name,
technique: self.technique,
sort: self.sort,
memory: HashMap::new(),
queue: self.queue,
}
}
}
impl<
R: gfx::Resources,
M: ::Material,
V: ::ToDepth + Copy,
T: ::Technique<R, M, V>,
Y: mem::Memory<(T::Kernel, gfx::Mesh<R>),
Object<V::Depth, T::Kernel, T::Params>
>,
>AbstractPhase<R, M, V> for Phase<R, M, V, T, Y> where
T::Params: Clone,
<T::Params as gfx::shade::ShaderParam>::Link: Clone,
{
fn enqueue(&mut self, orig_mesh: &gfx::Mesh<R>, slice: &gfx::Slice<R>,
material: &M, view_info: &V)
-> Result<bool, gfx::batch::Error> {
let kernel = match self.technique.test(orig_mesh, material) {
Some(k) => k,
None => return Ok(false),
};
let depth = view_info.to_depth();
let key = (kernel, orig_mesh.clone()); match self.memory.lookup(&key) {
Some(Ok(mut o)) => {
o.slice = slice.clone();
o.depth = depth;
assert_eq!(o.kernel, kernel);
self.technique.fix_params(material, view_info, &mut o.params);
self.queue.objects.push(o);
return Ok(true)
},
Some(Err(e)) => return Err(e),
None => ()
}
let (program, mut params, state, instancing) =
self.technique.compile(kernel);
self.technique.fix_params(material, view_info, &mut params);
let mut temp_mesh = gfx::Mesh::new(orig_mesh.num_vertices);
let (instances, mesh) = match instancing {
Some((num, extra_attributes)) => {
temp_mesh.attributes.extend(orig_mesh.attributes.iter()
.chain(extra_attributes.iter()).map(|a| a.clone()));
(Some(num), &temp_mesh)
},
None => (None, orig_mesh),
};
let object = gfx::batch::Core::new(mesh.clone(), program.clone())
.map(|b| Object {
batch: b,
params: params,
slice: slice.clone(),
instances: instances,
depth: depth,
kernel: kernel,
state: *state
});
self.memory.store(key, object.clone());
match object {
Ok(o) => {
self.queue.objects.push(o);
Ok(true)
},
Err(e) => {
warn!("Phase {}: batch creation failed: {:?}", self.name, e);
Err(e)
},
}
}
fn flush<S: gfx::Stream<R>>(&mut self, stream: &mut S)
-> Result<(), FlushError> {
match self.sort {
Some(fun) => {
let g = hprof::enter("sort");
self.queue.sort(fun);
drop(g);
let g = hprof::enter("draw to stream");
for o in self.queue.iter() {
try!(o.draw(stream));
}
drop(g);
},
None => {
let g = hprof::enter("draw to stream");
for o in self.queue.objects.iter() {
try!(o.draw(stream));
}
drop(g);
}
}
let _g = hprof::enter("clear");
self.queue.objects.clear();
self.memory.clear();
Ok(())
}
}