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}