Skip to main content

luminance_gl/gl33/
state.rs

1//! Graphics state.
2
3use crate::gl33::{
4  depth_stencil::{
5    comparison_to_glenum, glenum_to_comparison, glenum_to_stencil_op, stencil_op_to_glenum,
6  },
7  vertex_restart::VertexRestart,
8};
9use gl::types::*;
10use luminance::{
11  blending::{Equation, Factor},
12  depth_stencil::{Comparison, StencilOperations, StencilTest, Write},
13  face_culling::{FaceCullingMode, FaceCullingOrder},
14  scissor::ScissorRegion,
15};
16use std::{cell::RefCell, error, ffi::CStr, fmt, marker::PhantomData, os::raw::c_char};
17
18// TLS synchronization barrier for `GLState`.
19//
20// Note: disable on no_std.
21thread_local!(static TLS_ACQUIRE_GFX_STATE: RefCell<Option<()>> = RefCell::new(Some(())));
22
23#[derive(Debug)]
24pub(crate) struct BindingStack {
25  pub(crate) next_texture_unit: u32,
26  pub(crate) free_texture_units: Vec<u32>,
27  pub(crate) next_shader_data: u32,
28  pub(crate) free_shader_data: Vec<u32>,
29}
30
31impl BindingStack {
32  // Create a new, empty binding stack.
33  fn new() -> Self {
34    BindingStack {
35      next_texture_unit: 0,
36      free_texture_units: Vec::new(),
37      next_shader_data: 0,
38      free_shader_data: Vec::new(),
39    }
40  }
41}
42
43/// Cached value.
44///
45/// A cached value is used to prevent issuing costy GPU commands if we know the target value is
46/// already set to what the command tries to set. For instance, if you ask to use a texture ID
47/// `34` once, that value will be set on the GPU and cached on our side. Later, if no other texture
48/// setting has occurred, if you ask to use the texture ID `34` again, because the value is cached,
49/// we know the GPU is already using it, so we don’t have to perform anything GPU-wise.
50///
51/// This optimization has limits and sometimes, because of side-effects, it is not possible to cache
52/// something correctly.
53///
54/// Note: do not confuse [`Cached`] with [`Bind`]. The latter is for internal use only and
55/// is used to either use the regular cache mechanism or override it to force a value to be
56/// written. It cannot be used to invalidate a setting for later use.
57#[derive(Debug)]
58struct Cached<T>(Option<T>)
59where
60  T: PartialEq;
61
62impl<T> Cached<T>
63where
64  T: PartialEq,
65{
66  /// Cache a value.
67  fn new(initial: T) -> Self {
68    Cached(Some(initial))
69  }
70
71  /// Explicitly invalidate a value.
72  ///
73  /// This is necessary when we want to be able to force a GPU command to run.
74  fn invalidate(&mut self) {
75    self.0 = None;
76  }
77
78  fn set(&mut self, value: T) {
79    self.0 = Some(value);
80  }
81
82  /// Check if the cached value is invalid regarding a value.
83  ///
84  /// A non-cached value (i.e. empty) is always invalid whatever compared value. If a value is
85  /// already cached, then it’s invalid if it’s not equal ([`PartialEq`]) to the input value.
86  fn is_invalid(&self, new_val: &T) -> bool {
87    match &self.0 {
88      Some(ref t) => t != new_val,
89      _ => true,
90    }
91  }
92}
93
94/// The graphics state.
95///
96/// This type represents the current state of a given graphics context. It acts
97/// as a forward-gate to all the exposed features from the low-level API but
98/// adds a small cache layer over it to prevent from issuing the same API call (with
99/// the same parameters).
100#[derive(Debug)]
101pub struct GLState {
102  _a: PhantomData<*const ()>, // !Send and !Sync
103
104  // binding stack
105  binding_stack: BindingStack,
106
107  // viewport
108  viewport: Cached<[GLint; 4]>,
109
110  // clear buffers
111  clear_color: Cached<[GLfloat; 4]>,
112  clear_depth: Cached<GLfloat>,
113  clear_stencil: Cached<GLint>,
114
115  // blending
116  blending_state: Cached<BlendingState>,
117  blending_equations: Cached<BlendingEquations>,
118  blending_funcs: Cached<BlendingFactors>,
119
120  // depth test
121  depth_test: Cached<DepthTest>,
122  depth_test_comparison: Cached<Comparison>,
123
124  // depth write
125  depth_write: Cached<Write>,
126
127  // stencil test
128  stencil_test_enabled: Cached<bool>,
129  stencil_test: Cached<StencilTest>,
130  stencil_operations: Cached<StencilOperations>,
131
132  // face culling
133  face_culling_state: Cached<FaceCullingState>,
134  face_culling_order: Cached<FaceCullingOrder>,
135  face_culling_mode: Cached<FaceCullingMode>,
136
137  // scissor
138  scissor_state: Cached<ScissorState>,
139  scissor_region: Cached<ScissorRegion>,
140
141  // vertex restart
142  vertex_restart: Cached<VertexRestart>,
143
144  // patch primitive vertex number
145  patch_vertex_nb: Cached<usize>,
146
147  // texture
148  current_texture_unit: Cached<GLenum>,
149  bound_textures: Vec<(GLenum, GLuint)>,
150
151  // texture buffer used to optimize texture creation; regular textures typically will never ask
152  // for fetching from this set but framebuffers, who often generate several textures, might use
153  // this opportunity to get N textures (color, depth and stencil) at once, in a single CPU / GPU
154  // roundtrip
155  //
156  // fishy fishy
157  texture_swimming_pool: Vec<GLuint>,
158
159  // uniform buffer
160  bound_uniform_buffers: Vec<GLuint>,
161
162  // array buffer
163  bound_array_buffer: GLuint,
164
165  // element buffer
166  bound_element_array_buffer: GLuint,
167
168  // framebuffer
169  bound_draw_framebuffer: Cached<GLuint>,
170
171  // vertex array
172  bound_vertex_array: GLuint,
173
174  // shader program
175  current_program: GLuint,
176
177  // framebuffer sRGB
178  srgb_framebuffer_enabled: Cached<bool>,
179
180  // vendor name; cached when asked the first time and then re-used
181  vendor_name: Option<String>,
182
183  // renderer name; cached when asked the first time and then re-used
184  renderer_name: Option<String>,
185
186  // OpenGL version; cached when asked the first time and then re-used
187  gl_version: Option<String>,
188
189  // GLSL version; cached when asked the first time and then re-used
190  glsl_version: Option<String>,
191
192  /// Maximum number of elements a texture array can hold.
193  max_texture_array_elements: Option<usize>,
194}
195
196impl GLState {
197  /// Create a new `GLState`.
198  ///
199  /// > Note: keep in mind you can create only one per thread. However, if you’re building without
200  /// > standard library, this function will always return successfully. You have to take extra care
201  /// > in this case.
202  pub(crate) fn new() -> Result<Self, StateQueryError> {
203    TLS_ACQUIRE_GFX_STATE.with(|rc| {
204      let mut inner = rc.borrow_mut();
205
206      match *inner {
207        Some(_) => {
208          inner.take();
209          Self::get_from_context()
210        }
211
212        None => Err(StateQueryError::UnavailableGLState),
213      }
214    })
215  }
216
217  /// Get a `GraphicsContext` from the current OpenGL context.
218  fn get_from_context() -> Result<Self, StateQueryError> {
219    unsafe {
220      let binding_stack = BindingStack::new();
221      let viewport = Cached::new(get_ctx_viewport()?);
222      let clear_color = Cached::new(get_ctx_clear_color()?);
223      let clear_depth = Cached::new(get_ctx_clear_depth()?);
224      let clear_stencil = Cached::new(get_ctx_clear_stencil()?);
225      let blending_state = Cached::new(get_ctx_blending_state()?);
226      let blending_equations = Cached::new(get_ctx_blending_equations()?);
227      let blending_funcs = Cached::new(get_ctx_blending_factors()?);
228      let depth_test = Cached::new(get_ctx_depth_test()?);
229      let depth_test_comparison = Cached::new(Comparison::Less);
230      let depth_write = Cached::new(get_ctx_depth_write()?);
231      let stencil_test_enabled = Cached::new(get_ctx_stencil_test_enabled()?);
232      let stencil_test = Cached::new(get_ctx_stencil_test()?);
233      let stencil_operations = Cached::new(get_ctx_stencil_operations()?);
234      let face_culling_state = Cached::new(get_ctx_face_culling_state()?);
235      let face_culling_order = Cached::new(get_ctx_face_culling_order()?);
236      let face_culling_mode = Cached::new(get_ctx_face_culling_mode()?);
237      let vertex_restart = Cached::new(get_ctx_vertex_restart()?);
238      let patch_vertex_nb = Cached::new(0);
239      let current_texture_unit = Cached::new(get_ctx_current_texture_unit()?);
240      let bound_textures = vec![(gl::TEXTURE_2D, 0); 48]; // 48 is the platform minimal requirement
241      let texture_swimming_pool = Vec::new();
242      let bound_uniform_buffers = vec![0; 36]; // 36 is the platform minimal requirement
243      let bound_array_buffer = 0;
244      let bound_element_array_buffer = 0;
245      let bound_draw_framebuffer = Cached::new(get_ctx_bound_draw_framebuffer()?);
246      let bound_vertex_array = get_ctx_bound_vertex_array()?;
247      let current_program = get_ctx_current_program()?;
248      let srgb_framebuffer_enabled = Cached::new(get_ctx_srgb_framebuffer_enabled()?);
249      let scissor_state = Cached::new(get_ctx_scissor_state()?);
250      let scissor_region = Cached::new(get_ctx_scissor_region()?);
251      let vendor_name = None;
252      let renderer_name = None;
253      let gl_version = None;
254      let glsl_version = None;
255      let max_texture_array_elements = None;
256
257      Ok(GLState {
258        _a: PhantomData,
259        binding_stack,
260        viewport,
261        clear_color,
262        clear_depth,
263        clear_stencil,
264        blending_state,
265        blending_equations,
266        blending_funcs,
267        depth_test,
268        depth_test_comparison,
269        depth_write,
270        stencil_test_enabled,
271        stencil_test,
272        stencil_operations,
273        face_culling_state,
274        face_culling_order,
275        face_culling_mode,
276        vertex_restart,
277        patch_vertex_nb,
278        current_texture_unit,
279        bound_textures,
280        texture_swimming_pool,
281        bound_uniform_buffers,
282        bound_array_buffer,
283        bound_element_array_buffer,
284        bound_draw_framebuffer,
285        bound_vertex_array,
286        current_program,
287        srgb_framebuffer_enabled,
288        scissor_state,
289        scissor_region,
290        vendor_name,
291        renderer_name,
292        gl_version,
293        glsl_version,
294        max_texture_array_elements,
295      })
296    }
297  }
298
299  /// Invalidate the currently in-use vertex array.
300  pub fn invalidate_vertex_array(&mut self) {
301    self.bound_vertex_array = 0;
302  }
303
304  /// Invalidate the currently in-use array buffer.
305  pub fn invalidate_array_buffer(&mut self) {
306    self.bound_array_buffer = 0;
307  }
308
309  /// Invalidate the currently in-use shader program.
310  pub fn invalidate_shader_program(&mut self) {
311    self.current_program = 0;
312  }
313
314  /// Invalidate the currently in-use framebuffer.
315  pub fn invalidate_framebuffer(&mut self) {
316    self.bound_draw_framebuffer.invalidate();
317  }
318
319  /// Invalidate the currently in-use element array buffer.
320  pub fn invalidate_element_array_buffer(&mut self) {
321    self.bound_element_array_buffer = 0;
322  }
323
324  /// Invalidate the currently in-use texture unit.
325  pub fn invalidate_texture_unit(&mut self) {
326    self.current_texture_unit.invalidate();
327  }
328
329  /// Invalidate the texture bindings.
330  pub fn invalidate_bound_textures(&mut self) {
331    for t in &mut self.bound_textures {
332      *t = (gl::TEXTURE_2D, 0);
333    }
334  }
335
336  /// Invalidate the uniform buffer bindings.
337  pub fn invalidate_bound_uniform_buffers(&mut self) {
338    for b in &mut self.bound_uniform_buffers {
339      *b = 0;
340    }
341  }
342
343  /// Invalidate the currently in-use viewport.
344  pub fn invalidate_viewport(&mut self) {
345    self.viewport.invalidate()
346  }
347
348  /// Invalidate the currently in-use clear color.
349  pub fn invalidate_clear_color(&mut self) {
350    self.clear_color.invalidate()
351  }
352
353  /// Invalidate the currently in-use blending state.
354  pub fn invalidate_blending_state(&mut self) {
355    self.blending_state.invalidate()
356  }
357
358  /// Invalidate the currently in-use blending equation.
359  pub fn invalidate_blending_equation(&mut self) {
360    self.blending_equations.invalidate()
361  }
362
363  /// Invalidate the currently in-use blending function.
364  pub fn invalidate_blending_func(&mut self) {
365    self.blending_funcs.invalidate()
366  }
367
368  /// Invalidate the currently in-use depth test.
369  pub fn invalidate_depth_test(&mut self) {
370    self.depth_test.invalidate()
371  }
372
373  /// Invalidate the currently in-use depth test comparison.
374  pub fn invalidate_depth_test_comparison(&mut self) {
375    self.depth_test_comparison.invalidate()
376  }
377
378  /// Invalidate the currently in-use depth write state.
379  pub fn invalidate_depth_write(&mut self) {
380    self.depth_write.invalidate()
381  }
382
383  /// Invalidate the currently in-use face culling state.
384  pub fn invalidate_face_culling_state(&mut self) {
385    self.face_culling_state.invalidate()
386  }
387
388  /// Invalidate the currently in-use face culling order.
389  pub fn invalidate_face_culling_order(&mut self) {
390    self.face_culling_order.invalidate()
391  }
392
393  /// Invalidate the currently in-use face culling mode.
394  pub fn invalidate_face_culling_mode(&mut self) {
395    self.face_culling_mode.invalidate()
396  }
397
398  /// Invalidate the currently in-use vertex restart state.
399  pub fn invalidate_vertex_restart(&mut self) {
400    self.vertex_restart.invalidate()
401  }
402
403  /// Invalidate the currently in-use patch vertex number.
404  pub fn invalidate_patch_vertex_nb(&mut self) {
405    self.patch_vertex_nb.invalidate()
406  }
407
408  /// Invalidate the currently in-use sRGB framebuffer state.
409  pub fn invalidate_srgb_framebuffer_enabled(&mut self) {
410    self.srgb_framebuffer_enabled.invalidate()
411  }
412
413  /// Marshal a string represented as `*const c_uchar`, represented by the input argument, into a `&str`.
414  ///
415  /// The string is returned in a lossy way, which means that non-unicode characters go wheeeeeeeeeeee.
416  fn marshal_gl_string(repr: GLenum) -> String {
417    unsafe {
418      let name_ptr = gl::GetString(repr);
419      let name = CStr::from_ptr(name_ptr as *const c_char);
420      name.to_string_lossy().into_owned()
421    }
422  }
423
424  /// Get the OpenGL vendor name.
425  ///
426  /// Cache the name on the first call and then re-use it for later calls.
427  pub fn get_vendor_name(&mut self) -> String {
428    self.vendor_name.as_ref().cloned().unwrap_or_else(|| {
429      let name = Self::marshal_gl_string(gl::VENDOR);
430      self.vendor_name = Some(name.clone());
431      name
432    })
433  }
434
435  /// Get the OpenGL renderer name.
436  ///
437  /// Cache the name on the first call and then re-use it for later calls.
438  pub fn get_renderer_name(&mut self) -> String {
439    self.renderer_name.as_ref().cloned().unwrap_or_else(|| {
440      let name = Self::marshal_gl_string(gl::RENDERER);
441      self.renderer_name = Some(name.clone());
442      name
443    })
444  }
445
446  /// Get the OpenGL version.
447  ///
448  /// Cache the version on the first call and then re-use it for later calls.
449  pub fn get_gl_version(&mut self) -> String {
450    self.gl_version.as_ref().cloned().unwrap_or_else(|| {
451      let version = Self::marshal_gl_string(gl::VERSION);
452      self.gl_version = Some(version.clone());
453      version
454    })
455  }
456
457  /// Get the GLSL version.
458  ///
459  /// Cache the version on the first call and then re-use it for later calls.
460  pub fn get_glsl_version(&mut self) -> String {
461    self.glsl_version.as_ref().cloned().unwrap_or_else(|| {
462      let version = Self::marshal_gl_string(gl::SHADING_LANGUAGE_VERSION);
463      self.glsl_version = Some(version.clone());
464      version
465    })
466  }
467
468  /// Get the number of maximum elements an array texture can hold.
469  ///
470  /// Cache the number on the first call and then re-use it for later calls.
471  pub fn get_max_texture_array_elements(&mut self) -> usize {
472    self.max_texture_array_elements.unwrap_or_else(|| {
473      let mut max = 0;
474      unsafe { gl::GetIntegerv(gl::MAX_ARRAY_TEXTURE_LAYERS, &mut max) };
475      let max = max as usize;
476      self.max_texture_array_elements = Some(max);
477      max
478    })
479  }
480
481  pub(crate) fn binding_stack_mut(&mut self) -> &mut BindingStack {
482    &mut self.binding_stack
483  }
484
485  pub(crate) fn create_texture(&mut self) -> GLuint {
486    self.texture_swimming_pool.pop().unwrap_or_else(|| {
487      let mut texture = 0;
488
489      unsafe { gl::GenTextures(1, &mut texture) };
490      texture
491    })
492  }
493
494  /// Reserve at least a given number of textures.
495  pub(crate) fn reserve_textures(&mut self, nb: usize) {
496    let available = self.texture_swimming_pool.len();
497    let needed = nb.max(available) - available;
498
499    if needed > 0 {
500      // resize the internal buffer to hold all the new textures and create a slice starting from
501      // the previous end to the new end
502      self.texture_swimming_pool.resize(available + needed, 0);
503      let textures = &mut self.texture_swimming_pool[available..];
504
505      unsafe { gl::GenTextures(needed as _, textures.as_mut_ptr()) };
506    }
507  }
508
509  pub(crate) unsafe fn set_viewport(&mut self, viewport: [GLint; 4]) {
510    if self.viewport.is_invalid(&viewport) {
511      gl::Viewport(viewport[0], viewport[1], viewport[2], viewport[3]);
512      self.viewport.set(viewport);
513    }
514  }
515
516  pub(crate) unsafe fn set_clear_color(&mut self, clear_color: [GLfloat; 4]) {
517    if self.clear_color.is_invalid(&clear_color) {
518      gl::ClearColor(
519        clear_color[0],
520        clear_color[1],
521        clear_color[2],
522        clear_color[3],
523      );
524      self.clear_color.set(clear_color);
525    }
526  }
527
528  pub(crate) unsafe fn set_clear_depth(&mut self, clear_depth: GLfloat) {
529    if self.clear_depth.is_invalid(&clear_depth) {
530      gl::ClearDepth(clear_depth as _);
531      self.clear_depth.set(clear_depth);
532    }
533  }
534
535  pub(crate) unsafe fn set_clear_stencil(&mut self, clear_stencil: GLint) {
536    if self.clear_stencil.is_invalid(&clear_stencil) {
537      gl::ClearStencil(clear_stencil);
538      self.clear_stencil.set(clear_stencil);
539    }
540  }
541
542  pub(crate) unsafe fn set_blending_state(&mut self, state: BlendingState) {
543    if self.blending_state.is_invalid(&state) {
544      match state {
545        BlendingState::On => gl::Enable(gl::BLEND),
546        BlendingState::Off => gl::Disable(gl::BLEND),
547      }
548
549      self.blending_state.set(state);
550    }
551  }
552
553  pub(crate) unsafe fn set_scissor_state(&mut self, state: ScissorState) {
554    if self.scissor_state.is_invalid(&state) {
555      match state {
556        ScissorState::On => gl::Enable(gl::SCISSOR_TEST),
557        ScissorState::Off => gl::Disable(gl::SCISSOR_TEST),
558      }
559
560      self.scissor_state.set(state);
561    }
562  }
563
564  pub(crate) unsafe fn set_scissor_region(&mut self, region: &ScissorRegion) {
565    if self.scissor_region.is_invalid(region) {
566      let ScissorRegion {
567        x,
568        y,
569        width,
570        height,
571      } = *region;
572
573      gl::Scissor(x as GLint, y as GLint, width as GLint, height as GLint);
574
575      self.scissor_region.set(*region);
576    }
577  }
578
579  pub(crate) unsafe fn set_blending_equation(&mut self, equation: Equation) {
580    let equations = BlendingEquations {
581      rgb: equation,
582      alpha: equation,
583    };
584
585    if self.blending_equations.is_invalid(&equations) {
586      gl::BlendEquation(from_blending_equation(equation));
587      self.blending_equations.set(equations);
588    }
589  }
590
591  pub(crate) unsafe fn set_blending_equation_separate(
592    &mut self,
593    equation_rgb: Equation,
594    equation_alpha: Equation,
595  ) {
596    let equations = BlendingEquations {
597      rgb: equation_rgb,
598      alpha: equation_alpha,
599    };
600
601    if self.blending_equations.is_invalid(&equations) {
602      gl::BlendEquationSeparate(
603        from_blending_equation(equation_rgb),
604        from_blending_equation(equation_alpha),
605      );
606
607      self.blending_equations.set(equations);
608    }
609  }
610
611  pub(crate) unsafe fn set_blending_func(&mut self, src: Factor, dst: Factor) {
612    let funcs = BlendingFactors {
613      src_rgb: src,
614      dst_rgb: dst,
615      src_alpha: src,
616      dst_alpha: dst,
617    };
618
619    if self.blending_funcs.is_invalid(&funcs) {
620      gl::BlendFunc(from_blending_factor(src), from_blending_factor(dst));
621      self.blending_funcs.set(funcs);
622    }
623  }
624
625  pub(crate) unsafe fn set_blending_func_separate(
626    &mut self,
627    src_rgb: Factor,
628    dst_rgb: Factor,
629    src_alpha: Factor,
630    dst_alpha: Factor,
631  ) {
632    let funcs = BlendingFactors {
633      src_rgb,
634      dst_rgb,
635      src_alpha,
636      dst_alpha,
637    };
638
639    if self.blending_funcs.is_invalid(&funcs) {
640      gl::BlendFuncSeparate(
641        from_blending_factor(src_rgb),
642        from_blending_factor(dst_rgb),
643        from_blending_factor(src_alpha),
644        from_blending_factor(dst_alpha),
645      );
646
647      self.blending_funcs.set(funcs);
648    }
649  }
650
651  pub(crate) unsafe fn set_depth_test(&mut self, depth_test: DepthTest) {
652    if self.depth_test.is_invalid(&depth_test) {
653      match depth_test {
654        DepthTest::On => gl::Enable(gl::DEPTH_TEST),
655        DepthTest::Off => gl::Disable(gl::DEPTH_TEST),
656      }
657
658      self.depth_test.set(depth_test);
659    }
660  }
661
662  pub(crate) unsafe fn set_depth_test_comparison(&mut self, depth_test_comparison: Comparison) {
663    if self
664      .depth_test_comparison
665      .is_invalid(&depth_test_comparison)
666    {
667      gl::DepthFunc(comparison_to_glenum(depth_test_comparison));
668      self.depth_test_comparison.set(depth_test_comparison);
669    }
670  }
671
672  pub(crate) unsafe fn set_depth_write(&mut self, depth_write: Write) {
673    if self.depth_write.is_invalid(&depth_write) {
674      let enabled = match depth_write {
675        Write::On => gl::TRUE,
676        Write::Off => gl::FALSE,
677      };
678
679      gl::DepthMask(enabled);
680
681      self.depth_write.set(depth_write);
682    }
683  }
684
685  pub(crate) unsafe fn enable_stencil_test(&mut self, enable: bool) {
686    if self.stencil_test_enabled.is_invalid(&enable) {
687      if enable {
688        gl::Enable(gl::STENCIL_TEST);
689      } else {
690        gl::Disable(gl::STENCIL_TEST);
691      }
692
693      self.stencil_test_enabled.set(enable);
694    }
695  }
696
697  pub(crate) unsafe fn set_stencil_test(&mut self, stencil_test: StencilTest) {
698    if self.stencil_test.is_invalid(&stencil_test) {
699      let comparison = comparison_to_glenum(stencil_test.comparison);
700      gl::StencilFunc(
701        comparison,
702        stencil_test.reference as _,
703        stencil_test.mask as _,
704      );
705
706      self.stencil_test.set(stencil_test);
707    }
708  }
709
710  pub(crate) unsafe fn set_stencil_operations(&mut self, stencil_ops: StencilOperations) {
711    if self.stencil_operations.is_invalid(&stencil_ops) {
712      gl::StencilOp(
713        stencil_op_to_glenum(stencil_ops.depth_passes_stencil_fails),
714        stencil_op_to_glenum(stencil_ops.depth_fails_stencil_passes),
715        stencil_op_to_glenum(stencil_ops.depth_stencil_pass),
716      );
717
718      self.stencil_operations.set(stencil_ops);
719    }
720  }
721
722  pub(crate) unsafe fn set_face_culling_state(&mut self, state: FaceCullingState) {
723    if self.face_culling_state.is_invalid(&state) {
724      match state {
725        FaceCullingState::On => gl::Enable(gl::CULL_FACE),
726        FaceCullingState::Off => gl::Disable(gl::CULL_FACE),
727      }
728
729      self.face_culling_state.set(state);
730    }
731  }
732
733  pub(crate) unsafe fn set_face_culling_order(&mut self, order: FaceCullingOrder) {
734    if self.face_culling_order.is_invalid(&order) {
735      match order {
736        FaceCullingOrder::CW => gl::FrontFace(gl::CW),
737        FaceCullingOrder::CCW => gl::FrontFace(gl::CCW),
738      }
739
740      self.face_culling_order.set(order);
741    }
742  }
743
744  pub(crate) unsafe fn set_face_culling_mode(&mut self, mode: FaceCullingMode) {
745    if self.face_culling_mode.is_invalid(&mode) {
746      match mode {
747        FaceCullingMode::Front => gl::CullFace(gl::FRONT),
748        FaceCullingMode::Back => gl::CullFace(gl::BACK),
749        FaceCullingMode::Both => gl::CullFace(gl::FRONT_AND_BACK),
750      }
751
752      self.face_culling_mode.set(mode);
753    }
754  }
755
756  pub(crate) unsafe fn set_vertex_restart(&mut self, state: VertexRestart) {
757    if self.vertex_restart.is_invalid(&state) {
758      match state {
759        VertexRestart::On => gl::Enable(gl::PRIMITIVE_RESTART),
760        VertexRestart::Off => gl::Disable(gl::PRIMITIVE_RESTART),
761      }
762
763      self.vertex_restart.set(state);
764    }
765  }
766
767  pub(crate) unsafe fn set_patch_vertex_nb(&mut self, nb: usize) {
768    if self.patch_vertex_nb.is_invalid(&nb) {
769      gl::PatchParameteri(gl::PATCH_VERTICES, nb as GLint);
770      self.patch_vertex_nb.set(nb);
771    }
772  }
773
774  pub(crate) unsafe fn set_texture_unit(&mut self, unit: u32) {
775    let unit = unit as GLenum;
776
777    if self.current_texture_unit.is_invalid(&unit) {
778      gl::ActiveTexture(gl::TEXTURE0 + unit);
779      self.current_texture_unit.set(unit);
780    }
781  }
782
783  /// Bind a texture at the current texture unit.
784  pub(crate) unsafe fn bind_texture(&mut self, target: GLenum, handle: GLuint) {
785    // Unwrap should be safe here, because we should always bind the texture unit before we bind the texture.
786    // Maybe this should be handled differently?
787    let unit = self.current_texture_unit.0.unwrap();
788    self.bind_texture_at(target, handle, unit);
789  }
790
791  /// Bind the texture at the provided texture unit.
792  pub(crate) unsafe fn bind_texture_at(&mut self, target: GLenum, handle: GLuint, unit: u32) {
793    match self.bound_textures.get(unit as usize).cloned() {
794      // there’s a abound texture, which is different from the one we want to bind
795      Some((target_, handle_)) if target != target_ || handle != handle_ => {
796        self.set_texture_unit(unit);
797        gl::BindTexture(target, handle);
798        self.bound_textures[unit as usize] = (target, handle);
799      }
800
801      // no texture bound at this unit; bind it
802      None => {
803        self.set_texture_unit(unit);
804        gl::BindTexture(target, handle);
805
806        // not enough registered texture units; let’s grow a bit more
807        let unit = unit as usize;
808        self.bound_textures.resize(unit + 1, (gl::TEXTURE_2D, 0));
809        self.bound_textures[unit] = (target, handle);
810      }
811
812      _ => (), // cached
813    }
814  }
815
816  pub(crate) unsafe fn bind_array_buffer(&mut self, handle: GLuint, bind: Bind) {
817    if bind == Bind::Forced || self.bound_array_buffer != handle {
818      gl::BindBuffer(gl::ARRAY_BUFFER, handle);
819      self.bound_array_buffer = handle;
820    }
821  }
822
823  pub(crate) unsafe fn bind_element_array_buffer(&mut self, handle: GLuint, bind: Bind) {
824    if bind == Bind::Forced || self.bound_element_array_buffer != handle {
825      gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, handle);
826      self.bound_element_array_buffer = handle;
827    }
828  }
829
830  pub(crate) unsafe fn bind_uniform_buffer(&mut self, handle: GLuint, binding: u32) {
831    let binding_ = binding as usize;
832
833    match self.bound_uniform_buffers.get(binding_) {
834      Some(&handle_) if handle != handle_ => {
835        gl::BindBufferBase(gl::UNIFORM_BUFFER, binding as GLuint, handle);
836        self.bound_uniform_buffers[binding_] = handle;
837      }
838
839      None => {
840        gl::BindBufferBase(gl::UNIFORM_BUFFER, binding as GLuint, handle);
841
842        // not enough registered buffer bindings; let’s grow a bit more
843        self.bound_uniform_buffers.resize(binding_ + 1, 0);
844        self.bound_uniform_buffers[binding_] = handle;
845      }
846
847      _ => (), // cached
848    }
849  }
850
851  pub(crate) unsafe fn unbind_buffer(&mut self, handle: GLuint) {
852    if self.bound_array_buffer == handle {
853      self.bind_array_buffer(0, Bind::Cached);
854    } else if self.bound_element_array_buffer == handle {
855      self.bind_element_array_buffer(0, Bind::Cached);
856    } else if let Some(handle_) = self
857      .bound_uniform_buffers
858      .iter_mut()
859      .find(|h| **h == handle)
860    {
861      *handle_ = 0;
862    }
863  }
864
865  pub(crate) unsafe fn bind_draw_framebuffer(&mut self, handle: GLuint) {
866    if self.bound_draw_framebuffer.is_invalid(&handle) {
867      gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, handle);
868      self.bound_draw_framebuffer.set(handle);
869    }
870  }
871
872  pub(crate) unsafe fn bind_vertex_array(&mut self, handle: GLuint, bind: Bind) {
873    if bind == Bind::Forced || self.bound_vertex_array != handle {
874      gl::BindVertexArray(handle);
875      self.bound_vertex_array = handle;
876    }
877  }
878
879  pub(crate) unsafe fn unbind_vertex_array(&mut self) {
880    self.bind_vertex_array(0, Bind::Cached)
881  }
882
883  pub(crate) unsafe fn use_program(&mut self, handle: GLuint) {
884    if self.current_program != handle {
885      gl::UseProgram(handle);
886      self.current_program = handle;
887    }
888  }
889
890  pub(crate) unsafe fn enable_srgb_framebuffer(&mut self, srgb_framebuffer_enabled: bool) {
891    if self
892      .srgb_framebuffer_enabled
893      .is_invalid(&srgb_framebuffer_enabled)
894    {
895      if srgb_framebuffer_enabled {
896        gl::Enable(gl::FRAMEBUFFER_SRGB);
897      } else {
898        gl::Disable(gl::FRAMEBUFFER_SRGB);
899      }
900
901      self.srgb_framebuffer_enabled.set(srgb_framebuffer_enabled);
902    }
903  }
904}
905
906/// Should the binding be cached or forced to the provided value?
907#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
908pub(crate) enum Bind {
909  Forced,
910  Cached,
911}
912
913#[inline]
914fn from_blending_equation(equation: Equation) -> GLenum {
915  match equation {
916    Equation::Additive => gl::FUNC_ADD,
917    Equation::Subtract => gl::FUNC_SUBTRACT,
918    Equation::ReverseSubtract => gl::FUNC_REVERSE_SUBTRACT,
919    Equation::Min => gl::MIN,
920    Equation::Max => gl::MAX,
921  }
922}
923
924#[inline]
925fn from_blending_factor(factor: Factor) -> GLenum {
926  match factor {
927    Factor::One => gl::ONE,
928    Factor::Zero => gl::ZERO,
929    Factor::SrcColor => gl::SRC_COLOR,
930    Factor::SrcColorComplement => gl::ONE_MINUS_SRC_COLOR,
931    Factor::DestColor => gl::DST_COLOR,
932    Factor::DestColorComplement => gl::ONE_MINUS_DST_COLOR,
933    Factor::SrcAlpha => gl::SRC_ALPHA,
934    Factor::SrcAlphaComplement => gl::ONE_MINUS_SRC_ALPHA,
935    Factor::DstAlpha => gl::DST_ALPHA,
936    Factor::DstAlphaComplement => gl::ONE_MINUS_DST_ALPHA,
937    Factor::SrcAlphaSaturate => gl::SRC_ALPHA_SATURATE,
938  }
939}
940
941/// An error that might happen when the context is queried.
942#[non_exhaustive]
943#[derive(Debug)]
944pub enum StateQueryError {
945  /// The [`GLState`] object is unavailable.
946  ///
947  /// That might occur if the current thread doesn’t support allocating a new graphics state. It
948  /// might happen if you try to have more than one state on the same thread, for instance.
949  UnavailableGLState,
950  /// Corrupted blending state.
951  UnknownBlendingState(GLboolean),
952  /// Corrupted blending equation.
953  UnknownBlendingEquation(GLenum),
954  /// Corrupted blending source factor.
955  UnknownBlendingSrcFactor(GLenum),
956  /// Corrupted blending destination factor.
957  UnknownBlendingDstFactor(GLenum),
958  /// Corrupted depth test state.
959  UnknownDepthTestState(GLboolean),
960  /// Corrupted stencil test state.
961  UnknownStencilTestState(GLboolean),
962  /// Corrupted stencil test comparison.
963  UnknownStencilTestComparison(GLint),
964  /// Corrupted stencil op.
965  UnknownStencilOp(GLint),
966  /// Corrupted depth write state.
967  UnknownWriteState(GLboolean),
968  /// Corrupted face culling state.
969  UnknownFaceCullingState(GLboolean),
970  /// Corrupted face culling order.
971  UnknownFaceCullingOrder(GLenum),
972  /// Corrupted face culling mode.
973  UnknownFaceCullingMode(GLenum),
974  /// Corrupted vertex restart state.
975  UnknownVertexRestartState(GLboolean),
976  /// Corrupted sRGB framebuffer state.
977  UnknownSRGBFramebufferState(GLboolean),
978  /// Corrupted scissor state.
979  UnknownScissorState(GLboolean),
980}
981
982impl fmt::Display for StateQueryError {
983  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
984    match *self {
985      StateQueryError::UnavailableGLState => write!(f, "unavailable graphics state"),
986      StateQueryError::UnknownBlendingState(ref s) => write!(f, "unknown blending state: {}", s),
987      StateQueryError::UnknownBlendingEquation(ref e) => {
988        write!(f, "unknown blending equation: {}", e)
989      }
990      StateQueryError::UnknownBlendingSrcFactor(ref k) => {
991        write!(f, "unknown blending source factor: {}", k)
992      }
993      StateQueryError::UnknownBlendingDstFactor(ref k) => {
994        write!(f, "unknown blending destination factor: {}", k)
995      }
996      StateQueryError::UnknownDepthTestState(ref s) => write!(f, "unknown depth test state: {}", s),
997      StateQueryError::UnknownWriteState(ref s) => {
998        write!(f, "unknown depth write state: {}", s)
999      }
1000      StateQueryError::UnknownStencilTestState(ref s) => {
1001        write!(f, "unknown stencil test state: {}", s)
1002      }
1003      StateQueryError::UnknownStencilTestComparison(ref k) => {
1004        write!(f, "unknown stencil test comparison: {}", k)
1005      }
1006      StateQueryError::UnknownStencilOp(ref op) => {
1007        write!(f, "unknown stencil operation: {}", op)
1008      }
1009      StateQueryError::UnknownFaceCullingState(ref s) => {
1010        write!(f, "unknown face culling state: {}", s)
1011      }
1012      StateQueryError::UnknownFaceCullingOrder(ref o) => {
1013        write!(f, "unknown face culling order: {}", o)
1014      }
1015      StateQueryError::UnknownFaceCullingMode(ref m) => {
1016        write!(f, "unknown face culling mode: {}", m)
1017      }
1018      StateQueryError::UnknownVertexRestartState(ref s) => {
1019        write!(f, "unknown vertex restart state: {}", s)
1020      }
1021      StateQueryError::UnknownSRGBFramebufferState(ref s) => {
1022        write!(f, "unknown sRGB framebuffer state: {}", s)
1023      }
1024      StateQueryError::UnknownScissorState(ref s) => write!(f, "unknown scissor state: {}", s),
1025    }
1026  }
1027}
1028
1029impl error::Error for StateQueryError {}
1030
1031unsafe fn get_ctx_viewport() -> Result<[GLint; 4], StateQueryError> {
1032  let mut data = [0; 4];
1033  gl::GetIntegerv(gl::VIEWPORT, data.as_mut_ptr());
1034  Ok(data)
1035}
1036
1037unsafe fn get_ctx_clear_color() -> Result<[GLfloat; 4], StateQueryError> {
1038  let mut data = [0.; 4];
1039  gl::GetFloatv(gl::COLOR_CLEAR_VALUE, data.as_mut_ptr());
1040  Ok(data)
1041}
1042
1043unsafe fn get_ctx_clear_depth() -> Result<GLfloat, StateQueryError> {
1044  let mut data = 0.;
1045  gl::GetFloatv(gl::DEPTH_CLEAR_VALUE, &mut data);
1046  Ok(data)
1047}
1048
1049unsafe fn get_ctx_clear_stencil() -> Result<GLint, StateQueryError> {
1050  let mut data = 0;
1051  gl::GetIntegerv(gl::STENCIL_CLEAR_VALUE, &mut data);
1052  Ok(data)
1053}
1054
1055unsafe fn get_ctx_blending_state() -> Result<BlendingState, StateQueryError> {
1056  let state = gl::IsEnabled(gl::BLEND);
1057
1058  match state {
1059    gl::TRUE => Ok(BlendingState::On),
1060    gl::FALSE => Ok(BlendingState::Off),
1061    _ => Err(StateQueryError::UnknownBlendingState(state)),
1062  }
1063}
1064
1065unsafe fn get_ctx_scissor_state() -> Result<ScissorState, StateQueryError> {
1066  let state = gl::IsEnabled(gl::SCISSOR_TEST);
1067
1068  match state {
1069    gl::TRUE => Ok(ScissorState::On),
1070    gl::FALSE => Ok(ScissorState::Off),
1071    _ => Err(StateQueryError::UnknownScissorState(state)),
1072  }
1073}
1074
1075unsafe fn get_ctx_scissor_region() -> Result<ScissorRegion, StateQueryError> {
1076  let mut data = [0; 4];
1077  gl::GetIntegerv(gl::SCISSOR_BOX, data.as_mut_ptr());
1078
1079  Ok(ScissorRegion {
1080    x: data[0] as u32,
1081    y: data[1] as u32,
1082    width: data[2] as u32,
1083    height: data[3] as u32,
1084  })
1085}
1086
1087unsafe fn get_ctx_blending_equations() -> Result<BlendingEquations, StateQueryError> {
1088  let mut rgb = gl::FUNC_ADD as GLint;
1089  let mut alpha = gl::FUNC_ADD as GLint;
1090
1091  gl::GetIntegerv(gl::BLEND_EQUATION_RGB, &mut rgb);
1092  gl::GetIntegerv(gl::BLEND_EQUATION_ALPHA, &mut alpha);
1093
1094  let rgb = map_enum_to_blending_equation(rgb as GLenum)?;
1095  let alpha = map_enum_to_blending_equation(alpha as GLenum)?;
1096
1097  Ok(BlendingEquations { rgb, alpha })
1098}
1099
1100unsafe fn get_ctx_blending_factors() -> Result<BlendingFactors, StateQueryError> {
1101  let mut src_rgb = gl::ONE as GLint;
1102  let mut dst_rgb = gl::ZERO as GLint;
1103  let mut src_alpha = gl::ONE as GLint;
1104  let mut dst_alpha = gl::ZERO as GLint;
1105
1106  gl::GetIntegerv(gl::BLEND_SRC_RGB, &mut src_rgb);
1107  gl::GetIntegerv(gl::BLEND_DST_RGB, &mut dst_rgb);
1108  gl::GetIntegerv(gl::BLEND_SRC_ALPHA, &mut src_alpha);
1109  gl::GetIntegerv(gl::BLEND_DST_ALPHA, &mut dst_alpha);
1110
1111  let src_rgb = from_gl_blending_factor(src_rgb as GLenum)
1112    .map_err(StateQueryError::UnknownBlendingSrcFactor)?;
1113  let dst_rgb = from_gl_blending_factor(dst_rgb as GLenum)
1114    .map_err(StateQueryError::UnknownBlendingDstFactor)?;
1115  let src_alpha = from_gl_blending_factor(src_alpha as GLenum)
1116    .map_err(StateQueryError::UnknownBlendingSrcFactor)?;
1117  let dst_alpha = from_gl_blending_factor(dst_alpha as GLenum)
1118    .map_err(StateQueryError::UnknownBlendingDstFactor)?;
1119
1120  Ok(BlendingFactors {
1121    src_rgb,
1122    dst_rgb,
1123    src_alpha,
1124    dst_alpha,
1125  })
1126}
1127
1128#[inline]
1129fn map_enum_to_blending_equation(data: GLenum) -> Result<Equation, StateQueryError> {
1130  match data {
1131    gl::FUNC_ADD => Ok(Equation::Additive),
1132    gl::FUNC_SUBTRACT => Ok(Equation::Subtract),
1133    gl::FUNC_REVERSE_SUBTRACT => Ok(Equation::ReverseSubtract),
1134    gl::MIN => Ok(Equation::Min),
1135    gl::MAX => Ok(Equation::Max),
1136    _ => Err(StateQueryError::UnknownBlendingEquation(data)),
1137  }
1138}
1139
1140#[inline]
1141fn from_gl_blending_factor(factor: GLenum) -> Result<Factor, GLenum> {
1142  match factor {
1143    gl::ONE => Ok(Factor::One),
1144    gl::ZERO => Ok(Factor::Zero),
1145    gl::SRC_COLOR => Ok(Factor::SrcColor),
1146    gl::ONE_MINUS_SRC_COLOR => Ok(Factor::SrcColorComplement),
1147    gl::DST_COLOR => Ok(Factor::DestColor),
1148    gl::ONE_MINUS_DST_COLOR => Ok(Factor::DestColorComplement),
1149    gl::SRC_ALPHA => Ok(Factor::SrcAlpha),
1150    gl::ONE_MINUS_SRC_ALPHA => Ok(Factor::SrcAlphaComplement),
1151    gl::DST_ALPHA => Ok(Factor::DstAlpha),
1152    gl::ONE_MINUS_DST_ALPHA => Ok(Factor::DstAlphaComplement),
1153    gl::SRC_ALPHA_SATURATE => Ok(Factor::SrcAlphaSaturate),
1154    _ => Err(factor),
1155  }
1156}
1157
1158unsafe fn get_ctx_depth_test() -> Result<DepthTest, StateQueryError> {
1159  let state = gl::IsEnabled(gl::DEPTH_TEST);
1160
1161  match state {
1162    gl::TRUE => Ok(DepthTest::On),
1163    gl::FALSE => Ok(DepthTest::Off),
1164    _ => Err(StateQueryError::UnknownDepthTestState(state)),
1165  }
1166}
1167
1168unsafe fn get_ctx_depth_write() -> Result<Write, StateQueryError> {
1169  let mut state = gl::FALSE;
1170
1171  gl::GetBooleanv(gl::DEPTH_WRITEMASK, &mut state);
1172
1173  match state {
1174    gl::TRUE => Ok(Write::On),
1175    gl::FALSE => Ok(Write::Off),
1176    _ => Err(StateQueryError::UnknownWriteState(state)),
1177  }
1178}
1179
1180unsafe fn get_ctx_stencil_test_enabled() -> Result<bool, StateQueryError> {
1181  let state = gl::IsEnabled(gl::STENCIL_TEST);
1182
1183  match state {
1184    gl::TRUE => Ok(true),
1185    gl::FALSE => Ok(false),
1186    _ => Err(StateQueryError::UnknownStencilTestState(state)),
1187  }
1188}
1189
1190unsafe fn get_ctx_stencil_test() -> Result<StencilTest, StateQueryError> {
1191  // we need the comparison function, the reference value and the mask
1192  let mut data = gl::ALWAYS as GLint;
1193
1194  gl::GetIntegerv(gl::STENCIL_FUNC, &mut data);
1195  let comparison = glenum_to_comparison(data as GLenum)
1196    .ok_or_else(|| StateQueryError::UnknownStencilTestComparison(data))?;
1197
1198  gl::GetIntegerv(gl::STENCIL_REF, &mut data);
1199  let reference = data as u8;
1200
1201  gl::GetIntegerv(gl::STENCIL_VALUE_MASK, &mut data);
1202  let mask = data as u8;
1203
1204  Ok(StencilTest {
1205    comparison,
1206    reference,
1207    mask,
1208  })
1209}
1210
1211unsafe fn get_ctx_stencil_operations() -> Result<StencilOperations, StateQueryError> {
1212  let mut data = 0 as GLint;
1213
1214  gl::GetIntegerv(gl::STENCIL_FAIL, &mut data);
1215  let depth_passes_stencil_fails =
1216    glenum_to_stencil_op(data as _).ok_or_else(|| StateQueryError::UnknownStencilOp(data))?;
1217  gl::GetIntegerv(gl::STENCIL_PASS_DEPTH_FAIL, &mut data);
1218  let depth_fails_stencil_passes =
1219    glenum_to_stencil_op(data as _).ok_or_else(|| StateQueryError::UnknownStencilOp(data))?;
1220  gl::GetIntegerv(gl::STENCIL_PASS_DEPTH_PASS, &mut data);
1221  let depth_stencil_pass =
1222    glenum_to_stencil_op(data as _).ok_or_else(|| StateQueryError::UnknownStencilOp(data))?;
1223
1224  Ok(StencilOperations {
1225    depth_passes_stencil_fails,
1226    depth_fails_stencil_passes,
1227    depth_stencil_pass,
1228  })
1229}
1230
1231unsafe fn get_ctx_face_culling_state() -> Result<FaceCullingState, StateQueryError> {
1232  let state = gl::IsEnabled(gl::CULL_FACE);
1233
1234  match state {
1235    gl::TRUE => Ok(FaceCullingState::On),
1236    gl::FALSE => Ok(FaceCullingState::Off),
1237    _ => Err(StateQueryError::UnknownFaceCullingState(state)),
1238  }
1239}
1240
1241unsafe fn get_ctx_face_culling_order() -> Result<FaceCullingOrder, StateQueryError> {
1242  let mut order = gl::CCW as GLint;
1243  gl::GetIntegerv(gl::FRONT_FACE, &mut order);
1244
1245  let order = order as GLenum;
1246  match order {
1247    gl::CCW => Ok(FaceCullingOrder::CCW),
1248    gl::CW => Ok(FaceCullingOrder::CW),
1249    _ => Err(StateQueryError::UnknownFaceCullingOrder(order)),
1250  }
1251}
1252
1253unsafe fn get_ctx_face_culling_mode() -> Result<FaceCullingMode, StateQueryError> {
1254  let mut mode = gl::BACK as GLint;
1255  gl::GetIntegerv(gl::CULL_FACE_MODE, &mut mode);
1256
1257  let mode = mode as GLenum;
1258  match mode {
1259    gl::FRONT => Ok(FaceCullingMode::Front),
1260    gl::BACK => Ok(FaceCullingMode::Back),
1261    gl::FRONT_AND_BACK => Ok(FaceCullingMode::Both),
1262    _ => Err(StateQueryError::UnknownFaceCullingMode(mode)),
1263  }
1264}
1265
1266unsafe fn get_ctx_vertex_restart() -> Result<VertexRestart, StateQueryError> {
1267  let state = gl::IsEnabled(gl::PRIMITIVE_RESTART);
1268
1269  match state {
1270    gl::TRUE => Ok(VertexRestart::On),
1271    gl::FALSE => Ok(VertexRestart::Off),
1272    _ => Err(StateQueryError::UnknownVertexRestartState(state)),
1273  }
1274}
1275
1276unsafe fn get_ctx_current_texture_unit() -> Result<GLenum, StateQueryError> {
1277  let mut active_texture = gl::TEXTURE0 as GLint;
1278  gl::GetIntegerv(gl::ACTIVE_TEXTURE, &mut active_texture);
1279  Ok(active_texture as GLenum)
1280}
1281
1282unsafe fn get_ctx_bound_draw_framebuffer() -> Result<GLuint, StateQueryError> {
1283  let mut bound = 0 as GLint;
1284  gl::GetIntegerv(gl::DRAW_FRAMEBUFFER_BINDING, &mut bound);
1285  Ok(bound as GLuint)
1286}
1287
1288unsafe fn get_ctx_bound_vertex_array() -> Result<GLuint, StateQueryError> {
1289  let mut bound = 0 as GLint;
1290  gl::GetIntegerv(gl::VERTEX_ARRAY_BINDING, &mut bound);
1291  Ok(bound as GLuint)
1292}
1293
1294unsafe fn get_ctx_current_program() -> Result<GLuint, StateQueryError> {
1295  let mut used = 0 as GLint;
1296  gl::GetIntegerv(gl::CURRENT_PROGRAM, &mut used);
1297  Ok(used as GLuint)
1298}
1299
1300unsafe fn get_ctx_srgb_framebuffer_enabled() -> Result<bool, StateQueryError> {
1301  let state = gl::IsEnabled(gl::FRAMEBUFFER_SRGB);
1302
1303  match state {
1304    gl::TRUE => Ok(true),
1305    gl::FALSE => Ok(false),
1306    _ => Err(StateQueryError::UnknownSRGBFramebufferState(state)),
1307  }
1308}
1309
1310/// Whether or not enable blending.
1311#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1312pub(crate) enum BlendingState {
1313  /// Enable blending.
1314  On,
1315  /// Disable blending.
1316  Off,
1317}
1318
1319#[derive(Debug, PartialEq, Eq)]
1320pub(crate) struct BlendingFactors {
1321  src_rgb: Factor,
1322  dst_rgb: Factor,
1323  src_alpha: Factor,
1324  dst_alpha: Factor,
1325}
1326
1327#[derive(Debug, PartialEq, Eq)]
1328pub(crate) struct BlendingEquations {
1329  rgb: Equation,
1330  alpha: Equation,
1331}
1332
1333/// Whether or not depth test should be enabled.
1334#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1335pub(crate) enum DepthTest {
1336  /// The depth test is enabled.
1337  On,
1338  /// The depth test is disabled.
1339  Off,
1340}
1341
1342/// Should face culling be enabled?
1343#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1344pub(crate) enum FaceCullingState {
1345  /// Enable face culling.
1346  On,
1347  /// Disable face culling.
1348  Off,
1349}
1350
1351/// Whether or not enable scissor test.
1352#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1353pub(crate) enum ScissorState {
1354  /// Enable scissor.
1355  On,
1356  /// Disable scissor.
1357  Off,
1358}