use std::cmp::Ordering;
use std::collections::HashMap;
use draw_queue;
use gfx;
use mem;
pub type FlushError = gfx::DrawError<gfx::batch::OutOfBounds>;
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::CoreBatch<P>,
pub params: P,
pub slice: gfx::Slice<P::Resources>,
pub depth: S,
pub kernel: K,
}
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(),
depth: self.depth,
kernel: self.kernel,
}
}
}
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.cmp_program(&b.batch)
}
pub fn mesh<S, K, P: ShaderParam>(a: &Object<S, K, P>, b: &Object<S, K, P>)
-> Ordering
{
a.batch.cmp_mesh(&b.batch)
}
pub fn state<S, K, P: ShaderParam>(a: &Object<S, K, P>, b: &Object<S, K, P>)
-> Ordering
{
a.batch.cmp_state(&b.batch)
}
}
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>>,
context: gfx::batch::Context<R>,
}
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(),
context: gfx::batch::Context::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,
context: self.context,
}
}
}
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, inst_mesh, state) =
self.technique.compile(kernel, view_info);
self.technique.fix_params(material, view_info, &mut params);
let mut temp_mesh = gfx::Mesh::new(orig_mesh.num_vertices);
let mesh = match inst_mesh {
Some(m) => {
temp_mesh.attributes.extend(orig_mesh.attributes.iter()
.chain(m.attributes.iter()).map(|a| a.clone()));
&temp_mesh
},
None => orig_mesh,
};
let object = self.context.make_core(program, mesh, state)
.map(|b| Object {
batch: b,
params: params,
slice: slice.clone(),
depth: depth,
kernel: kernel,
});
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) => self.queue.sort(fun),
None => self.queue.update(),
}
for o in self.queue.iter() {
match stream.draw(&self.context.bind(&o.batch, &o.slice, &o.params)) {
Ok(_) => (),
e => return e,
}
}
self.queue.objects.clear();
Ok(())
}
}