use std::cell::RefCell;
use device::shade;
use device::shade::UniformValue;
use device::{handle, Resources};
use super::ParamStorage;
pub use device::shade::{Stage, CreateShaderError};
macro_rules! uniform {
($ty_src:ty, $ty_dst:ident) => {
impl Into<UniformValue> for $ty_src {
fn into(self) -> UniformValue {
UniformValue::$ty_dst(self)
}
}
}
}
uniform!(i32, I32);
uniform!(f32, F32);
uniform!([i32; 2], I32Vector2);
uniform!([i32; 3], I32Vector3);
uniform!([i32; 4], I32Vector4);
uniform!([f32; 2], F32Vector2);
uniform!([f32; 3], F32Vector3);
uniform!([f32; 4], F32Vector4);
uniform!([[f32; 2]; 2], F32Matrix2);
uniform!([[f32; 3]; 3], F32Matrix3);
uniform!([[f32; 4]; 4], F32Matrix4);
pub type TextureParam<R: Resources> = (handle::Texture<R>, Option<handle::Sampler<R>>);
#[derive(Clone, PartialEq, Debug)]
pub enum ParameterError {
MissingSelf,
MissingUniform(String),
BadUniform(String),
MissingBlock(String),
BadBlock(String),
MissingTexture(String),
BadTexture(String),
}
pub type ParameterId = u16;
pub trait Parameter<R: Resources> {
fn check_uniform(&shade::UniformVar) -> bool { false }
fn check_block(&shade::BlockVar) -> bool { false }
fn check_texture(&shade::SamplerVar) -> bool { false }
fn put(&self, ParameterId, &mut ParamStorage<R>);
}
impl<T: Clone + Into<UniformValue>, R: Resources> Parameter<R> for T {
fn check_uniform(_var: &shade::UniformVar) -> bool {
true }
fn put(&self, id: ParameterId, storage: &mut ParamStorage<R>) {
storage.uniforms[id as usize] = Some(self.clone().into());
}
}
impl<R: Resources> Parameter<R> for handle::RawBuffer<R> {
fn check_block(_var: &shade::BlockVar) -> bool {
true
}
fn put(&self, id: ParameterId, storage: &mut ParamStorage<R>) {
storage.blocks[id as usize] = Some(self.clone());
}
}
impl<R: Resources> Parameter<R> for TextureParam<R> {
fn check_texture(_var: &shade::SamplerVar) -> bool {
true
}
fn put(&self, id: ParameterId, storage: &mut ParamStorage<R>) {
storage.textures[id as usize] = Some(self.clone());
}
}
#[allow(missing_docs)]
pub trait ShaderParam {
type Resources: Resources;
type Link;
fn create_link(Option<&Self>, &shade::ProgramInfo) -> Result<Self::Link, ParameterError>;
fn fill_params(&self, &Self::Link, &mut ParamStorage<Self::Resources>);
}
impl<R: Resources> ShaderParam for Option<R> {
type Resources = R;
type Link = ();
fn create_link(_: Option<&Option<R>>, info: &shade::ProgramInfo) -> Result<(), ParameterError> {
match info.uniforms[..].first() {
Some(u) => return Err(ParameterError::MissingUniform(u.name.clone())),
None => (),
}
match info.blocks[..].first() {
Some(b) => return Err(ParameterError::MissingBlock(b.name.clone())),
None => (),
}
match info.textures[..].first() {
Some(t) => return Err(ParameterError::MissingTexture(t.name.clone())),
None => (),
}
Ok(())
}
fn fill_params(&self, _: &(), _: &mut ParamStorage<R>) {
}
}
pub struct NamedCell<T> {
pub name: String,
pub value: RefCell<T>,
}
pub struct ParamDictionary<R: Resources> {
pub uniforms: Vec<NamedCell<shade::UniformValue>>,
pub blocks: Vec<NamedCell<handle::RawBuffer<R>>>,
pub textures: Vec<NamedCell<TextureParam<R>>>,
}
pub struct ParamDictionaryLink {
uniforms: Vec<usize>,
blocks: Vec<usize>,
textures: Vec<usize>,
}
impl<R: Resources> ShaderParam for ParamDictionary<R> {
type Resources = R;
type Link = ParamDictionaryLink;
fn create_link(this: Option<&ParamDictionary<R>>, info: &shade::ProgramInfo)
-> Result<ParamDictionaryLink, ParameterError> {
let this = match this {
Some(d) => d,
None => return Err(ParameterError::MissingSelf),
};
Ok(ParamDictionaryLink {
uniforms: info.uniforms.iter().map(|var|
this.uniforms.iter().position(|c| c.name == var.name).unwrap()
).collect(),
blocks: info.blocks.iter().map(|var|
this.blocks .iter().position(|c| c.name == var.name).unwrap()
).collect(),
textures: info.textures.iter().map(|var|
this.textures.iter().position(|c| c.name == var.name).unwrap()
).collect(),
})
}
fn fill_params(&self, link: &ParamDictionaryLink, params: &mut ParamStorage<R>) {
for &id in link.uniforms.iter() {
params.uniforms[id] = Some(self.uniforms[id].value.borrow().clone());
}
for &id in link.blocks.iter() {
params.blocks[id] = Some(self.blocks[id].value.borrow().clone());
}
for &id in link.textures.iter() {
params.textures[id] = Some(self.textures[id].value.borrow().clone());
}
}
}