Skip to main content

surfman/multi/
surface.rs

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