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;
8
9use euclid::{Point2D, Rect, Size2D};
10use glow::{self as gl, Context as Gl, HasContext, PixelUnpackData};
11use surfman::chains::{PreserveBuffer, SwapChains, SwapChainsAPI};
12use surfman::{Context as SurfmanContext, Device as SurfmanDevice, SurfaceAccess, SurfaceTexture};
13use webxr_api::{
14    ContextId, Error, GLContexts, GLTypes, LayerId, LayerInit, LayerManagerAPI, SubImage,
15    SubImages, Viewports,
16};
17
18use crate::gl_utils::GlClearer;
19
20#[derive(Clone, Copy, Debug)]
21pub enum SurfmanGL {}
22
23impl GLTypes for SurfmanGL {
24    type Device = SurfmanDevice;
25    type Context = SurfmanContext;
26    type Bindings = Gl;
27}
28
29pub struct SurfmanLayerManager {
30    layers: Vec<(ContextId, LayerId)>,
31    swap_chains: SwapChains<LayerId, SurfmanDevice>,
32    surface_textures: HashMap<LayerId, SurfaceTexture>,
33    depth_stencil_textures: HashMap<LayerId, Option<gl::NativeTexture>>,
34    viewports: Viewports,
35    clearer: GlClearer,
36}
37
38impl SurfmanLayerManager {
39    pub fn new(
40        viewports: Viewports,
41        swap_chains: SwapChains<LayerId, SurfmanDevice>,
42    ) -> SurfmanLayerManager {
43        let layers = Vec::new();
44        let surface_textures = HashMap::new();
45        let depth_stencil_textures = HashMap::new();
46        let clearer = GlClearer::new(false);
47        SurfmanLayerManager {
48            layers,
49            swap_chains,
50            surface_textures,
51            depth_stencil_textures,
52            viewports,
53            clearer,
54        }
55    }
56}
57
58impl LayerManagerAPI<SurfmanGL> for SurfmanLayerManager {
59    fn create_layer(
60        &mut self,
61        device: &mut SurfmanDevice,
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        if has_depth_stencil {
76            let gl = contexts
77                .bindings(device, context_id)
78                .ok_or(Error::NoMatchingDevice)?;
79            let depth_stencil_texture = unsafe { gl.create_texture().ok() };
80            unsafe {
81                gl.bind_texture(gl::TEXTURE_2D, depth_stencil_texture);
82                gl.tex_image_2d(
83                    gl::TEXTURE_2D,
84                    0,
85                    gl::DEPTH24_STENCIL8 as _,
86                    size.width,
87                    size.height,
88                    0,
89                    gl::DEPTH_STENCIL,
90                    gl::UNSIGNED_INT_24_8,
91                    PixelUnpackData::Slice(None),
92                );
93            }
94            self.depth_stencil_textures
95                .insert(layer_id, depth_stencil_texture);
96        }
97        let context = contexts
98            .context(device, context_id)
99            .ok_or(Error::NoMatchingDevice)?;
100        self.swap_chains
101            .create_detached_swap_chain(layer_id, size, device, context, access)
102            .map_err(|err| Error::BackendSpecific(format!("{:?}", err)))?;
103        self.layers.push((context_id, layer_id));
104        Ok(layer_id)
105    }
106
107    fn destroy_layer(
108        &mut self,
109        device: &mut SurfmanDevice,
110        contexts: &mut dyn GLContexts<SurfmanGL>,
111        context_id: ContextId,
112        layer_id: LayerId,
113    ) {
114        self.clearer
115            .destroy_layer(device, contexts, context_id, layer_id);
116        let context = match contexts.context(device, context_id) {
117            Some(context) => context,
118            None => return,
119        };
120        self.layers.retain(|&ids| ids != (context_id, layer_id));
121        let _ = self.swap_chains.destroy(layer_id, device, context);
122        self.surface_textures.remove(&layer_id);
123        if let Some(depth_stencil_texture) = self.depth_stencil_textures.remove(&layer_id) {
124            let gl = contexts.bindings(device, context_id).unwrap();
125            if let Some(depth_stencil_texture) = depth_stencil_texture {
126                unsafe {
127                    gl.delete_texture(depth_stencil_texture);
128                }
129            }
130        }
131    }
132
133    fn layers(&self) -> &[(ContextId, LayerId)] {
134        &self.layers[..]
135    }
136
137    fn begin_frame(
138        &mut self,
139        device: &mut SurfmanDevice,
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 context = contexts
147                    .context(device, context_id)
148                    .ok_or(Error::NoMatchingDevice)?;
149                let swap_chain = self
150                    .swap_chains
151                    .get(layer_id)
152                    .ok_or(Error::NoMatchingDevice)?;
153                let surface_size = Size2D::from_untyped(swap_chain.size());
154                let surface_texture = swap_chain
155                    .take_surface_texture(device, context)
156                    .map_err(|_| Error::NoMatchingDevice)?;
157                let color_texture = device.surface_texture_object(&surface_texture);
158                let color_target = device.surface_gl_texture_target();
159                let depth_stencil_texture = self
160                    .depth_stencil_textures
161                    .get(&layer_id)
162                    .cloned()
163                    .flatten();
164                let texture_array_index = None;
165                let origin = Point2D::new(0, 0);
166                let sub_image = Some(SubImage {
167                    color_texture: color_texture.map(|nt| nt.0),
168                    depth_stencil_texture: depth_stencil_texture.map(|nt| nt.0),
169                    texture_array_index,
170                    viewport: Rect::new(origin, surface_size),
171                });
172                let view_sub_images = self
173                    .viewports
174                    .viewports
175                    .iter()
176                    .map(|&viewport| SubImage {
177                        color_texture: color_texture.map(|nt| nt.0),
178                        depth_stencil_texture: depth_stencil_texture.map(|texture| texture.0),
179                        texture_array_index,
180                        viewport,
181                    })
182                    .collect();
183                self.surface_textures.insert(layer_id, surface_texture);
184                self.clearer.clear(
185                    device,
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        device: &mut SurfmanDevice,
205        contexts: &mut dyn GLContexts<SurfmanGL>,
206        layers: &[(ContextId, LayerId)],
207    ) -> Result<(), Error> {
208        for &(context_id, layer_id) in layers {
209            let gl = contexts
210                .bindings(device, context_id)
211                .ok_or(Error::NoMatchingDevice)?;
212            unsafe {
213                gl.flush();
214            }
215            let context = contexts
216                .context(device, 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            swap_chain
227                .recycle_surface_texture(device, context, surface_texture)
228                .map_err(|err| Error::BackendSpecific(format!("{:?}", err)))?;
229            swap_chain
230                .swap_buffers(device, context, PreserveBuffer::No)
231                .map_err(|err| Error::BackendSpecific(format!("{:?}", err)))?;
232        }
233        Ok(())
234    }
235}