#![allow(missing_docs)]
pub use self::pso::{Data, Init, Meta};
use fnv::FnvHashMap as HashMap;
use gfx::{
buffer::{Info as BufferInfo, Role as BufferRole},
handle::{Buffer, RawBuffer},
memory::{Bind, Usage},
preset::depth::{LESS_EQUAL_TEST, LESS_EQUAL_WRITE},
pso::buffer::{ElemStride, InstanceRate},
shade::{core::UniformValue, ProgramError, ToUniform},
state::{Blend, ColorMask, Comparison, CullFace, Depth, MultiSample, Rasterizer, Stencil},
traits::Pod,
Primitive, ShaderSet,
};
use glsl_layout::Std140;
use crate::{
error::{Error, Result},
pipe::Target,
types::{Encoder, Factory, PipelineState, Resources, Slice},
vertex::Attributes,
};
mod pso;
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum DepthMode {
LessEqualTest,
LessEqualWrite,
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub(crate) enum ProgramSource<'a> {
Simple(&'a [u8], &'a [u8]),
Geometry(&'a [u8], &'a [u8], &'a [u8]),
Tessellated(&'a [u8], &'a [u8], &'a [u8], &'a [u8]),
}
impl<'a> ProgramSource<'a> {
pub fn compile(&self, fac: &mut Factory) -> Result<ShaderSet<Resources>> {
use gfx::traits::FactoryExt;
use gfx::Factory;
match *self {
ProgramSource::Simple(ref vs, ref ps) => fac
.create_shader_set(vs, ps)
.map_err(Error::ProgramCreation),
ProgramSource::Geometry(ref vs, ref gs, ref ps) => {
let v = fac.create_shader_vertex(vs).map_err(ProgramError::Vertex)?;
let g = fac
.create_shader_geometry(gs)
.expect("Geometry shader creation failed");
let p = fac.create_shader_pixel(ps).map_err(ProgramError::Pixel)?;
Ok(ShaderSet::Geometry(v, g, p))
}
ProgramSource::Tessellated(ref vs, ref hs, ref ds, ref ps) => fac
.create_shader_set_tessellation(vs, hs, ds, ps)
.map_err(Error::ProgramCreation),
}
}
}
#[derive(Derivative)]
#[derivative(Clone, Debug, Eq, PartialEq)]
pub struct Effect {
pub pso: PipelineState<Meta>,
pub data: Data,
const_bufs: HashMap<String, usize>,
globals: HashMap<String, usize>,
}
impl Effect {
pub fn update_global<N: AsRef<str>, T: ToUniform>(&mut self, name: N, data: T) {
match self.globals.get(name.as_ref()) {
Some(i) => self.data.globals[*i] = data.convert(),
None => {
warn!(
"Global update for effect failed! Global not found: {:?}",
name.as_ref()
);
}
}
}
pub fn update_buffer<N, T>(&mut self, name: N, data: &[T], enc: &mut Encoder)
where
N: AsRef<str>,
T: Pod,
{
match self.const_bufs.get(name.as_ref()) {
Some(i) => {
let raw = &self.data.const_bufs[*i];
enc.update_buffer::<T>(
unsafe { &*(raw as *const RawBuffer<_> as *const Buffer<_, _>) },
&data[..],
0,
)
.expect("Failed to update buffer (TODO: replace expect)");
}
None => {
warn!(
"Buffer update for effect failed! Buffer not found: {:?}",
name.as_ref()
);
}
}
}
pub fn update_constant_buffer<N, T>(&mut self, name: N, data: &T, enc: &mut Encoder)
where
N: AsRef<str>,
T: Std140,
{
match self.const_bufs.get(name.as_ref()) {
Some(i) => {
let raw = &self.data.const_bufs[*i];
enc.update_constant_buffer::<T>(
unsafe { &*(raw as *const RawBuffer<_> as *const Buffer<_, _>) },
&data,
)
}
None => {
warn!(
"Buffer update for effect failed! Buffer not found: {:?}",
name.as_ref()
);
}
}
}
pub fn clear(&mut self) {
self.data.textures.clear();
self.data.samplers.clear();
self.data.vertex_bufs.clear();
}
pub fn draw(&mut self, slice: &Slice, enc: &mut Encoder) {
enc.draw(&slice, &self.pso, &self.data);
}
}
pub struct NewEffect<'f> {
pub factory: &'f mut Factory,
out: &'f Target,
multisampling: u16,
}
impl<'f> NewEffect<'f> {
pub(crate) fn new(fac: &'f mut Factory, out: &'f Target, multisampling: u16) -> Self {
NewEffect {
factory: fac,
out,
multisampling,
}
}
pub fn simple<S: Into<&'f [u8]>>(self, vs: S, ps: S) -> EffectBuilder<'f> {
let src = ProgramSource::Simple(vs.into(), ps.into());
EffectBuilder::new(self.factory, self.out, self.multisampling, src)
}
pub fn geom<S: Into<&'f [u8]>>(self, vs: S, gs: S, ps: S) -> EffectBuilder<'f> {
let src = ProgramSource::Geometry(vs.into(), gs.into(), ps.into());
EffectBuilder::new(self.factory, self.out, self.multisampling, src)
}
pub fn tess<S: Into<&'f [u8]>>(self, vs: S, hs: S, ds: S, ps: S) -> EffectBuilder<'f> {
let src = ProgramSource::Tessellated(vs.into(), hs.into(), ds.into(), ps.into());
EffectBuilder::new(self.factory, self.out, self.multisampling, src)
}
}
pub struct EffectBuilder<'a> {
factory: &'a mut Factory,
out: &'a Target,
init: Init<'a>,
prim: Primitive,
prog: ProgramSource<'a>,
rast: Rasterizer,
const_bufs: Vec<BufferInfo>,
}
impl<'a> EffectBuilder<'a> {
pub(crate) fn new(
fac: &'a mut Factory,
out: &'a Target,
multisampling: u16,
src: ProgramSource<'a>,
) -> Self {
let mut rast = Rasterizer::new_fill().with_cull_back();
if multisampling > 0 {
rast.samples = Some(MultiSample);
}
EffectBuilder {
factory: fac,
out,
init: Init::default(),
prim: Primitive::TriangleList,
rast,
prog: src,
const_bufs: Vec::new(),
}
}
pub fn without_back_face_culling(&mut self) -> &mut Self {
self.rast.cull_face = CullFace::Nothing;
self
}
pub fn with_raw_global(&mut self, name: &'a str) -> &mut Self {
self.init.globals.push(name);
self
}
pub fn with_raw_constant_buffer(
&mut self,
name: &'a str,
size: usize,
num: usize,
) -> &mut Self {
self.const_bufs.push(BufferInfo {
role: BufferRole::Constant,
bind: Bind::empty(),
usage: Usage::Dynamic,
size: num * size,
stride: size,
});
self.init.const_bufs.push(name);
self
}
pub fn with_primitive_type(&mut self, prim: Primitive) -> &mut Self {
self.prim = prim;
self
}
pub fn with_output(&mut self, name: &'a str, depth: Option<DepthMode>) -> &mut Self {
if let Some(depth) = depth {
self.init.out_depth = Some((
match depth {
DepthMode::LessEqualTest => LESS_EQUAL_TEST,
DepthMode::LessEqualWrite => LESS_EQUAL_WRITE,
},
Stencil::default(),
));
}
if cfg!(target_os = "macos") && depth.is_none() {
self.init.out_depth = Some((
Depth {
fun: Comparison::Always,
write: true,
},
Stencil::default(),
));
}
self.init.out_colors.push(name);
self
}
pub fn with_blended_output(
&mut self,
name: &'a str,
mask: ColorMask,
blend: Blend,
depth: Option<DepthMode>,
) -> &mut Self {
if let Some(depth) = depth {
self.init.out_depth = Some((
match depth {
DepthMode::LessEqualTest => LESS_EQUAL_TEST,
DepthMode::LessEqualWrite => LESS_EQUAL_WRITE,
},
Stencil::default(),
));
}
if cfg!(target_os = "macos") && depth.is_none() {
self.init.out_depth = Some((
Depth {
fun: Comparison::Always,
write: true,
},
Stencil::default(),
));
}
self.init.out_blends.push((name, mask, blend));
self
}
pub fn with_texture(&mut self, name: &'a str) -> &mut Self {
self.init.samplers.push(name);
self.init.textures.push(name);
self
}
pub fn with_raw_vertex_buffer(
&mut self,
attrs: Attributes<'a>,
stride: ElemStride,
rate: InstanceRate,
) -> &mut Self {
self.init.vertex_bufs.push((attrs, stride, rate));
self
}
pub fn build(&mut self) -> Result<Effect> {
use gfx::traits::FactoryExt;
use gfx::Factory;
debug!("Building effect");
debug!("Compiling shaders");
let fac = &mut self.factory;
let prog = self.prog.compile(fac)?;
debug!("Creating pipeline state");
let pso = fac.create_pipeline_state(&prog, self.prim, self.rast, self.init.clone())?;
let mut data = Data::default();
debug!("Creating raw constant buffers");
let const_bufs = self
.init
.const_bufs
.iter()
.enumerate()
.zip(self.const_bufs.drain(..))
.map(|((i, name), info)| {
let cbuf = fac.create_buffer_raw(info)?;
data.const_bufs.push(cbuf);
Ok((name.to_string(), i))
})
.collect::<Result<HashMap<_, _>>>()?;
debug!("Set global uniforms");
let globals = self
.init
.globals
.iter()
.enumerate()
.map(|(i, name)| {
data.globals.push(UniformValue::F32Vector4([0.0; 4]));
(name.to_string(), i)
})
.collect::<HashMap<_, _>>();
debug!("Process Color/Depth/Blend outputs");
data.out_colors.extend(
self.out
.color_bufs()
.iter()
.map(|cb| &cb.as_output)
.cloned(),
);
data.out_blends.extend(
self.out
.color_bufs()
.iter()
.map(|cb| &cb.as_output)
.cloned(),
);
data.out_depth = self
.out
.depth_buf()
.map(|db| (db.as_output.clone(), (0, 0)));
debug!("Finished building effect");
Ok(Effect {
pso,
data,
const_bufs,
globals,
})
}
}