use crate::context::ContextID;
use crate::egl::types::EGLNativeWindowType;
use crate::egl::types::EGLSurface;
use crate::egl::{self, EGLint};
use crate::gl::types::{GLenum, GLint, GLuint};
use crate::gl;
use crate::platform::generic::egl::device::EGL_FUNCTIONS;
use crate::platform::generic::egl::error::ToWindowingApiError;
use crate::platform::generic::egl::ffi::EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE;
use crate::platform::generic::egl::ffi::EGL_D3D_TEXTURE_ANGLE;
use crate::platform::generic::egl::ffi::EGL_DXGI_KEYED_MUTEX_ANGLE;
use crate::platform::generic::egl::ffi::EGL_EXTENSION_FUNCTIONS;
use crate::{Error, SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType};
use super::context::{Context, ContextDescriptor, GL_FUNCTIONS};
use super::device::Device;
use euclid::default::Size2D;
use std::fmt::{self, Debug, Formatter};
use std::marker::PhantomData;
use std::os::raw::c_void;
use std::ptr;
use std::thread;
use winapi::shared::dxgi::IDXGIKeyedMutex;
use winapi::shared::winerror::S_OK;
use winapi::um::d3d11;
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::winbase::INFINITE;
use winapi::um::winnt::HANDLE;
use wio::com::ComPtr;
const SURFACE_GL_TEXTURE_TARGET: GLenum = gl::TEXTURE_2D;
pub struct Surface {
pub(crate) egl_surface: EGLSurface,
pub(crate) size: Size2D<i32>,
pub(crate) context_id: ContextID,
pub(crate) context_descriptor: ContextDescriptor,
pub(crate) win32_objects: Win32Objects,
}
pub struct SurfaceTexture {
pub(crate) surface: Surface,
pub(crate) local_egl_surface: EGLSurface,
pub(crate) local_keyed_mutex: Option<ComPtr<IDXGIKeyedMutex>>,
pub(crate) gl_texture: GLuint,
pub(crate) phantom: PhantomData<*const ()>,
}
unsafe impl Send for Surface {}
impl Debug for Surface {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "Surface({:x})", self.id().0)
}
}
impl Drop for Surface {
fn drop(&mut self) {
if self.egl_surface != egl::NO_SURFACE && !thread::panicking() {
panic!("Should have destroyed the surface first with `destroy_surface()`!")
}
}
}
impl Debug for SurfaceTexture {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "SurfaceTexture({:?})", self.surface)
}
}
pub(crate) enum Win32Objects {
Window,
Pbuffer {
share_handle: HANDLE,
synchronization: Synchronization,
texture: Option<ComPtr<d3d11::ID3D11Texture2D>>,
}
}
pub(crate) enum Synchronization {
KeyedMutex(ComPtr<IDXGIKeyedMutex>),
GLFinish,
None,
}
#[repr(C)]
pub struct NativeWidget {
pub egl_native_window: EGLNativeWindowType,
}
impl Device {
pub fn create_surface(&mut self,
context: &Context,
_: SurfaceAccess,
surface_type: SurfaceType<NativeWidget>)
-> Result<Surface, Error> {
match surface_type {
SurfaceType::Generic { ref size } => self.create_pbuffer_surface(context, size, None),
SurfaceType::Widget { ref native_widget } => {
self.create_window_surface(context, native_widget)
}
}
}
#[allow(non_snake_case)]
fn create_pbuffer_surface(&mut self,
context: &Context,
size: &Size2D<i32>,
texture: Option<ComPtr<d3d11::ID3D11Texture2D>>)
-> Result<Surface, Error> {
let context_descriptor = self.context_descriptor(context);
let egl_config = self.context_descriptor_to_egl_config(&context_descriptor);
unsafe {
let attributes = [
egl::WIDTH as EGLint, size.width as EGLint,
egl::HEIGHT as EGLint, size.height as EGLint,
egl::TEXTURE_FORMAT as EGLint, egl::TEXTURE_RGBA as EGLint,
egl::TEXTURE_TARGET as EGLint, egl::TEXTURE_2D as EGLint,
egl::NONE as EGLint, 0,
0, 0,
];
EGL_FUNCTIONS.with(|egl| {
let egl_surface = if let Some(ref texture) = texture {
let surface =
egl.CreatePbufferFromClientBuffer(self.egl_display,
EGL_D3D_TEXTURE_ANGLE,
texture.as_raw() as *const _,
egl_config,
attributes.as_ptr());
assert_ne!(surface, egl::NO_SURFACE);
surface
} else {
let surface = egl.CreatePbufferSurface(self.egl_display,
egl_config,
attributes.as_ptr());
assert_ne!(surface, egl::NO_SURFACE);
surface
};
let eglQuerySurfacePointerANGLE =
EGL_EXTENSION_FUNCTIONS.QuerySurfacePointerANGLE
.expect("Where's the `EGL_ANGLE_query_surface_pointer` \
extension?");
let mut share_handle = INVALID_HANDLE_VALUE;
let result =
eglQuerySurfacePointerANGLE(self.egl_display,
egl_surface,
EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE as EGLint,
&mut share_handle);
assert_ne!(result, egl::FALSE);
assert_ne!(share_handle, INVALID_HANDLE_VALUE);
let mut keyed_mutex: *mut IDXGIKeyedMutex = ptr::null_mut();
let result = eglQuerySurfacePointerANGLE(
self.egl_display,
egl_surface,
EGL_DXGI_KEYED_MUTEX_ANGLE as EGLint,
&mut keyed_mutex as *mut *mut IDXGIKeyedMutex as *mut *mut c_void);
let synchronization = if result != egl::FALSE && !keyed_mutex.is_null() {
let keyed_mutex = ComPtr::from_raw(keyed_mutex);
keyed_mutex.AddRef();
Synchronization::KeyedMutex(keyed_mutex)
} else if texture.is_none() {
Synchronization::GLFinish
} else {
Synchronization::None
};
Ok(Surface {
egl_surface,
size: *size,
context_id: context.id,
context_descriptor,
win32_objects: Win32Objects::Pbuffer {
share_handle,
synchronization,
texture,
},
})
})
}
}
pub unsafe fn create_surface_from_texture(
&mut self,
context: &Context,
size: &Size2D<i32>,
texture: ComPtr<d3d11::ID3D11Texture2D>,
) -> Result<Surface, Error> {
self.create_pbuffer_surface(context, size, Some(texture))
}
fn create_window_surface(&mut self, context: &Context, native_widget: &NativeWidget)
-> Result<Surface, Error> {
let context_descriptor = self.context_descriptor(context);
let egl_config = self.context_descriptor_to_egl_config(&context_descriptor);
unsafe {
EGL_FUNCTIONS.with(|egl| {
let attributes = [egl::NONE as EGLint];
let egl_surface = egl.CreateWindowSurface(self.egl_display,
egl_config,
native_widget.egl_native_window,
attributes.as_ptr());
assert_ne!(egl_surface, egl::NO_SURFACE);
let mut width = 0;
let mut height = 0;
egl.QuerySurface(self.egl_display, egl_surface, egl::WIDTH as EGLint, &mut width);
egl.QuerySurface(self.egl_display, egl_surface, egl::HEIGHT as EGLint, &mut height);
assert_ne!(width, 0);
assert_ne!(height, 0);
Ok(Surface {
egl_surface,
size: Size2D::new(width, height),
context_id: context.id,
context_descriptor,
win32_objects: Win32Objects::Window,
})
})
}
}
#[allow(non_snake_case)]
pub fn create_surface_texture(&self, context: &mut Context, surface: Surface)
-> Result<SurfaceTexture, (Error, Surface)> {
let share_handle = match surface.win32_objects {
Win32Objects::Window => return Err((Error::WidgetAttached, surface)),
Win32Objects::Pbuffer { share_handle, .. } => share_handle,
};
let local_egl_config = self.context_descriptor_to_egl_config(&surface.context_descriptor);
EGL_FUNCTIONS.with(|egl| {
unsafe {
let pbuffer_attributes = [
egl::WIDTH as EGLint, surface.size.width,
egl::HEIGHT as EGLint, surface.size.height,
egl::TEXTURE_FORMAT as EGLint, egl::TEXTURE_RGBA as EGLint,
egl::TEXTURE_TARGET as EGLint, egl::TEXTURE_2D as EGLint,
egl::NONE as EGLint, 0,
0, 0,
];
let local_egl_surface =
egl.CreatePbufferFromClientBuffer(self.egl_display,
EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
share_handle,
local_egl_config,
pbuffer_attributes.as_ptr());
if local_egl_surface == egl::NO_SURFACE {
let windowing_api_error = egl.GetError().to_windowing_api_error();
return Err((Error::SurfaceImportFailed(windowing_api_error), surface));
}
let mut local_keyed_mutex: *mut IDXGIKeyedMutex = ptr::null_mut();
let eglQuerySurfacePointerANGLE = EGL_EXTENSION_FUNCTIONS.QuerySurfacePointerANGLE
.unwrap();
let result = eglQuerySurfacePointerANGLE(
self.egl_display,
local_egl_surface,
EGL_DXGI_KEYED_MUTEX_ANGLE as EGLint,
&mut local_keyed_mutex as *mut *mut IDXGIKeyedMutex as *mut *mut c_void);
let local_keyed_mutex = if result != egl::FALSE && !local_keyed_mutex.is_null() {
let local_keyed_mutex = ComPtr::from_raw(local_keyed_mutex);
local_keyed_mutex.AddRef();
let result = local_keyed_mutex.AcquireSync(0, INFINITE);
assert_eq!(result, S_OK);
Some(local_keyed_mutex)
} else {
None
};
self.create_surface_texture_from_local_surface(
context,
surface,
local_egl_surface,
local_keyed_mutex,
)
}
})
}
fn create_surface_texture_from_local_surface(
&self,
context: &Context,
surface: Surface,
local_egl_surface: EGLSurface,
local_keyed_mutex: Option<ComPtr<IDXGIKeyedMutex>>,
) -> Result<SurfaceTexture, (Error, Surface)> {
EGL_FUNCTIONS.with(|egl| {
unsafe {
let _guard = self.temporarily_make_context_current(context);
GL_FUNCTIONS.with(|gl| {
let mut texture = 0;
gl.GenTextures(1, &mut texture);
debug_assert_ne!(texture, 0);
gl.BindTexture(gl::TEXTURE_2D, texture);
if egl.BindTexImage(self.egl_display,
local_egl_surface,
egl::BACK_BUFFER as GLint) == egl::FALSE {
let windowing_api_error = egl.GetError().to_windowing_api_error();
return Err((Error::SurfaceTextureCreationFailed(windowing_api_error),
surface));
}
gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint);
gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint);
gl.TexParameteri(gl::TEXTURE_2D,
gl::TEXTURE_WRAP_S,
gl::CLAMP_TO_EDGE as GLint);
gl.TexParameteri(gl::TEXTURE_2D,
gl::TEXTURE_WRAP_T,
gl::CLAMP_TO_EDGE as GLint);
gl.BindTexture(gl::TEXTURE_2D, 0);
debug_assert_eq!(gl.GetError(), gl::NO_ERROR);
Ok(SurfaceTexture {
surface,
local_egl_surface,
local_keyed_mutex,
gl_texture: texture,
phantom: PhantomData,
})
})
}
})
}
pub unsafe fn create_surface_texture_from_texture(
&mut self,
context: &mut Context,
size: &Size2D<i32>,
texture: ComPtr<d3d11::ID3D11Texture2D>,
) -> Result<SurfaceTexture, Error> {
let surface = self.create_pbuffer_surface(context, size, Some(texture))?;
let local_egl_surface = surface.egl_surface;
self.create_surface_texture_from_local_surface(context, surface, local_egl_surface, None)
.map_err(|(err, mut surface)| {
let _ = self.destroy_surface(context, &mut surface);
err
})
}
pub fn destroy_surface(&self, context: &mut Context, surface: &mut Surface)
-> Result<(), Error> {
if context.id != surface.context_id {
return Err(Error::IncompatibleSurface);
}
EGL_FUNCTIONS.with(|egl| {
unsafe {
if egl.GetCurrentSurface(egl::READ as EGLint) == surface.egl_surface ||
egl.GetCurrentSurface(egl::DRAW as EGLint) == surface.egl_surface {
self.make_no_context_current()?;
}
egl.DestroySurface(self.egl_display, surface.egl_surface);
surface.egl_surface = egl::NO_SURFACE;
if let Win32Objects::Pbuffer { ref mut texture, .. } = surface.win32_objects {
texture.take();
}
}
Ok(())
})
}
pub fn destroy_surface_texture(&self, _: &mut Context, mut surface_texture: SurfaceTexture)
-> Result<Surface, (Error, SurfaceTexture)> {
unsafe {
GL_FUNCTIONS.with(|gl| gl.DeleteTextures(1, &surface_texture.gl_texture));
surface_texture.gl_texture = 0;
if let Some(ref local_keyed_mutex) = surface_texture.local_keyed_mutex {
let result = local_keyed_mutex.ReleaseSync(0);
assert_eq!(result, S_OK);
}
EGL_FUNCTIONS.with(|egl| {
egl.DestroySurface(self.egl_display,
surface_texture.local_egl_surface);
})
}
Ok(surface_texture.surface)
}
#[inline]
pub fn surface_gl_texture_target(&self) -> GLenum {
SURFACE_GL_TEXTURE_TARGET
}
#[inline]
pub fn lock_surface_data<'s>(&self, _surface: &'s mut Surface)
-> Result<SurfaceDataGuard<'s>, Error> {
Err(Error::Unimplemented)
}
pub fn present_surface(&self, _: &Context, surface: &mut Surface) -> Result<(), Error> {
match surface.win32_objects {
Win32Objects::Window { .. } => {}
_ => return Err(Error::NoWidgetAttached),
}
EGL_FUNCTIONS.with(|egl| {
unsafe {
let ok = egl.SwapBuffers(self.egl_display, surface.egl_surface);
assert_ne!(ok, egl::FALSE);
Ok(())
}
})
}
pub fn resize_surface(&self, _context: &Context, surface: &mut Surface, size: Size2D<i32>) -> Result<(), Error> {
surface.size = size;
Ok(())
}
#[inline]
pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo {
SurfaceInfo {
size: surface.size,
id: surface.id(),
context_id: surface.context_id,
framebuffer_object: 0,
}
}
#[inline]
pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> GLuint {
surface_texture.gl_texture
}
}
impl Surface {
#[inline]
fn id(&self) -> SurfaceID {
SurfaceID(self.egl_surface as usize)
}
#[inline]
pub(crate) fn uses_gl_finish(&self) -> bool {
match self.win32_objects {
Win32Objects::Pbuffer { synchronization: Synchronization::GLFinish, .. } => true,
_ => false,
}
}
}
pub struct SurfaceDataGuard<'a> {
phantom: PhantomData<&'a ()>,
}