use std::fmt;
use std::cmp::Ordering;
use std::marker::PhantomData;
use std::ops::Deref;
use draw_state::DrawState;
use device::{Resources, PrimitiveType};
use device::handle::Program as ProgramHandle;
use render::mesh;
use render::mesh::ToSlice;
use shade::{ParameterError, ShaderParam};
use super::ParamStorage;
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
Mesh(mesh::Error),
Parameters(ParameterError),
ContextFull,
}
pub type BatchData<'a, R: Resources> = (&'a mesh::Mesh<R>, mesh::AttributeIter,
&'a mesh::Slice<R>, &'a DrawState);
pub trait Batch<R: Resources> {
type Error: fmt::Debug;
fn get_data(&self) -> Result<BatchData<R>, Self::Error>;
fn fill_params(&self, &mut ParamStorage<R>)
-> Result<&ProgramHandle<R>, Self::Error>;
}
pub type ImplicitBatch<'a, T: ShaderParam> = (
&'a mesh::Mesh<T::Resources>,
mesh::Slice<T::Resources>,
&'a ProgramHandle<T::Resources>,
&'a T,
&'a DrawState
);
pub fn bind<'a, T: ShaderParam>(draw_state: &'a DrawState,
mesh: &'a mesh::Mesh<T::Resources>,
slice: mesh::Slice<T::Resources>,
program: &'a ProgramHandle<T::Resources>,
data: &'a T) -> ImplicitBatch<'a, T> {
(mesh, slice, program, data, draw_state)
}
impl<'a, T: ShaderParam> Batch<T::Resources> for ImplicitBatch<'a, T> {
type Error = Error;
fn get_data(&self) -> Result<BatchData<T::Resources>, Error> {
let (mesh, ref slice, program, _, state) = *self;
match mesh::Link::new(mesh, program.get_info()) {
Ok(link) => Ok((mesh, link.to_iter(), &slice, state)),
Err(e) => Err(Error::Mesh(e)),
}
}
fn fill_params(&self, values: &mut ParamStorage<T::Resources>)
-> Result<&ProgramHandle<T::Resources>, Error> {
let (_, _, program, params, _) = *self;
match ShaderParam::create_link(None::<&T>, program.get_info()) {
Ok(link) => {
values.reserve(program.get_info());
params.fill_params(&link, values);
Ok(program)
},
Err(e) => return Err(Error::Parameters(e)),
}
}
}
pub struct OwnedBatch<T: ShaderParam> {
mesh: mesh::Mesh<T::Resources>,
mesh_link: mesh::Link,
pub slice: mesh::Slice<T::Resources>,
pub param: T,
program: ProgramHandle<T::Resources>,
param_link: T::Link,
pub state: DrawState,
}
impl<T: ShaderParam> OwnedBatch<T> {
pub fn new(mesh: mesh::Mesh<T::Resources>, program: ProgramHandle<T::Resources>, param: T)
-> Result<OwnedBatch<T>, Error> {
let slice = mesh.to_slice(PrimitiveType::TriangleList);
let mesh_link = match mesh::Link::new(&mesh, program.get_info()) {
Ok(l) => l,
Err(e) => return Err(Error::Mesh(e)),
};
let param_link = match ShaderParam::create_link(None::<&T>, program.get_info()) {
Ok(l) => l,
Err(e) => return Err(Error::Parameters(e)),
};
Ok(OwnedBatch {
mesh: mesh,
mesh_link: mesh_link,
slice: slice,
program: program,
param: param,
param_link: param_link,
state: DrawState::new(),
})
}
}
impl<T: ShaderParam> Batch<T::Resources> for OwnedBatch<T> {
type Error = ();
fn get_data(&self) -> Result<BatchData<T::Resources>, ()> {
Ok((&self.mesh, self.mesh_link.to_iter(), &self.slice, &self.state))
}
fn fill_params(&self, values: &mut ParamStorage<T::Resources>)
-> Result<&ProgramHandle<T::Resources>, ()> {
values.reserve(self.program.get_info());
self.param.fill_params(&self.param_link, values);
Ok(&self.program)
}
}
type Index = u16;
struct Id<T>(Index, PhantomData<T>);
impl<T> Copy for Id<T> {}
impl<T> Clone for Id<T> {
fn clone(&self) -> Id<T> { *self }
}
impl<T> Id<T> {
fn unwrap(&self) -> Index {
let Id(i, _) = *self;
i
}
}
impl<T> fmt::Debug for Id<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Id(i, _) = *self;
write!(f, "Id({})", i)
}
}
impl<T> PartialEq for Id<T> {
fn eq(&self, other: &Id<T>) -> bool {
self.unwrap() == other.unwrap()
}
}
impl<T> Eq for Id<T> {}
impl<T> PartialOrd for Id<T> {
fn partial_cmp(&self, other: &Id<T>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for Id<T> {
fn cmp(&self, other: &Id<T>) -> Ordering {
self.unwrap().cmp(&other.unwrap())
}
}
struct Array<T> {
data: Vec<T>,
}
#[derive(Debug)]
pub struct OutOfBounds(pub usize);
impl<T> Array<T> {
fn new() -> Array<T> {
Array {
data: Vec::new(),
}
}
fn get(&self, id: Id<T>) -> Result<&T, OutOfBounds> {
let Id(i, _) = id;
if (i as usize) < self.data.len() {
Ok(&self.data[i as usize])
}else {
Err(OutOfBounds(i as usize))
}
}
}
impl<T: Clone + PartialEq> Array<T> {
fn find_or_insert(&mut self, value: &T) -> Option<Id<T>> {
use num::traits::FromPrimitive;
match self.data.iter().position(|v| v == value) {
Some(i) => FromPrimitive::from_u32(i as u32)
.map(|id| Id(id, PhantomData)),
None => {
FromPrimitive::from_u32(self.data.len() as u32).map(|id| {
self.data.push(value.clone());
Id(id, PhantomData)
})
},
}
}
}
pub struct CoreBatch<T: ShaderParam> {
mesh_id: Id<mesh::Mesh<T::Resources>>,
mesh_link: mesh::Link,
program_id: Id<ProgramHandle<T::Resources>>,
param_link: T::Link,
state_id: Id<DrawState>,
}
impl<T: ShaderParam> Copy for CoreBatch<T> where T::Link: Copy {}
impl<T: ShaderParam> Clone for CoreBatch<T> where T::Link: Clone {
fn clone(&self) -> CoreBatch<T> {
CoreBatch {
mesh_id: self.mesh_id,
mesh_link: self.mesh_link,
program_id: self.program_id,
param_link: self.param_link.clone(),
state_id: self.state_id,
}
}
}
impl<T: ShaderParam> fmt::Debug for CoreBatch<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "CoreBatch(mesh: {:?}, program: {:?}, state: {:?})",
self.mesh_id, self.program_id, self.state_id)
}
}
impl<T: ShaderParam> PartialEq for CoreBatch<T> {
fn eq(&self, other: &CoreBatch<T>) -> bool {
self.program_id == other.program_id &&
self.state_id == other.state_id &&
self.mesh_id == other.mesh_id
}
}
impl<T: ShaderParam> Eq for CoreBatch<T> {}
impl<T: ShaderParam> PartialOrd for CoreBatch<T> {
fn partial_cmp(&self, other: &CoreBatch<T>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T: ShaderParam> Ord for CoreBatch<T> {
fn cmp(&self, other: &CoreBatch<T>) -> Ordering {
(&self.program_id, &self.state_id, &self.mesh_id).cmp(
&(&other.program_id, &other.state_id, &other.mesh_id))
}
}
impl<T: ShaderParam> CoreBatch<T> {
pub fn cmp_mesh(&self, other: &CoreBatch<T>) -> Ordering {
self.mesh_id.cmp(&other.mesh_id)
}
pub fn cmp_program(&self, other: &CoreBatch<T>) -> Ordering {
self.program_id.cmp(&other.program_id)
}
pub fn cmp_state(&self, other: &CoreBatch<T>) -> Ordering {
self.state_id.cmp(&other.state_id)
}
}
pub type CoreBatchFull<'a, T: ShaderParam> = (
&'a CoreBatch<T>,
&'a mesh::Slice<T::Resources>,
&'a T,
&'a Context<T::Resources>
);
pub struct RefBatch<T: ShaderParam> {
pub core: CoreBatch<T>,
pub slice: mesh::Slice<T::Resources>,
pub params: T,
}
impl<T: ShaderParam> Deref for RefBatch<T> {
type Target = CoreBatch<T>;
fn deref(&self) -> &CoreBatch<T> {
&self.core
}
}
impl<T: ShaderParam + Clone> Clone for RefBatch<T> where T::Link: Copy {
fn clone(&self) -> RefBatch<T> {
RefBatch {
core: self.core,
slice: self.slice.clone(),
params: self.params.clone(),
}
}
}
impl<T: ShaderParam + fmt::Debug> fmt::Debug for RefBatch<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "RefBatch(core: {:?}, slice: {:?}, params: {:?}",
self.core, self.slice, self.params)
}
}
pub struct Context<R: Resources> {
meshes: Array<mesh::Mesh<R>>,
programs: Array<ProgramHandle<R>>,
states: Array<DrawState>,
}
pub type RefBatchFull<'a, T: ShaderParam> = (
&'a RefBatch<T>,
&'a Context<T::Resources>
);
impl<R: Resources> Context<R> {
pub fn new() -> Context<R> {
Context {
meshes: Array::new(),
programs: Array::new(),
states: Array::new(),
}
}
}
impl<R: Resources> Context<R> {
fn make<T: ShaderParam<Resources = R>>(&mut self,
program: &ProgramHandle<R>,
params: Option<&T>,
mesh: &mesh::Mesh<R>,
state: &DrawState)
-> Result<CoreBatch<T>, Error> {
let mesh_link = match mesh::Link::new(mesh, program.get_info()) {
Ok(l) => l,
Err(e) => return Err(Error::Mesh(e)),
};
let link = match ShaderParam::create_link(params, program.get_info()) {
Ok(l) => l,
Err(e) => return Err(Error::Parameters(e))
};
let mesh_id = match self.meshes.find_or_insert(mesh) {
Some(id) => id,
None => return Err(Error::ContextFull),
};
let program_id = match self.programs.find_or_insert(program) {
Some(id) => id,
None => return Err(Error::ContextFull),
};
let state_id = match self.states.find_or_insert(state) {
Some(id) => id,
None => return Err(Error::ContextFull),
};
Ok(CoreBatch {
mesh_id: mesh_id,
mesh_link: mesh_link,
program_id: program_id,
param_link: link,
state_id: state_id,
})
}
pub fn make_core<T: ShaderParam<Resources = R>>(&mut self,
program: &ProgramHandle<R>,
mesh: &mesh::Mesh<R>,
state: &DrawState)
-> Result<CoreBatch<T>, Error> {
self.make(program, None, mesh, state)
}
pub fn make_batch<T: ShaderParam<Resources = R>>(&mut self,
program: &ProgramHandle<R>,
params: T,
mesh: &mesh::Mesh<R>,
slice: mesh::Slice<R>,
state: &DrawState)
-> Result<RefBatch<T>, Error> {
self.make(program, Some(¶ms), mesh, state)
.map(|core| RefBatch {
core: core,
slice: slice,
params: params,
})
}
pub fn bind<'a, T: ShaderParam<Resources = R> + 'a>(&'a self,
core: &'a CoreBatch<T>, slice: &'a mesh::Slice<R>,
params: &'a T) -> CoreBatchFull<'a, T> {
(core, slice, params, self)
}
pub fn get_data<'a, T: ShaderParam<Resources = R> + 'a>(&'a self,
core: &CoreBatch<T>, slice: &'a mesh::Slice<R>)
-> Result<BatchData<'a, T::Resources>, OutOfBounds> {
Ok((try!(self.meshes.get(core.mesh_id)),
core.mesh_link.to_iter(),
slice,
try!(self.states.get(core.state_id))
))
}
}
impl<'a, T: ShaderParam + 'a> Batch<T::Resources> for CoreBatchFull<'a, T> {
type Error = OutOfBounds;
fn get_data(&self) -> Result<BatchData<T::Resources>, OutOfBounds> {
let (b, slice, _, ctx) = *self;
ctx.get_data(b, slice)
}
fn fill_params(&self, values: &mut ParamStorage<T::Resources>)
-> Result<&ProgramHandle<T::Resources>, OutOfBounds> {
let (b, _, data, ctx) = *self;
match ctx.programs.get(b.program_id) {
Ok(program) => {
values.reserve(program.get_info());
data.fill_params(&b.param_link, values);
Ok(program)
},
e => e,
}
}
}
impl<'a, T: ShaderParam + 'a> Batch<T::Resources> for RefBatchFull<'a, T> {
type Error = OutOfBounds;
fn get_data(&self) -> Result<BatchData<T::Resources>, OutOfBounds> {
let (b, ctx) = *self;
ctx.get_data(&b.core, &b.slice)
}
fn fill_params(&self, values: &mut ParamStorage<T::Resources>)
-> Result<&ProgramHandle<T::Resources>, OutOfBounds> {
let (b, ctx) = *self;
match ctx.programs.get(b.core.program_id) {
Ok(program) => {
values.reserve(program.get_info());
b.params.fill_params(&b.core.param_link, values);
Ok(program)
},
e => e,
}
}
}