<NME> surface.rs
<BEF> //! EGL surface related structs

use std::fmt;
use crate::backend::egl::display::EGLDisplayHandle;
use crate::backend::graphics::PixelFormat;
use nix::libc::c_int;
use std::sync::Arc;
use std::{
    cell::Cell,
    ops::{Deref, DerefMut},
};

/// EGL surface of a given EGL context for rendering
pub struct EGLSurface<N: native::NativeSurface> {
    pub(crate) display: Arc<EGLDisplayHandle>,
    native: N,
    pub(crate) surface: Cell<ffi::egl::types::EGLSurface>,
    config_id: ffi::egl::types::EGLConfig,
    pixel_format: PixelFormat,
    surface_attributes: Vec<c_int>,
    logger: ::slog::Logger,
}

impl<N: native::NativeSurface> Deref for EGLSurface<N> {
    type Target = N;
impl fmt::Debug for EGLSurface {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("EGLSurface")
            .field("display", &self.display)
            // native does not necessarily implement Debug
            .field("surface", &self.surface)
            .field("config_id", &self.config_id)
            .field("pixel_format", &self.pixel_format)
            .field("logger", &self.logger)
            .finish()
    }
}

// safe because EGLConfig can be moved between threads
// and the other types are thread-safe
unsafe impl Send for EGLSurface {}

impl EGLSurface {
    /// Create a new `EGLSurface`.
    ///
    /// Requires:
    /// - A EGLDisplay supported by the corresponding platform matching the surface type
    /// - A pixel format
    /// - A valid `EGLConfig` (see `EGLContext::config_id()`)
    /// - A native type backing the surface matching the used platform
    /// - An (optional) Logger
    pub fn new<N, L>(
        display: &EGLDisplay,
        pixel_format: PixelFormat,
        config: ffi::egl::types::EGLConfig,
        native: N,
        log: L,
    ) -> Result<EGLSurface, EGLError>
    where
        N: EGLNativeSurface + Send + 'static,
        L: Into<Option<::slog::Logger>>,
    {
        let log = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "renderer_egl"));

        let surface = native.create(&display.get_display_handle(), config)?;
        if surface == ffi::egl::NO_SURFACE {
            return Err(EGLError::BadSurface);
        }

        Ok(EGLSurface {
            display: display.get_display_handle(),
            native: Box::new(native),
            surface: AtomicPtr::new(surface as *mut _),
            config_id: config,
            pixel_format,
            damage_impl: display.supports_damage_impl(),
            logger: log,
        })
    }
        Ok(EGLSurface {
            display,
            native,
            surface: Cell::new(surface),
            config_id: config,
            pixel_format,
            surface_attributes,
                **self.display,
                surface as *const _,
                ffi::egl::BUFFER_AGE_EXT as i32,

    /// Swaps buffers at the end of a frame.
    pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError<N::Error>> {
        let surface = self.surface.get();

        let result = if !surface.is_null() {
            self.native.swap_buffers(&self.display, surface)
                self,
                EGLError::from_last_call().unwrap_err()
            );
            None
        } else {
            Some(age)
        }
    }

        };

        if self.native.needs_recreation() || surface.is_null() || is_bad_surface {
            if !surface.is_null() {
                let _ = unsafe { ffi::egl::DestroySurface(**self.display, surface as *const _) };
            }
            self.surface.set(unsafe {
                self.native
                    .create(&self.display, self.config_id, &self.surface_attributes)
                    .map_err(|err| match err {
                        SurfaceCreationError::EGLSurfaceCreationFailed(err) => {
                            SwapBuffersError::EGLCreateWindowSurface(err)
                        }
                        SurfaceCreationError::NativeSurfaceCreationFailed(err) => {
                            SwapBuffersError::Underlying(err)
                        }
                    })?
            });

            // if a recreation is pending anyway, ignore page-flip errors.
            // lets see if we still fail after the next commit.
                self,
                EGLError::from_last_call().unwrap_err()
            );
            None
        } else {
            Some(Size::from((width, height)))
        }
    }

    /// Returns true if the OpenGL surface is the current one in the thread.
    pub fn is_current(&self) -> bool {
        unsafe {
            ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _
                && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _
        }
    }

                .swap_buffers(&self.display, surface, damage, self.damage_impl)
        } else {
            Err(SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface))
        };

        // workaround for missing `PartialEq` impl
        let is_bad_surface = matches!(
            result,
            Err(SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface))
        );

impl<N: native::NativeSurface> Drop for EGLSurface<N> {
    fn drop(&mut self) {
        unsafe {
            ffi::egl::DestroySurface(**self.display, self.surface.get() as *const _);
        }
    }
}
                        .map_err(SwapBuffersError::EGLCreateSurface)? as *mut _,
                    Ordering::SeqCst,
                    Ordering::SeqCst,
                )
                .expect("The surface pointer changed in between?");
            if previous == surface && !surface.is_null() {
                let _ = unsafe { ffi::egl::DestroySurface(**self.display, surface as *const _) };
            }

            // if a recreation is pending anyway, ignore page-flip errors.
            // lets see if we still fail after the next commit.
            result.map_err(|err| {
                debug!(self.logger, "Hiding page-flip error *before* recreation: {}", err);
                SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface)
            })
        } else {
            result
        }
    }

    /// Returns true if the OpenGL surface is the current one in the thread.
    pub fn is_current(&self) -> bool {
        let surface = self.surface.load(Ordering::SeqCst);
        unsafe {
            ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == surface as *const _
                && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == surface as *const _
        }
    }

    /// Returns the egl config for this context
    pub fn config_id(&self) -> ffi::egl::types::EGLConfig {
        self.config_id
    }

    /// Returns the pixel format of the main framebuffer of the context.
    pub fn pixel_format(&self) -> PixelFormat {
        self.pixel_format
    }

    /// Tries to resize the underlying native surface.
    ///
    /// The two first arguments (width, height) are the new size of the surface,
    /// the two others (dx, dy) represent the displacement of the top-left corner of the surface.
    /// It allows you to control the direction of the resizing if necessary.
    ///
    /// Implementations may ignore the dx and dy arguments.
    ///
    /// Returns true if the resize was successful.
    pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) -> bool {
        self.native.resize(width, height, dx, dy)
    }

    /// Get a raw handle to the underlying surface
    ///
    /// *Note*: The surface might get dynamically recreated during swap-buffers
    /// causing the pointer to become invalid.
    ///
    /// The pointer will become invalid, when this struct is destroyed.
    pub fn get_surface_handle(&self) -> ffi::egl::types::EGLSurface {
        self.surface.load(Ordering::SeqCst)
    }
}

