use std::collections::HashSet;
use std::os::raw::c_int;
use std::sync::atomic::Ordering;
use super::{ffi, wrap_egl_call, Error, MakeCurrentError};
use crate::backend::allocator::Format as DrmFormat;
use crate::backend::egl::display::{EGLDisplay, PixelFormat};
use crate::backend::egl::EGLSurface;
use slog::{info, o, trace};
#[derive(Debug)]
pub struct EGLContext {
context: ffi::egl::types::EGLContext,
pub(crate) display: EGLDisplay,
config_id: ffi::egl::types::EGLConfig,
pixel_format: Option<PixelFormat>,
}
unsafe impl Send for EGLContext {}
unsafe impl Sync for EGLContext {}
impl EGLContext {
pub fn new<L>(display: &EGLDisplay, log: L) -> Result<EGLContext, Error>
where
L: Into<Option<::slog::Logger>>,
{
Self::new_internal(display, None, None, log)
}
pub fn new_with_config<L>(
display: &EGLDisplay,
attributes: GlAttributes,
reqs: PixelFormatRequirements,
log: L,
) -> Result<EGLContext, Error>
where
L: Into<Option<::slog::Logger>>,
{
Self::new_internal(display, None, Some((attributes, reqs)), log)
}
pub fn new_shared<L>(display: &EGLDisplay, share: &EGLContext, log: L) -> Result<EGLContext, Error>
where
L: Into<Option<::slog::Logger>>,
{
Self::new_internal(display, Some(share), None, log)
}
pub fn new_shared_with_config<L>(
display: &EGLDisplay,
share: &EGLContext,
attributes: GlAttributes,
reqs: PixelFormatRequirements,
log: L,
) -> Result<EGLContext, Error>
where
L: Into<Option<::slog::Logger>>,
{
Self::new_internal(display, Some(share), Some((attributes, reqs)), log)
}
fn new_internal<L>(
display: &EGLDisplay,
shared: Option<&EGLContext>,
config: Option<(GlAttributes, PixelFormatRequirements)>,
log: L,
) -> Result<EGLContext, Error>
where
L: Into<Option<::slog::Logger>>,
{
let log = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "backend_egl"));
let (pixel_format, config_id) = match config {
Some((attributes, reqs)) => {
let (format, config_id) = display.choose_config(attributes, reqs)?;
(Some(format), config_id)
}
None => {
if !display
.extensions
.iter()
.any(|x| x == "EGL_KHR_no_config_context")
&& !display
.extensions
.iter()
.any(|x| x == "EGL_MESA_configless_context")
&& !display
.extensions
.iter()
.any(|x| x == "EGL_KHR_surfaceless_context")
{
return Err(Error::EglExtensionNotSupported(&[
"EGL_KHR_no_config_context",
"EGL_MESA_configless_context",
"EGL_KHR_surfaceless_context",
]));
}
(None, ffi::egl::NO_CONFIG_KHR)
}
};
let mut context_attributes = Vec::with_capacity(10);
if let Some((attributes, _)) = config {
let version = attributes.version;
if display.egl_version >= (1, 5)
|| display.extensions.iter().any(|s| s == "EGL_KHR_create_context")
{
trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0);
context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
context_attributes.push(version.0 as i32);
trace!(log, "Setting CONTEXT_MINOR_VERSION to {}", version.1);
context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
context_attributes.push(version.1 as i32);
if attributes.debug && display.egl_version >= (1, 5) {
trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE");
context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
context_attributes.push(ffi::egl::TRUE as i32);
}
context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
context_attributes.push(0);
} else if display.egl_version >= (1, 3) {
trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0);
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
context_attributes.push(version.0 as i32);
}
} else {
trace!(log, "Setting CONTEXT_CLIENT_VERSION to 2");
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
context_attributes.push(2);
}
context_attributes.push(ffi::egl::NONE as i32);
trace!(log, "Creating EGL context...");
let context = wrap_egl_call(|| unsafe {
ffi::egl::CreateContext(
**display.display,
config_id,
shared
.map(|context| context.context)
.unwrap_or(ffi::egl::NO_CONTEXT),
context_attributes.as_ptr(),
)
})
.map_err(Error::CreationFailed)?;
info!(log, "EGL context created");
Ok(EGLContext {
context,
display: display.clone(),
config_id,
pixel_format,
})
}
pub unsafe fn make_current_with_surface(&self, surface: &EGLSurface) -> Result<(), MakeCurrentError> {
let surface_ptr = surface.surface.load(Ordering::SeqCst);
wrap_egl_call(|| {
ffi::egl::MakeCurrent(**self.display.display, surface_ptr, surface_ptr, self.context)
})
.map(|_| ())
.map_err(Into::into)
}
pub unsafe fn make_current(&self) -> Result<(), MakeCurrentError> {
wrap_egl_call(|| {
ffi::egl::MakeCurrent(
**self.display.display,
ffi::egl::NO_SURFACE,
ffi::egl::NO_SURFACE,
self.context,
)
})
.map(|_| ())
.map_err(Into::into)
}
pub fn is_current(&self) -> bool {
unsafe { ffi::egl::GetCurrentContext() == self.context as *const _ }
}
pub fn config_id(&self) -> ffi::egl::types::EGLConfig {
self.config_id
}
pub fn pixel_format(&self) -> Option<PixelFormat> {
self.pixel_format
}
pub fn unbind(&self) -> Result<(), MakeCurrentError> {
if self.is_current() {
wrap_egl_call(|| unsafe {
ffi::egl::MakeCurrent(
**self.display.display,
ffi::egl::NO_SURFACE,
ffi::egl::NO_SURFACE,
ffi::egl::NO_CONTEXT,
)
})?;
}
Ok(())
}
pub fn dmabuf_render_formats(&self) -> &HashSet<DrmFormat> {
&self.display.dmabuf_render_formats
}
pub fn dmabuf_texture_formats(&self) -> &HashSet<DrmFormat> {
&self.display.dmabuf_import_formats
}
}
impl Drop for EGLContext {
fn drop(&mut self) {
unsafe {
let _ = self.unbind();
ffi::egl::DestroyContext(**self.display.display, self.context);
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GlAttributes {
pub version: (u8, u8),
pub profile: Option<GlProfile>,
pub debug: bool,
pub vsync: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GlProfile {
Compatibility,
Core,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelFormatRequirements {
pub hardware_accelerated: Option<bool>,
pub color_bits: Option<u8>,
pub float_color_buffer: bool,
pub alpha_bits: Option<u8>,
pub depth_bits: Option<u8>,
pub stencil_bits: Option<u8>,
pub multisampling: Option<u16>,
}
impl Default for PixelFormatRequirements {
fn default() -> Self {
PixelFormatRequirements {
hardware_accelerated: Some(true),
color_bits: Some(24),
float_color_buffer: false,
alpha_bits: Some(8),
depth_bits: Some(24),
stencil_bits: Some(8),
multisampling: None,
}
}
}
impl PixelFormatRequirements {
pub fn create_attributes(&self, out: &mut Vec<c_int>, logger: &slog::Logger) {
if let Some(hardware_accelerated) = self.hardware_accelerated {
out.push(ffi::egl::CONFIG_CAVEAT as c_int);
out.push(if hardware_accelerated {
trace!(logger, "Setting CONFIG_CAVEAT to NONE");
ffi::egl::NONE as c_int
} else {
trace!(logger, "Setting CONFIG_CAVEAT to SLOW_CONFIG");
ffi::egl::SLOW_CONFIG as c_int
});
}
if let Some(color) = self.color_bits {
trace!(logger, "Setting RED_SIZE to {}", color / 3);
out.push(ffi::egl::RED_SIZE as c_int);
out.push((color / 3) as c_int);
trace!(
logger,
"Setting GREEN_SIZE to {}",
color / 3 + if color % 3 != 0 { 1 } else { 0 }
);
out.push(ffi::egl::GREEN_SIZE as c_int);
out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
trace!(
logger,
"Setting BLUE_SIZE to {}",
color / 3 + if color % 3 == 2 { 1 } else { 0 }
);
out.push(ffi::egl::BLUE_SIZE as c_int);
out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
}
if let Some(alpha) = self.alpha_bits {
trace!(logger, "Setting ALPHA_SIZE to {}", alpha);
out.push(ffi::egl::ALPHA_SIZE as c_int);
out.push(alpha as c_int);
}
if let Some(depth) = self.depth_bits {
trace!(logger, "Setting DEPTH_SIZE to {}", depth);
out.push(ffi::egl::DEPTH_SIZE as c_int);
out.push(depth as c_int);
}
if let Some(stencil) = self.stencil_bits {
trace!(logger, "Setting STENCIL_SIZE to {}", stencil);
out.push(ffi::egl::STENCIL_SIZE as c_int);
out.push(stencil as c_int);
}
if let Some(multisampling) = self.multisampling {
trace!(logger, "Setting SAMPLES to {}", multisampling);
out.push(ffi::egl::SAMPLES as c_int);
out.push(multisampling as c_int);
}
}
}