luminance_webgl/webgl2/
state.rs

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