Skip to main content

surfman/wayland/
device.rs

1//! A wrapper around Wayland `EGLDisplay`s.
2
3use super::connection::{Connection, NativeConnectionWrapper};
4use super::context::{Context, ContextDescriptor, NativeContext};
5use super::surface::{NativeWidget, Surface, SurfaceDataGuard, SurfaceTexture};
6use crate::base::egl::context::{self, CurrentContextGuard, EGLBackedContext};
7use crate::base::egl::surface::EGLBackedSurface;
8use crate::context::ContextID;
9use crate::egl;
10use crate::egl::types::EGLint;
11use crate::gl;
12pub use crate::mesa_surfaceless::device::Adapter;
13use crate::surface::Framebuffer;
14use crate::{ContextAttributes, Gl, SurfaceInfo};
15use crate::{Error, GLApi, SurfaceAccess, SurfaceType};
16use euclid::default::Size2D;
17use glow::Texture;
18use std::os::raw::c_void;
19use std::sync::Arc;
20use wayland_sys::client::wl_proxy;
21use wayland_sys::egl::{wayland_egl_handle, wl_egl_window};
22
23// FIXME(pcwalton): Is this right, or should it be `TEXTURE_EXTERNAL_OES`?
24const SURFACE_GL_TEXTURE_TARGET: u32 = gl::TEXTURE_2D;
25
26/// A thread-local handle to a device.
27///
28/// Devices contain most of the relevant surface management methods.
29pub struct Device {
30    pub(crate) native_connection: Arc<NativeConnectionWrapper>,
31    pub(crate) adapter: Adapter,
32}
33
34/// Wraps an adapter.
35///
36/// On Wayland, devices and adapters are essentially identical types.
37#[derive(Clone)]
38pub struct NativeDevice {
39    /// The hardware adapter corresponding to this device.
40    pub adapter: Adapter,
41}
42
43impl Device {
44    #[inline]
45    pub(crate) fn new(connection: &Connection, adapter: &Adapter) -> Result<Device, Error> {
46        Ok(Device {
47            native_connection: connection.native_connection.clone(),
48            adapter: (*adapter).clone(),
49        })
50    }
51
52    /// Returns the native device corresponding to this device.
53    ///
54    /// This method is essentially an alias for the `adapter()` method on Wayland, since there is
55    /// no explicit concept of a device on this backend.
56    #[inline]
57    pub fn native_device(&self) -> NativeDevice {
58        NativeDevice {
59            adapter: self.adapter(),
60        }
61    }
62
63    /// Returns the display server connection that this device was created with.
64    #[inline]
65    pub fn connection(&self) -> Connection {
66        Connection {
67            native_connection: self.native_connection.clone(),
68        }
69    }
70
71    /// Returns the adapter that this device was created with.
72    #[inline]
73    pub fn adapter(&self) -> Adapter {
74        self.adapter.clone()
75    }
76
77    /// Returns the OpenGL API flavor that this device supports (OpenGL or OpenGL ES).
78    #[inline]
79    pub fn gl_api(&self) -> GLApi {
80        if std::env::var("SURFMAN_FORCE_GLES").is_ok() {
81            GLApi::GLES
82        } else {
83            GLApi::GL
84        }
85    }
86
87    /// Creates a context descriptor with the given attributes.
88    ///
89    /// Context descriptors are local to this device.
90    #[inline]
91    pub fn create_context_descriptor(
92        &self,
93        attributes: &ContextAttributes,
94    ) -> Result<ContextDescriptor, Error> {
95        // Set environment variables as appropriate.
96        self.adapter.set_environment_variables();
97
98        unsafe {
99            ContextDescriptor::new(
100                self.native_connection.egl_display,
101                attributes,
102                &[
103                    egl::SURFACE_TYPE as EGLint,
104                    egl::WINDOW_BIT as EGLint,
105                    egl::RENDERABLE_TYPE as EGLint,
106                    egl::OPENGL_BIT as EGLint,
107                ],
108            )
109        }
110    }
111
112    /// Creates a new OpenGL context.
113    ///
114    /// The context initially has no surface attached. Until a surface is bound to it, rendering
115    /// commands will fail or have no effect.
116    #[inline]
117    pub fn create_context(
118        &self,
119        descriptor: &ContextDescriptor,
120        share_with: Option<&Context>,
121    ) -> Result<Context, Error> {
122        unsafe {
123            let context = EGLBackedContext::new(
124                self.native_connection.egl_display,
125                descriptor,
126                share_with.map(|ctx| &ctx.0),
127                self.gl_api(),
128            )?;
129            context.make_current(self.native_connection.egl_display)?;
130            Ok(Context(
131                context,
132                Gl::from_loader_function(context::get_proc_address),
133            ))
134        }
135    }
136
137    /// Wraps an `EGLContext` in a native context and returns it.
138    ///
139    /// The context is not retained, as there is no way to do this in the EGL API. Therefore,
140    /// it is the caller's responsibility to ensure that the returned `Context` object remains
141    /// alive as long as the `EGLContext` is.
142    #[inline]
143    pub unsafe fn create_context_from_native_context(
144        &self,
145        native_context: NativeContext,
146    ) -> Result<Context, Error> {
147        Ok(Context(
148            EGLBackedContext::from_native_context(native_context),
149            Gl::from_loader_function(context::get_proc_address),
150        ))
151    }
152
153    /// Destroys a context.
154    ///
155    /// The context must have been created on this device.
156    pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> {
157        if let Ok(Some(mut surface)) = self.unbind_surface_from_context(context) {
158            self.destroy_surface(context, &mut surface)?;
159        }
160
161        unsafe {
162            context.0.destroy(self.native_connection.egl_display);
163            Ok(())
164        }
165    }
166
167    /// Given a context, returns its underlying EGL context and attached surfaces.
168    #[inline]
169    pub fn native_context(&self, context: &Context) -> NativeContext {
170        context.0.native_context()
171    }
172
173    /// Returns the descriptor that this context was created with.
174    #[inline]
175    pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor {
176        unsafe {
177            ContextDescriptor::from_egl_context(
178                &context.1,
179                self.native_connection.egl_display,
180                context.0.egl_context,
181            )
182        }
183    }
184
185    /// Makes the context the current OpenGL context for this thread.
186    ///
187    /// After calling this function, it is valid to use OpenGL rendering commands.
188    #[inline]
189    pub fn make_context_current(&self, context: &Context) -> Result<(), Error> {
190        unsafe { context.0.make_current(self.native_connection.egl_display) }
191    }
192
193    /// Removes the current OpenGL context from this thread.
194    ///
195    /// After calling this function, OpenGL rendering commands will fail until a new context is
196    /// made current.
197    #[inline]
198    pub fn make_no_context_current(&self) -> Result<(), Error> {
199        unsafe { context::make_no_context_current(self.native_connection.egl_display) }
200    }
201
202    #[inline]
203    pub(crate) fn temporarily_make_context_current(
204        &self,
205        context: &Context,
206    ) -> Result<CurrentContextGuard, Error> {
207        let guard = CurrentContextGuard::new();
208        self.make_context_current(context)?;
209        Ok(guard)
210    }
211
212    /// Returns the attributes that the context descriptor was created with.
213    #[inline]
214    pub fn context_descriptor_attributes(
215        &self,
216        context_descriptor: &ContextDescriptor,
217    ) -> ContextAttributes {
218        unsafe { context_descriptor.attributes(self.native_connection.egl_display) }
219    }
220
221    /// Fetches the address of an OpenGL function associated with this context.
222    ///
223    /// OpenGL functions are local to a context. You should not use OpenGL functions on one context
224    /// with any other context.
225    ///
226    /// This method is typically used with a function like `gl::load_with()` from the `gl` crate to
227    /// load OpenGL function pointers.
228    #[inline]
229    pub fn get_proc_address(&self, _: &Context, symbol_name: &str) -> *const c_void {
230        context::get_proc_address(symbol_name)
231    }
232
233    /// Attaches a surface to a context for rendering.
234    ///
235    /// This function takes ownership of the surface. The surface must have been created with this
236    /// context, or an `IncompatibleSurface` error is returned.
237    ///
238    /// If this function is called with a surface already bound, a `SurfaceAlreadyBound` error is
239    /// returned. To avoid this error, first unbind the existing surface with
240    /// `unbind_surface_from_context`.
241    ///
242    /// If an error is returned, the surface is returned alongside it.
243    #[inline]
244    pub fn bind_surface_to_context(
245        &self,
246        context: &mut Context,
247        surface: Surface,
248    ) -> Result<(), (Error, Surface)> {
249        unsafe {
250            context
251                .0
252                .bind_surface(self.native_connection.egl_display, surface.0)
253                .map_err(|(err, surface)| (err, Surface(surface)))
254        }
255    }
256
257    /// Removes and returns any attached surface from this context.
258    ///
259    /// Any pending OpenGL commands targeting this surface will be automatically flushed, so the
260    /// surface is safe to read from immediately when this function returns.
261    pub fn unbind_surface_from_context(
262        &self,
263        context: &mut Context,
264    ) -> Result<Option<Surface>, Error> {
265        unsafe {
266            context
267                .0
268                .unbind_surface(&context.1, self.native_connection.egl_display)
269                .map(|maybe_surface| maybe_surface.map(Surface))
270        }
271    }
272
273    /// Displays the contents of the currently bound surface to the screen, if
274    /// it is a widget surface.
275    ///
276    /// Widget surfaces are internally double-buffered, so changes to them don't
277    /// show up in their associated widgets until this method is called.
278    pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> {
279        context
280            .0
281            .present_bound_surface(self.native_connection.egl_display)
282    }
283
284    /// If the currently bound surface is a widget surface, resize it,
285    pub fn resize_bound_surface(
286        &self,
287        context: &mut Context,
288        size: Size2D<i32>,
289    ) -> Result<(), Error> {
290        match &mut context.0.framebuffer {
291            Framebuffer::Surface(surface) => surface.resize_for_wayland(size),
292            _ => Ok(()),
293        }
294    }
295
296    /// Returns a unique ID representing a context.
297    ///
298    /// This ID is unique to all currently-allocated contexts. If you destroy a context and create
299    /// a new one, the new context might have the same ID as the destroyed one.
300    #[inline]
301    pub fn context_id(&self, context: &Context) -> ContextID {
302        context.0.id
303    }
304
305    /// Returns various information about the surface attached to a context.
306    ///
307    /// This includes, most notably, the OpenGL framebuffer object needed to render to the surface.
308    #[inline]
309    pub fn context_surface_info(&self, context: &Context) -> Result<Option<SurfaceInfo>, Error> {
310        context.0.surface_info()
311    }
312
313    /// Creates either a generic or a widget surface, depending on the supplied surface type.
314    ///
315    /// Only the given context may ever render to the surface, but generic surfaces can be wrapped
316    /// up in a `SurfaceTexture` for reading by other contexts.
317    pub fn create_surface(
318        &self,
319        context: &Context,
320        _: SurfaceAccess,
321        surface_type: SurfaceType<NativeWidget>,
322    ) -> Result<Surface, Error> {
323        match surface_type {
324            SurfaceType::Generic { size } => self.create_generic_surface(context, &size),
325            SurfaceType::Widget { native_widget } => unsafe {
326                self.create_window_surface(
327                    context,
328                    native_widget.wayland_surface,
329                    &native_widget.size,
330                )
331            },
332        }
333    }
334
335    fn create_generic_surface(
336        &self,
337        context: &Context,
338        size: &Size2D<i32>,
339    ) -> Result<Surface, Error> {
340        let _guard = self.temporarily_make_context_current(context)?;
341        let context_descriptor = self.context_descriptor(context);
342        let context_attributes = self.context_descriptor_attributes(&context_descriptor);
343
344        Ok(Surface(EGLBackedSurface::new_generic(
345            &context.1,
346            self.native_connection.egl_display,
347            context.0.egl_context,
348            context.0.id,
349            &context_attributes,
350            size,
351        )))
352    }
353
354    unsafe fn create_window_surface(
355        &self,
356        context: &Context,
357        wayland_surface: *mut wl_proxy,
358        size: &Size2D<i32>,
359    ) -> Result<Surface, Error> {
360        let egl_window =
361            (wayland_egl_handle().wl_egl_window_create)(wayland_surface, size.width, size.height);
362        assert!(!egl_window.is_null());
363
364        let context_descriptor = self.context_descriptor(context);
365        let egl_config = context::egl_config_from_id(
366            self.native_connection.egl_display,
367            context_descriptor.egl_config_id,
368        );
369
370        Ok(Surface(EGLBackedSurface::new_window(
371            self.native_connection.egl_display,
372            egl_config,
373            egl_window as *mut c_void,
374            context.0.id,
375            size,
376        )))
377    }
378
379    /// Creates a surface texture from an existing generic surface for use with the given context.
380    ///
381    /// The surface texture is local to the supplied context and takes ownership of the surface.
382    /// Destroying the surface texture allows you to retrieve the surface again.
383    ///
384    /// *The supplied context does not have to be the same context that the surface is associated
385    /// with.* This allows you to render to a surface in one context and sample from that surface
386    /// in another context.
387    ///
388    /// Calling this method on a widget surface returns a `WidgetAttached` error.
389    pub fn create_surface_texture(
390        &self,
391        context: &mut Context,
392        surface: Surface,
393    ) -> Result<SurfaceTexture, (Error, Surface)> {
394        let _guard = match self.temporarily_make_context_current(context) {
395            Ok(guard) => guard,
396            Err(err) => return Err((err, surface)),
397        };
398
399        match surface.0.to_surface_texture(&context.1) {
400            Ok(surface_texture) => Ok(SurfaceTexture(surface_texture)),
401            Err((err, surface)) => Err((err, Surface(surface))),
402        }
403    }
404
405    /// Destroys a surface.
406    ///
407    /// The supplied context must be the context the surface is associated with, or this returns
408    /// an `IncompatibleSurface` error.
409    ///
410    /// You must explicitly call this method to dispose of a surface. Otherwise, a panic occurs in
411    /// the `drop` method.
412    pub fn destroy_surface(
413        &self,
414        context: &mut Context,
415        surface: &mut Surface,
416    ) -> Result<(), Error> {
417        let egl_display = self.native_connection.egl_display;
418        if let Some(wayland_egl_window) =
419            surface.0.destroy(&context.1, egl_display, context.0.id)?
420        {
421            unsafe {
422                let wayland_egl_window = wayland_egl_window as *mut wl_egl_window;
423                (wayland_egl_handle().wl_egl_window_destroy)(wayland_egl_window);
424            }
425        }
426        Ok(())
427    }
428
429    /// Destroys a surface texture and returns the underlying surface.
430    ///
431    /// The supplied context must be the same context the surface texture was created with, or an
432    /// `IncompatibleSurfaceTexture` error is returned.
433    ///
434    /// All surface textures must be explicitly destroyed with this function, or a panic will
435    /// occur.
436    pub fn destroy_surface_texture(
437        &self,
438        context: &mut Context,
439        surface_texture: SurfaceTexture,
440    ) -> Result<Surface, (Error, SurfaceTexture)> {
441        match self.temporarily_make_context_current(context) {
442            Ok(_guard) => Ok(Surface(surface_texture.0.destroy(&context.1))),
443            Err(err) => Err((err, surface_texture)),
444        }
445    }
446
447    /// Displays the contents of a widget surface on screen.
448    ///
449    /// Widget surfaces are internally double-buffered, so changes to them don't show up in their
450    /// associated widgets until this method is called.
451    ///
452    /// The supplied context must match the context the surface was created with, or an
453    /// `IncompatibleSurface` error is returned.
454    pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> {
455        surface
456            .0
457            .present(self.native_connection.egl_display, context.0.egl_context)
458    }
459
460    /// Resizes a widget surface.
461    pub fn resize_surface(
462        &self,
463        _context: &Context,
464        surface: &mut Surface,
465        size: Size2D<i32>,
466    ) -> Result<(), Error> {
467        surface.0.resize_for_wayland(size)
468    }
469
470    /// Returns a pointer to the underlying surface data for reading or writing by the CPU.
471    #[inline]
472    pub fn lock_surface_data<'s>(&self, _: &'s mut Surface) -> Result<SurfaceDataGuard<'s>, Error> {
473        Err(Error::Unimplemented)
474    }
475
476    /// Returns the OpenGL texture target needed to read from this surface texture.
477    ///
478    /// This will be `GL_TEXTURE_2D` or `GL_TEXTURE_RECTANGLE`, depending on platform.
479    #[inline]
480    pub fn surface_gl_texture_target(&self) -> u32 {
481        SURFACE_GL_TEXTURE_TARGET
482    }
483
484    /// Returns various information about the surface, including the framebuffer object needed to
485    /// render to this surface.
486    ///
487    /// Before rendering to a surface attached to a context, you must call `glBindFramebuffer()`
488    /// on the framebuffer object returned by this function. This framebuffer object may or not be
489    /// 0, the default framebuffer, depending on platform.
490    pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo {
491        surface.0.info()
492    }
493
494    /// Returns the OpenGL texture object containing the contents of this surface.
495    ///
496    /// It is only legal to read from, not write to, this texture object.
497    #[inline]
498    pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option<Texture> {
499        surface_texture.0.texture_object
500    }
501}