Skip to main content

servo_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
124        if let Some(surface_texture) = self.surface_textures.remove(&layer_id) &&
125            let Ok(mut surface) = device.destroy_surface_texture(context, surface_texture)
126        {
127            let _ = device.destroy_surface(context, &mut surface);
128        }
129
130        if let Some(depth_stencil_texture) = self.depth_stencil_textures.remove(&layer_id) {
131            let gl = contexts.bindings(context_id).unwrap();
132            if let Some(depth_stencil_texture) = depth_stencil_texture {
133                unsafe {
134                    gl.delete_texture(depth_stencil_texture);
135                }
136            }
137        }
138    }
139
140    fn layers(&self) -> &[(ContextId, LayerId)] {
141        &self.layers[..]
142    }
143
144    fn begin_frame(
145        &mut self,
146        contexts: &mut dyn GLContexts<SurfmanGL>,
147        layers: &[(ContextId, LayerId)],
148    ) -> Result<Vec<SubImages>, Error> {
149        layers
150            .iter()
151            .map(|&(context_id, layer_id)| {
152                let device = contexts.device(context_id).ok_or(Error::NoMatchingDevice)?;
153                let context = contexts
154                    .context(context_id)
155                    .ok_or(Error::NoMatchingDevice)?;
156                let swap_chain = self
157                    .swap_chains
158                    .get(layer_id)
159                    .ok_or(Error::NoMatchingDevice)?;
160                let surface_size = Size2D::from_untyped(swap_chain.size());
161                let surface_texture = swap_chain
162                    .take_surface_texture(&device, context)
163                    .map_err(|_| Error::NoMatchingDevice)?;
164                let color_texture = device.surface_texture_object(&surface_texture);
165                let color_target = device.surface_gl_texture_target();
166                let depth_stencil_texture = self
167                    .depth_stencil_textures
168                    .get(&layer_id)
169                    .cloned()
170                    .flatten();
171                let texture_array_index = None;
172                let origin = Point2D::new(0, 0);
173                let sub_image = Some(SubImage {
174                    color_texture: color_texture.map(|nt| nt.0),
175                    depth_stencil_texture: depth_stencil_texture.map(|nt| nt.0),
176                    texture_array_index,
177                    viewport: Rect::new(origin, surface_size),
178                });
179                let view_sub_images = self
180                    .viewports
181                    .viewports
182                    .iter()
183                    .map(|&viewport| SubImage {
184                        color_texture: color_texture.map(|nt| nt.0),
185                        depth_stencil_texture: depth_stencil_texture.map(|texture| texture.0),
186                        texture_array_index,
187                        viewport,
188                    })
189                    .collect();
190                self.surface_textures.insert(layer_id, surface_texture);
191                self.clearer.clear(
192                    contexts,
193                    context_id,
194                    layer_id,
195                    color_texture,
196                    color_target,
197                    depth_stencil_texture,
198                );
199                Ok(SubImages {
200                    layer_id,
201                    sub_image,
202                    view_sub_images,
203                })
204            })
205            .collect()
206    }
207
208    fn end_frame(
209        &mut self,
210        contexts: &mut dyn GLContexts<SurfmanGL>,
211        layers: &[(ContextId, LayerId)],
212    ) -> Result<(), Error> {
213        for &(context_id, layer_id) in layers {
214            let gl = contexts
215                .bindings(context_id)
216                .ok_or(Error::NoMatchingDevice)?;
217            unsafe {
218                gl.flush();
219            }
220            let device = contexts.device(context_id).ok_or(Error::NoMatchingDevice)?;
221            let context = contexts
222                .context(context_id)
223                .ok_or(Error::NoMatchingDevice)?;
224            let surface_texture = self
225                .surface_textures
226                .remove(&layer_id)
227                .ok_or(Error::NoMatchingDevice)?;
228            let swap_chain = self
229                .swap_chains
230                .get(layer_id)
231                .ok_or(Error::NoMatchingDevice)?;
232
233            swap_chain
234                .recycle_surface_texture(&device, context, surface_texture)
235                .map_err(|err| Error::BackendSpecific(format!("{:?}", err)))?;
236            swap_chain
237                .swap_buffers(&device, context, PreserveBuffer::No)
238                .map_err(|err| Error::BackendSpecific(format!("{:?}", err)))?;
239        }
240        Ok(())
241    }
242}