use glow::HasContext;
use super::device::Device;
use super::surface::{Surface, SurfaceObjects};
use crate::context::{ContextID, CREATE_CONTEXT_MUTEX};
use crate::egl;
use crate::egl::types::{EGLConfig, EGLContext, EGLSurface, EGLint};
use crate::platform::generic::egl::context::{self, CurrentContextGuard};
use crate::platform::generic::egl::device::EGL_FUNCTIONS;
use crate::platform::generic::egl::error::ToWindowingApiError;
use crate::platform::generic::egl::surface::ExternalEGLSurfaces;
use crate::surface::Framebuffer;
use crate::{ContextAttributes, Error, Gl, SurfaceInfo};
use std::mem;
use std::os::raw::c_void;
use std::thread;
pub use crate::platform::generic::egl::context::{ContextDescriptor, NativeContext};
pub struct Context {
pub(crate) egl_context: EGLContext,
pub(crate) id: ContextID,
pub(crate) pbuffer: EGLSurface,
pub(crate) gl: Gl,
framebuffer: Framebuffer<Surface, ExternalEGLSurfaces>,
context_is_owned: bool,
}
impl Drop for Context {
#[inline]
fn drop(&mut self) {
if self.egl_context != egl::NO_CONTEXT && !thread::panicking() {
panic!("Contexts must be destroyed explicitly with `destroy_context`!")
}
}
}
impl Device {
#[inline]
pub fn create_context_descriptor(
&self,
attributes: &ContextAttributes,
) -> Result<ContextDescriptor, Error> {
unsafe {
ContextDescriptor::new(
self.egl_display,
attributes,
&[
egl::COLOR_BUFFER_TYPE as EGLint,
egl::RGB_BUFFER as EGLint,
egl::SURFACE_TYPE as EGLint,
egl::PBUFFER_BIT as EGLint,
egl::RENDERABLE_TYPE as EGLint,
egl::OPENGL_ES2_BIT as EGLint,
],
)
}
}
pub fn create_context(
&self,
descriptor: &ContextDescriptor,
share_with: Option<&Context>,
) -> Result<Context, Error> {
let mut next_context_id = CREATE_CONTEXT_MUTEX.lock().unwrap();
let egl_display = self.egl_display;
unsafe {
let gl_api = self.gl_api();
let egl_context = context::create_context(
egl_display,
descriptor,
share_with.map_or(egl::NO_CONTEXT, |ctx| ctx.egl_context),
gl_api,
)?;
let pbuffer = context::create_dummy_pbuffer(egl_display, egl_context).unwrap();
EGL_FUNCTIONS.with(|egl| {
if egl.MakeCurrent(egl_display, pbuffer, pbuffer, egl_context) == egl::FALSE {
let err = egl.GetError().to_windowing_api_error();
return Err(Error::MakeCurrentFailed(err));
}
Ok(())
})?;
let context = Context {
egl_context,
id: *next_context_id,
pbuffer,
framebuffer: Framebuffer::None,
context_is_owned: true,
gl: Gl::from_loader_function(context::get_proc_address),
};
next_context_id.0 += 1;
Ok(context)
}
}
pub unsafe fn create_context_from_native_context(
&self,
native_context: NativeContext,
) -> Result<Context, Error> {
let mut next_context_id = CREATE_CONTEXT_MUTEX.lock().unwrap();
let pbuffer =
context::create_dummy_pbuffer(self.egl_display, native_context.egl_context).unwrap();
let context = Context {
egl_context: native_context.egl_context,
id: *next_context_id,
pbuffer,
framebuffer: Framebuffer::External(ExternalEGLSurfaces {
draw: native_context.egl_draw_surface,
read: native_context.egl_read_surface,
}),
context_is_owned: false,
gl: Gl::from_loader_function(context::get_proc_address),
};
next_context_id.0 += 1;
Ok(context)
}
pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> {
if context.egl_context == egl::NO_CONTEXT {
return Ok(());
}
unsafe {
if let Framebuffer::Surface(mut target) =
mem::replace(&mut context.framebuffer, Framebuffer::None)
{
self.destroy_surface(context, &mut target)?;
}
EGL_FUNCTIONS.with(|egl| {
let result = egl.DestroySurface(self.egl_display, context.pbuffer);
assert_ne!(result, egl::FALSE);
context.pbuffer = egl::NO_SURFACE;
egl.MakeCurrent(
self.egl_display,
egl::NO_SURFACE,
egl::NO_SURFACE,
egl::NO_CONTEXT,
);
if context.context_is_owned {
let result = egl.DestroyContext(self.egl_display, context.egl_context);
assert_ne!(result, egl::FALSE);
}
context.egl_context = egl::NO_CONTEXT;
});
}
Ok(())
}
pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor {
unsafe {
ContextDescriptor::from_egl_context(&context.gl, self.egl_display, context.egl_context)
}
}
pub fn make_context_current(&self, context: &Context) -> Result<(), Error> {
unsafe {
let egl_display = self.egl_display;
let egl_context = context.egl_context;
let (egl_draw_surface, egl_read_surface) = match context.framebuffer {
Framebuffer::Surface(Surface {
objects: SurfaceObjects::Window { egl_surface },
..
}) => (egl_surface, egl_surface),
Framebuffer::External(ExternalEGLSurfaces { draw, read }) => (draw, read),
Framebuffer::Surface(Surface {
objects: SurfaceObjects::HardwareBuffer { .. },
..
}) => (context.pbuffer, context.pbuffer),
Framebuffer::None => (context.pbuffer, context.pbuffer),
};
EGL_FUNCTIONS.with(|egl| {
let result =
egl.MakeCurrent(egl_display, egl_draw_surface, egl_read_surface, egl_context);
if result == egl::FALSE {
let err = egl.GetError().to_windowing_api_error();
return Err(Error::MakeCurrentFailed(err));
}
Ok(())
})
}
}
pub fn make_no_context_current(&self) -> Result<(), Error> {
unsafe { context::make_no_context_current(self.egl_display) }
}
pub fn bind_surface_to_context(
&self,
context: &mut Context,
new_surface: Surface,
) -> Result<(), (Error, Surface)> {
if context.id != new_surface.context_id {
return Err((Error::IncompatibleSurface, new_surface));
}
match context.framebuffer {
Framebuffer::External { .. } => return Err((Error::ExternalRenderTarget, new_surface)),
Framebuffer::Surface(_) => return Err((Error::SurfaceAlreadyBound, new_surface)),
Framebuffer::None => {}
}
context.framebuffer = Framebuffer::Surface(new_surface);
Ok(())
}
pub fn unbind_surface_from_context(
&self,
context: &mut Context,
) -> Result<Option<Surface>, Error> {
match context.framebuffer {
Framebuffer::External { .. } => return Err(Error::ExternalRenderTarget),
Framebuffer::None => return Ok(None),
Framebuffer::Surface(_) => {}
}
let _guard = self.temporarily_make_context_current(context)?;
unsafe {
context.gl.flush();
};
match mem::replace(&mut context.framebuffer, Framebuffer::None) {
Framebuffer::Surface(surface) => return Ok(Some(surface)),
Framebuffer::External { .. } | Framebuffer::None => unreachable!(),
}
}
pub fn context_descriptor_attributes(
&self,
context_descriptor: &ContextDescriptor,
) -> ContextAttributes {
unsafe { context_descriptor.attributes(self.egl_display) }
}
#[inline]
pub fn get_proc_address(&self, _: &Context, symbol_name: &str) -> *const c_void {
context::get_proc_address(symbol_name)
}
pub(crate) fn context_to_egl_config(&self, context: &Context) -> EGLConfig {
unsafe {
context::egl_config_from_id(
self.egl_display,
context::get_context_attr(
self.egl_display,
context.egl_context,
egl::CONFIG_ID as EGLint,
),
)
}
}
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_id(&self, context: &Context) -> ContextID {
context.id
}
pub fn context_surface_info(&self, context: &Context) -> Result<Option<SurfaceInfo>, Error> {
match context.framebuffer {
Framebuffer::None => Ok(None),
Framebuffer::External { .. } => Err(Error::ExternalRenderTarget),
Framebuffer::Surface(ref surface) => Ok(Some(self.surface_info(surface))),
}
}
pub fn native_context(&self, context: &Context) -> NativeContext {
let (egl_draw_surface, egl_read_surface) = match context.framebuffer {
Framebuffer::Surface(Surface {
objects: SurfaceObjects::Window { egl_surface },
..
}) => (egl_surface, egl_surface),
Framebuffer::External(ExternalEGLSurfaces { draw, read }) => (draw, read),
Framebuffer::Surface(Surface {
objects: SurfaceObjects::HardwareBuffer { .. },
..
}) => (context.pbuffer, context.pbuffer),
Framebuffer::None => (context.pbuffer, context.pbuffer),
};
NativeContext {
egl_context: context.egl_context,
egl_draw_surface,
egl_read_surface,
}
}
}