webxr/
surfman_layer_manager.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! An implementation of layer management using surfman
6
7use std::collections::HashMap;
8use std::rc::Rc;
9
10use euclid::{Point2D, Rect, Size2D};
11use glow::{self as gl, Context as Gl, HasContext, PixelUnpackData};
12use surfman::chains::{PreserveBuffer, SwapChains, SwapChainsAPI};
13use surfman::{Context as SurfmanContext, Device as SurfmanDevice, SurfaceAccess, SurfaceTexture};
14use webxr_api::{
15    ContextId, Error, GLContexts, GLTypes, LayerId, LayerInit, LayerManagerAPI, SubImage,
16    SubImages, Viewports,
17};
18
19use crate::gl_utils::GlClearer;
20
21#[derive(Clone, Copy, Debug)]
22pub enum SurfmanGL {}
23
24impl GLTypes for SurfmanGL {
25    type Device = Rc<SurfmanDevice>;
26    type Context = SurfmanContext;
27    type Bindings = Gl;
28}
29
30pub struct SurfmanLayerManager {
31    layers: Vec<(ContextId, LayerId)>,
32    swap_chains: SwapChains<LayerId, SurfmanDevice>,
33    surface_textures: HashMap<LayerId, SurfaceTexture>,
34    depth_stencil_textures: HashMap<LayerId, Option<gl::NativeTexture>>,
35    viewports: Viewports,
36    clearer: GlClearer,
37}
38
39impl SurfmanLayerManager {
40    pub fn new(
41        viewports: Viewports,
42        swap_chains: SwapChains<LayerId, SurfmanDevice>,
43    ) -> SurfmanLayerManager {
44        let layers = Vec::new();
45        let surface_textures = HashMap::new();
46        let depth_stencil_textures = HashMap::new();
47        let clearer = GlClearer::new(false);
48        SurfmanLayerManager {
49            layers,
50            swap_chains,
51            surface_textures,
52            depth_stencil_textures,
53            viewports,
54            clearer,
55        }
56    }
57}
58
59impl LayerManagerAPI<SurfmanGL> for SurfmanLayerManager {
60    fn create_layer(
61        &mut self,
62        contexts: &mut dyn GLContexts<SurfmanGL>,
63        context_id: ContextId,
64        init: LayerInit,
65    ) -> Result<LayerId, Error> {
66        let texture_size = init.texture_size(&self.viewports);
67        let layer_id = LayerId::default();
68        let access = SurfaceAccess::GPUOnly;
69        let size = texture_size.to_untyped();
70        // TODO: Treat depth and stencil separately?
71        let has_depth_stencil = match init {
72            LayerInit::WebGLLayer { stencil, depth, .. } => stencil | depth,
73            LayerInit::ProjectionLayer { stencil, depth, .. } => stencil | depth,
74        };
75        let device = contexts.device(context_id).ok_or(Error::NoMatchingDevice)?;
76        if has_depth_stencil {
77            let gl = contexts
78                .bindings(context_id)
79                .ok_or(Error::NoMatchingDevice)?;
80            let depth_stencil_texture = unsafe { gl.create_texture().ok() };
81            unsafe {
82                gl.bind_texture(gl::TEXTURE_2D, depth_stencil_texture);
83                gl.tex_image_2d(
84                    gl::TEXTURE_2D,
85                    0,
86                    gl::DEPTH24_STENCIL8 as _,
87                    size.width,
88                    size.height,
89                    0,
90                    gl::DEPTH_STENCIL,
91                    gl::UNSIGNED_INT_24_8,
92                    PixelUnpackData::Slice(None),
93                );
94            }
95            self.depth_stencil_textures
96                .insert(layer_id, depth_stencil_texture);
97        }
98        let context = contexts
99            .context(context_id)
100            .ok_or(Error::NoMatchingDevice)?;
101        self.swap_chains
102            .create_detached_swap_chain(layer_id, size, &device, context, access)
103            .map_err(|err| Error::BackendSpecific(format!("{:?}", err)))?;
104        self.layers.push((context_id, layer_id));
105        Ok(layer_id)
106    }
107
108    fn destroy_layer(
109        &mut self,
110        contexts: &mut dyn GLContexts<SurfmanGL>,
111        context_id: ContextId,
112        layer_id: LayerId,
113    ) {
114        self.clearer.destroy_layer(contexts, context_id, layer_id);
115        let Some(device) = contexts.device(context_id) else {
116            return;
117        };
118        let Some(context) = contexts.context(context_id) else {
119            return;
120        };
121        self.layers.retain(|&ids| ids != (context_id, layer_id));
122        let _ = self.swap_chains.destroy(layer_id, &device, context);
123        self.surface_textures.remove(&layer_id);
124        if let Some(depth_stencil_texture) = self.depth_stencil_textures.remove(&layer_id) {
125            let gl = contexts.bindings(context_id).unwrap();
126            if let Some(depth_stencil_texture) = depth_stencil_texture {
127                unsafe {
128                    gl.delete_texture(depth_stencil_texture);
129                }
130            }
131        }
132    }
133
134    fn layers(&self) -> &[(ContextId, LayerId)] {
135        &self.layers[..]
136    }
137
138    fn begin_frame(
139        &mut self,
140        contexts: &mut dyn GLContexts<SurfmanGL>,
141        layers: &[(ContextId, LayerId)],
142    ) -> Result<Vec<SubImages>, Error> {
143        layers
144            .iter()
145            .map(|&(context_id, layer_id)| {
146                let device = contexts.device(context_id).ok_or(Error::NoMatchingDevice)?;
147                let context = contexts
148                    .context(context_id)
149                    .ok_or(Error::NoMatchingDevice)?;
150                let swap_chain = self
151                    .swap_chains
152                    .get(layer_id)
153                    .ok_or(Error::NoMatchingDevice)?;
154                let surface_size = Size2D::from_untyped(swap_chain.size());
155                let surface_texture = swap_chain
156                    .take_surface_texture(&device, context)
157                    .map_err(|_| Error::NoMatchingDevice)?;
158                let color_texture = device.surface_texture_object(&surface_texture);
159                let color_target = device.surface_gl_texture_target();
160                let depth_stencil_texture = self
161                    .depth_stencil_textures
162                    .get(&layer_id)
163                    .cloned()
164                    .flatten();
165                let texture_array_index = None;
166                let origin = Point2D::new(0, 0);
167                let sub_image = Some(SubImage {
168                    color_texture: color_texture.map(|nt| nt.0),
169                    depth_stencil_texture: depth_stencil_texture.map(|nt| nt.0),
170                    texture_array_index,
171                    viewport: Rect::new(origin, surface_size),
172                });
173                let view_sub_images = self
174                    .viewports
175                    .viewports
176                    .iter()
177                    .map(|&viewport| SubImage {
178                        color_texture: color_texture.map(|nt| nt.0),
179                        depth_stencil_texture: depth_stencil_texture.map(|texture| texture.0),
180                        texture_array_index,
181                        viewport,
182                    })
183                    .collect();
184                self.surface_textures.insert(layer_id, surface_texture);
185                self.clearer.clear(
186                    contexts,
187                    context_id,
188                    layer_id,
189                    color_texture,
190                    color_target,
191                    depth_stencil_texture,
192                );
193                Ok(SubImages {
194                    layer_id,
195                    sub_image,
196                    view_sub_images,
197                })
198            })
199            .collect()
200    }
201
202    fn end_frame(
203        &mut self,
204        contexts: &mut dyn GLContexts<SurfmanGL>,
205        layers: &[(ContextId, LayerId)],
206    ) -> Result<(), Error> {
207        for &(context_id, layer_id) in layers {
208            let gl = contexts
209                .bindings(context_id)
210                .ok_or(Error::NoMatchingDevice)?;
211            unsafe {
212                gl.flush();
213            }
214            let device = contexts.device(context_id).ok_or(Error::NoMatchingDevice)?;
215            let context = contexts
216                .context(context_id)
217                .ok_or(Error::NoMatchingDevice)?;
218            let surface_texture = self
219                .surface_textures
220                .remove(&layer_id)
221                .ok_or(Error::NoMatchingDevice)?;
222            let swap_chain = self
223                .swap_chains
224                .get(layer_id)
225                .ok_or(Error::NoMatchingDevice)?;
226
227            swap_chain
228                .recycle_surface_texture(&device, context, surface_texture)
229                .map_err(|err| Error::BackendSpecific(format!("{:?}", err)))?;
230            swap_chain
231                .swap_buffers(&device, context, PreserveBuffer::No)
232                .map_err(|err| Error::BackendSpecific(format!("{:?}", err)))?;
233        }
234        Ok(())
235    }
236}