Skip to main content

surfman/x11/
device.rs

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