use crate::lib::*; macro_rules! APPLICATOR { ($n: ident, $($t: ident),+) => { pub trait $n<$($t),+> { #![allow(dead_code)] fn apply(&self, _: unsafe fn($($t),+)); } #[allow(unused_parens)] impl<$($t: GLPrimitive),+> $n<$($t),+> for ($($t),+) { fn apply(&self, func: unsafe fn($($t),+)) { let ($($t),+) = self; GL!(func($(*$t),+)); } } }; } APPLICATOR!(UnpackTuple0, t); APPLICATOR!(UnpackTuple1, t0, t1); APPLICATOR!(UnpackTuple2, t0, t1, t2); APPLICATOR!(UnpackTuple3, t0, t1, t2, t3); APPLICATOR!(UnpackTuple4, t0, t1, t2, t3, t4); APPLICATOR!(UnpackTuple5, t0, t1, t2, t3, t4, t5); pub fn states_map() -> &'static mut HashMap { LocalStatic!(HashMap) } macro_rules! FUNC { ($m: ident, $n: ident, $($t: ty),+) => { pub struct $n; #[allow(unused_parens)] impl $n { fn state() -> &'static mut ($($t),+) { LocalStatic!(($($t),+), { ($(0 as $t),+) }) } fn saved_state() -> &'static mut ($($t),+) { LocalStatic!(($($t),+), { ($(0 as $t),+) }) } pub fn Set(state: ($($t),+)) { let last_s = Self::state(); debug_assert!({ states_map().entry($m::$n as *const () as usize).or_insert_with(|| { ASSERT!(state != *last_s, "First call to GL::{}::Set() must not have all zeros as arguments", stringify!($n)); (false, false) }); true }); if *last_s != state { *last_s = state; state.apply($m::$n); DEBUG!("Set GL::{}({state:?})", stringify!($n)); } } pub fn Save() { debug_assert!({ let (valid, _) = states_map().get_mut(&($m::$n as *const () as usize)).expect(&format!("GL::{}::Save() with default state, nothing to save", stringify!($n))); *valid = true; true }); *Self::saved_state() = *Self::state(); } pub fn Restore() { ASSERT!( { let (valid, _) = states_map().entry($m::$n as *const () as usize).or_insert((false, false)); let r = *valid; *valid = false; r }, "GL::{}::Restore() call not paired with Set()", stringify!($n) ); let state = Self::state(); let prev_s = Self::saved_state(); if *state != *prev_s { *state = *prev_s; state.apply($m::$n); DEBUG!("Restored GL::{}({state:?})", stringify!($n)); } } } }; } pub trait State { fn gl_enable(t: GLenum) { GL!(gl::Enable(t)) } fn gl_disable(t: GLenum) { GL!(gl::Disable(t)) } } pub fn overflow_map() -> &'static mut HashMap { LocalStatic!(HashMap) } macro_rules! SWITCH { ($n: ident) => { impl State for $n {} SWITCH_IMPL!($n, 0); }; ($n: ident, DEFAULT_TRUE) => { impl State for $n {} SWITCH_IMPL!($n, 18446744073709551615); }; ($n: ident, $e: expr, $d: expr, $i: literal) => { impl State for $n { fn gl_enable(_: GLenum) { GL!($e) } fn gl_disable(_: GLenum) { GL!($d) } } SWITCH_IMPL!($n, $i); }; ($n: ident, $e: expr, $d: expr) => { SWITCH!($n, $e, $d, 0); }; ($n: ident, $e: expr, $d: expr, DEFAULT_TRUE) => { SWITCH!($n, $e, $d, 18446744073709551615); }; } macro_rules! SWITCH_IMPL { ($n: ident, $i: literal) => { pub struct $n; impl $n { fn state() -> &'static mut u64 { LocalStatic!(u64, { $i }) } pub fn Enable() { let state = Self::state(); if (*state & 1u64) != 1u64 { Self::gl_enable(gl::$n); DEBUG!("Enabled GL::{}", stringify!($n)); *state |= 1u64; } } pub fn Disable() { let state = Self::state(); if (*state & 1u64) != 0u64 { Self::gl_disable(gl::$n); DEBUG!("Disabled GL::{}", stringify!($n)); *state &= !1u64; } } pub fn Save() { debug_assert!({ let v = overflow_map().entry(gl::$n).or_insert(0); *v = 64.min(*v + 1); true }); let state = Self::state(); *state = (*state & 1u64) | (*state << 1); } pub fn Restore() { ASSERT!( { let v = overflow_map().entry(gl::$n).or_insert(0); *v -= 1; *v >= 0 }, "No state for GL::{} in stack", stringify!($n) ); let state = Self::state(); let s = *state & 1u64; *state >>= 1; if s != (*state & 1u64) { DEBUG!("Restored GL::{}({s})", stringify!($n)); if *state == 0u64 { Self::gl_disable(gl::$n); } else { Self::gl_enable(gl::$n); } } } } }; } pub trait GLPrimitive: Copy {} impl_trait_for!(GLPrimitive = u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize, isize);