use crate::context::{CREATE_CONTEXT_MUTEX, ContextID};
use crate::egl::types::{EGLConfig, EGLContext, 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, Synchronization, Win32Objects};
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};
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,
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::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(&mut 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())?;
let context = Context {
egl_context,
id: *next_context_id,
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 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,
};
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 {
GL_FUNCTIONS.with(|gl| {
ContextDescriptor::from_egl_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 {
GL_FUNCTIONS.with(|gl| 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,
}
}
}