use std::collections::*; use crate::glezz; use crate::shader_closure::*; #[derive (Copy, Clone, PartialEq, Eq)] pub enum FrontFace { Cw, Ccw, } impl From for u32 { fn from (v: FrontFace) -> Self { use FrontFace::*; { use gl::*; match v { Cw => CW, Ccw => CCW, } } } } #[derive (Copy, Clone, PartialEq, Eq)] pub enum StencilOp { Keep, Zero, Replace, Incr, Decr, Invert, IncrWrap, DecrWrap, } impl From for u32 { fn from (v: StencilOp) -> Self { use StencilOp::*; { use gl::*; match v { Keep => KEEP, Zero => ZERO, Replace => REPLACE, Incr => INCR, Decr => DECR, Invert => INVERT, IncrWrap => INCR_WRAP, DecrWrap => DECR_WRAP, } } } } #[derive (Copy, Clone, PartialEq, Eq)] pub enum StencilFunc { Never, Always, Less, LessEqual, Equal, Greater, GreaterEqual, NotEqual, } impl From for u32 { fn from (v: StencilFunc) -> Self { use StencilFunc::*; { use gl::*; match v { Never => NEVER, Always => ALWAYS, Less => LESS, LessEqual => LEQUAL, Equal => EQUAL, Greater => GREATER, GreaterEqual => GEQUAL, NotEqual => NOTEQUAL, } } } } #[derive (Copy, Clone, PartialEq, Eq)] pub enum DepthFunc { Never, Always, Less, LessEqual, Equal, Greater, GreaterEqual, NotEqual, } impl From for u32 { fn from (v: DepthFunc) -> Self { use DepthFunc::*; { use gl::*; match v { Never => NEVER, Always => ALWAYS, Less => LESS, LessEqual => LEQUAL, Equal => EQUAL, Greater => GREATER, GreaterEqual => GEQUAL, NotEqual => NOTEQUAL, } } } } #[derive (Copy, Clone, PartialEq, Eq)] pub struct StencilOpState { pub sfail: StencilOp, pub dpfail: StencilOp, pub dppass: StencilOp, } #[derive (Copy, Clone, PartialEq, Eq)] pub struct StencilFuncState { pub func: StencilFunc, pub reference: i32, pub mask: u32, } #[derive (Copy, Clone, PartialEq, Eq)] pub struct StencilState { pub op: StencilOpState, pub func: StencilFuncState, } // These are POD where no extra data is needed // to safely use the flags / numbers #[derive (Clone)] pub struct IsoGlState { pub shader_id: Option , pub flags: HashMap , pub front_face: Option , pub stencil: Option , pub depth_func: Option , pub color_mask: Option <[u8; 4]>, pub depth_mask: Option , pub stencil_mask: Option , } impl Default for IsoGlState { fn default () -> Self { Self { shader_id: None, flags: hashmap! {}, front_face: None, stencil: None, depth_func: None, color_mask: None, depth_mask: None, stencil_mask: None, } } } // These are POD IDs or hashes of non-POD data pub struct NonIsoGlState { } impl Default for NonIsoGlState { fn default () -> Self { Self { } } } pub struct GlState { pub iso: IsoGlState, pub non_iso: NonIsoGlState, } impl std::default::Default for GlState { fn default () -> Self { Self { iso: Default::default (), non_iso: Default::default (), } } } #[derive (Clone, Default)] pub struct Pass { // In the context of a Pass, "None" means "Don't care" pub iso: IsoGlState, } // Builder methods impl Pass { pub fn shader (&mut self, x: &crate::shader_closure::ShaderClosure) -> &mut Self { self.iso.shader_id = Some (x.get_id ()); self } pub fn shader_id >> (&mut self, x: T) -> &mut Self { self.iso.shader_id = x.into (); self } pub fn color_mask >> (&mut self, x: T) -> &mut Self { self.iso.color_mask = x.into (); self } pub fn depth_mask >> (&mut self, x: T) -> &mut Self { self.iso.depth_mask = x.into (); self } pub fn stencil_mask >> (&mut self, x: T) -> &mut Self { self.iso.stencil_mask = x.into (); self } pub fn flag (&mut self, k: u32, v: bool) -> &mut Self { self.iso.flags.insert (k, v); self } pub fn flags > (&mut self, i: I) -> &mut Self { for (k, v) in i { self.flag (k, v); } self } pub fn front_face >> (&mut self, x: T) -> &mut Self { self.iso.front_face = x.into (); self } } // State-diffing stuff impl Pass { pub fn apply_diff (&self, old_state: &mut IsoGlState) { let state = &self.iso; let mut flag_elision_count = 0; for (flag, value) in state.flags.iter () { let old_entry = old_state.flags.entry (*flag); if match &old_entry { hash_map::Entry::Vacant (_) => true, hash_map::Entry::Occupied (o) => o.get () != value, } { if *value { glezz::enable (*flag); } else { glezz::disable (*flag); } (*old_entry.or_insert (*value)) = *value; } else { flag_elision_count += 1; } } if flag_elision_count > 0 { //println! ("Elided {} flags", flag_elision_count); } if let Some (v) = &state.front_face { if old_state.front_face != state.front_face { glezz::front_face ((*v).into ()); old_state.front_face = state.front_face; } else { //println! ("Elided front_face ()"); } } if let Some (v) = &state.stencil { if old_state.stencil != state.stencil { let func = &v.func; unsafe { gl::StencilFunc ( func.func.into (), func.reference.into (), func.mask.into () ); } let op = &v.op; unsafe { gl::StencilOp ( op.sfail.into (), op.dpfail.into (), op.dppass.into () ); } } else { //println! ("Elided stencil state"); } } if let Some (v) = &state.depth_func { if old_state.depth_func != state.depth_func { glezz::depth_func ((*v).into ()); old_state.depth_func = state.depth_func; } else { //println! ("Elided depth_func ()"); } } if let Some ([r, g, b, a]) = &state.color_mask { if old_state.color_mask != state.color_mask { glezz::color_mask (*r, *g, *b, *a); old_state.color_mask = state.color_mask; } else { //println! ("Elided color_mask ()"); } } if let Some (v) = &state.depth_mask { if old_state.depth_mask != state.depth_mask { glezz::depth_mask (*v); old_state.depth_mask = state.depth_mask; } else { //println! ("Elided depth_mask ()"); } } if let Some (v) = &state.stencil_mask { if old_state.stencil_mask != state.stencil_mask { glezz::stencil_mask (*v); old_state.stencil_mask = state.stencil_mask; } else { //println! ("Elided stencil_mask ()"); } } } pub fn apply_slow (&self) { let mut iso = IsoGlState::default (); self.apply_diff (&mut iso); } pub fn with (&self, gl_state: &mut GlState, callback: F) where F: Fn () { self.apply_diff (&mut gl_state.iso); callback (); } pub fn with_shader ( &self, gl_state: &mut GlState, shader_component: &S, callback: F ) where F: Fn (BorrowedShaderVars), S: ShaderLookup { if let Some (s) = self.iso.shader_id { self.apply_diff (&mut gl_state.iso); //self.apply_slow (); shader_component.lookup (s).with (gl_state.iso.shader_id, |shader_vars| { callback (shader_vars); }); gl_state.iso.shader_id = Some (s); } else { panic! ("Called with_shader on a pass with no shader"); } } }