surfman/platform/unix/generic/
surface.rs

1//! surfman/surfman/src/platform/unix/generic/surface.rs
2//!
3//! Wrapper for EGL surfaces on Mesa.
4
5use super::context::Context;
6use super::device::Device;
7use crate::gl;
8use crate::platform::generic::egl::surface::{EGLBackedSurface, EGLSurfaceTexture};
9use crate::{Error, SurfaceAccess, SurfaceInfo, SurfaceType};
10
11use euclid::default::Size2D;
12use glow::Texture;
13use std::marker::PhantomData;
14
15// FIXME(pcwalton): Is this right, or should it be `TEXTURE_EXTERNAL_OES`?
16const SURFACE_GL_TEXTURE_TARGET: u32 = gl::TEXTURE_2D;
17
18/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either
19/// displayed in a native widget or bound to a texture for reading.
20///
21/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a
22/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation,
23/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a
24/// widget but not bound to a texture.
25///
26/// Surfaces are specific to a given context and cannot be rendered to from any context other than
27/// the one they were created with. However, they can be *read* from any context on any thread (as
28/// long as that context shares the same adapter and connection), by wrapping them in a
29/// `SurfaceTexture`.
30///
31/// Depending on the platform, each surface may be internally double-buffered.
32///
33/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur.
34#[derive(Debug)]
35pub struct Surface(pub(crate) EGLBackedSurface);
36
37/// Represents an OpenGL texture that wraps a surface.
38///
39/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to
40/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that
41/// framebuffer).
42///
43/// Surface textures are local to a context, but that context does not have to be the same context
44/// as that associated with the underlying surface. The texture must be destroyed with the
45/// `destroy_surface_texture()` method, or a panic will occur.
46#[derive(Debug)]
47pub struct SurfaceTexture(pub(crate) EGLSurfaceTexture);
48
49/// A placeholder wrapper for a native widget.
50#[derive(Clone)]
51pub struct NativeWidget;
52
53unsafe impl Send for Surface {}
54
55impl Device {
56    /// Creates either a generic or a widget surface, depending on the supplied surface type.
57    ///
58    /// Only the given context may ever render to the surface, but generic surfaces can be wrapped
59    /// up in a `SurfaceTexture` for reading by other contexts.
60    pub fn create_surface(
61        &mut self,
62        context: &Context,
63        _: SurfaceAccess,
64        surface_type: SurfaceType<NativeWidget>,
65    ) -> Result<Surface, Error> {
66        match surface_type {
67            SurfaceType::Generic { size } => self.create_generic_surface(context, &size),
68            SurfaceType::Widget { .. } => Err(Error::UnsupportedOnThisPlatform),
69        }
70    }
71
72    fn create_generic_surface(
73        &mut self,
74        context: &Context,
75        size: &Size2D<i32>,
76    ) -> Result<Surface, Error> {
77        let _guard = self.temporarily_make_context_current(context)?;
78        let context_descriptor = self.context_descriptor(context);
79        let context_attributes = self.context_descriptor_attributes(&context_descriptor);
80
81        Ok(Surface(EGLBackedSurface::new_generic(
82            &context.1,
83            self.native_connection.egl_display,
84            context.0.egl_context,
85            context.0.id,
86            &context_attributes,
87            size,
88        )))
89    }
90
91    /// Creates a surface texture from an existing generic surface for use with the given context.
92    ///
93    /// The surface texture is local to the supplied context and takes ownership of the surface.
94    /// Destroying the surface texture allows you to retrieve the surface again.
95    ///
96    /// *The supplied context does not have to be the same context that the surface is associated
97    /// with.* This allows you to render to a surface in one context and sample from that surface
98    /// in another context.
99    ///
100    /// Calling this method on a widget surface returns a `WidgetAttached` error.
101    pub fn create_surface_texture(
102        &self,
103        context: &mut Context,
104        surface: Surface,
105    ) -> Result<SurfaceTexture, (Error, Surface)> {
106        let _guard = match self.temporarily_make_context_current(context) {
107            Ok(guard) => guard,
108            Err(err) => return Err((err, surface)),
109        };
110
111        match surface.0.to_surface_texture(&context.1) {
112            Ok(surface_texture) => Ok(SurfaceTexture(surface_texture)),
113            Err((err, surface)) => Err((err, Surface(surface))),
114        }
115    }
116
117    /// Destroys a surface.
118    ///
119    /// The supplied context must be the context the surface is associated with, or this returns
120    /// an `IncompatibleSurface` error.
121    ///
122    /// You must explicitly call this method to dispose of a surface. Otherwise, a panic occurs in
123    /// the `drop` method.
124    pub fn destroy_surface(
125        &self,
126        context: &mut Context,
127        surface: &mut Surface,
128    ) -> Result<(), Error> {
129        let egl_display = self.native_connection.egl_display;
130        let window = surface.0.destroy(&context.1, egl_display, context.0.id)?;
131        debug_assert!(window.is_none());
132        Ok(())
133    }
134
135    /// Destroys a surface texture and returns the underlying surface.
136    ///
137    /// The supplied context must be the same context the surface texture was created with, or an
138    /// `IncompatibleSurfaceTexture` error is returned.
139    ///
140    /// All surface textures must be explicitly destroyed with this function, or a panic will
141    /// occur.
142    pub fn destroy_surface_texture(
143        &self,
144        context: &mut Context,
145        surface_texture: SurfaceTexture,
146    ) -> Result<Surface, (Error, SurfaceTexture)> {
147        match self.temporarily_make_context_current(context) {
148            Ok(_guard) => Ok(Surface(surface_texture.0.destroy(&context.1))),
149            Err(err) => Err((err, surface_texture)),
150        }
151    }
152
153    /// Displays the contents of a widget surface on screen.
154    ///
155    /// Widget surfaces are internally double-buffered, so changes to them don't show up in their
156    /// associated widgets until this method is called.
157    ///
158    /// The supplied context must match the context the surface was created with, or an
159    /// `IncompatibleSurface` error is returned.
160    pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> {
161        surface
162            .0
163            .present(self.native_connection.egl_display, context.0.egl_context)
164    }
165
166    /// Resizes a widget surface.
167    pub fn resize_surface(
168        &self,
169        _context: &Context,
170        surface: &mut Surface,
171        size: Size2D<i32>,
172    ) -> Result<(), Error> {
173        surface.0.size = size;
174        Ok(())
175    }
176
177    /// Returns a pointer to the underlying surface data for reading or writing by the CPU.
178    #[inline]
179    pub fn lock_surface_data<'s>(&self, _: &'s mut Surface) -> Result<SurfaceDataGuard<'s>, Error> {
180        Err(Error::Unimplemented)
181    }
182
183    /// Returns the OpenGL texture target needed to read from this surface texture.
184    ///
185    /// This will be `GL_TEXTURE_2D` or `GL_TEXTURE_RECTANGLE`, depending on platform.
186    #[inline]
187    pub fn surface_gl_texture_target(&self) -> u32 {
188        SURFACE_GL_TEXTURE_TARGET
189    }
190
191    /// Returns various information about the surface, including the framebuffer object needed to
192    /// render to this surface.
193    ///
194    /// Before rendering to a surface attached to a context, you must call `glBindFramebuffer()`
195    /// on the framebuffer object returned by this function. This framebuffer object may or not be
196    /// 0, the default framebuffer, depending on platform.
197    pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo {
198        surface.0.info()
199    }
200
201    /// Returns the OpenGL texture object containing the contents of this surface.
202    ///
203    /// It is only legal to read from, not write to, this texture object.
204    #[inline]
205    pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option<Texture> {
206        surface_texture.0.texture_object
207    }
208}
209
210/// Represents the CPU view of the pixel data of this surface.
211pub struct SurfaceDataGuard<'a> {
212    phantom: PhantomData<&'a ()>,
213}