use super::context::{Context, ContextDescriptor};
use super::device::Device;
use crate::context::ContextID;
use crate::egl::types::EGLNativeWindowType;
use crate::egl::types::EGLSurface;
use crate::egl::{self, EGLint};
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 euclid::default::Size2D;
use glow::HasContext;
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: u32 = 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: Option<glow::Texture>,
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(
&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(
&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(
&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(
&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);
let gl = &context.gl;
let mut texture = gl.create_texture().unwrap();
gl.bind_texture(gl::TEXTURE_2D, Some(texture));
if egl.BindTexImage(self.egl_display, local_egl_surface, egl::BACK_BUFFER as _)
== egl::FALSE
{
let windowing_api_error = egl.GetError().to_windowing_api_error();
return Err((
Error::SurfaceTextureCreationFailed(windowing_api_error),
surface,
));
}
gl.tex_parameter_i32(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as _);
gl.tex_parameter_i32(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as _);
gl.tex_parameter_i32(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as _);
gl.tex_parameter_i32(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as _);
gl.bind_texture(gl::TEXTURE_2D, None);
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
Ok(SurfaceTexture {
surface,
local_egl_surface,
local_keyed_mutex,
gl_texture: Some(texture),
phantom: PhantomData,
})
}
})
}
pub unsafe fn create_surface_texture_from_texture(
&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,
context: &mut Context,
mut surface_texture: SurfaceTexture,
) -> Result<Surface, (Error, SurfaceTexture)> {
unsafe {
if let Some(texture) = surface_texture.gl_texture.take() {
context.gl.delete_texture(texture);
}
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) -> u32 {
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: None,
}
}
#[inline]
pub fn surface_texture_object(
&self,
surface_texture: &SurfaceTexture,
) -> Option<glow::Texture> {
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,
}
}
#[inline]
pub fn share_handle(&self) -> Option<HANDLE> {
match self.win32_objects {
Win32Objects::Pbuffer { share_handle, .. } => Some(share_handle),
_ => None,
}
}
}
pub struct SurfaceDataGuard<'a> {
phantom: PhantomData<&'a ()>,
}