use crate::context::CommandContext;
use crate::version::Api;
use crate::version::Version;
use crate::DrawError;
use crate::gl;
#[derive(Debug, Copy, Clone)]
pub struct Depth {
pub test: DepthTest,
pub write: bool,
pub range: (f32, f32),
pub clamp: DepthClamp,
}
impl Default for Depth {
#[inline]
fn default() -> Depth {
Depth {
test: DepthTest::Overwrite,
write: false,
range: (0.0, 1.0),
clamp: DepthClamp::NoClamp,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DepthTest {
Ignore,
Overwrite,
IfEqual,
IfNotEqual,
IfMore,
IfMoreOrEqual,
IfLess,
IfLessOrEqual
}
impl DepthTest {
#[inline]
pub fn requires_depth_buffer(&self) -> bool {
match *self {
DepthTest::Ignore => true,
DepthTest::Overwrite => false,
DepthTest::IfEqual => true,
DepthTest::IfNotEqual => true,
DepthTest::IfMore => true,
DepthTest::IfMoreOrEqual => true,
DepthTest::IfLess => true,
DepthTest::IfLessOrEqual => true,
}
}
#[inline]
fn to_glenum(&self) -> gl::types::GLenum {
match *self {
DepthTest::Ignore => gl::NEVER,
DepthTest::Overwrite => gl::ALWAYS,
DepthTest::IfEqual => gl::EQUAL,
DepthTest::IfNotEqual => gl::NOTEQUAL,
DepthTest::IfMore => gl::GREATER,
DepthTest::IfMoreOrEqual => gl::GEQUAL,
DepthTest::IfLess => gl::LESS,
DepthTest::IfLessOrEqual => gl::LEQUAL,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DepthClamp {
NoClamp,
Clamp,
ClampNear,
ClampFar,
}
pub fn sync_depth(ctxt: &mut CommandContext<'_>, depth: &Depth) -> Result<(), DrawError> {
{
let state = &mut *ctxt.state;
match (depth.clamp, &mut state.enabled_depth_clamp_near,
&mut state.enabled_depth_clamp_far)
{
(DepthClamp::NoClamp, &mut false, &mut false) => (),
(DepthClamp::Clamp, &mut true, &mut true) => (),
(DepthClamp::NoClamp, near, far) => {
if ctxt.version >= &Version(Api::Gl, 3, 0) || ctxt.extensions.gl_arb_depth_clamp ||
ctxt.extensions.gl_nv_depth_clamp
{
unsafe { ctxt.gl.Disable(gl::DEPTH_CLAMP) };
*near = false;
*far = false;
} else {
return Err(DrawError::DepthClampNotSupported);
}
},
(DepthClamp::Clamp, near, far) => {
if ctxt.version >= &Version(Api::Gl, 3, 0) || ctxt.extensions.gl_arb_depth_clamp ||
ctxt.extensions.gl_nv_depth_clamp
{
unsafe { ctxt.gl.Enable(gl::DEPTH_CLAMP) };
*near = true;
*far = true;
} else {
return Err(DrawError::DepthClampNotSupported);
}
},
(DepthClamp::ClampNear, &mut true, &mut false) => (),
(DepthClamp::ClampFar, &mut false, &mut true) => (),
(DepthClamp::ClampNear, &mut true, far) => {
if ctxt.extensions.gl_amd_depth_clamp_separate {
unsafe { ctxt.gl.Disable(gl::DEPTH_CLAMP_FAR_AMD) };
*far = false;
} else {
return Err(DrawError::DepthClampNotSupported);
}
},
(DepthClamp::ClampNear, near @ &mut false, far) => {
if ctxt.extensions.gl_amd_depth_clamp_separate {
unsafe { ctxt.gl.Enable(gl::DEPTH_CLAMP_NEAR_AMD) };
if *far { unsafe { ctxt.gl.Disable(gl::DEPTH_CLAMP_FAR_AMD); } }
*near = true;
*far = false;
} else {
return Err(DrawError::DepthClampNotSupported);
}
},
(DepthClamp::ClampFar, near, &mut true) => {
if ctxt.extensions.gl_amd_depth_clamp_separate {
unsafe { ctxt.gl.Disable(gl::DEPTH_CLAMP_NEAR_AMD) };
*near = false;
} else {
return Err(DrawError::DepthClampNotSupported);
}
},
(DepthClamp::ClampFar, near, far @ &mut false) => {
if ctxt.extensions.gl_amd_depth_clamp_separate {
unsafe { ctxt.gl.Enable(gl::DEPTH_CLAMP_FAR_AMD) };
if *near { unsafe { ctxt.gl.Disable(gl::DEPTH_CLAMP_NEAR_AMD); } }
*near = false;
*far = true;
} else {
return Err(DrawError::DepthClampNotSupported);
}
},
}
}
if depth.range.0 < 0.0 || depth.range.0 > 1.0 ||
depth.range.1 < 0.0 || depth.range.1 > 1.0
{
return Err(DrawError::InvalidDepthRange);
}
if depth.range != ctxt.state.depth_range {
unsafe {
ctxt.gl.DepthRange(depth.range.0 as f64, depth.range.1 as f64);
}
ctxt.state.depth_range = depth.range;
}
if depth.test == DepthTest::Overwrite && !depth.write {
if ctxt.state.enabled_depth_test {
unsafe { ctxt.gl.Disable(gl::DEPTH_TEST) };
ctxt.state.enabled_depth_test = false;
}
return Ok(());
} else if !ctxt.state.enabled_depth_test {
unsafe { ctxt.gl.Enable(gl::DEPTH_TEST) };
ctxt.state.enabled_depth_test = true;
}
unsafe {
let depth_test = depth.test.to_glenum();
if ctxt.state.depth_func != depth_test {
ctxt.gl.DepthFunc(depth_test);
ctxt.state.depth_func = depth_test;
}
}
if depth.write != ctxt.state.depth_mask {
unsafe {
ctxt.gl.DepthMask(if depth.write { gl::TRUE } else { gl::FALSE });
}
ctxt.state.depth_mask = depth.write;
}
Ok(())
}