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