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}