//! Phase infrastructure for Gfx. use std::cmp::Ordering; use std::collections::HashMap; use draw_queue; use gfx; use mem; use hprof; /// Potential error occuring during rendering. pub type FlushError = gfx::DrawError; /// An abstract rendering phase. pub trait AbstractPhase { /// Add an entity to the queue. fn enqueue(&mut self, &gfx::Mesh, &gfx::Slice, &M, &V) -> Result; /// Flush the queue into a given stream. fn flush>(&mut self, stream: &mut S) -> Result<(), FlushError>; } /// A rendering object, encapsulating the batch and additional info /// needed for sorting. It is only exposed for this matter and /// accessed by immutable references by the user. #[allow(missing_docs)] pub struct Object { pub batch: gfx::batch::Core

, pub params: P, pub slice: gfx::Slice, pub instances: Option, pub depth: S, pub kernel: K, pub state: gfx::DrawState, } impl Clone for Object where P::Link: Clone, { fn clone(&self) -> Object { 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 { fn draw(&self, stream: &mut X) -> Result<(), gfx::DrawError> where X: gfx::Stream, { 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 Object { /// A helper method to compare the depth, which is only partially ordered. pub fn cmp_depth(&self, other: &Object) -> Ordering { self.depth.partial_cmp(&other.depth) .unwrap_or(Ordering::Equal) } } /// A container for the standard sorting methods. pub mod sort { use std::cmp::Ordering; use gfx::shade::ShaderParam; use super::Object; /// Sort by depth, front-to-back. Useful for opaque objects that updates /// the depth buffer. The front stuff will occlude more pixels, leaving /// less work to be done for the farther objects. pub fn front_to_back( a: &Object, b: &Object) -> Ordering { a.cmp_depth(b) } /// Sort by depth, back-to-front. Useful for transparent objects, since /// blending should take into account everything that lies behind. pub fn back_to_front( a: &Object, b: &Object) -> Ordering { b.cmp_depth(a) } /// Sort by shader program. Switching a program is one of the heaviest /// state changes, so this variant is useful when the order is not important. pub fn program(a: &Object, b: &Object) -> Ordering { a.batch.program().cmp_ref(&b.batch.program()) } /// Sort by mesh. Allows minimizing the vertex format changes. pub fn mesh(a: &Object, b: &Object) -> 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 } /* TODO /// Sort by draw state. pub fn state(a: &Object, b: &Object) -> Ordering { a.batch.state.partial_cmp(&b.batch.state) }*/ } /// Ordering function. pub type OrderFun = fn(&Object, &Object) -> Ordering; /// Phase is doing batch construction, accumulation, and memorization, /// based on a given technique. pub struct Phase< R: gfx::Resources, M: ::Material, V: ::ToDepth, T: ::Technique, Y, // Memory >{ /// Phase name. pub name: String, /// Contained technique. pub technique: T, /// Sorting function. pub sort: Option>, /// Phase memory. memory: Y, /// Sorted draw queue. queue: draw_queue::Queue>, } /// Memory typedef using a `HashMap`. pub type CacheMap< R: gfx::Resources, M: ::Material, V: ::ToDepth, T: ::Technique, > = HashMap<(T::Kernel, gfx::Mesh), mem::MemResult>, >; /// A render phase that caches created render objects. pub type CachedPhase< R: gfx::Resources, M: ::Material, V: ::ToDepth, T: ::Technique, > = Phase>; impl< R: gfx::Resources, M: ::Material, V: ::ToDepth, T: ::Technique, > Phase { /// Create a new phase from a given technique. pub fn new(name: &str, tech: T) -> Phase { Phase { name: name.to_string(), technique: tech, sort: None, memory: (), queue: draw_queue::Queue::new(), } } /// Enable sorting of rendered objects. pub fn with_sort(self, fun: OrderFun) -> Phase { Phase { sort: Some(fun), .. self } } /// Enable caching of created render objects. pub fn with_cache(self) -> CachedPhase { 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, Y: mem::Memory<(T::Kernel, gfx::Mesh), Object >, >AbstractPhase for Phase where T::Params: Clone, ::Link: Clone, { fn enqueue(&mut self, orig_mesh: &gfx::Mesh, slice: &gfx::Slice, material: &M, view_info: &V) -> Result { 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()); //TODO: avoid clone() here // Try recalling from memory 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 => () } // Compile with the technique 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), }; // Create queue object 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 }); // Remember and return 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>(&mut self, stream: &mut S) -> Result<(), FlushError> { match self.sort { Some(fun) => { let g = hprof::enter("sort"); // sort the queue self.queue.sort(fun); drop(g); // accumulate the sorted draws into the renderer 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"); // accumulate the raw draws into the renderer for o in self.queue.objects.iter() { try!(o.draw(stream)); } drop(g); } } // done let _g = hprof::enter("clear"); self.queue.objects.clear(); self.memory.clear(); Ok(()) } }