1use 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 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#[derive(Debug)]
43pub struct WebGL2State {
44 _phantom: PhantomData<*const ()>, pub(crate) ctx: WebGl2RenderingContext,
48
49 binding_stack: BindingStack,
51
52 viewport: [i32; 4],
54
55 clear_color: [f32; 4],
57 clear_depth: f32,
58 clear_stencil: i32,
59
60 blending_state: BlendingState,
62 blending_equations: BlendingEquations,
63 blending_funcs: BlendingFactors,
64
65 depth_test_enabled: bool,
67 depth_test_comparison: Comparison,
68
69 stencil_test_enabled: bool,
71 stencil_test: StencilTest,
72 stencil_operations: StencilOperations,
73
74 depth_write: Write,
76
77 face_culling_state: FaceCullingState,
79 face_culling_order: FaceCullingOrder,
80 face_culling_mode: FaceCullingMode,
81
82 scissor_state: ScissorState,
84 scissor_region: ScissorRegion,
85
86 current_texture_unit: u32,
88 bound_textures: Vec<(u32, Option<WebGlTexture>)>,
89
90 texture_swimming_pool: Vec<Option<WebGlTexture>>,
97
98 bound_uniform_buffers: Vec<Option<WebGlBuffer>>,
100
101 bound_array_buffer: Option<WebGlBuffer>,
103 bound_element_array_buffer: Option<WebGlBuffer>,
105 bound_uniform_buffer: Option<WebGlBuffer>,
107
108 bound_draw_framebuffer: Option<WebGlFramebuffer>,
110 bound_read_framebuffer: Option<WebGlFramebuffer>,
111
112 readback_framebuffer: Option<WebGlFramebuffer>,
116
117 bound_vertex_array: Option<WebGlVertexArrayObject>,
119
120 current_program: Option<WebGlProgram>,
122
123 vendor_name: Option<String>,
125
126 renderer_name: Option<String>,
128
129 webgl_version: Option<String>,
131
132 glsl_version: Option<String>,
134
135 max_texture_array_elements: Option<usize>,
137}
138
139impl WebGL2State {
140 pub(crate) fn new(ctx: WebGl2RenderingContext) -> Result<Self, StateQueryError> {
146 Self::get_from_context(ctx)
147 }
148
149 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]; let texture_swimming_pool = Vec::new();
176 let bound_uniform_buffers = vec![None; 36]; 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 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 _ => (), }
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 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 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 self
375 .bound_textures
376 .resize(unit + 1, (WebGl2RenderingContext::TEXTURE_2D, None));
377 self.bound_textures[unit] = (target, handle.cloned());
378 }
379
380 _ => (), }
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 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 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 self
724 .ctx
725 .delete_framebuffer(self.readback_framebuffer.as_ref());
726 }
727}
728
729#[non_exhaustive]
731#[derive(Debug)]
732pub enum StateQueryError {
733 UnavailableWebGL2State,
740 UnknownArrayBufferInitialState,
742 UnknownViewportInitialState,
744 UnknownClearColorInitialState,
746 UnknownClearDepthInitialState,
748 UnknownClearStencilInitialState,
750 UnknownStencilComparisonInitialState,
752 UnknownStencilReferenceState,
754 UnknownStencilMaskState,
756 UnknownStencilOpState,
758 UnknownDepthWriteMaskState,
760 UnknownBlendingEquation(u32),
762 CannotRetrieveBlendingEquationRGB,
764 CannotRetrieveBlendingEquationAlpha,
766 CannotRetrieveBlendingSrcFactorRGB,
768 CannotRetrieveBlendingSrcFactorAlpha,
770 CannotRetrieveBlendingDstFactorRGB,
772 CannotRetrieveBlendingDstFactorAlpha,
774 CannotRetrieveRequiredWebGL2Extensions(Vec<String>),
776 UnknownBlendingSrcFactorRGB(u32),
778 UnknownBlendingSrcFactorAlpha(u32),
780 UnknownBlendingDstFactorRGB(u32),
782 UnknownBlendingDstFactorAlpha(u32),
784 UnknownFaceCullingOrder,
786 UnknownFaceCullingMode,
788 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); 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); 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); 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#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1239pub enum Bind {
1240 Forced,
1241 Cached,
1242}
1243
1244#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1246pub(crate) enum BlendingState {
1247 On,
1249 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#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1269pub(crate) enum FaceCullingState {
1270 On,
1272 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
1304trait 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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1358pub(crate) enum ScissorState {
1359 On,
1361 Off,
1363}