Skip to main content

surfman/mesa_surfaceless/
device.rs

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