surfman/platform/unix/x11/
surface.rs

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