1use 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 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#[derive(Debug)]
41pub struct GlowState {
42 _phantom: PhantomData<*const ()>, pub(crate) ctx: glow::Context,
46
47 binding_stack: BindingStack,
49
50 viewport: [i32; 4],
52
53 clear_color: [f32; 4],
55
56 blending_state: BlendingState,
58 blending_equations: BlendingEquations,
59 blending_funcs: BlendingFactors,
60
61 depth_test: DepthTest,
63 depth_test_comparison: DepthComparison,
64
65 depth_write: DepthWrite,
67
68 face_culling_state: FaceCullingState,
70 face_culling_order: FaceCullingOrder,
71 face_culling_mode: FaceCullingMode,
72
73 scissor_state: ScissorState,
75 scissor_region: ScissorRegion,
76
77 current_texture_unit: u32,
79 bound_textures: Vec<(u32, Option<glow::Texture>)>,
80
81 texture_swimming_pool: Vec<Option<glow::Texture>>,
88
89 bound_uniform_buffers: Vec<Option<glow::Buffer>>,
91
92 bound_array_buffer: Option<glow::Buffer>,
94 bound_element_array_buffer: Option<glow::Buffer>,
96
97 bound_draw_framebuffer: Option<glow::Framebuffer>,
99 bound_read_framebuffer: Option<glow::Framebuffer>,
100
101 readback_framebuffer: Option<glow::Framebuffer>,
105
106 bound_vertex_array: Option<glow::VertexArray>,
108 current_program: Option<glow::Program>,
110
111 pub(crate) is_webgl1: bool,
113 pub(crate) shader_version: ShaderVersion,
114}
115
116impl GlowState {
117 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 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]; let texture_swimming_pool = Vec::new();
154 let bound_uniform_buffers = vec![None; 36]; 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 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 _ => (), }
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 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 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 self.bound_textures
328 .resize(unit + 1, (glow::TEXTURE_2D, None));
329 self.bound_textures[unit] = (target, handle);
330 }
331
332 _ => (), }
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 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 self.readback_framebuffer
609 .map(|x| self.ctx.delete_framebuffer(x));
610 }
611 }
612}
613
614#[non_exhaustive]
616#[derive(Debug)]
617pub enum StateQueryError {
618 UnavailableGlowState,
623 UnknownArrayBufferInitialState,
625 UnknownViewportInitialState,
627 UnknownClearColorInitialState,
629 UnknownDepthWriteMaskState,
631 UnknownBlendingEquation(u32),
633 CannotRetrieveBlendingEquationRGB,
635 CannotRetrieveBlendingEquationAlpha,
637 CannotRetrieveBlendingSrcFactorRGB,
639 CannotRetrieveBlendingSrcFactorAlpha,
641 CannotRetrieveBlendingDstFactorRGB,
643 CannotRetrieveBlendingDstFactorAlpha,
645 CannotRetrieveRequiredGlowExtensions(Vec<String>),
647 UnknownBlendingSrcFactorRGB(u32),
649 UnknownBlendingSrcFactorAlpha(u32),
651 UnknownBlendingDstFactorRGB(u32),
653 UnknownBlendingDstFactorAlpha(u32),
655 UnknownFaceCullingOrder,
657 UnknownFaceCullingMode,
659 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#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
967pub(crate) enum Bind {
968 Forced,
969 Cached,
970}
971
972#[derive(Copy, Clone, Debug, Eq, PartialEq)]
974pub(crate) enum BlendingState {
975 On,
977 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#[derive(Copy, Clone, Debug, Eq, PartialEq)]
997pub(crate) enum DepthTest {
998 On,
1000 Off,
1002}
1003
1004#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1006pub(crate) enum FaceCullingState {
1007 On,
1009 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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1057pub(crate) enum ScissorState {
1058 On,
1060 Off,
1062}