use super::connection::{Connection, NativeConnectionWrapper};
use super::surface::SurfaceDataGuard;
use crate::base::egl::context::{self, CurrentContextGuard, EGLBackedContext};
use crate::base::egl::surface::EGLBackedSurface;
use crate::context::ContextID;
use crate::egl;
use crate::egl::types::EGLint;
use crate::gl;
use crate::mesa_surfaceless::context::{Context, ContextDescriptor, NativeContext};
use crate::mesa_surfaceless::surface::{NativeWidget, Surface, SurfaceTexture};
use crate::{ContextAttributes, Gl, SurfaceInfo};
use crate::{Error, GLApi, SurfaceAccess, SurfaceType};
use euclid::default::Size2D;
use glow::Texture;
use std::env;
use std::os::raw::c_void;
use std::sync::Arc;
static MESA_SOFTWARE_RENDERING_ENV_VAR: &str = "LIBGL_ALWAYS_SOFTWARE";
static MESA_DRI_PRIME_ENV_VAR: &str = "DRI_PRIME";
const SURFACE_GL_TEXTURE_TARGET: u32 = gl::TEXTURE_2D;
#[derive(Clone, Debug)]
pub enum Adapter {
#[doc(hidden)]
Hardware,
#[doc(hidden)]
HardwarePrime,
#[doc(hidden)]
Software,
}
impl Adapter {
#[inline]
pub(crate) fn hardware() -> Adapter {
Adapter::HardwarePrime
}
#[inline]
pub(crate) fn low_power() -> Adapter {
Adapter::Hardware
}
#[inline]
pub(crate) fn software() -> Adapter {
Adapter::Software
}
pub(crate) fn set_environment_variables(&self) {
match *self {
Adapter::Hardware | Adapter::HardwarePrime => {
env::remove_var(MESA_SOFTWARE_RENDERING_ENV_VAR);
}
Adapter::Software => {
env::set_var(MESA_SOFTWARE_RENDERING_ENV_VAR, "1");
}
}
match *self {
Adapter::Software => {}
Adapter::Hardware => {
env::remove_var(MESA_DRI_PRIME_ENV_VAR);
}
Adapter::HardwarePrime => {
env::set_var(MESA_DRI_PRIME_ENV_VAR, "1");
}
}
}
}
pub struct Device {
pub(crate) native_connection: Arc<NativeConnectionWrapper>,
pub(crate) adapter: Adapter,
}
#[derive(Clone)]
pub struct NativeDevice {
pub adapter: Adapter,
}
impl Device {
#[inline]
pub(crate) fn new(connection: &Connection, adapter: &Adapter) -> Result<Device, Error> {
Ok(Device {
native_connection: connection.native_connection.clone(),
adapter: (*adapter).clone(),
})
}
#[inline]
pub fn native_device(&self) -> NativeDevice {
NativeDevice {
adapter: self.adapter(),
}
}
#[inline]
pub fn connection(&self) -> Connection {
Connection {
native_connection: self.native_connection.clone(),
}
}
#[inline]
pub fn adapter(&self) -> Adapter {
self.adapter.clone()
}
#[inline]
pub fn gl_api(&self) -> GLApi {
GLApi::GL
}
#[inline]
pub fn create_context_descriptor(
&self,
attributes: &ContextAttributes,
) -> Result<ContextDescriptor, Error> {
self.adapter.set_environment_variables();
unsafe {
ContextDescriptor::new(
self.native_connection.egl_display,
attributes,
&[
egl::SURFACE_TYPE as EGLint,
egl::PBUFFER_BIT as EGLint,
egl::RENDERABLE_TYPE as EGLint,
egl::OPENGL_BIT as EGLint,
egl::COLOR_BUFFER_TYPE as EGLint,
egl::RGB_BUFFER as EGLint,
],
)
}
}
#[inline]
pub fn create_context(
&self,
descriptor: &ContextDescriptor,
share_with: Option<&Context>,
) -> Result<Context, Error> {
unsafe {
let context = EGLBackedContext::new(
self.native_connection.egl_display,
descriptor,
share_with.map(|ctx| &ctx.0),
self.gl_api(),
)?;
context.make_current(self.native_connection.egl_display)?;
Ok(Context(
context,
Gl::from_loader_function(context::get_proc_address),
))
}
}
#[inline]
pub unsafe fn create_context_from_native_context(
&self,
native_context: NativeContext,
) -> Result<Context, Error> {
Ok(Context(
EGLBackedContext::from_native_context(native_context),
Gl::from_loader_function(context::get_proc_address),
))
}
pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> {
if let Ok(Some(mut surface)) = self.unbind_surface_from_context(context) {
self.destroy_surface(context, &mut surface)?;
}
unsafe {
context.0.destroy(self.native_connection.egl_display);
Ok(())
}
}
#[inline]
pub fn native_context(&self, context: &Context) -> NativeContext {
context.0.native_context()
}
#[inline]
pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor {
unsafe {
ContextDescriptor::from_egl_context(
&context.1,
self.native_connection.egl_display,
context.0.egl_context,
)
}
}
#[inline]
pub fn make_context_current(&self, context: &Context) -> Result<(), Error> {
unsafe { context.0.make_current(self.native_connection.egl_display) }
}
#[inline]
pub fn make_no_context_current(&self) -> Result<(), Error> {
unsafe { context::make_no_context_current(self.native_connection.egl_display) }
}
#[inline]
pub(crate) fn temporarily_make_context_current(
&self,
context: &Context,
) -> Result<CurrentContextGuard, Error> {
let guard = CurrentContextGuard::new();
self.make_context_current(context)?;
Ok(guard)
}
#[inline]
pub fn context_descriptor_attributes(
&self,
context_descriptor: &ContextDescriptor,
) -> ContextAttributes {
unsafe { context_descriptor.attributes(self.native_connection.egl_display) }
}
#[inline]
pub fn get_proc_address(&self, _: &Context, symbol_name: &str) -> *const c_void {
context::get_proc_address(symbol_name)
}
#[inline]
pub fn bind_surface_to_context(
&self,
context: &mut Context,
surface: Surface,
) -> Result<(), (Error, Surface)> {
unsafe {
context
.0
.bind_surface(self.native_connection.egl_display, surface.0)
.map_err(|(err, surface)| (err, Surface(surface)))
}
}
pub fn unbind_surface_from_context(
&self,
context: &mut Context,
) -> Result<Option<Surface>, Error> {
unsafe {
context
.0
.unbind_surface(&context.1, self.native_connection.egl_display)
.map(|maybe_surface| maybe_surface.map(Surface))
}
}
pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> {
context
.0
.present_bound_surface(self.native_connection.egl_display)
}
pub fn resize_bound_surface(
&self,
context: &mut Context,
size: Size2D<i32>,
) -> Result<(), Error> {
context.0.resize_bound_surface(size)
}
#[inline]
pub fn context_id(&self, context: &Context) -> ContextID {
context.0.id
}
#[inline]
pub fn context_surface_info(&self, context: &Context) -> Result<Option<SurfaceInfo>, Error> {
context.0.surface_info()
}
pub fn create_surface(
&self,
context: &Context,
_: SurfaceAccess,
surface_type: SurfaceType<NativeWidget>,
) -> Result<Surface, Error> {
match surface_type {
SurfaceType::Generic { size } => self.create_generic_surface(context, &size),
SurfaceType::Widget { .. } => Err(Error::UnsupportedOnThisPlatform),
}
}
fn create_generic_surface(
&self,
context: &Context,
size: &Size2D<i32>,
) -> Result<Surface, Error> {
let _guard = self.temporarily_make_context_current(context)?;
let context_descriptor = self.context_descriptor(context);
let context_attributes = self.context_descriptor_attributes(&context_descriptor);
Ok(Surface(EGLBackedSurface::new_generic(
&context.1,
self.native_connection.egl_display,
context.0.egl_context,
context.0.id,
&context_attributes,
size,
)))
}
pub fn create_surface_texture(
&self,
context: &mut Context,
surface: Surface,
) -> Result<SurfaceTexture, (Error, Surface)> {
let _guard = match self.temporarily_make_context_current(context) {
Ok(guard) => guard,
Err(err) => return Err((err, surface)),
};
match surface.0.to_surface_texture(&context.1) {
Ok(surface_texture) => Ok(SurfaceTexture(surface_texture)),
Err((err, surface)) => Err((err, Surface(surface))),
}
}
pub fn destroy_surface(
&self,
context: &mut Context,
surface: &mut Surface,
) -> Result<(), Error> {
let egl_display = self.native_connection.egl_display;
let window = surface.0.destroy(&context.1, egl_display, context.0.id)?;
debug_assert!(window.is_none());
Ok(())
}
pub fn destroy_surface_texture(
&self,
context: &mut Context,
surface_texture: SurfaceTexture,
) -> Result<Surface, (Error, SurfaceTexture)> {
match self.temporarily_make_context_current(context) {
Ok(_guard) => Ok(Surface(surface_texture.0.destroy(&context.1))),
Err(err) => Err((err, surface_texture)),
}
}
pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> {
surface
.0
.present(self.native_connection.egl_display, context.0.egl_context)
}
pub fn resize_surface(
&self,
_context: &Context,
surface: &mut Surface,
size: Size2D<i32>,
) -> Result<(), Error> {
surface.0.size = size;
Ok(())
}
#[inline]
pub fn lock_surface_data<'s>(&self, _: &'s mut Surface) -> Result<SurfaceDataGuard<'s>, Error> {
Err(Error::Unimplemented)
}
#[inline]
pub fn surface_gl_texture_target(&self) -> u32 {
SURFACE_GL_TEXTURE_TARGET
}
pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo {
surface.0.info()
}
#[inline]
pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option<Texture> {
surface_texture.0.texture_object
}
}