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

use super::{ffi, native, wrap_egl_call, EGLError, SwapBuffersError};
use crate::backend::egl::display::EGLDisplayHandle;
use crate::backend::graphics::PixelFormat;
use nix::libc::c_int;
};

use crate::backend::egl::{
    display::{DamageSupport, EGLDisplay, EGLDisplayHandle, PixelFormat},
    ffi,
    native::EGLNativeSurface,
    EGLError, SwapBuffersError,
};
use crate::utils::{Physical, Rectangle, Size};

use slog::{debug, o};

/// EGL surface of a given EGL context for rendering
pub struct EGLSurface {
    pub(crate) display: Arc<EGLDisplayHandle>,
    native: Box<dyn EGLNativeSurface + Send + 'static>,
    pub(crate) surface: AtomicPtr<nix::libc::c_void>,
    config_id: ffi::egl::types::EGLConfig,
    pixel_format: PixelFormat,
    damage_impl: DamageSupport,
    logger: ::slog::Logger,
}

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()
    }
}
        config: ffi::egl::types::EGLConfig,
        native: N,
        log: L,
    ) -> Result<EGLSurface<N>, EGLError>
    where
        L: Into<Option<::slog::Logger>>,
    {
    ///
    /// 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);
        let surface = unsafe { native.create(&*display, config, &surface_attributes)? };

        if surface == ffi::egl::NO_SURFACE {
            return Err(EGLError::BadSurface);
        }

        Ok(EGLSurface {
            logger: log,
        })
    }

    /// Returns the buffer age of the underlying back buffer
    pub fn buffer_age(&self) -> Option<i32> {
        let surface = self.surface.load(Ordering::SeqCst);
        let mut age = 0;
        let ret = unsafe {
            ffi::egl::QuerySurface(
                **self.display,
                surface as *const _,
                ffi::egl::BUFFER_AGE_EXT as i32,
                &mut age as *mut _,
            )
        };
        if ret == ffi::egl::FALSE {
            slog::debug!(
                self.logger,
                "Failed to query buffer age value for surface {:?}: {}",
                self,
                EGLError::from_last_call().unwrap_err()
            );
            None
        } else {
            Some(age)
        }
        };

        if self.native.needs_recreation() || surface.is_null() || is_bad_surface {
            self.native.recreate().map_err(SwapBuffersError::Underlying)?;
            if !surface.is_null() {
                let _ = unsafe { ffi::egl::DestroySurface(**self.display, surface as *const _) };
            }
            //TODO remove recreate
            self.native.recreate();
            self.surface.set(unsafe {
                self.native
                    .create(&*self.display, self.config_id, &self.surface_attributes)
                    .map_err(SwapBuffersError::EGLCreateWindowSurface)?
            });

            result.map_err(|err| {
                &mut width as *mut _,
            )
        };
        if ret_h == ffi::egl::FALSE || ret_w == ffi::egl::FALSE {
            slog::debug!(
                self.logger,
                "Failed to query size value for surface {:?}: {}",
                self,
                EGLError::from_last_call().unwrap_err()
            );
            None
        } else {
            Some(Size::from((width, height)))
        }
    }

    /// Swaps buffers at the end of a frame.
    pub fn swap_buffers(
        &self,
        damage: Option<&mut [Rectangle<i32, Physical>]>,
    ) -> ::std::result::Result<(), SwapBuffersError> {
        let surface = self.surface.load(Ordering::SeqCst);

        let result = if !surface.is_null() {
            self.native
                .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))
        );

        if self.native.needs_recreation() || surface.is_null() || is_bad_surface {
            let previous = self
                .surface
                .compare_exchange(
                    surface,
                    self.native
                        .create(&self.display, self.config_id)
                        .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> egl: NativeSurface: replace recreate with create

<DFF> @@ -1,6 +1,6 @@
 //! EGL surface related structs
 
-use super::{ffi, native, wrap_egl_call, EGLError, SwapBuffersError};
+use super::{ffi, native, wrap_egl_call, EGLError, SurfaceCreationError, SwapBuffersError};
 use crate::backend::egl::display::EGLDisplayHandle;
 use crate::backend::graphics::PixelFormat;
 use nix::libc::c_int;
@@ -42,7 +42,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
         config: ffi::egl::types::EGLConfig,
         native: N,
         log: L,
-    ) -> Result<EGLSurface<N>, EGLError>
+    ) -> Result<EGLSurface<N>, SurfaceCreationError<N::Error>>
     where
         L: Into<Option<::slog::Logger>>,
     {
@@ -72,7 +72,9 @@ impl<N: native::NativeSurface> EGLSurface<N> {
         let surface = unsafe { native.create(&*display, config, &surface_attributes)? };
 
         if surface == ffi::egl::NO_SURFACE {
-            return Err(EGLError::BadSurface);
+            return Err(SurfaceCreationError::EGLSurfaceCreationFailed(
+                EGLError::BadSurface,
+            ));
         }
 
         Ok(EGLSurface {
@@ -106,16 +108,20 @@ impl<N: native::NativeSurface> EGLSurface<N> {
         };
 
         if self.native.needs_recreation() || surface.is_null() || is_bad_surface {
-            self.native.recreate().map_err(SwapBuffersError::Underlying)?;
             if !surface.is_null() {
                 let _ = unsafe { ffi::egl::DestroySurface(**self.display, surface as *const _) };
             }
-            //TODO remove recreate
-            self.native.recreate();
             self.surface.set(unsafe {
                 self.native
                     .create(&*self.display, self.config_id, &self.surface_attributes)
-                    .map_err(SwapBuffersError::EGLCreateWindowSurface)?
+                    .map_err(|err| match err {
+                        SurfaceCreationError::EGLSurfaceCreationFailed(err) => {
+                            SwapBuffersError::EGLCreateWindowSurface(err)
+                        }
+                        SurfaceCreationError::NativeSurfaceCreationFailed(err) => {
+                            SwapBuffersError::Underlying(err)
+                        }
+                    })?
             });
 
             result.map_err(|err| {

egl: NativeSurface: replace recreate with create