use super::context::{self, Context, WGL_EXTENSION_FUNCTIONS};
use super::device::Device;
use crate::error::WindowingApiError;
use crate::renderbuffers::Renderbuffers;
use crate::{ContextID, Error, SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType};
use crate::gl;
type GLenum = c_uint;
type GLint = c_int;
use crate::gl_utils;
use euclid::default::Size2D;
use glow::HasContext;
use std::ffi::{c_int, c_uint};
use std::fmt::{self, Debug, Formatter};
use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_void;
use std::ptr;
use std::thread;
use winapi::shared::dxgi::IDXGIResource;
use winapi::shared::dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM;
use winapi::shared::dxgitype::DXGI_SAMPLE_DESC;
use winapi::shared::minwindef::{FALSE, UINT};
use winapi::shared::ntdef::HANDLE;
use winapi::shared::windef::HWND;
use winapi::shared::winerror;
use winapi::um::d3d11::{ID3D11Texture2D, D3D11_USAGE_DEFAULT};
use winapi::um::d3d11::{D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE};
use winapi::um::d3d11::{D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX, D3D11_TEXTURE2D_DESC};
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::wingdi;
use winapi::um::winuser;
use winapi::Interface;
use wio::com::ComPtr;
const SURFACE_GL_TEXTURE_TARGET: GLenum = gl::TEXTURE_2D;
const WGL_ACCESS_READ_ONLY_NV: GLenum = 0x0000;
const WGL_ACCESS_READ_WRITE_NV: GLenum = 0x0001;
pub struct Surface {
pub(crate) size: Size2D<i32>,
pub(crate) context_id: ContextID,
pub(crate) win32_objects: Win32Objects,
pub(crate) destroyed: bool,
}
pub(crate) enum Win32Objects {
Texture {
d3d11_texture: ComPtr<ID3D11Texture2D>,
dxgi_share_handle: HANDLE,
gl_dx_interop_object: HANDLE,
gl_texture: Option<glow::Texture>,
gl_framebuffer: Option<glow::Framebuffer>,
renderbuffers: Renderbuffers,
},
Widget {
window_handle: HWND,
},
}
pub struct SurfaceTexture {
pub(crate) surface: Surface,
#[allow(dead_code)]
pub(crate) local_d3d11_texture: ComPtr<ID3D11Texture2D>,
local_gl_dx_interop_object: HANDLE,
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.destroyed && !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 struct NativeWidget {
pub window_handle: HWND,
}
impl Device {
pub fn create_surface(
&self,
context: &Context,
_: SurfaceAccess,
surface_type: SurfaceType<NativeWidget>,
) -> Result<Surface, Error> {
match surface_type {
SurfaceType::Generic { size } => self.create_generic_surface(context, &size),
SurfaceType::Widget { native_widget } => {
self.create_widget_surface(context, native_widget)
}
}
}
fn create_generic_surface(
&self,
context: &Context,
size: &Size2D<i32>,
) -> Result<Surface, Error> {
let dx_interop_functions = match WGL_EXTENSION_FUNCTIONS.dx_interop_functions {
None => return Err(Error::RequiredExtensionUnavailable),
Some(ref dx_interop_functions) => dx_interop_functions,
};
unsafe {
let _guard = self.temporarily_make_context_current(context)?;
let d3d11_texture2d_desc = D3D11_TEXTURE2D_DESC {
Width: size.width as UINT,
Height: size.height as UINT,
MipLevels: 1,
ArraySize: 1,
Format: DXGI_FORMAT_R8G8B8A8_UNORM,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Usage: D3D11_USAGE_DEFAULT,
BindFlags: D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET,
CPUAccessFlags: 0,
MiscFlags: D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX,
};
let mut d3d11_texture = ptr::null_mut();
let mut result = self.d3d11_device.CreateTexture2D(
&d3d11_texture2d_desc,
ptr::null(),
&mut d3d11_texture,
);
if !winerror::SUCCEEDED(result) {
return Err(Error::SurfaceCreationFailed(WindowingApiError::Failed));
}
assert!(!d3d11_texture.is_null());
let d3d11_texture = ComPtr::from_raw(d3d11_texture);
let mut dxgi_resource: *mut IDXGIResource = ptr::null_mut();
result = d3d11_texture.QueryInterface(
&IDXGIResource::uuidof(),
&mut dxgi_resource as *mut *mut IDXGIResource as *mut *mut c_void,
);
assert!(winerror::SUCCEEDED(result));
assert!(!dxgi_resource.is_null());
let dxgi_resource = ComPtr::from_raw(dxgi_resource);
let mut dxgi_share_handle = INVALID_HANDLE_VALUE;
result = dxgi_resource.GetSharedHandle(&mut dxgi_share_handle);
assert!(winerror::SUCCEEDED(result));
assert_ne!(dxgi_share_handle, INVALID_HANDLE_VALUE);
let ok = (dx_interop_functions.DXSetResourceShareHandleNV)(
d3d11_texture.as_raw() as *mut c_void,
dxgi_share_handle,
);
assert_ne!(ok, FALSE);
let gl_texture = context.gl.create_texture().unwrap();
let gl_dx_interop_object = (dx_interop_functions.DXRegisterObjectNV)(
self.gl_dx_interop_device,
d3d11_texture.as_raw() as *mut c_void,
gl_texture.0.get(),
gl::TEXTURE_2D,
WGL_ACCESS_READ_WRITE_NV,
);
if gl_dx_interop_object.is_null() {
let msg = std::io::Error::last_os_error(); error!(
"Unable to share surface between OpenGL and DirectX. OS error '{}'.",
msg
);
return Err(Error::SurfaceCreationFailed(WindowingApiError::Failed));
}
let gl_framebuffer = context.gl.create_framebuffer().unwrap();
let _guard = self.temporarily_bind_framebuffer(context, Some(gl_framebuffer));
context.gl.framebuffer_texture_2d(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
SURFACE_GL_TEXTURE_TARGET,
Some(gl_texture),
0,
);
let context_descriptor = self.context_descriptor(context);
let context_attributes = self.context_descriptor_attributes(&context_descriptor);
let renderbuffers = Renderbuffers::new(&context.gl, &size, &context_attributes);
renderbuffers.bind_to_current_framebuffer(&context.gl);
Ok(Surface {
size: *size,
context_id: context.id,
win32_objects: Win32Objects::Texture {
d3d11_texture,
dxgi_share_handle,
gl_dx_interop_object,
gl_texture: Some(gl_texture),
gl_framebuffer: Some(gl_framebuffer),
renderbuffers,
},
destroyed: false,
})
}
}
fn create_widget_surface(
&self,
context: &Context,
native_widget: NativeWidget,
) -> Result<Surface, Error> {
unsafe {
let mut widget_rect = mem::zeroed();
let ok = winuser::GetWindowRect(native_widget.window_handle, &mut widget_rect);
if ok == FALSE {
return Err(Error::InvalidNativeWidget);
}
{
let context_dc_guard = self.get_context_dc(context);
let pixel_format = wingdi::GetPixelFormat(context_dc_guard.dc);
let window_dc = winuser::GetDC(native_widget.window_handle);
context::set_dc_pixel_format(window_dc, pixel_format);
}
Ok(Surface {
size: Size2D::new(
widget_rect.right - widget_rect.left,
widget_rect.bottom - widget_rect.top,
),
context_id: context.id,
win32_objects: Win32Objects::Widget {
window_handle: native_widget.window_handle,
},
destroyed: false,
})
}
}
pub fn destroy_surface(
&self,
context: &mut Context,
surface: &mut Surface,
) -> Result<(), Error> {
let dx_interop_functions = WGL_EXTENSION_FUNCTIONS
.dx_interop_functions
.as_ref()
.expect("How did you make a surface without DX interop?");
if context.id != surface.context_id {
return Err(Error::IncompatibleSurface);
}
let _guard = self.temporarily_make_context_current(context)?;
unsafe {
match surface.win32_objects {
Win32Objects::Texture {
ref mut gl_dx_interop_object,
ref mut gl_texture,
ref mut gl_framebuffer,
ref mut renderbuffers,
d3d11_texture: _,
dxgi_share_handle: _,
} => {
renderbuffers.destroy(&context.gl);
if let Some(fbo) = gl_framebuffer.take() {
gl_utils::destroy_framebuffer(&context.gl, fbo);
}
if let Some(texture) = gl_texture.take() {
context.gl.delete_texture(texture);
}
let ok = (dx_interop_functions.DXUnregisterObjectNV)(
self.gl_dx_interop_device,
*gl_dx_interop_object,
);
assert_ne!(ok, FALSE);
*gl_dx_interop_object = INVALID_HANDLE_VALUE;
}
Win32Objects::Widget { window_handle: _ } => {}
}
surface.destroyed = true;
}
Ok(())
}
pub fn create_surface_texture(
&self,
context: &mut Context,
surface: Surface,
) -> Result<SurfaceTexture, (Error, Surface)> {
let dxgi_share_handle = match surface.win32_objects {
Win32Objects::Widget { .. } => return Err((Error::WidgetAttached, surface)),
Win32Objects::Texture {
dxgi_share_handle, ..
} => dxgi_share_handle,
};
let dx_interop_functions = WGL_EXTENSION_FUNCTIONS
.dx_interop_functions
.as_ref()
.expect("How did you make a surface without DX interop?");
let _guard = match self.temporarily_make_context_current(context) {
Ok(guard) => guard,
Err(err) => return Err((err, surface)),
};
unsafe {
let mut local_d3d11_texture = ptr::null_mut();
let result = self.d3d11_device.OpenSharedResource(
dxgi_share_handle,
&ID3D11Texture2D::uuidof(),
&mut local_d3d11_texture,
);
if !winerror::SUCCEEDED(result) || local_d3d11_texture.is_null() {
return Err((
Error::SurfaceImportFailed(WindowingApiError::Failed),
surface,
));
}
let local_d3d11_texture = ComPtr::from_raw(local_d3d11_texture as *mut ID3D11Texture2D);
let ok = (dx_interop_functions.DXSetResourceShareHandleNV)(
local_d3d11_texture.as_raw() as *mut c_void,
dxgi_share_handle,
);
assert_ne!(ok, FALSE);
let gl_texture = context.gl.create_texture().unwrap();
let mut local_gl_dx_interop_object = (dx_interop_functions.DXRegisterObjectNV)(
self.gl_dx_interop_device,
local_d3d11_texture.as_raw() as *mut c_void,
gl_texture.0.get(),
gl::TEXTURE_2D,
WGL_ACCESS_READ_ONLY_NV,
);
let ok = (dx_interop_functions.DXLockObjectsNV)(
self.gl_dx_interop_device,
1,
&mut local_gl_dx_interop_object,
);
assert_ne!(ok, FALSE);
context.gl.bind_texture(gl::TEXTURE_2D, Some(gl_texture));
context.gl.tex_parameter_i32(
gl::TEXTURE_2D,
gl::TEXTURE_MAG_FILTER,
gl::LINEAR as GLint,
);
context.gl.tex_parameter_i32(
gl::TEXTURE_2D,
gl::TEXTURE_MIN_FILTER,
gl::LINEAR as GLint,
);
context.gl.tex_parameter_i32(
gl::TEXTURE_2D,
gl::TEXTURE_WRAP_S,
gl::CLAMP_TO_EDGE as GLint,
);
context.gl.tex_parameter_i32(
gl::TEXTURE_2D,
gl::TEXTURE_WRAP_T,
gl::CLAMP_TO_EDGE as GLint,
);
Ok(SurfaceTexture {
surface,
local_d3d11_texture,
local_gl_dx_interop_object,
gl_texture: Some(gl_texture),
phantom: PhantomData,
})
}
}
pub fn destroy_surface_texture(
&self,
context: &mut Context,
mut surface_texture: SurfaceTexture,
) -> Result<Surface, (Error, SurfaceTexture)> {
let dx_interop_functions = WGL_EXTENSION_FUNCTIONS
.dx_interop_functions
.as_ref()
.expect("How did you make a surface without DX interop?");
let _guard = match self.temporarily_make_context_current(context) {
Ok(guard) => guard,
Err(err) => return Err((err, surface_texture)),
};
unsafe {
let ok = (dx_interop_functions.DXUnlockObjectsNV)(
self.gl_dx_interop_device,
1,
&mut surface_texture.local_gl_dx_interop_object,
);
assert_ne!(ok, FALSE);
let ok = (dx_interop_functions.DXUnregisterObjectNV)(
self.gl_dx_interop_device,
surface_texture.local_gl_dx_interop_object,
);
assert_ne!(ok, FALSE);
surface_texture.local_gl_dx_interop_object = INVALID_HANDLE_VALUE;
if let Some(texture) = surface_texture.gl_texture.take() {
context.gl.delete_texture(texture);
}
}
Ok(surface_texture.surface)
}
pub(crate) fn lock_surface(&self, surface: &Surface) {
let mut gl_dx_interop_object = match surface.win32_objects {
Win32Objects::Widget { .. } => return,
Win32Objects::Texture {
gl_dx_interop_object,
..
} => gl_dx_interop_object,
};
let dx_interop_functions = WGL_EXTENSION_FUNCTIONS
.dx_interop_functions
.as_ref()
.expect("How did you make a surface without DX interop?");
unsafe {
let ok = (dx_interop_functions.DXLockObjectsNV)(
self.gl_dx_interop_device,
1,
&mut gl_dx_interop_object,
);
assert_ne!(ok, FALSE);
}
}
pub(crate) fn unlock_surface(&self, surface: &Surface) {
let mut gl_dx_interop_object = match surface.win32_objects {
Win32Objects::Widget { .. } => return,
Win32Objects::Texture {
gl_dx_interop_object,
..
} => gl_dx_interop_object,
};
let dx_interop_functions = WGL_EXTENSION_FUNCTIONS
.dx_interop_functions
.as_ref()
.expect("How did you make a surface without DX interop?");
unsafe {
let ok = (dx_interop_functions.DXUnlockObjectsNV)(
self.gl_dx_interop_device,
1,
&mut gl_dx_interop_object,
);
assert_ne!(ok, FALSE);
}
}
#[inline]
pub fn lock_surface_data<'s>(
&self,
_surface: &'s mut Surface,
) -> Result<SurfaceDataGuard<'s>, Error> {
Err(Error::Unimplemented)
}
#[inline]
pub fn surface_gl_texture_target(&self) -> GLenum {
gl::TEXTURE_2D
}
pub fn present_surface(&self, _: &Context, surface: &mut Surface) -> Result<(), Error> {
let window_handle = match surface.win32_objects {
Win32Objects::Widget { window_handle } => window_handle,
_ => return Err(Error::NoWidgetAttached),
};
unsafe {
let dc = winuser::GetDC(window_handle);
let ok = wingdi::SwapBuffers(dc);
assert_ne!(ok, FALSE);
winuser::ReleaseDC(window_handle, dc);
Ok(())
}
}
pub fn resize_surface(
&self,
_scontext: &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: match surface.win32_objects {
Win32Objects::Texture { gl_framebuffer, .. } => gl_framebuffer,
Win32Objects::Widget { .. } => None,
},
}
}
#[inline]
pub fn surface_texture_object(
&self,
surface_texture: &SurfaceTexture,
) -> Option<glow::Texture> {
surface_texture.gl_texture
}
}
impl Surface {
pub(crate) fn id(&self) -> SurfaceID {
match self.win32_objects {
Win32Objects::Texture {
ref d3d11_texture, ..
} => SurfaceID((*d3d11_texture).as_raw() as usize),
Win32Objects::Widget { window_handle } => SurfaceID(window_handle as usize),
}
}
#[inline]
pub fn share_handle(&self) -> Option<HANDLE> {
match self.win32_objects {
Win32Objects::Texture {
dxgi_share_handle, ..
} => Some(dxgi_share_handle),
_ => None,
}
}
}
pub struct SurfaceDataGuard<'a> {
phantom: PhantomData<&'a ()>,
}