luminance_glow/
state.rs

1//! Graphics state.
2
3use glow::HasContext;
4use luminance::{
5    blending::{Equation, Factor},
6    depth_test::{DepthComparison, DepthWrite},
7    face_culling::{FaceCullingMode, FaceCullingOrder},
8    scissor::ScissorRegion,
9};
10use std::{fmt, marker::PhantomData};
11
12use crate::ShaderVersion;
13
14#[derive(Debug)]
15pub(crate) struct BindingStack {
16    pub(crate) next_texture_unit: u32,
17    pub(crate) free_texture_units: Vec<u32>,
18    pub(crate) next_buffer_binding: u32,
19    pub(crate) free_buffer_bindings: Vec<u32>,
20}
21
22impl BindingStack {
23    // Create a new, empty binding stack.
24    fn new() -> Self {
25        BindingStack {
26            next_texture_unit: 0,
27            free_texture_units: Vec::new(),
28            next_buffer_binding: 0,
29            free_buffer_bindings: Vec::new(),
30        }
31    }
32}
33
34/// The graphics state.
35///
36/// This type represents the current state of a given graphics context. It acts
37/// as a forward-gate to all the exposed features from the low-level API but
38/// adds a small cache layer over it to prevent from issuing the same API call (with
39/// the same parameters).
40#[derive(Debug)]
41pub struct GlowState {
42    _phantom: PhantomData<*const ()>, // !Send and !Sync
43
44    // WebGL context
45    pub(crate) ctx: glow::Context,
46
47    // binding stack
48    binding_stack: BindingStack,
49
50    // viewport
51    viewport: [i32; 4],
52
53    // clear buffers
54    clear_color: [f32; 4],
55
56    // blending
57    blending_state: BlendingState,
58    blending_equations: BlendingEquations,
59    blending_funcs: BlendingFactors,
60
61    // depth test
62    depth_test: DepthTest,
63    depth_test_comparison: DepthComparison,
64
65    // depth write
66    depth_write: DepthWrite,
67
68    // face culling
69    face_culling_state: FaceCullingState,
70    face_culling_order: FaceCullingOrder,
71    face_culling_mode: FaceCullingMode,
72
73    // scissor
74    scissor_state: ScissorState,
75    scissor_region: ScissorRegion,
76
77    // texture
78    current_texture_unit: u32,
79    bound_textures: Vec<(u32, Option<glow::Texture>)>,
80
81    // texture buffer used to optimize texture creation; regular textures typically will never ask
82    // for fetching from this set but framebuffers, who often generate several textures, might use
83    // this opportunity to get N textures (color, depth and stencil) at once, in a single CPU / GPU
84    // roundtrip
85    //
86    // fishy fishy
87    texture_swimming_pool: Vec<Option<glow::Texture>>,
88
89    // uniform buffer
90    bound_uniform_buffers: Vec<Option<glow::Buffer>>,
91
92    // array buffer
93    bound_array_buffer: Option<glow::Buffer>,
94    // element buffer
95    bound_element_array_buffer: Option<glow::Buffer>,
96
97    // framebuffer
98    bound_draw_framebuffer: Option<glow::Framebuffer>,
99    bound_read_framebuffer: Option<glow::Framebuffer>,
100
101    // A special framebuffer used to read textures (workaround the fact WebGL2 doesn’t have
102    // support of glGetTexImage). That object will never be created until trying to read a
103    // texture’s image.
104    readback_framebuffer: Option<glow::Framebuffer>,
105
106    // vertex array
107    bound_vertex_array: Option<glow::VertexArray>,
108    // shader program
109    current_program: Option<glow::Program>,
110
111    // Whether or not this is a WebGL1 state
112    pub(crate) is_webgl1: bool,
113    pub(crate) shader_version: ShaderVersion,
114}
115
116impl GlowState {
117    /// Create a new `GLState`.
118    ///
119    /// > Note: keep in mind you can create only one per thread. However, if you’re building without
120    /// > standard library, this function will always return successfully. You have to take extra care
121    /// > in this case.
122    pub(crate) fn new(
123        ctx: glow::Context,
124        is_webgl1: bool,
125        shader_version: ShaderVersion,
126    ) -> Result<Self, StateQueryError> {
127        Self::get_from_context(ctx, is_webgl1, shader_version)
128    }
129
130    /// Get a `GraphicsContext` from the current OpenGL context.
131    fn get_from_context(
132        mut ctx: glow::Context,
133        is_webgl1: bool,
134        shader_version: ShaderVersion,
135    ) -> Result<Self, StateQueryError> {
136        let binding_stack = BindingStack::new();
137        let viewport = get_ctx_viewport(&mut ctx)?;
138        let clear_color = get_ctx_clear_color(&mut ctx)?;
139        let blending_state = get_ctx_blending_state(&mut ctx);
140        let blending_equations = get_ctx_blending_equations(&mut ctx)?;
141        let blending_funcs = get_ctx_blending_factors(&mut ctx)?;
142        let depth_test = get_ctx_depth_test(&mut ctx);
143        let depth_test_comparison = DepthComparison::Less;
144        let depth_write = get_ctx_depth_write(&mut ctx)?;
145        let face_culling_state = get_ctx_face_culling_state(&mut ctx);
146        let face_culling_order = get_ctx_face_culling_order(&mut ctx)?;
147        let face_culling_mode = get_ctx_face_culling_mode(&mut ctx)?;
148        let scissor_state = get_ctx_scissor_state(&mut ctx)?;
149        let scissor_region = get_ctx_scissor_region(&mut ctx)?;
150
151        let current_texture_unit = 0;
152        let bound_textures = vec![(glow::TEXTURE0, None); 48]; // 48 is the platform minimal requirement
153        let texture_swimming_pool = Vec::new();
154        let bound_uniform_buffers = vec![None; 36]; // 36 is the platform minimal requirement
155        let bound_array_buffer = None;
156        let bound_element_array_buffer = None;
157        let bound_draw_framebuffer = None;
158        let bound_read_framebuffer = None;
159        let readback_framebuffer = None;
160        let bound_vertex_array = None;
161        let current_program = None;
162
163        Ok(GlowState {
164            _phantom: PhantomData,
165            ctx: ctx,
166            binding_stack,
167            viewport,
168            clear_color,
169            blending_state,
170            blending_equations,
171            blending_funcs,
172            depth_test,
173            depth_test_comparison,
174            depth_write,
175            face_culling_state,
176            face_culling_order,
177            face_culling_mode,
178            scissor_state,
179            scissor_region,
180            current_texture_unit,
181            bound_textures,
182            texture_swimming_pool,
183            bound_uniform_buffers,
184            bound_array_buffer,
185            bound_element_array_buffer,
186            bound_draw_framebuffer,
187            bound_read_framebuffer,
188            readback_framebuffer,
189            bound_vertex_array,
190            current_program,
191            is_webgl1,
192            shader_version,
193        })
194    }
195
196    pub(crate) fn binding_stack_mut(&mut self) -> &mut BindingStack {
197        &mut self.binding_stack
198    }
199
200    pub(crate) fn create_buffer(&mut self) -> Result<glow::Buffer, String> {
201        unsafe { self.ctx.create_buffer() }
202    }
203
204    pub(crate) fn bind_buffer_base(&mut self, handle: glow::Buffer, binding: u32) {
205        unsafe {
206            match self.bound_uniform_buffers.get(binding as usize) {
207                Some(&handle_) if Some(handle) != handle_ => {
208                    self.ctx
209                        .bind_buffer_base(glow::UNIFORM_BUFFER, binding, Some(handle));
210                    self.bound_uniform_buffers[binding as usize] = Some(handle.clone());
211                }
212
213                None => {
214                    self.ctx
215                        .bind_buffer_base(glow::UNIFORM_BUFFER, binding, Some(handle));
216
217                    // not enough registered buffer bindings; let’s grow a bit more
218                    self.bound_uniform_buffers
219                        .resize(binding as usize + 1, None);
220                    self.bound_uniform_buffers[binding as usize] = Some(handle.clone());
221                }
222
223                _ => (), // cached
224            }
225        }
226    }
227
228    pub(crate) fn bind_array_buffer(&mut self, buffer: Option<glow::Buffer>, bind: Bind) {
229        unsafe {
230            if bind == Bind::Forced || self.bound_array_buffer != buffer {
231                self.ctx.bind_buffer(glow::ARRAY_BUFFER, buffer);
232                self.bound_array_buffer = buffer;
233            }
234        }
235    }
236
237    pub(crate) fn bind_element_array_buffer(&mut self, buffer: Option<glow::Buffer>, bind: Bind) {
238        unsafe {
239            if bind == Bind::Forced || self.bound_element_array_buffer != buffer {
240                self.ctx.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, buffer);
241                self.bound_element_array_buffer = buffer;
242            }
243        }
244    }
245
246    pub(crate) fn unbind_buffer(&mut self, buffer: &glow::Buffer) {
247        if self.bound_array_buffer.as_ref() == Some(buffer) {
248            self.bind_array_buffer(None, Bind::Cached);
249        } else if self.bound_element_array_buffer.as_ref() == Some(buffer) {
250            self.bind_element_array_buffer(None, Bind::Cached);
251        } else if let Some(handle_) = self
252            .bound_uniform_buffers
253            .iter_mut()
254            .find(|h| h.as_ref() == Some(buffer))
255        {
256            *handle_ = None;
257        }
258    }
259
260    pub(crate) fn create_vertex_array(&mut self) -> Result<glow::VertexArray, String> {
261        unsafe { self.ctx.create_vertex_array() }
262    }
263
264    pub(crate) fn bind_vertex_array(&mut self, vao: Option<&glow::VertexArray>, bind: Bind) {
265        unsafe {
266            if bind == Bind::Forced || self.bound_vertex_array.as_ref() != vao {
267                self.ctx.bind_vertex_array(vao.cloned());
268                self.bound_vertex_array = vao.cloned();
269            }
270        }
271    }
272
273    pub(crate) fn create_texture(&mut self) -> Result<glow::Texture, String> {
274        unsafe {
275            if let Some(tex) = self.texture_swimming_pool.pop().flatten() {
276                Ok(tex)
277            } else {
278                self.ctx.create_texture()
279            }
280        }
281    }
282
283    /// Reserve at least a given number of textures.
284    pub(crate) fn reserve_textures(&mut self, nb: usize) {
285        unsafe {
286            let available = self.texture_swimming_pool.len();
287            let needed = nb.max(available) - available;
288
289            if needed > 0 {
290                // resize the internal buffer to hold all the new textures and create a slice starting from
291                // the previous end to the new end
292                self.texture_swimming_pool.resize(available + needed, None);
293
294                for _ in 0..needed {
295                    match self.ctx.create_texture() {
296                        Ok(texture) => self.texture_swimming_pool.push(Some(texture)),
297                        Err(_) => break,
298                    }
299                }
300            }
301        }
302    }
303
304    pub(crate) fn set_texture_unit(&mut self, unit: u32) {
305        unsafe {
306            if self.current_texture_unit != unit {
307                self.ctx.active_texture(glow::TEXTURE0 + unit);
308                self.current_texture_unit = unit;
309            }
310        }
311    }
312
313    pub(crate) fn bind_texture(&mut self, target: u32, handle: Option<glow::Texture>) {
314        unsafe {
315            let unit = self.current_texture_unit as usize;
316
317            match self.bound_textures.get(unit) {
318                Some((t, ref h)) if target != *t || handle != *h => {
319                    self.ctx.bind_texture(target, handle);
320                    self.bound_textures[unit] = (target, handle);
321                }
322
323                None => {
324                    self.ctx.bind_texture(target, handle);
325
326                    // not enough available texture units; let’s grow a bit more
327                    self.bound_textures
328                        .resize(unit + 1, (glow::TEXTURE_2D, None));
329                    self.bound_textures[unit] = (target, handle);
330                }
331
332                _ => (), // cached
333            }
334        }
335    }
336
337    pub(crate) fn create_framebuffer(&mut self) -> Result<glow::Framebuffer, String> {
338        unsafe { self.ctx.create_framebuffer() }
339    }
340
341    pub(crate) fn create_or_get_readback_framebuffer(&mut self) -> Option<glow::Framebuffer> {
342        self.readback_framebuffer.clone().or_else(|| {
343            // create the readback framebuffer if not already created
344            self.readback_framebuffer = self.create_framebuffer().ok();
345            self.readback_framebuffer.clone()
346        })
347    }
348
349    pub(crate) fn bind_draw_framebuffer(&mut self, handle: Option<glow::Framebuffer>) {
350        unsafe {
351            if self.bound_draw_framebuffer != handle {
352                self.ctx.bind_framebuffer(glow::FRAMEBUFFER, handle);
353                self.bound_draw_framebuffer = handle;
354            }
355        }
356    }
357
358    pub(crate) fn bind_read_framebuffer(&mut self, handle: Option<glow::Framebuffer>) {
359        unsafe {
360            if self.bound_read_framebuffer != handle {
361                self.ctx.bind_framebuffer(glow::READ_FRAMEBUFFER, handle);
362                self.bound_read_framebuffer = handle;
363            }
364        }
365    }
366
367    pub(crate) fn use_program(&mut self, handle: Option<glow::Program>) {
368        unsafe {
369            if self.current_program != handle {
370                self.ctx.use_program(handle);
371                self.current_program = handle;
372            }
373        }
374    }
375
376    pub(crate) fn set_viewport(&mut self, viewport: [i32; 4]) {
377        unsafe {
378            if self.viewport != viewport {
379                self.ctx
380                    .viewport(viewport[0], viewport[1], viewport[2], viewport[3]);
381                self.viewport = viewport;
382            }
383        }
384    }
385
386    pub(crate) fn set_clear_color(&mut self, clear_color: [f32; 4]) {
387        unsafe {
388            if self.clear_color != clear_color {
389                self.ctx.clear_color(
390                    clear_color[0],
391                    clear_color[1],
392                    clear_color[2],
393                    clear_color[3],
394                );
395                self.clear_color = clear_color;
396            }
397        }
398    }
399
400    pub(crate) fn set_blending_state(&mut self, state: BlendingState) {
401        unsafe {
402            if self.blending_state != state {
403                match state {
404                    BlendingState::On => self.ctx.enable(glow::BLEND),
405                    BlendingState::Off => self.ctx.disable(glow::BLEND),
406                }
407
408                self.blending_state = state;
409            }
410        }
411    }
412
413    pub(crate) fn set_blending_equation(&mut self, equation: Equation) {
414        unsafe {
415            let equations = BlendingEquations {
416                rgb: equation,
417                alpha: equation,
418            };
419
420            if self.blending_equations != equations {
421                self.ctx.blend_equation(blending_equation_to_glow(equation));
422                self.blending_equations = equations;
423            }
424        }
425    }
426
427    pub(crate) fn set_blending_equation_separate(
428        &mut self,
429        equation_rgb: Equation,
430        equation_alpha: Equation,
431    ) {
432        unsafe {
433            let equations = BlendingEquations {
434                rgb: equation_rgb,
435                alpha: equation_alpha,
436            };
437
438            if self.blending_equations != equations {
439                self.ctx.blend_equation_separate(
440                    blending_equation_to_glow(equation_rgb),
441                    blending_equation_to_glow(equation_alpha),
442                );
443
444                self.blending_equations = equations;
445            }
446        }
447    }
448
449    pub(crate) fn set_blending_func(&mut self, src: Factor, dst: Factor) {
450        unsafe {
451            let funcs = BlendingFactors {
452                src_rgb: src,
453                dst_rgb: dst,
454                src_alpha: src,
455                dst_alpha: dst,
456            };
457
458            if self.blending_funcs != funcs {
459                self.ctx
460                    .blend_func(blending_factor_to_glow(src), blending_factor_to_glow(dst));
461
462                self.blending_funcs = funcs;
463            }
464        }
465    }
466
467    pub(crate) fn set_blending_func_separate(
468        &mut self,
469        src_rgb: Factor,
470        dst_rgb: Factor,
471        src_alpha: Factor,
472        dst_alpha: Factor,
473    ) {
474        unsafe {
475            let funcs = BlendingFactors {
476                src_rgb,
477                dst_rgb,
478                src_alpha,
479                dst_alpha,
480            };
481            if self.blending_funcs != funcs {
482                self.ctx.blend_func_separate(
483                    blending_factor_to_glow(src_rgb),
484                    blending_factor_to_glow(dst_rgb),
485                    blending_factor_to_glow(src_alpha),
486                    blending_factor_to_glow(dst_alpha),
487                );
488
489                self.blending_funcs = funcs;
490            }
491        }
492    }
493
494    pub(crate) fn set_depth_test(&mut self, depth_test: DepthTest) {
495        unsafe {
496            if self.depth_test != depth_test {
497                match depth_test {
498                    DepthTest::On => self.ctx.enable(glow::DEPTH_TEST),
499                    DepthTest::Off => self.ctx.disable(glow::DEPTH_TEST),
500                }
501
502                self.depth_test = depth_test;
503            }
504        }
505    }
506
507    pub(crate) fn set_depth_test_comparison(&mut self, depth_test_comparison: DepthComparison) {
508        unsafe {
509            if self.depth_test_comparison != depth_test_comparison {
510                self.ctx
511                    .depth_func(depth_comparison_to_glow(depth_test_comparison));
512
513                self.depth_test_comparison = depth_test_comparison;
514            }
515        }
516    }
517
518    pub(crate) fn set_depth_write(&mut self, depth_write: DepthWrite) {
519        unsafe {
520            if self.depth_write != depth_write {
521                let enabled = match depth_write {
522                    DepthWrite::On => true,
523                    DepthWrite::Off => false,
524                };
525
526                self.ctx.depth_mask(enabled);
527
528                self.depth_write = depth_write;
529            }
530        }
531    }
532
533    pub(crate) fn set_face_culling_state(&mut self, state: FaceCullingState) {
534        unsafe {
535            if self.face_culling_state != state {
536                match state {
537                    FaceCullingState::On => self.ctx.enable(glow::CULL_FACE),
538                    FaceCullingState::Off => self.ctx.disable(glow::CULL_FACE),
539                }
540
541                self.face_culling_state = state;
542            }
543        }
544    }
545
546    pub(crate) fn set_face_culling_order(&mut self, order: FaceCullingOrder) {
547        unsafe {
548            if self.face_culling_order != order {
549                match order {
550                    FaceCullingOrder::CW => self.ctx.front_face(glow::CW),
551                    FaceCullingOrder::CCW => self.ctx.front_face(glow::CCW),
552                }
553
554                self.face_culling_order = order;
555            }
556        }
557    }
558
559    pub(crate) fn set_face_culling_mode(&mut self, mode: FaceCullingMode) {
560        unsafe {
561            if self.face_culling_mode != mode {
562                match mode {
563                    FaceCullingMode::Front => self.ctx.cull_face(glow::FRONT),
564                    FaceCullingMode::Back => self.ctx.cull_face(glow::BACK),
565                    FaceCullingMode::Both => self.ctx.cull_face(glow::FRONT_AND_BACK),
566                }
567
568                self.face_culling_mode = mode;
569            }
570        }
571    }
572
573    pub(crate) fn set_scissor_state(&mut self, state: ScissorState) {
574        unsafe {
575            if self.scissor_state != state {
576                match state {
577                    ScissorState::On => self.ctx.enable(glow::SCISSOR_TEST),
578                    ScissorState::Off => self.ctx.disable(glow::SCISSOR_TEST),
579                }
580
581                self.scissor_state = state;
582            }
583        }
584    }
585
586    pub(crate) fn set_scissor_region(&mut self, region: &ScissorRegion) {
587        unsafe {
588            if self.scissor_region != *region {
589                let ScissorRegion {
590                    x,
591                    y,
592                    width,
593                    height,
594                } = *region;
595
596                self.ctx
597                    .scissor(x as i32, y as i32, width as i32, height as i32);
598                self.scissor_region = *region;
599            }
600        }
601    }
602}
603
604impl Drop for GlowState {
605    fn drop(&mut self) {
606        unsafe {
607            // drop the readback framebuffer if it was allocated
608            self.readback_framebuffer
609                .map(|x| self.ctx.delete_framebuffer(x));
610        }
611    }
612}
613
614/// An error that might happen when the context is queried
615#[non_exhaustive]
616#[derive(Debug)]
617pub enum StateQueryError {
618    /// The Glow state object is unavailable.
619    ///
620    /// That might occur if the current thread doesn’t support allocating a new graphics state. It
621    /// might happen if you try to have more than one state on the same thread, for instance.
622    UnavailableGlowState,
623    /// Unknown array buffer initial state.
624    UnknownArrayBufferInitialState,
625    /// Unknown viewport initial state.
626    UnknownViewportInitialState,
627    /// Unknown clear color initial state.
628    UnknownClearColorInitialState,
629    /// Unknown depth write mask initial state.
630    UnknownDepthWriteMaskState,
631    /// Corrupted blending equation.
632    UnknownBlendingEquation(u32),
633    /// RGB blending equation couldn’t be retrieved when initializing the Glow state.
634    CannotRetrieveBlendingEquationRGB,
635    /// Alpha blending equation couldn’t be retrieved when initializing the Glow state.
636    CannotRetrieveBlendingEquationAlpha,
637    /// Source RGB factor couldn’t be retrieved when initializing the Glow state.
638    CannotRetrieveBlendingSrcFactorRGB,
639    /// Source alpha factor couldn’t be retrieved when initializing the Glow state.
640    CannotRetrieveBlendingSrcFactorAlpha,
641    /// Destination RGB factor couldn’t be retrieved when initializing the Glow state.
642    CannotRetrieveBlendingDstFactorRGB,
643    /// Destination alpha factor couldn’t be retrieved when initializing the Glow state.
644    CannotRetrieveBlendingDstFactorAlpha,
645    /// Required WebGL extensions cannot be enabled
646    CannotRetrieveRequiredGlowExtensions(Vec<String>),
647    /// Corrupted blending source factor (RGB).
648    UnknownBlendingSrcFactorRGB(u32),
649    /// Corrupted blending source factor (alpha).
650    UnknownBlendingSrcFactorAlpha(u32),
651    /// Corrupted blending destination factor (RGB).
652    UnknownBlendingDstFactorRGB(u32),
653    /// Corrupted blending destination factor (alpha).
654    UnknownBlendingDstFactorAlpha(u32),
655    /// Corrupted face culling order.
656    UnknownFaceCullingOrder,
657    /// Corrupted face culling mode.
658    UnknownFaceCullingMode,
659    /// Unknown scissor region initial state.
660    UnknownScissorRegionInitialState,
661}
662
663impl fmt::Display for StateQueryError {
664    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
665        match *self {
666            StateQueryError::UnavailableGlowState => write!(f, "unavailable graphics state"),
667
668            StateQueryError::UnknownArrayBufferInitialState => {
669                write!(f, "unknown array buffer initial state")
670            }
671
672            StateQueryError::UnknownViewportInitialState => {
673                write!(f, "unknown viewport initial state")
674            }
675
676            StateQueryError::UnknownClearColorInitialState => {
677                write!(f, "unknown clear color initial state")
678            }
679
680            StateQueryError::UnknownDepthWriteMaskState => {
681                f.write_str("unkonwn depth write mask state")
682            }
683
684            StateQueryError::UnknownBlendingEquation(ref e) => {
685                write!(f, "unknown blending equation: {}", e)
686            }
687
688            StateQueryError::CannotRetrieveBlendingEquationRGB => {
689                f.write_str("cannot retrieve blending equation (RGB)")
690            }
691
692            StateQueryError::CannotRetrieveBlendingEquationAlpha => {
693                f.write_str("cannot retrieve blending equation (alpha)")
694            }
695
696            StateQueryError::CannotRetrieveBlendingSrcFactorRGB => {
697                f.write_str("cannot retrieve blending source factor (RGB)")
698            }
699
700            StateQueryError::CannotRetrieveBlendingSrcFactorAlpha => {
701                f.write_str("cannot retrieve blending source factor (alpha)")
702            }
703
704            StateQueryError::CannotRetrieveBlendingDstFactorRGB => {
705                f.write_str("cannot retrieve blending destination factor (RGB)")
706            }
707
708            StateQueryError::CannotRetrieveBlendingDstFactorAlpha => {
709                f.write_str("cannot retrieve blending destination factor (alpha)")
710            }
711
712            StateQueryError::CannotRetrieveRequiredGlowExtensions(ref extensions) => write!(
713                f,
714                "missing Glow extensions: [{}]",
715                extensions.join(", ").as_str()
716            ),
717
718            StateQueryError::UnknownBlendingSrcFactorRGB(ref k) => {
719                write!(f, "unknown blending source factor (RGB): {}", k)
720            }
721
722            StateQueryError::UnknownBlendingSrcFactorAlpha(ref k) => {
723                write!(f, "unknown blending source factor (alpha): {}", k)
724            }
725
726            StateQueryError::UnknownBlendingDstFactorRGB(ref k) => {
727                write!(f, "unknown blending destination factor (RGB): {}", k)
728            }
729
730            StateQueryError::UnknownBlendingDstFactorAlpha(ref k) => {
731                write!(f, "unknown blending destination factor (alpha): {}", k)
732            }
733
734            StateQueryError::UnknownFaceCullingOrder => f.write_str("unknown face culling order"),
735
736            StateQueryError::UnknownFaceCullingMode => f.write_str("unknown face culling mode"),
737
738            StateQueryError::UnknownScissorRegionInitialState => {
739                write!(f, "unknown scissor region initial state")
740            }
741        }
742    }
743}
744
745impl std::error::Error for StateQueryError {}
746
747fn get_ctx_viewport(ctx: &mut glow::Context) -> Result<[i32; 4], StateQueryError> {
748    let mut viewport = [0; 4];
749
750    unsafe { ctx.get_parameter_i32_slice(glow::VIEWPORT, &mut viewport) };
751
752    Ok(viewport)
753}
754
755fn get_ctx_clear_color(ctx: &mut glow::Context) -> Result<[f32; 4], StateQueryError> {
756    let mut color = [0.0; 4];
757
758    unsafe { ctx.get_parameter_f32_slice(glow::COLOR_CLEAR_VALUE, &mut color) };
759
760    Ok(color)
761}
762
763fn get_ctx_blending_state(ctx: &mut glow::Context) -> BlendingState {
764    unsafe {
765        if ctx.is_enabled(glow::BLEND) {
766            BlendingState::On
767        } else {
768            BlendingState::Off
769        }
770    }
771}
772
773fn get_ctx_blending_equations(
774    ctx: &mut glow::Context,
775) -> Result<BlendingEquations, StateQueryError> {
776    unsafe {
777        let rgb =
778            map_enum_to_blending_equation(ctx.get_parameter_i32(glow::BLEND_EQUATION_RGB) as u32)?;
779
780        let alpha = map_enum_to_blending_equation(
781            ctx.get_parameter_i32(glow::BLEND_EQUATION_ALPHA) as u32,
782        )?;
783
784        Ok(BlendingEquations { rgb, alpha })
785    }
786}
787
788#[inline]
789fn map_enum_to_blending_equation(data: u32) -> Result<Equation, StateQueryError> {
790    match data {
791        glow::FUNC_ADD => Ok(Equation::Additive),
792        glow::FUNC_SUBTRACT => Ok(Equation::Subtract),
793        glow::FUNC_REVERSE_SUBTRACT => Ok(Equation::ReverseSubtract),
794        glow::MIN => Ok(Equation::Min),
795        glow::MAX => Ok(Equation::Max),
796        _ => Err(StateQueryError::UnknownBlendingEquation(data)),
797    }
798}
799
800fn get_ctx_blending_factors(ctx: &mut glow::Context) -> Result<BlendingFactors, StateQueryError> {
801    unsafe {
802        let src_rgb = ctx.get_parameter_i32(glow::BLEND_SRC_RGB) as u32;
803        let src_rgb = from_gl_blending_factor(src_rgb)
804            .map_err(StateQueryError::UnknownBlendingSrcFactorRGB)?;
805
806        let src_alpha = ctx.get_parameter_i32(glow::BLEND_SRC_ALPHA) as u32;
807        let src_alpha = from_gl_blending_factor(src_alpha)
808            .map_err(StateQueryError::UnknownBlendingSrcFactorAlpha)?;
809
810        let dst_rgb = ctx.get_parameter_i32(glow::BLEND_DST_RGB) as u32;
811        let dst_rgb = from_gl_blending_factor(dst_rgb)
812            .map_err(StateQueryError::UnknownBlendingDstFactorRGB)?;
813
814        let dst_alpha = ctx.get_parameter_i32(glow::BLEND_DST_ALPHA) as u32;
815        let dst_alpha = from_gl_blending_factor(dst_alpha)
816            .map_err(StateQueryError::UnknownBlendingDstFactorAlpha)?;
817
818        Ok(BlendingFactors {
819            src_rgb,
820            dst_rgb,
821            src_alpha,
822            dst_alpha,
823        })
824    }
825}
826
827#[inline]
828fn from_gl_blending_factor(factor: u32) -> Result<Factor, u32> {
829    match factor {
830        glow::ONE => Ok(Factor::One),
831        glow::ZERO => Ok(Factor::Zero),
832        glow::SRC_COLOR => Ok(Factor::SrcColor),
833        glow::ONE_MINUS_SRC_COLOR => Ok(Factor::SrcColorComplement),
834        glow::DST_COLOR => Ok(Factor::DestColor),
835        glow::ONE_MINUS_DST_COLOR => Ok(Factor::DestColorComplement),
836        glow::SRC_ALPHA => Ok(Factor::SrcAlpha),
837        glow::ONE_MINUS_SRC_ALPHA => Ok(Factor::SrcAlphaComplement),
838        glow::DST_ALPHA => Ok(Factor::DstAlpha),
839        glow::ONE_MINUS_DST_ALPHA => Ok(Factor::DstAlphaComplement),
840        glow::SRC_ALPHA_SATURATE => Ok(Factor::SrcAlphaSaturate),
841        _ => Err(factor),
842    }
843}
844
845fn get_ctx_depth_test(ctx: &mut glow::Context) -> DepthTest {
846    unsafe {
847        let enabled = ctx.is_enabled(glow::DEPTH_TEST);
848
849        if enabled {
850            DepthTest::On
851        } else {
852            DepthTest::Off
853        }
854    }
855}
856
857fn get_ctx_depth_write(ctx: &mut glow::Context) -> Result<DepthWrite, StateQueryError> {
858    unsafe {
859        let enabled = ctx.get_parameter_i32(glow::DEPTH_WRITEMASK) != 0;
860
861        if enabled {
862            Ok(DepthWrite::On)
863        } else {
864            Ok(DepthWrite::Off)
865        }
866    }
867}
868
869fn get_ctx_face_culling_state(ctx: &mut glow::Context) -> FaceCullingState {
870    unsafe {
871        let enabled = ctx.is_enabled(glow::CULL_FACE);
872
873        if enabled {
874            FaceCullingState::On
875        } else {
876            FaceCullingState::Off
877        }
878    }
879}
880
881fn get_ctx_face_culling_order(
882    ctx: &mut glow::Context,
883) -> Result<FaceCullingOrder, StateQueryError> {
884    unsafe {
885        let order = ctx.get_parameter_i32(glow::FRONT_FACE) as u32;
886
887        match order {
888            glow::CCW => Ok(FaceCullingOrder::CCW),
889            glow::CW => Ok(FaceCullingOrder::CW),
890            _ => Err(StateQueryError::UnknownFaceCullingOrder),
891        }
892    }
893}
894
895fn get_ctx_face_culling_mode(ctx: &mut glow::Context) -> Result<FaceCullingMode, StateQueryError> {
896    unsafe {
897        let mode = ctx.get_parameter_i32(glow::CULL_FACE_MODE) as u32;
898
899        match mode {
900            glow::FRONT => Ok(FaceCullingMode::Front),
901            glow::BACK => Ok(FaceCullingMode::Back),
902            glow::FRONT_AND_BACK => Ok(FaceCullingMode::Both),
903            _ => Err(StateQueryError::UnknownFaceCullingMode),
904        }
905    }
906}
907
908fn get_ctx_scissor_state(ctx: &mut glow::Context) -> Result<ScissorState, StateQueryError> {
909    unsafe {
910        let state = if ctx.is_enabled(glow::SCISSOR_TEST) {
911            ScissorState::On
912        } else {
913            ScissorState::Off
914        };
915
916        Ok(state)
917    }
918}
919
920fn get_ctx_scissor_region(ctx: &mut glow::Context) -> Result<ScissorRegion, StateQueryError> {
921    unsafe {
922        let mut region = [0; 4];
923        ctx.get_parameter_i32_slice(glow::SCISSOR_BOX, &mut region);
924
925        Ok(ScissorRegion {
926            x: region[0] as u32,
927            y: region[1] as u32,
928            width: region[2] as u32,
929            height: region[3] as u32,
930        })
931    }
932}
933
934// I think that Glow handles this for us, though I don't know exactly what will happen if we don't
935// have the exensions that we need.
936//
937// fn load_glow2_extensions(ctx: &mut glow::Context) -> Result<(), StateQueryError> {
938//     let required_extensions = [
939//         "OES_texture_float_linear",
940//         "EXT_color_buffer_float",
941//         "EXT_float_blend",
942//     ];
943
944//     let available_extensions: Vec<&str> = required_extensions
945//         .iter()
946//         .map(|ext| (*ext, ctx.get_extension(ext)))
947//         .flat_map(|(ext, result)| result.ok().flatten().map(|_| ext))
948//         .collect();
949
950//     if available_extensions.len() < required_extensions.len() {
951//         let missing_extensions: Vec<String> = required_extensions
952//             .iter()
953//             .filter(|e| !available_extensions.contains(e))
954//             .map(|e| e.to_string())
955//             .collect();
956
957//         return Err(StateQueryError::CannotRetrieveRequiredGlowExtensions(
958//             missing_extensions,
959//         ));
960//     }
961
962//     Ok(())
963// }
964
965/// Should the binding be cached or forced to the provided value?
966#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
967pub(crate) enum Bind {
968    Forced,
969    Cached,
970}
971
972/// Whether or not enable blending.
973#[derive(Copy, Clone, Debug, Eq, PartialEq)]
974pub(crate) enum BlendingState {
975    /// Enable blending.
976    On,
977    /// Disable blending.
978    Off,
979}
980
981#[derive(Debug, PartialEq, Eq)]
982pub(crate) struct BlendingFactors {
983    src_rgb: Factor,
984    dst_rgb: Factor,
985    src_alpha: Factor,
986    dst_alpha: Factor,
987}
988
989#[derive(Debug, PartialEq, Eq)]
990pub(crate) struct BlendingEquations {
991    rgb: Equation,
992    alpha: Equation,
993}
994
995/// Whether or not depth test should be enabled.
996#[derive(Copy, Clone, Debug, Eq, PartialEq)]
997pub(crate) enum DepthTest {
998    /// The depth test is enabled.
999    On,
1000    /// The depth test is disabled.
1001    Off,
1002}
1003
1004/// Should face culling be enabled?
1005#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1006pub(crate) enum FaceCullingState {
1007    /// Enable face culling.
1008    On,
1009    /// Disable face culling.
1010    Off,
1011}
1012
1013#[inline]
1014fn depth_comparison_to_glow(dc: DepthComparison) -> u32 {
1015    match dc {
1016        DepthComparison::Never => glow::NEVER,
1017        DepthComparison::Always => glow::ALWAYS,
1018        DepthComparison::Equal => glow::EQUAL,
1019        DepthComparison::NotEqual => glow::NOTEQUAL,
1020        DepthComparison::Less => glow::LESS,
1021        DepthComparison::LessOrEqual => glow::LEQUAL,
1022        DepthComparison::Greater => glow::GREATER,
1023        DepthComparison::GreaterOrEqual => glow::GEQUAL,
1024    }
1025}
1026
1027#[inline]
1028fn blending_equation_to_glow(equation: Equation) -> u32 {
1029    match equation {
1030        Equation::Additive => glow::FUNC_ADD,
1031        Equation::Subtract => glow::FUNC_SUBTRACT,
1032        Equation::ReverseSubtract => glow::FUNC_REVERSE_SUBTRACT,
1033        Equation::Min => glow::MIN,
1034        Equation::Max => glow::MAX,
1035    }
1036}
1037
1038#[inline]
1039fn blending_factor_to_glow(factor: Factor) -> u32 {
1040    match factor {
1041        Factor::One => glow::ONE,
1042        Factor::Zero => glow::ZERO,
1043        Factor::SrcColor => glow::SRC_COLOR,
1044        Factor::SrcColorComplement => glow::ONE_MINUS_SRC_COLOR,
1045        Factor::DestColor => glow::DST_COLOR,
1046        Factor::DestColorComplement => glow::ONE_MINUS_DST_COLOR,
1047        Factor::SrcAlpha => glow::SRC_ALPHA,
1048        Factor::SrcAlphaComplement => glow::ONE_MINUS_SRC_ALPHA,
1049        Factor::DstAlpha => glow::DST_ALPHA,
1050        Factor::DstAlphaComplement => glow::ONE_MINUS_DST_ALPHA,
1051        Factor::SrcAlphaSaturate => glow::SRC_ALPHA_SATURATE,
1052    }
1053}
1054
1055/// Scissor state.
1056#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1057pub(crate) enum ScissorState {
1058    /// Enabled.
1059    On,
1060    /// Disabled
1061    Off,
1062}