use gl;
use gl::types::*;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::ops::Deref;
use std::rc::Rc;
use buffer::{Buffer, RawBuffer};
use blending::BlendingState;
use context::GraphicsContext;
use face_culling::FaceCullingState;
use framebuffer::{ColorSlot, DepthSlot, Framebuffer};
use pixel::Pixel;
use render_state::RenderState;
use shader::program::{Dim, Program, Type, Uniform, Uniformable, UniformInterface};
use state::GraphicsState;
use tess::TessSlice;
use texture::{Dimensionable, Layerable, Texture};
use vertex::{CompatibleVertex, Vertex};
struct BindingStack {
gfx_state: Rc<RefCell<GraphicsState>>,
next_texture_unit: u32,
free_texture_units: Vec<u32>,
next_buffer_binding: u32,
free_buffer_bindings: Vec<u32>
}
impl BindingStack {
fn new(gfx_state: Rc<RefCell<GraphicsState>>) -> Self {
BindingStack {
gfx_state,
next_texture_unit: 0,
free_texture_units: Vec::new(),
next_buffer_binding: 0,
free_buffer_bindings: Vec::new()
}
}
}
pub struct Builder {
binding_stack: Rc<RefCell<BindingStack>>
}
impl Builder {
pub fn new(gfx_state: Rc<RefCell<GraphicsState>>) -> Self {
Builder {
binding_stack: Rc::new(RefCell::new(BindingStack::new(gfx_state))),
}
}
pub fn pipeline<'a, L, D, CS, DS, F>(
&self,
framebuffer: &Framebuffer<L, D, CS, DS>,
clear_color: [f32; 4],
f: F
)
where L: Layerable,
D: Dimensionable,
CS: ColorSlot<L, D>,
DS: DepthSlot<L, D>,
F: FnOnce(Pipeline, ShadingGate) {
let binding_stack = &self.binding_stack;
unsafe {
let bs = binding_stack.borrow();
bs.gfx_state.borrow_mut().bind_draw_framebuffer(framebuffer.handle());
gl::Viewport(0, 0, framebuffer.width() as GLint, framebuffer.height() as GLint);
gl::ClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]);
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
}
let p = Pipeline { binding_stack };
let shd_gt = ShadingGate { binding_stack };
f(p, shd_gt);
}
}
pub struct Pipeline<'a> {
binding_stack: &'a Rc<RefCell<BindingStack>>
}
impl<'a> Pipeline<'a> {
pub fn bind_texture<L, D, P>(
&'a self,
texture: &'a Texture<L, D, P>
) -> BoundTexture<'a, L, D, P>
where L: 'a + Layerable,
D: 'a + Dimensionable,
P: 'a + Pixel {
let mut bstack = self.binding_stack.borrow_mut();
let unit = bstack.free_texture_units.pop().unwrap_or_else(|| {
let unit = bstack.next_texture_unit;
bstack.next_texture_unit += 1;
unit
});
unsafe {
let mut state = bstack.gfx_state.borrow_mut();
state.set_texture_unit(unit);
state.bind_texture(texture.target(), texture.handle());
}
BoundTexture::new(self.binding_stack, unit)
}
pub fn bind_buffer<T>(&'a self, buffer: &'a T) -> BoundBuffer<'a, T> where T: Deref<Target = RawBuffer> {
let mut bstack = self.binding_stack.borrow_mut();
let binding = bstack.free_buffer_bindings.pop().unwrap_or_else(|| {
let binding = bstack.next_buffer_binding;
bstack.next_buffer_binding += 1;
binding
});
unsafe {
bstack.gfx_state.borrow_mut().bind_buffer_base(buffer.handle(), binding);
}
BoundBuffer::new(self.binding_stack, binding)
}
}
pub struct BoundTexture<'a, L, D, P>
where L: 'a + Layerable,
D: 'a + Dimensionable,
P: 'a + Pixel {
unit: u32,
binding_stack: &'a Rc<RefCell<BindingStack>>,
_t: PhantomData<&'a Texture<L, D, P>>,
}
impl<'a, L, D, P> BoundTexture<'a, L, D, P>
where L: 'a + Layerable,
D: 'a + Dimensionable,
P: 'a + Pixel {
fn new(binding_stack: &'a Rc<RefCell<BindingStack>>, unit: u32) -> Self {
BoundTexture {
unit,
binding_stack,
_t: PhantomData
}
}
}
impl<'a, L, D, P> Drop for BoundTexture<'a, L, D, P>
where L: 'a + Layerable,
D: 'a + Dimensionable,
P: 'a + Pixel {
fn drop(&mut self) {
let mut bstack = self.binding_stack.borrow_mut();
bstack.free_texture_units.push(self.unit);
}
}
unsafe impl<'a, 'b, L, D, P> Uniformable for &'b BoundTexture<'a, L, D, P>
where L: 'a + Layerable,
D: 'a + Dimensionable,
P: 'a + Pixel {
fn update(self, u: &Uniform<Self>) {
unsafe { gl::Uniform1i(u.index(), self.unit as GLint) }
}
fn ty() -> Type { Type::TextureUnit }
fn dim() -> Dim { Dim::Dim1 }
}
pub struct BoundBuffer<'a, T> where T: 'a {
binding: u32,
binding_stack: &'a Rc<RefCell<BindingStack>>,
_t: PhantomData<&'a Buffer<T>>
}
impl<'a, T> BoundBuffer<'a, T> {
fn new(binding_stack: &'a Rc<RefCell<BindingStack>>, binding: u32) -> Self {
BoundBuffer {
binding,
binding_stack,
_t: PhantomData
}
}
}
impl<'a, T> Drop for BoundBuffer<'a, T> {
fn drop(&mut self) {
let mut bstack = self.binding_stack.borrow_mut();
bstack.free_buffer_bindings.push(self.binding);
}
}
unsafe impl<'a, 'b, T> Uniformable for &'b BoundBuffer<'a, T> {
fn update(self, u: &Uniform<Self>) {
unsafe { gl::UniformBlockBinding(u.program(), u.index() as GLuint, self.binding as GLuint) }
}
fn ty() -> Type { Type::BufferBinding }
fn dim() -> Dim { Dim::Dim1 }
}
pub struct ShadingGate<'a> {
binding_stack: &'a Rc<RefCell<BindingStack>>
}
impl<'a> ShadingGate<'a> {
pub fn shade<In, Out, Uni, F>(
&self,
program: &Program<In, Out, Uni>,
f: F
) where In: Vertex,
Uni: UniformInterface,
F: FnOnce(&RenderGate<In>, &Uni) {
unsafe {
let bstack = self.binding_stack.borrow_mut();
bstack.gfx_state.borrow_mut().use_program(program.handle());
};
let render_gate = RenderGate {
binding_stack: self.binding_stack,
_v: PhantomData,
};
let uni_iface = program.uniform_interface();
f(&render_gate, uni_iface);
}
}
pub struct RenderGate<'a, V> {
binding_stack: &'a Rc<RefCell<BindingStack>>,
_v: PhantomData<*const V>
}
impl<'a, V> RenderGate<'a, V> {
pub fn render<F>(&self, rdr_st: RenderState, f: F) where F: FnOnce(&TessGate<V>) {
unsafe {
let bstack = self.binding_stack.borrow_mut();
let mut gfx_state = bstack.gfx_state.borrow_mut();
match rdr_st.blending {
Some((equation, src_factor, dst_factor)) => {
gfx_state.set_blending_state(BlendingState::Enabled);
gfx_state.set_blending_equation(equation);
gfx_state.set_blending_func(src_factor, dst_factor);
},
None => {
gfx_state.set_blending_state(BlendingState::Disabled);
}
}
gfx_state.set_depth_test(rdr_st.depth_test);
match rdr_st.face_culling {
Some(face_culling) => {
gfx_state.set_face_culling_state(FaceCullingState::Enabled);
gfx_state.set_face_culling_order(face_culling.order);
gfx_state.set_face_culling_mode(face_culling.mode);
},
None => {
gfx_state.set_face_culling_state(FaceCullingState::Disabled);
}
}
}
let tess_gate = TessGate {
_v: PhantomData,
};
f(&tess_gate);
}
}
pub struct TessGate<V> {
_v: PhantomData<*const V>
}
impl<V> TessGate<V> where V: Vertex {
pub fn render<C, W>(
&self,
ctx: &mut C,
tess: TessSlice<W>
) where C: GraphicsContext, W: CompatibleVertex<V> {
tess.render(ctx);
}
}