#[cfg(feature = "std")]
use std::cell::RefCell;
#[cfg(feature = "std")]
use std::marker::PhantomData;
#[cfg(feature = "std")]
use std::ops::Deref;
#[cfg(feature = "std")]
use std::rc::Rc;
#[cfg(not(feature = "std"))]
use alloc::rc::Rc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use core::cell::RefCell;
#[cfg(not(feature = "std"))]
use core::marker::PhantomData;
#[cfg(not(feature = "std"))]
use core::ops::Deref;
use crate::blending::BlendingState;
use crate::buffer::{Buffer, RawBuffer};
use crate::context::GraphicsContext;
use crate::face_culling::FaceCullingState;
use crate::framebuffer::{ColorSlot, DepthSlot, Framebuffer};
use crate::metagl::*;
use crate::pixel::{Pixel, SamplerType, Type as PxType};
use crate::render_state::RenderState;
use crate::shader::program::{Program, ProgramInterface, Type, Uniform, UniformInterface, Uniformable};
use crate::state::GraphicsState;
use crate::tess::TessSlice;
use crate::texture::{Dim, Dimensionable, Layerable, Texture};
use crate::vertex::Semantics;
struct BindingStack {
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(state: Rc<RefCell<GraphicsState>>) -> Self {
BindingStack {
state,
next_texture_unit: 0,
free_texture_units: Vec::new(),
next_buffer_binding: 0,
free_buffer_bindings: Vec::new(),
}
}
}
pub struct Builder<'a, C> where C: ?Sized {
ctx: &'a mut C,
binding_stack: Rc<RefCell<BindingStack>>,
_borrow: PhantomData<&'a mut ()>,
}
impl<'a, C> Builder<'a, C> where C: ?Sized + GraphicsContext {
pub fn new(ctx: &'a mut C) -> Self {
let state = ctx.state().clone();
Builder {
ctx,
binding_stack: Rc::new(RefCell::new(BindingStack::new(state))),
_borrow: PhantomData,
}
}
pub fn pipeline<'b, L, D, CS, DS, F>(
&'b mut 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<'b>, ShadingGate<'b, C>) {
unsafe {
self.ctx.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 binding_stack = &self.binding_stack;
let p = Pipeline { binding_stack };
let shd_gt = ShadingGate {
ctx: self.ctx,
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::SamplerType>
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.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
.state
.borrow_mut()
.bind_buffer_base(buffer.handle(), binding);
}
BoundBuffer::new(self.binding_stack, binding)
}
}
pub struct BoundTexture<'a, L, D, S>
where L: 'a + Layerable,
D: 'a + Dimensionable,
S: 'a + SamplerType, {
unit: u32,
binding_stack: &'a Rc<RefCell<BindingStack>>,
_t: PhantomData<&'a (L, D, S)>,
}
impl<'a, L, D, S> BoundTexture<'a, L, D, S>
where L: 'a + Layerable,
D: 'a + Dimensionable,
S: 'a + SamplerType {
fn new(binding_stack: &'a Rc<RefCell<BindingStack>>, unit: u32) -> Self {
BoundTexture {
unit,
binding_stack,
_t: PhantomData,
}
}
}
impl<'a, L, D, S> Drop for BoundTexture<'a, L, D, S>
where L: 'a + Layerable,
D: 'a + Dimensionable,
S: 'a + SamplerType {
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, S> Uniformable for &'b BoundTexture<'a, L, D, S>
where L: 'a + Layerable,
D: 'a + Dimensionable,
S: 'a + SamplerType {
fn update(self, u: &Uniform<Self>) {
unsafe { gl::Uniform1i(u.index(), self.unit as GLint) }
}
fn ty() -> Type {
match (S::sample_type(), D::dim()) {
(PxType::NormIntegral, Dim::Dim1) => Type::Sampler1D,
(PxType::NormUnsigned, Dim::Dim1) => Type::Sampler1D,
(PxType::Integral, Dim::Dim1) => Type::ISampler1D,
(PxType::Unsigned, Dim::Dim1) => Type::UISampler1D,
(PxType::Floating, Dim::Dim1) => Type::Sampler1D,
(PxType::NormIntegral, Dim::Dim2) => Type::Sampler2D,
(PxType::NormUnsigned, Dim::Dim2) => Type::Sampler2D,
(PxType::Integral, Dim::Dim2) => Type::ISampler2D,
(PxType::Unsigned, Dim::Dim2) => Type::UISampler2D,
(PxType::Floating, Dim::Dim2) => Type::Sampler2D,
(PxType::NormIntegral, Dim::Dim3) => Type::Sampler3D,
(PxType::NormUnsigned, Dim::Dim3) => Type::Sampler3D,
(PxType::Integral, Dim::Dim3) => Type::ISampler3D,
(PxType::Unsigned, Dim::Dim3) => Type::UISampler3D,
(PxType::Floating, Dim::Dim3) => Type::Sampler3D,
(PxType::NormIntegral, Dim::Cubemap) => Type::Cubemap,
(PxType::NormUnsigned, Dim::Cubemap) => Type::Cubemap,
(PxType::Integral, Dim::Cubemap) => Type::ICubemap,
(PxType::Unsigned, Dim::Cubemap) => Type::UICubemap,
(PxType::Floating, Dim::Cubemap) => Type::Cubemap,
}
}
}
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
}
}
pub struct ShadingGate<'a, C> where C: ?Sized {
ctx: &'a mut C,
binding_stack: &'a Rc<RefCell<BindingStack>>,
}
impl<'a, C> ShadingGate<'a, C> where C: ?Sized + GraphicsContext {
pub fn shade<'b, In, Out, Uni, F>(&'b mut self, program: &Program<In, Out, Uni>, f: F)
where In: Semantics,
Uni: UniformInterface,
F: FnOnce(ProgramInterface<Uni>, RenderGate<'b, C>) {
unsafe {
let bstack = self.binding_stack.borrow_mut();
bstack.state.borrow_mut().use_program(program.handle());
};
let render_gate = RenderGate {
ctx: self.ctx,
binding_stack: self.binding_stack,
};
let program_interface = program.interface();
f(program_interface, render_gate);
}
}
pub struct RenderGate<'a, C> where C: ?Sized {
ctx: &'a mut C,
binding_stack: &'a Rc<RefCell<BindingStack>>,
}
impl<'a, C> RenderGate<'a, C> where C: ?Sized + GraphicsContext {
pub fn render<'b, F>(&'b mut self, rdr_st: RenderState, f: F) where F: FnOnce(TessGate<'b, C>) {
unsafe {
let bstack = self.binding_stack.borrow_mut();
let mut gfx_state = bstack.state.borrow_mut();
match rdr_st.blending {
Some((equation, src_factor, dst_factor)) => {
gfx_state.set_blending_state(BlendingState::On);
gfx_state.set_blending_equation(equation);
gfx_state.set_blending_func(src_factor, dst_factor);
}
None => {
gfx_state.set_blending_state(BlendingState::Off);
}
}
gfx_state.set_depth_test(rdr_st.depth_test);
match rdr_st.face_culling {
Some(face_culling) => {
gfx_state.set_face_culling_state(FaceCullingState::On);
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::Off);
}
}
}
let tess_gate = TessGate {
ctx: self.ctx,
};
f(tess_gate);
}
}
pub struct TessGate<'a, C> where C: ?Sized {
ctx: &'a mut C,
}
impl<'a, C> TessGate<'a, C> where C: ?Sized + GraphicsContext {
pub fn render<'b, T>(&'b mut self, tess: T) where T: Into<TessSlice<'b>> {
tess.into().render(self.ctx);
}
}