impl Drop for EGLSurface {
    fn drop(&mut self) {
        unsafe {
            ffi::egl::DestroySurface(**self.display, *self.surface.get_mut() as *const _);
        }
    }
}

<MSG> drm: Make surfaces `Send`

<DFF> @@ -4,22 +4,26 @@ use super::{ffi, native, EGLError, SurfaceCreationError, SwapBuffersError};
 use crate::backend::egl::display::EGLDisplayHandle;
 use crate::backend::graphics::PixelFormat;
 use nix::libc::c_int;
-use std::sync::Arc;
-use std::{
-    cell::Cell,
-    ops::{Deref, DerefMut},
+use std::ops::{Deref, DerefMut};
+use std::sync::{
+    atomic::{AtomicPtr, Ordering},
+    Arc,
 };
 
 /// EGL surface of a given EGL context for rendering
 pub struct EGLSurface<N: native::NativeSurface> {
     pub(crate) display: Arc<EGLDisplayHandle>,
     native: N,
-    pub(crate) surface: Cell<ffi::egl::types::EGLSurface>,
+    pub(crate) surface: AtomicPtr<nix::libc::c_void>,
     config_id: ffi::egl::types::EGLConfig,
     pixel_format: PixelFormat,
     surface_attributes: Vec<c_int>,
     logger: ::slog::Logger,
 }
+// safe because EGLConfig can be moved between threads
+// and the other types are thread-safe
+unsafe impl<N: native::NativeSurface + Send> Send for EGLSurface<N> {}
+unsafe impl<N: native::NativeSurface + Send + Sync> Sync for EGLSurface<N> {}
 
 impl<N: native::NativeSurface> Deref for EGLSurface<N> {
     type Target = N;
@@ -80,7 +84,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
         Ok(EGLSurface {
             display,
             native,
-            surface: Cell::new(surface),
+            surface: AtomicPtr::new(surface as *mut _),
             config_id: config,
             pixel_format,
             surface_attributes,
@@ -90,7 +94,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
 
     /// Swaps buffers at the end of a frame.
     pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError<N::Error>> {
-        let surface = self.surface.get();
+        let surface = self.surface.load(Ordering::SeqCst);
 
         let result = if !surface.is_null() {
             self.native.swap_buffers(&self.display, surface)
@@ -106,21 +110,25 @@ impl<N: native::NativeSurface> EGLSurface<N> {
         };
 
         if self.native.needs_recreation() || surface.is_null() || is_bad_surface {
-            if !surface.is_null() {
+            let previous = self.surface.compare_and_swap(
+                surface,
+                unsafe {
+                    self.native
+                        .create(&self.display, self.config_id, &self.surface_attributes)
+                        .map_err(|err| match err {
+                            SurfaceCreationError::EGLSurfaceCreationFailed(err) => {
+                                SwapBuffersError::EGLCreateWindowSurface(err)
+                            }
+                            SurfaceCreationError::NativeSurfaceCreationFailed(err) => {
+                                SwapBuffersError::Underlying(err)
+                            }
+                        })? as *mut _
+                },
+                Ordering::SeqCst,
+            );
+            if previous == surface && !surface.is_null() {
                 let _ = unsafe { ffi::egl::DestroySurface(**self.display, surface as *const _) };
             }
-            self.surface.set(unsafe {
-                self.native
-                    .create(&self.display, self.config_id, &self.surface_attributes)
-                    .map_err(|err| match err {
-                        SurfaceCreationError::EGLSurfaceCreationFailed(err) => {
-                            SwapBuffersError::EGLCreateWindowSurface(err)
-                        }
-                        SurfaceCreationError::NativeSurfaceCreationFailed(err) => {
-                            SwapBuffersError::Underlying(err)
-                        }
-                    })?
-            });
 
             // if a recreation is pending anyway, ignore page-flip errors.
             // lets see if we still fail after the next commit.
@@ -135,9 +143,10 @@ impl<N: native::NativeSurface> EGLSurface<N> {
 
     /// Returns true if the OpenGL surface is the current one in the thread.
     pub fn is_current(&self) -> bool {
+        let surface = self.surface.load(Ordering::SeqCst);
         unsafe {
-            ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _
-                && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _
+            ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == surface as *const _
+                && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == surface as *const _
         }
     }
 
@@ -155,7 +164,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
 impl<N: native::NativeSurface> Drop for EGLSurface<N> {
     fn drop(&mut self) {
         unsafe {
-            ffi::egl::DestroySurface(**self.display, self.surface.get() as *const _);
+            ffi::egl::DestroySurface(**self.display, *self.surface.get_mut() as *const _);
         }
     }
 }

drm: Make surfaces `Send`