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 pub(crate) fn clear(
89 &mut self,
90 contexts: &mut dyn GLContexts<SurfmanGL>,
91 context_id: ContextId,
92 layer_id: LayerId,
93 color: Option<glow::NativeTexture>,
94 color_target: u32,
95 depth_stencil: Option<glow::NativeTexture>,
96 ) {
97 let gl = match contexts.bindings(context_id) {
98 None => return,
99 Some(gl) => gl,
100 };
101 let fbo = self.fbo(gl, layer_id, color, color_target, depth_stencil);
102 unsafe {
103 let mut clear_color = [0., 0., 0., 0.];
105 let mut clear_depth = [0.];
106 let mut clear_stencil = [0];
107 let mut stencil_mask = [0];
108 let scissor_enabled = gl.is_enabled(gl::SCISSOR_TEST);
109 let rasterizer_enabled = gl.is_enabled(gl::RASTERIZER_DISCARD);
110
111 let draw_fbo = gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING);
112 let read_fbo = gl.get_parameter_framebuffer(gl::READ_FRAMEBUFFER_BINDING);
113 gl.get_parameter_f32_slice(gl::COLOR_CLEAR_VALUE, &mut clear_color[..]);
114 gl.get_parameter_f32_slice(gl::DEPTH_CLEAR_VALUE, &mut clear_depth[..]);
115 gl.get_parameter_i32_slice(gl::STENCIL_CLEAR_VALUE, &mut clear_stencil[..]);
116 let depth_mask = gl.get_parameter_bool(gl::DEPTH_WRITEMASK);
117 gl.get_parameter_i32_slice(gl::STENCIL_WRITEMASK, &mut stencil_mask[..]);
118 let color_mask = gl.get_parameter_bool_array::<4>(gl::COLOR_WRITEMASK);
119
120 gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
122 gl.clear_color(0., 0., 0., 1.);
123 gl.clear_depth(1.);
124 gl.clear_stencil(0);
125 gl.disable(gl::SCISSOR_TEST);
126 gl.disable(gl::RASTERIZER_DISCARD);
127 gl.depth_mask(true);
128 gl.stencil_mask(0xFFFFFFFF);
129 gl.color_mask(true, true, true, true);
130 gl.clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
131
132 gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, draw_fbo);
134 gl.bind_framebuffer(gl::READ_FRAMEBUFFER, read_fbo);
135 gl.clear_color(
136 clear_color[0],
137 clear_color[1],
138 clear_color[2],
139 clear_color[3],
140 );
141 gl.color_mask(color_mask[0], color_mask[1], color_mask[2], color_mask[3]);
142 gl.clear_depth(clear_depth[0] as f64);
143 gl.clear_stencil(clear_stencil[0]);
144 gl.depth_mask(depth_mask);
145 gl.stencil_mask(stencil_mask[0] as _);
146 if scissor_enabled {
147 gl.enable(gl::SCISSOR_TEST);
148 }
149 if rasterizer_enabled {
150 gl.enable(gl::RASTERIZER_DISCARD);
151 }
152 debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
153 }
154 }
155
156 pub(crate) fn destroy_layer(
157 &mut self,
158 contexts: &mut dyn GLContexts<SurfmanGL>,
159 context_id: ContextId,
160 layer_id: LayerId,
161 ) {
162 let gl = match contexts.bindings(context_id) {
163 None => return,
164 Some(gl) => gl,
165 };
166 self.fbos.retain(|&(other_id, _, _), &mut fbo| {
167 if layer_id != other_id {
168 true
169 } else {
170 if let Some(fbo) = fbo {
171 unsafe { gl.delete_framebuffer(fbo) };
172 }
173 false
174 }
175 })
176 }
177}