use super::device::Device;
use super::surface::{Surface, Synchronization, Win32Objects};
use crate::context::{ContextID, CREATE_CONTEXT_MUTEX};
use crate::egl;
use crate::egl::types::{EGLConfig, EGLContext, 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 glow::HasContext;
use std::mem;
use std::os::raw::c_void;
use std::thread;
use winapi::shared::winerror::S_OK;
use winapi::um::winbase::INFINITE;
pub use crate::platform::generic::egl::context::{ContextDescriptor, NativeContext};
pub struct Context {
pub(crate) egl_context: EGLContext,
pub(crate) id: ContextID,
framebuffer: Framebuffer<Surface, ExternalEGLSurfaces>,
context_is_owned: bool,
pub(crate) gl: Gl,
}
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::BIND_TO_TEXTURE_RGBA as EGLint,
1 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();
unsafe {
let egl_context = context::create_context(
self.egl_display,
descriptor,
share_with.map_or(egl::NO_CONTEXT, |ctx| ctx.egl_context),
self.gl_api(),
)?;
EGL_FUNCTIONS.with(|egl| {
let result = egl.MakeCurrent(
self.egl_display,
egl::NO_SURFACE,
egl::NO_SURFACE,
egl_context,
);
if result == 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,
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 context = Context {
egl_context: native_context.egl_context,
id: *next_context_id,
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(());
}
if let Ok(Some(mut surface)) = self.unbind_surface_from_context(context) {
self.destroy_surface(context, &mut surface)?;
}
EGL_FUNCTIONS.with(|egl| unsafe {
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);
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_draw_surface, egl_read_surface) = match context.framebuffer {
Framebuffer::Surface(ref surface) => (surface.egl_surface, surface.egl_surface),
Framebuffer::None => (egl::NO_SURFACE, egl::NO_SURFACE),
Framebuffer::External(ref surfaces) => (surfaces.draw, surfaces.read),
};
EGL_FUNCTIONS.with(|egl| {
let result = egl.MakeCurrent(
self.egl_display,
egl_draw_surface,
egl_read_surface,
context.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(crate) fn temporarily_make_context_current(
&self,
context: &Context,
) -> Result<CurrentContextGuard, Error> {
let guard = CurrentContextGuard::new();
self.make_context_current(context)?;
Ok(guard)
}
pub(crate) fn context_is_current(&self, context: &Context) -> bool {
EGL_FUNCTIONS.with(|egl| unsafe { egl.GetCurrentContext() == context.egl_context })
}
#[inline]
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)
}
#[inline]
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 fn bind_surface_to_context(
&self,
context: &mut Context,
surface: Surface,
) -> Result<(), (Error, Surface)> {
if context.id != surface.context_id {
return Err((Error::IncompatibleSurface, surface));
}
match context.framebuffer {
Framebuffer::None => {}
Framebuffer::External(_) => return Err((Error::ExternalRenderTarget, surface)),
Framebuffer::Surface(_) => return Err((Error::SurfaceAlreadyBound, surface)),
}
if surface.uses_gl_finish() {
if let Ok(_guard) = self.temporarily_make_context_current(context) {
unsafe {
context.gl.finish();
}
}
}
let is_current = self.context_is_current(context);
match surface.win32_objects {
Win32Objects::Pbuffer {
synchronization: Synchronization::KeyedMutex(ref keyed_mutex),
..
} => unsafe {
let result = keyed_mutex.AcquireSync(0, INFINITE);
assert_eq!(result, S_OK);
},
_ => {}
}
context.framebuffer = Framebuffer::Surface(surface);
if is_current {
drop(self.make_context_current(context));
}
Ok(())
}
pub fn unbind_surface_from_context(
&self,
context: &mut Context,
) -> Result<Option<Surface>, Error> {
match context.framebuffer {
Framebuffer::None => return Ok(None),
Framebuffer::External(_) => return Err(Error::ExternalRenderTarget),
Framebuffer::Surface(_) => {}
}
let surface = match mem::replace(&mut context.framebuffer, Framebuffer::None) {
Framebuffer::Surface(surface) => surface,
Framebuffer::None | Framebuffer::External(_) => unreachable!(),
};
match surface.win32_objects {
Win32Objects::Pbuffer {
synchronization: Synchronization::KeyedMutex(ref keyed_mutex),
..
} => unsafe {
let result = keyed_mutex.ReleaseSync(0);
assert_eq!(result, S_OK);
},
_ => {}
}
Ok(Some(surface))
}
#[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 { egl_surface, .. }) => (egl_surface, egl_surface),
Framebuffer::External(ExternalEGLSurfaces { draw, read }) => (draw, read),
Framebuffer::None => (egl::NO_SURFACE, egl::NO_SURFACE),
};
NativeContext {
egl_context: context.egl_context,
egl_draw_surface,
egl_read_surface,
}
}
}