diff --git a/src/bin/pumpkin.rs b/src/bin/pumpkin.rs index 214dc27..d7bec29 100644 --- a/src/bin/pumpkin.rs +++ b/src/bin/pumpkin.rs @@ -152,7 +152,7 @@ struct RenderableArrow { inv_model_mat: Mat4, } -#[derive (Copy, Clone)] +#[derive (Copy, Clone, PartialEq, Eq)] pub enum FrontFace { Cw, Ccw, @@ -171,7 +171,7 @@ impl From for u32 { } } -#[derive (Copy, Clone)] +#[derive (Copy, Clone, PartialEq, Eq)] pub enum StencilOp { Keep, Zero, @@ -202,7 +202,7 @@ impl From for u32 { } } -#[derive (Copy, Clone)] +#[derive (Copy, Clone, PartialEq, Eq)] pub enum StencilFunc { Never, Always, @@ -233,26 +233,30 @@ impl From for u32 { } } +#[derive (Copy, Clone, PartialEq, Eq)] pub struct StencilOpState { sfail: StencilOp, dpfail: StencilOp, dppass: StencilOp, } +#[derive (Copy, Clone, PartialEq, Eq)] pub struct StencilFuncState { func: StencilFunc, reference: i32, mask: u32, } +#[derive (Copy, Clone, PartialEq, Eq)] pub struct StencilState { op: StencilOpState, func: StencilFuncState, } -// Anything that's None is "unknown" +// These are POD where no extra data is needed +// to safely use the flags / numbers -pub struct GlState { +pub struct IsoGlState { flags: HashMap , front_face: Option , stencil: Option , @@ -262,83 +266,174 @@ pub struct GlState { stencil_mask: Option , } +impl std::default::Default for IsoGlState { + fn default () -> Self { + Self { + flags: hashmap! {}, + front_face: None, + stencil: None, + color_mask: None, + depth_mask: None, + stencil_mask: None, + } + } +} + +// These are POD IDs or hashes of non-POD data + +pub struct NonIsoGlState { + shader_id: Option , +} + +impl std::default::Default for NonIsoGlState { + fn default () -> Self { + Self { + shader_id: None, + } + } +} + +pub struct GlState { + iso: IsoGlState, + non_iso: NonIsoGlState, +} + +impl std::default::Default for GlState { + fn default () -> Self { + Self { + iso: Default::default (), + non_iso: Default::default (), + } + } +} + pub struct Pass <'a> { + // In the context of a Pass, "None" means "Don't care" + + iso: IsoGlState, + + // Non-iso state must have its sources here + shader: Option <&'a ShaderClosure>, - - // Anything that's None is "don't care" - - required_state: GlState, } impl Pass <'_> { - pub fn apply_slow (&self) { - let state = &self.required_state; + 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 () { - if *value { - glezz::enable (*flag); + 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); } else { - glezz::disable (*flag); + flag_elision_count += 1; } } + if flag_elision_count > 0 { + //println! ("Elided {} flags", flag_elision_count); + } if let Some (v) = &state.front_face { - glezz::front_face ((*v).into ()); + 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 { - let func = &v.func; - let op = &v.op; - - unsafe { - gl::StencilFunc ( - func.func.into (), - func.reference.into (), - func.mask.into () - ); - gl::StencilOp ( - op.sfail.into (), - op.dpfail.into (), - op.dppass.into () - ); + 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 ((r, g, b, a)) = &state.color_mask { - unsafe { - gl::ColorMask (*r, *g, *b, *a); + 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 { - unsafe { - gl::DepthMask (*v); + 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 { - unsafe { - gl::StencilMask (*v); + 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 with (&self, callback: F) + 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_slow (); + self.apply_diff (&mut gl_state.iso); callback (); } - pub fn with_shader (&self, callback: F) + pub fn with_shader (&self, gl_state: &mut GlState, callback: F) where F: Fn (BorrowedShaderVars) { if let Some (s) = self.shader { - s.with (|shader_vars| { - self.apply_slow (); + self.apply_diff (&mut gl_state.iso); + s.with (gl_state.non_iso.shader_id, |shader_vars| { callback (shader_vars); }); + gl_state.non_iso.shader_id = Some (s.get_id ()); } else { panic! ("Called with_shader on a pass with no shader"); @@ -388,7 +483,7 @@ impl GameGraphics { let shader_diffuse = ShaderClosure::new (shader_from_files ("shaders/pumpkin-vert.glsl", "shaders/pumpkin-frag.glsl"), &uniform_names, &attr_names); let shader_shadow = ShaderClosure::new (shader_from_files ("shaders/shadow-vert.glsl", "shaders/shadow-frag.glsl"), &uniform_names, &attr_names); - shader_diffuse.with (|shader_vars| { + shader_diffuse.with (None, |shader_vars| { let attrs = shader_vars.attrs; use renderable_model::attributes::*; glezz::enable_vertex_attrib_array (attrs [POS]); @@ -462,6 +557,7 @@ impl GameGraphics { pub fn draw ( &self, state: &WorldState, + gl_state: &mut GlState, arrows: &[RenderableArrow] ) { @@ -500,11 +596,13 @@ impl GameGraphics { let light = Vec3::from ((0.25, -0.125, 1.0)).normalize (); + //println! ("Started frame"); + let passes = vec! [ // Clear everything Pass { shader: None, - required_state: GlState { + iso: IsoGlState { flags: hashmap! {}, front_face: None, stencil: None, @@ -516,7 +614,7 @@ impl GameGraphics { // Draw world Pass { shader: Some (&self.shader_diffuse), - required_state: GlState { + iso: IsoGlState { flags: hashmap! { gl::CULL_FACE => true, gl::DEPTH_TEST => true, @@ -533,7 +631,7 @@ impl GameGraphics { // Write shadows into stencil buffer Pass { shader: Some (&self.shader_shadow), - required_state: GlState { + iso: IsoGlState { flags: hashmap! { gl::CULL_FACE => true, gl::DEPTH_TEST => true, @@ -560,7 +658,7 @@ impl GameGraphics { // Draw lit ground Pass { shader: Some (&self.shader_diffuse), - required_state: GlState { + iso: IsoGlState { flags: hashmap! { gl::CULL_FACE => true, gl::DEPTH_TEST => true, @@ -588,7 +686,7 @@ impl GameGraphics { // Draw unlit ground Pass { shader: Some (&self.shader_diffuse), - required_state: GlState { + iso: IsoGlState { flags: hashmap! { gl::CULL_FACE => true, gl::DEPTH_TEST => true, @@ -615,7 +713,7 @@ impl GameGraphics { }, ]; - passes [0].with (|| { + passes [0].with (gl_state, || { glezz::clear_color (1.0f32, 0.0f32, 1.0f32, 1.0f32); glezz::clear (gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); }); @@ -632,7 +730,7 @@ impl GameGraphics { use uniforms::*; // Draw the world except the ground plane - passes [1].with_shader (|shader_vars| { + passes [1].with_shader (gl_state, |shader_vars| { let unis = shader_vars.unis; let attrs = shader_vars.attrs; @@ -688,7 +786,7 @@ impl GameGraphics { }); // Draw shadows into stencil buffer - passes [2].with_shader (|shader_vars| { + passes [2].with_shader (gl_state, |shader_vars| { let unis = shader_vars.unis; let attrs = shader_vars.attrs; @@ -715,7 +813,7 @@ impl GameGraphics { let object_space_light = make_object_space_vec (&inverse_pumpkin, &light); // Draw unlit ground - passes [3].with_shader (|shader_vars| { + passes [3].with_shader (gl_state, |shader_vars| { let unis = shader_vars.unis; let attrs = shader_vars.attrs; @@ -737,7 +835,7 @@ impl GameGraphics { }); // Draw lit ground - passes [4].with_shader (|shader_vars| { + passes [4].with_shader (gl_state, |shader_vars| { let unis = shader_vars.unis; let attrs = shader_vars.attrs; @@ -771,6 +869,7 @@ fn main () { let mut state = WorldState::new (); let graphics = GameGraphics::new (); + let mut gl_state = Default::default (); let mut event_pump = sdl_context.event_pump ().unwrap (); 'running: loop { @@ -848,7 +947,7 @@ fn main () { window.gl_make_current (&gl_ctx).unwrap (); - graphics.draw (&state, &renderable_arrows); + graphics.draw (&state, &mut gl_state, &renderable_arrows); window.gl_swap_window (); diff --git a/src/glezz.rs b/src/glezz.rs index 0c2d1e3..04cee65 100644 --- a/src/glezz.rs +++ b/src/glezz.rs @@ -79,6 +79,24 @@ pub fn uniform_matrix_4fv (uni: i32, m: &Mat4) { } } +pub fn color_mask (r: u8, g: u8, b: u8, a: u8) { + unsafe { + gl::ColorMask (r, g, b, a); + } +} + +pub fn depth_mask (v: u8) { + unsafe { + gl::DepthMask (v); + } +} + +pub fn stencil_mask (v: u32) { + unsafe { + gl::StencilMask (v); + } +} + // More abstract things below here diff --git a/src/shader.rs b/src/shader.rs index 7ebe715..207309c 100644 --- a/src/shader.rs +++ b/src/shader.rs @@ -119,6 +119,10 @@ impl ShaderProgram { } } + pub fn get_id (&self) -> u32 { + self.id + } + // 'use' is a keyword pub fn use_program (&self) { diff --git a/src/shader_closure.rs b/src/shader_closure.rs index 47c24dd..6141979 100644 --- a/src/shader_closure.rs +++ b/src/shader_closure.rs @@ -67,10 +67,19 @@ impl ShaderClosure { } } - pub fn with (&self, callback: F) + pub fn get_id (&self) -> u32 { + self.program.get_id () + } + + pub fn with (&self, previous_id: Option , callback: F) where F: Fn (BorrowedShaderVars) { - self.program.use_program (); + if previous_id != Some (self.get_id ()) { + self.program.use_program (); + } + else { + //println! ("Elided use_program ()"); + } callback (BorrowedShaderVars { unis: &self.uniforms,