use std::fmt;
use std::sync::{
atomic::{AtomicPtr, Ordering},
Arc,
};
use crate::backend::egl::{
display::{EGLDisplay, EGLDisplayHandle, PixelFormat},
ffi,
native::EGLNativeSurface,
EGLError, SwapBuffersError,
};
use slog::{debug, o};
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,
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)
.field("surface", &self.surface)
.field("config_id", &self.config_id)
.field("pixel_format", &self.pixel_format)
.field("logger", &self.logger)
.finish()
}
}
unsafe impl Send for EGLSurface {}
impl EGLSurface {
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.display, config)?;
if surface == ffi::egl::NO_SURFACE {
return Err(EGLError::BadSurface);
}
Ok(EGLSurface {
display: display.display.clone(),
native: Box::new(native),
surface: AtomicPtr::new(surface as *mut _),
config_id: config,
pixel_format,
logger: log,
})
}
pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
let surface = self.surface.load(Ordering::SeqCst);
let result = if !surface.is_null() {
self.native.swap_buffers(&self.display, surface)
} else {
Err(SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface))
};
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 _) };
}
result.map_err(|err| {
debug!(self.logger, "Hiding page-flip error *before* recreation: {}", err);
SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface)
})
} else {
result
}
}
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 _
}
}
pub fn config_id(&self) -> ffi::egl::types::EGLConfig {
self.config_id
}
pub fn pixel_format(&self) -> PixelFormat {
self.pixel_format
}
pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) -> bool {
self.native.resize(width, height, dx, dy)
}
}
impl Drop for EGLSurface {
fn drop(&mut self) {
unsafe {
ffi::egl::DestroySurface(**self.display, *self.surface.get_mut() as *const _);
}
}
}