surfman/platform/generic/multi/
surface.rs

1// surfman/surfman/src/platform/generic/multi/surface.rs
2//
3//! A surface abstraction that allows the choice of backends dynamically.
4
5use super::context::Context;
6use super::device::Device;
7use crate::connection::Connection as ConnectionInterface;
8use crate::device::Device as DeviceInterface;
9use crate::{Error, SurfaceAccess, SurfaceInfo, SurfaceType};
10use euclid::default::Size2D;
11use glow::Texture;
12
13use std::fmt::{self, Debug, Formatter};
14
15/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either
16/// displayed in a native widget or bound to a texture for reading.
17///
18/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a
19/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation,
20/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a
21/// widget but not bound to a texture.
22///
23/// Surfaces are specific to a given context and cannot be rendered to from any context other than
24/// the one they were created with. However, they can be *read* from any context on any thread (as
25/// long as that context shares the same adapter and connection), by wrapping them in a
26/// `SurfaceTexture`.
27///
28/// Depending on the platform, each surface may be internally double-buffered.
29///
30/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur.
31pub enum Surface<Def, Alt>
32where
33    Def: DeviceInterface,
34    Alt: DeviceInterface,
35{
36    /// The default surface type.
37    Default(Def::Surface),
38    /// The alternate surface type.
39    Alternate(Alt::Surface),
40}
41
42/// Represents an OpenGL texture that wraps a surface.
43///
44/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to
45/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that
46/// framebuffer).
47///
48/// Surface textures are local to a context, but that context does not have to be the same context
49/// as that associated with the underlying surface. The texture must be destroyed with the
50/// `destroy_surface_texture()` method, or a panic will occur.
51pub enum SurfaceTexture<Def, Alt>
52where
53    Def: DeviceInterface,
54    Alt: DeviceInterface,
55{
56    /// The default surface texture type.
57    Default(Def::SurfaceTexture),
58    /// The alternate surface texture type.
59    Alternate(Alt::SurfaceTexture),
60}
61
62/// A native widget/window type that can dynamically switch between backends.
63pub enum NativeWidget<Def, Alt>
64where
65    Def: DeviceInterface,
66    Alt: DeviceInterface,
67{
68    /// The default native widget type.
69    Default(<Def::Connection as ConnectionInterface>::NativeWidget),
70    /// The alternate native widget type.
71    Alternate(<Alt::Connection as ConnectionInterface>::NativeWidget),
72}
73
74impl<Def, Alt> Debug for Surface<Def, Alt>
75where
76    Def: DeviceInterface,
77    Alt: DeviceInterface,
78{
79    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
80        write!(f, "Surface")
81    }
82}
83
84impl<Def, Alt> Debug for SurfaceTexture<Def, Alt>
85where
86    Def: DeviceInterface,
87    Alt: DeviceInterface,
88{
89    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
90        write!(f, "SurfaceTexture")
91    }
92}
93
94impl<Def, Alt> Device<Def, Alt>
95where
96    Def: DeviceInterface,
97    Alt: DeviceInterface,
98{
99    /// Creates either a generic or a widget surface, depending on the supplied surface type.
100    ///
101    /// Only the given context may ever render to the surface, but generic surfaces can be wrapped
102    /// up in a `SurfaceTexture` for reading by other contexts.
103    pub fn create_surface(
104        &mut self,
105        context: &Context<Def, Alt>,
106        surface_access: SurfaceAccess,
107        surface_type: SurfaceType<NativeWidget<Def, Alt>>,
108    ) -> Result<Surface<Def, Alt>, Error> {
109        match (&mut *self, context) {
110            (&mut Device::Default(ref mut device), Context::Default(context)) => {
111                let surface_type = match surface_type {
112                    SurfaceType::Generic { size } => SurfaceType::Generic { size },
113                    SurfaceType::Widget {
114                        native_widget: NativeWidget::Default(native_widget),
115                    } => SurfaceType::Widget { native_widget },
116                    SurfaceType::Widget { native_widget: _ } => {
117                        return Err(Error::IncompatibleNativeWidget)
118                    }
119                };
120                device
121                    .create_surface(context, surface_access, surface_type)
122                    .map(Surface::Default)
123            }
124            (&mut Device::Alternate(ref mut device), Context::Alternate(context)) => {
125                let surface_type = match surface_type {
126                    SurfaceType::Generic { size } => SurfaceType::Generic { size },
127                    SurfaceType::Widget {
128                        native_widget: NativeWidget::Alternate(native_widget),
129                    } => SurfaceType::Widget { native_widget },
130                    SurfaceType::Widget { native_widget: _ } => {
131                        return Err(Error::IncompatibleNativeWidget)
132                    }
133                };
134                device
135                    .create_surface(context, surface_access, surface_type)
136                    .map(Surface::Alternate)
137            }
138            _ => Err(Error::IncompatibleContext),
139        }
140    }
141
142    /// Creates a surface texture from an existing generic surface for use with the given context.
143    ///
144    /// The surface texture is local to the supplied context and takes ownership of the surface.
145    /// Destroying the surface texture allows you to retrieve the surface again.
146    ///
147    /// *The supplied context does not have to be the same context that the surface is associated
148    /// with.* This allows you to render to a surface in one context and sample from that surface
149    /// in another context.
150    ///
151    /// Calling this method on a widget surface returns a `WidgetAttached` error.
152    pub fn create_surface_texture(
153        &self,
154        context: &mut Context<Def, Alt>,
155        surface: Surface<Def, Alt>,
156    ) -> Result<SurfaceTexture<Def, Alt>, (Error, Surface<Def, Alt>)> {
157        match (self, &mut *context) {
158            (Device::Default(device), &mut Context::Default(ref mut context)) => match surface {
159                Surface::Default(surface) => {
160                    match device.create_surface_texture(context, surface) {
161                        Ok(surface_texture) => Ok(SurfaceTexture::Default(surface_texture)),
162                        Err((err, surface)) => Err((err, Surface::Default(surface))),
163                    }
164                }
165                _ => Err((Error::IncompatibleSurface, surface)),
166            },
167            (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => {
168                match surface {
169                    Surface::Alternate(surface) => {
170                        match device.create_surface_texture(context, surface) {
171                            Ok(surface_texture) => Ok(SurfaceTexture::Alternate(surface_texture)),
172                            Err((err, surface)) => Err((err, Surface::Alternate(surface))),
173                        }
174                    }
175                    _ => Err((Error::IncompatibleSurface, surface)),
176                }
177            }
178            _ => Err((Error::IncompatibleContext, surface)),
179        }
180    }
181
182    /// Destroys a surface.
183    ///
184    /// The supplied context must be the context the surface is associated with, or this returns
185    /// an `IncompatibleSurface` error.
186    ///
187    /// You must explicitly call this method to dispose of a surface. Otherwise, a panic occurs in
188    /// the `drop` method.
189    pub fn destroy_surface(
190        &self,
191        context: &mut Context<Def, Alt>,
192        surface: &mut Surface<Def, Alt>,
193    ) -> Result<(), Error> {
194        match (self, &mut *context) {
195            (Device::Default(device), &mut Context::Default(ref mut context)) => match *surface {
196                Surface::Default(ref mut surface) => device.destroy_surface(context, surface),
197                _ => Err(Error::IncompatibleSurface),
198            },
199            (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => match *surface
200            {
201                Surface::Alternate(ref mut surface) => device.destroy_surface(context, surface),
202                _ => Err(Error::IncompatibleSurface),
203            },
204            _ => Err(Error::IncompatibleContext),
205        }
206    }
207
208    /// Destroys a surface texture and returns the underlying surface.
209    ///
210    /// The supplied context must be the same context the surface texture was created with, or an
211    /// `IncompatibleSurfaceTexture` error is returned.
212    ///
213    /// All surface textures must be explicitly destroyed with this function, or a panic will
214    /// occur.
215    pub fn destroy_surface_texture(
216        &self,
217        context: &mut Context<Def, Alt>,
218        surface_texture: SurfaceTexture<Def, Alt>,
219    ) -> Result<Surface<Def, Alt>, (Error, SurfaceTexture<Def, Alt>)> {
220        match (self, &mut *context) {
221            (Device::Default(device), &mut Context::Default(ref mut context)) => {
222                match surface_texture {
223                    SurfaceTexture::Default(surface_texture) => {
224                        match device.destroy_surface_texture(context, surface_texture) {
225                            Ok(surface) => Ok(Surface::Default(surface)),
226                            Err((err, surface_texture)) => {
227                                Err((err, SurfaceTexture::Default(surface_texture)))
228                            }
229                        }
230                    }
231                    _ => Err((Error::IncompatibleSurfaceTexture, surface_texture)),
232                }
233            }
234            (Device::Alternate(device), &mut Context::Alternate(ref mut context)) => {
235                match surface_texture {
236                    SurfaceTexture::Alternate(surface_texture) => {
237                        match device.destroy_surface_texture(context, surface_texture) {
238                            Ok(surface) => Ok(Surface::Alternate(surface)),
239                            Err((err, surface_texture)) => {
240                                Err((err, SurfaceTexture::Alternate(surface_texture)))
241                            }
242                        }
243                    }
244                    _ => Err((Error::IncompatibleSurfaceTexture, surface_texture)),
245                }
246            }
247            _ => Err((Error::IncompatibleContext, surface_texture)),
248        }
249    }
250
251    /// Displays the contents of a widget surface on screen.
252    ///
253    /// Widget surfaces are internally double-buffered, so changes to them don't show up in their
254    /// associated widgets until this method is called.
255    ///
256    /// The supplied context must match the context the surface was created with, or an
257    /// `IncompatibleSurface` error is returned.
258    pub fn present_surface(
259        &self,
260        context: &Context<Def, Alt>,
261        surface: &mut Surface<Def, Alt>,
262    ) -> Result<(), Error> {
263        match (self, context) {
264            (Device::Default(device), Context::Default(context)) => match *surface {
265                Surface::Default(ref mut surface) => device.present_surface(context, surface),
266                _ => Err(Error::IncompatibleSurface),
267            },
268            (Device::Alternate(device), Context::Alternate(context)) => match *surface {
269                Surface::Alternate(ref mut surface) => device.present_surface(context, surface),
270                _ => Err(Error::IncompatibleSurface),
271            },
272            _ => Err(Error::IncompatibleContext),
273        }
274    }
275
276    /// Resizes a widget surface.
277    pub fn resize_surface(
278        &self,
279        context: &Context<Def, Alt>,
280        surface: &mut Surface<Def, Alt>,
281        size: Size2D<i32>,
282    ) -> Result<(), Error> {
283        match (self, context) {
284            (Device::Default(device), Context::Default(context)) => match *surface {
285                Surface::Default(ref mut surface) => device.resize_surface(context, surface, size),
286                _ => Err(Error::IncompatibleSurface),
287            },
288            (Device::Alternate(device), Context::Alternate(context)) => match *surface {
289                Surface::Alternate(ref mut surface) => {
290                    device.resize_surface(context, surface, size)
291                }
292                _ => Err(Error::IncompatibleSurface),
293            },
294            _ => Err(Error::IncompatibleContext),
295        }
296    }
297
298    /// Returns the OpenGL texture target needed to read from this surface texture.
299    ///
300    /// This will be `GL_TEXTURE_2D` or `GL_TEXTURE_RECTANGLE`, depending on platform.
301    #[inline]
302    pub fn surface_gl_texture_target(&self) -> u32 {
303        match *self {
304            Device::Default(ref device) => device.surface_gl_texture_target(),
305            Device::Alternate(ref device) => device.surface_gl_texture_target(),
306        }
307    }
308
309    /// Returns various information about the surface, including the framebuffer object needed to
310    /// render to this surface.
311    ///
312    /// Before rendering to a surface attached to a context, you must call `glBindFramebuffer()`
313    /// on the framebuffer object returned by this function. This framebuffer object may or not be
314    /// 0, the default framebuffer, depending on platform.
315    pub fn surface_info(&self, surface: &Surface<Def, Alt>) -> SurfaceInfo {
316        match (self, surface) {
317            (Device::Default(device), Surface::Default(ref surface)) => {
318                device.surface_info(surface)
319            }
320            (Device::Alternate(device), Surface::Alternate(ref surface)) => {
321                device.surface_info(surface)
322            }
323            _ => panic!("Incompatible context!"),
324        }
325    }
326
327    /// Returns the OpenGL texture object containing the contents of this surface.
328    ///
329    /// It is only legal to read from, not write to, this texture object.
330    pub fn surface_texture_object(
331        &self,
332        surface_texture: &SurfaceTexture<Def, Alt>,
333    ) -> Option<Texture> {
334        match (self, surface_texture) {
335            (Device::Default(device), SurfaceTexture::Default(ref surface_texture)) => {
336                device.surface_texture_object(surface_texture)
337            }
338            (Device::Alternate(device), SurfaceTexture::Alternate(ref surface_texture)) => {
339                device.surface_texture_object(surface_texture)
340            }
341            _ => panic!("Incompatible context!"),
342        }
343    }
344}