surfman/platform/unix/wayland/
surface.rs

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