use callback::{AsWrapper, IntoCallback, Wrapper};
use handlers::render::gl::GLInit;
use handlers::store::{Store, StoreKey};
use opengles_graphics::{GlGraphics, OpenGL, gl};
use slog;
use slog_scope;
use wlc::{Callback, Output};
use wlc::render::{RenderOutput, RenderView};
pub struct GraphicsRenderer<C: Callback> {
child: C,
logger: slog::Logger,
}
impl StoreKey for GlGraphics {
type Value = GlGraphics;
}
impl<C: Callback + 'static> AsWrapper for GraphicsRenderer<C> {
fn child(&mut self) -> Option<&mut Callback> {
Some(&mut self.child)
}
}
impl<C: Callback + 'static> Callback for Wrapper<GraphicsRenderer<C>> {
fn output_context_created(&mut self, output: &Output) {
output.insert::<GlGraphics>(GlGraphics::new(OpenGL::V2_1));
debug!(self.logger, "GlGraphics initialized");
self.child.output_context_created(output)
}
fn output_context_destroyed(&mut self, output: &Output) {
self.child.output_context_destroyed(output);
output.remove::<GlGraphics>();
}
fn output_render_pre(&mut self, output: &mut RenderOutput) {
{
let graphics = output.get::<GlGraphics>().unwrap();
let mut lock = graphics.write().unwrap();
lock.clear_draw_state();
lock.clear_program();
}
unsafe {
self.wrap(move |self_ref: &mut GraphicsRenderer<C>| self_ref.child.output_render_pre(output))
}
}
fn output_render_post(&mut self, output: &mut RenderOutput) {
{
let graphics = output.get::<GlGraphics>().unwrap();
let mut lock = graphics.write().unwrap();
lock.clear_draw_state();
lock.clear_program();
}
unsafe {
self.wrap(move |self_ref: &mut GraphicsRenderer<C>| self_ref.child.output_render_post(output))
}
}
fn view_render_pre(&mut self, view: &mut RenderView) {
unsafe { self.wrap(move |self_ref: &mut GraphicsRenderer<C>| self_ref.child.view_render_pre(view)) }
}
fn view_render_post(&mut self, view: &mut RenderView) {
unsafe { self.wrap(move |self_ref: &mut GraphicsRenderer<C>| self_ref.child.view_render_post(view)) }
}
}
impl<C: Callback + 'static> GraphicsRenderer<C> {
pub fn new<I: IntoCallback<C>>(renderer: I) -> GLInit<Wrapper<GraphicsRenderer<C>>> {
let renderer = GraphicsRenderer {
child: renderer.into_callback(),
logger: slog_scope::logger().new(o!("handler" => "GraphicsRenderer")),
};
unsafe { GLInit::new::<GraphicsRenderer<C>>(renderer) }
}
unsafe fn wrap<F, R>(&mut self, func: F) -> R
where F: FnOnce(&mut Self) -> R
{
trace!(self.logger, "Backup GLState");
let mut viewport: [gl::types::GLint; 4] = [-1, -1, -1, -1];
gl::GetIntegerv(gl::VIEWPORT, viewport.as_mut_ptr());
let mut program: gl::types::GLint = -1;
gl::GetIntegerv(gl::CURRENT_PROGRAM, &mut program as *mut _);
let mut vao: gl::types::GLint = -1;
gl::GetIntegerv(gl::VERTEX_ARRAY_BINDING, &mut vao as *mut _);
let mut vbo: gl::types::GLint = -1;
gl::GetIntegerv(gl::ARRAY_BUFFER_BINDING, &mut vbo as *mut _);
let is_cullface = gl::IsEnabled(gl::CULL_FACE) == gl::TRUE;
let mut texture: gl::types::GLint = -1;
gl::GetIntegerv(gl::TEXTURE_BINDING_2D, &mut texture as *mut _);
let scissor = match gl::IsEnabled(gl::SCISSOR_TEST) {
gl::TRUE => {
let mut scissor: [gl::types::GLint; 4] = [0, 0, 0, 0];
gl::GetIntegerv(gl::SCISSOR_BOX, scissor.as_mut_ptr());
Some(scissor)
}
_ => None,
};
let stencil = match gl::IsEnabled(gl::STENCIL_TEST) {
gl::TRUE => {
let mut stencil_func: gl::types::GLint = -1;
gl::GetIntegerv(gl::STENCIL_FUNC, &mut stencil_func as *mut _);
let mut stencil_ref: gl::types::GLint = -1;
gl::GetIntegerv(gl::STENCIL_REF, &mut stencil_ref as *mut _);
let mut stencil_value_mask: gl::types::GLint = -1;
gl::GetIntegerv(gl::STENCIL_VALUE_MASK, &mut stencil_value_mask as *mut _);
let mut stencil_writemask: gl::types::GLint = -1;
gl::GetIntegerv(gl::STENCIL_WRITEMASK, &mut stencil_writemask as *mut _);
let mut stencil_op_fail: gl::types::GLint = -1;
gl::GetIntegerv(gl::STENCIL_FAIL, &mut stencil_op_fail as *mut _);
let mut stencil_op_pass_depth_fail: gl::types::GLint = -1;
gl::GetIntegerv(gl::STENCIL_PASS_DEPTH_FAIL,
&mut stencil_op_pass_depth_fail as *mut _);
let mut stencil_op_pass_depth_pass: gl::types::GLint = -1;
gl::GetIntegerv(gl::STENCIL_PASS_DEPTH_PASS,
&mut stencil_op_pass_depth_pass as *mut _);
Some((stencil_func,
stencil_ref,
stencil_value_mask,
stencil_writemask,
stencil_op_fail,
stencil_op_pass_depth_fail,
stencil_op_pass_depth_pass))
}
_ => None,
};
let blend = match gl::IsEnabled(gl::BLEND) {
gl::TRUE => {
let mut blend_color: [gl::types::GLfloat; 4] = [0.0, 0.0, 0.0, 0.0];
gl::GetFloatv(gl::BLEND_COLOR, blend_color.as_mut_ptr());
let mut blend_equation_rgb: gl::types::GLint = -1;
gl::GetIntegerv(gl::BLEND_EQUATION_RGB, &mut blend_equation_rgb as *mut _);
let mut blend_equation_alpha: gl::types::GLint = -1;
gl::GetIntegerv(gl::BLEND_EQUATION_ALPHA,
&mut blend_equation_alpha as *mut _);
let mut blend_src_rgb: gl::types::GLint = -1;
gl::GetIntegerv(gl::BLEND_SRC_RGB, &mut blend_src_rgb as *mut _);
let mut blend_dst_rgb: gl::types::GLint = -1;
gl::GetIntegerv(gl::BLEND_DST_RGB, &mut blend_dst_rgb as *mut _);
let mut blend_src_alpha: gl::types::GLint = -1;
gl::GetIntegerv(gl::BLEND_SRC_ALPHA, &mut blend_src_alpha as *mut _);
let mut blend_dst_alpha: gl::types::GLint = -1;
gl::GetIntegerv(gl::BLEND_DST_ALPHA, &mut blend_dst_alpha as *mut _);
Some((blend_color,
blend_equation_rgb,
blend_equation_alpha,
blend_src_rgb,
blend_dst_rgb,
blend_src_alpha,
blend_dst_alpha))
}
_ => None,
};
trace!(self.logger, "Rendering");
let result = func(self);
trace!(self.logger, "Rendering done");
match blend {
Some((blend_color,
blend_equation_rgb,
blend_equation_alpha,
blend_src_rgb,
blend_dst_rgb,
blend_src_alpha,
blend_dst_alpha)) => {
gl::Enable(gl::BLEND);
gl::BlendColor(blend_color[0],
blend_color[1],
blend_color[2],
blend_color[3]);
gl::BlendEquationSeparate(blend_equation_rgb as gl::types::GLuint,
blend_equation_alpha as gl::types::GLuint);
gl::BlendFuncSeparate(blend_src_rgb as gl::types::GLuint,
blend_dst_rgb as gl::types::GLuint,
blend_src_alpha as gl::types::GLuint,
blend_dst_alpha as gl::types::GLuint);
}
None => gl::Disable(gl::BLEND),
};
match stencil {
Some((stencil_func,
stencil_ref,
stencil_value_mask,
stencil_writemask,
stencil_op_fail,
stencil_op_pass_depth_fail,
stencil_op_pass_depth_pass)) => {
gl::Enable(gl::STENCIL_TEST);
gl::StencilFunc(stencil_func as gl::types::GLuint,
stencil_ref,
stencil_value_mask as gl::types::GLuint);
gl::StencilMask(stencil_writemask as gl::types::GLuint);
gl::StencilOp(stencil_op_fail as gl::types::GLuint,
stencil_op_pass_depth_fail as gl::types::GLuint,
stencil_op_pass_depth_pass as gl::types::GLuint);
}
None => gl::Disable(gl::STENCIL_TEST),
};
match scissor {
Some(scissor) => {
gl::Enable(gl::SCISSOR_TEST);
gl::Scissor(scissor[0], scissor[1], scissor[2], scissor[3]);
}
None => gl::Disable(gl::SCISSOR_TEST),
};
gl::BindTexture(gl::TEXTURE_2D, texture as gl::types::GLuint);
if is_cullface {
gl::Enable(gl::CULL_FACE)
} else {
gl::Disable(gl::CULL_FACE)
};
gl::BindBuffer(gl::ARRAY_BUFFER, vbo as gl::types::GLuint);
gl::BindVertexArray(vao as gl::types::GLuint);
gl::UseProgram(program as gl::types::GLuint);
gl::Viewport(viewport[0], viewport[1], viewport[2], viewport[3]);
trace!(self.logger, "GLState Restored");
result
}
}