use crate::context::{CREATE_CONTEXT_MUTEX, ContextID};
use crate::egl::types::{EGLConfig, EGLContext, EGLSurface, EGLint};
use crate::egl;
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 super::device::Device;
use super::surface::{Surface, SurfaceObjects};
use std::mem;
use std::os::raw::c_void;
use std::thread;
pub use crate::platform::generic::egl::context::{ContextDescriptor, NativeContext};
thread_local! {
#[doc(hidden)]
pub static GL_FUNCTIONS: Gl = Gl::load_with(context::get_proc_address);
}
pub struct Context {
pub(crate) egl_context: EGLContext,
pub(crate) id: ContextID,
pub(crate) pbuffer: EGLSurface,
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(&mut 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);
let context = Context {
egl_context,
id: *next_context_id,
pbuffer,
framebuffer: Framebuffer::None,
context_is_owned: true,
};
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);
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,
};
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 {
GL_FUNCTIONS.with(|gl| {
unsafe {
ContextDescriptor::from_egl_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 { .. },
..
}) | 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)?;
GL_FUNCTIONS.with(|gl| {
unsafe {
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_descriptor_to_egl_config(&self, context_descriptor: &ContextDescriptor)
-> EGLConfig {
unsafe {
context::egl_config_from_id(self.egl_display,
context_descriptor.egl_config_id)
}
}
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 { .. },
..
}) | Framebuffer::None => (context.pbuffer, context.pbuffer),
};
NativeContext {
egl_context: context.egl_context,
egl_draw_surface,
egl_read_surface,
}
}
}