1use std::collections::HashMap;
6
7use glow as gl;
8use glow::{Context as Gl, HasContext};
9use webxr_api::{ContextId, GLContexts, LayerId};
10
11use crate::SurfmanGL;
12
13pub(crate) struct GlClearer {
15 fbos: HashMap<
16 (
17 LayerId,
18 Option<gl::NativeTexture>,
19 Option<gl::NativeTexture>,
20 ),
21 Option<gl::NativeFramebuffer>,
22 >,
23 should_reverse_winding: bool,
24}
25
26impl GlClearer {
27 pub(crate) fn new(should_reverse_winding: bool) -> GlClearer {
28 let fbos = HashMap::new();
29 GlClearer {
30 fbos,
31 should_reverse_winding,
32 }
33 }
34
35 fn fbo(
36 &mut self,
37 gl: &Gl,
38 layer_id: LayerId,
39 color: Option<gl::NativeTexture>,
40 color_target: u32,
41 depth_stencil: Option<gl::NativeTexture>,
42 ) -> Option<gl::NativeFramebuffer> {
43 let should_reverse_winding = self.should_reverse_winding;
44 *self
45 .fbos
46 .entry((layer_id, color, depth_stencil))
47 .or_insert_with(|| {
48 unsafe {
50 let draw_fbo = gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING);
51 let read_fbo = gl.get_parameter_framebuffer(gl::READ_FRAMEBUFFER_BINDING);
52
53 let fbo = gl.create_framebuffer().ok();
55
56 gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
57 gl.framebuffer_texture_2d(
58 gl::FRAMEBUFFER,
59 gl::COLOR_ATTACHMENT0,
60 color_target,
61 color,
62 0,
63 );
64 gl.framebuffer_texture_2d(
65 gl::FRAMEBUFFER,
66 gl::DEPTH_STENCIL_ATTACHMENT,
67 gl::TEXTURE_2D,
68 depth_stencil,
69 0,
70 );
71
72 if should_reverse_winding {
75 gl.front_face(gl::CW);
76 }
77
78 gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, draw_fbo);
80 gl.bind_framebuffer(gl::READ_FRAMEBUFFER, read_fbo);
81 debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
82
83 fbo
84 }
85 })
86 }
87
88 #[allow(clippy::too_many_arguments)]
89 pub(crate) fn clear(
90 &mut self,
91 contexts: &mut dyn GLContexts<SurfmanGL>,
92 context_id: ContextId,
93 layer_id: LayerId,
94 color: Option<glow::NativeTexture>,
95 color_target: u32,
96 depth_stencil: Option<glow::NativeTexture>,
97 ) {
98 let gl = match contexts.bindings(context_id) {
99 None => return,
100 Some(gl) => gl,
101 };
102 let fbo = self.fbo(gl, layer_id, color, color_target, depth_stencil);
103 unsafe {
104 let mut clear_color = [0., 0., 0., 0.];
106 let mut clear_depth = [0.];
107 let mut clear_stencil = [0];
108 let mut stencil_mask = [0];
109 let scissor_enabled = gl.is_enabled(gl::SCISSOR_TEST);
110 let rasterizer_enabled = gl.is_enabled(gl::RASTERIZER_DISCARD);
111
112 let draw_fbo = gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING);
113 let read_fbo = gl.get_parameter_framebuffer(gl::READ_FRAMEBUFFER_BINDING);
114 gl.get_parameter_f32_slice(gl::COLOR_CLEAR_VALUE, &mut clear_color[..]);
115 gl.get_parameter_f32_slice(gl::DEPTH_CLEAR_VALUE, &mut clear_depth[..]);
116 gl.get_parameter_i32_slice(gl::STENCIL_CLEAR_VALUE, &mut clear_stencil[..]);
117 let depth_mask = gl.get_parameter_bool(gl::DEPTH_WRITEMASK);
118 gl.get_parameter_i32_slice(gl::STENCIL_WRITEMASK, &mut stencil_mask[..]);
119 let color_mask = gl.get_parameter_bool_array::<4>(gl::COLOR_WRITEMASK);
120
121 gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
123 gl.clear_color(0., 0., 0., 1.);
124 gl.clear_depth(1.);
125 gl.clear_stencil(0);
126 gl.disable(gl::SCISSOR_TEST);
127 gl.disable(gl::RASTERIZER_DISCARD);
128 gl.depth_mask(true);
129 gl.stencil_mask(0xFFFFFFFF);
130 gl.color_mask(true, true, true, true);
131 gl.clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
132
133 gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, draw_fbo);
135 gl.bind_framebuffer(gl::READ_FRAMEBUFFER, read_fbo);
136 gl.clear_color(
137 clear_color[0],
138 clear_color[1],
139 clear_color[2],
140 clear_color[3],
141 );
142 gl.color_mask(color_mask[0], color_mask[1], color_mask[2], color_mask[3]);
143 gl.clear_depth(clear_depth[0] as f64);
144 gl.clear_stencil(clear_stencil[0]);
145 gl.depth_mask(depth_mask);
146 gl.stencil_mask(stencil_mask[0] as _);
147 if scissor_enabled {
148 gl.enable(gl::SCISSOR_TEST);
149 }
150 if rasterizer_enabled {
151 gl.enable(gl::RASTERIZER_DISCARD);
152 }
153 debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
154 }
155 }
156
157 pub(crate) fn destroy_layer(
158 &mut self,
159 contexts: &mut dyn GLContexts<SurfmanGL>,
160 context_id: ContextId,
161 layer_id: LayerId,
162 ) {
163 let gl = match contexts.bindings(context_id) {
164 None => return,
165 Some(gl) => gl,
166 };
167 self.fbos.retain(|&(other_id, _, _), &mut fbo| {
168 if layer_id != other_id {
169 true
170 } else {
171 if let Some(fbo) = fbo {
172 unsafe { gl.delete_framebuffer(fbo) };
173 }
174 false
175 }
176 })
177 }
178}