Support to create surface from visual on Windows (#2434)

* Support to create surface from visual on Windows, add mpo(Multiple Plane Overlay) feature to AdapterInfo

* Expose create_surface_from_visual method

* Fix code style

* Make code more concise

* Revert mpo from AdapterInfo
This commit is contained in:
Xiaopeng Li
2022-02-08 03:57:57 +08:00
committed by GitHub
parent af83b7f2c8
commit 8c351970ce
7 changed files with 137 additions and 32 deletions

View File

@@ -523,6 +523,30 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
id.0
}
#[cfg(dx12)]
pub unsafe fn instance_create_surface_from_visual(
&self,
visual: *mut std::ffi::c_void,
id_in: Input<G, SurfaceId>,
) -> SurfaceId {
profiling::scope!("instance_create_surface_from_visual", "Instance");
let surface = Surface {
presentation: None,
#[cfg(vulkan)]
vulkan: None,
dx12: self.instance.dx12.as_ref().map(|inst| HalSurface {
raw: { inst.create_surface_from_visual(visual as _) },
}),
#[cfg(gl)]
gl: None,
};
let mut token = Token::root();
let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
id.0
}
pub fn surface_drop(&self, id: SurfaceId) {
profiling::scope!("drop", "Surface");
let mut token = Token::root();

View File

@@ -73,7 +73,7 @@ egl = { package = "khronos-egl", version = "4.1", features = ["static", "no-pkg-
libloading = { version = "0.7", optional = true }
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["libloaderapi", "windef", "winuser"] }
winapi = { version = "0.3", features = ["libloaderapi", "windef", "winuser", "dcomp"] }
native = { package = "d3d12", version = "0.4.1", features = ["libloading"], optional = true }
[target.'cfg(any(target_os="macos", target_os="ios"))'.dependencies]

View File

@@ -1,4 +1,4 @@
use super::{conv, HResult as _};
use super::{conv, HResult as _, SurfaceTarget};
use std::{mem, sync::Arc, thread};
use winapi::{
shared::{dxgi, dxgi1_2, dxgi1_5, minwindef, windef, winerror},
@@ -389,16 +389,21 @@ impl crate::Adapter<super::Api> for super::Adapter {
surface: &super::Surface,
) -> Option<crate::SurfaceCapabilities> {
let current_extent = {
let mut rect: windef::RECT = mem::zeroed();
if winuser::GetClientRect(surface.wnd_handle, &mut rect) != 0 {
Some(wgt::Extent3d {
width: (rect.right - rect.left) as u32,
height: (rect.bottom - rect.top) as u32,
depth_or_array_layers: 1,
})
} else {
log::warn!("Unable to get the window client rect");
None
match surface.target {
SurfaceTarget::WndHandle(wnd_handle) => {
let mut rect: windef::RECT = mem::zeroed();
if winuser::GetClientRect(wnd_handle, &mut rect) != 0 {
Some(wgt::Extent3d {
width: (rect.right - rect.left) as u32,
height: (rect.bottom - rect.top) as u32,
depth_or_array_layers: 1,
})
} else {
log::warn!("Unable to get the window client rect");
None
}
}
SurfaceTarget::Visual(_) => None,
}
};

View File

@@ -1,4 +1,4 @@
use super::HResult as _;
use super::{HResult as _, SurfaceTarget};
use std::{borrow::Cow, slice, sync::Arc};
use winapi::{
shared::{dxgi, dxgi1_2, dxgi1_6, winerror},
@@ -153,7 +153,7 @@ impl crate::Instance<super::Api> for super::Instance {
match has_handle.raw_window_handle() {
raw_window_handle::RawWindowHandle::Win32(handle) => Ok(super::Surface {
factory: self.factory,
wnd_handle: handle.hwnd as *mut _,
target: SurfaceTarget::WndHandle(handle.hwnd as *mut _),
swap_chain: None,
}),
_ => Err(crate::InstanceError),

View File

@@ -46,7 +46,7 @@ use parking_lot::Mutex;
use std::{borrow::Cow, ffi, mem, num::NonZeroU32, ptr, sync::Arc};
use winapi::{
shared::{dxgi, dxgi1_2, dxgi1_4, dxgitype, windef, winerror},
um::{d3d12, synchapi, winbase, winnt},
um::{d3d12, dcomp, synchapi, winbase, winnt},
Interface as _,
};
@@ -129,6 +129,19 @@ pub struct Instance {
flags: crate::InstanceFlags,
}
impl Instance {
pub unsafe fn create_surface_from_visual(
&self,
visual: *mut dcomp::IDCompositionVisual,
) -> Surface {
Surface {
factory: self.factory,
target: SurfaceTarget::Visual(native::WeakPtr::from_raw(visual)),
swap_chain: None,
}
}
}
unsafe impl Send for Instance {}
unsafe impl Sync for Instance {}
@@ -144,9 +157,14 @@ struct SwapChain {
size: wgt::Extent3d,
}
enum SurfaceTarget {
WndHandle(windef::HWND),
Visual(native::WeakPtr<dcomp::IDCompositionVisual>),
}
pub struct Surface {
factory: native::WeakPtr<dxgi1_4::IDXGIFactory4>,
wnd_handle: windef::HWND,
target: SurfaceTarget,
swap_chain: Option<SwapChain>,
}
@@ -617,15 +635,28 @@ impl crate::Surface<Api> for Surface {
};
let hr = {
profiling::scope!("IDXGIFactory4::CreateSwapChainForHwnd");
self.factory.CreateSwapChainForHwnd(
device.present_queue.as_mut_ptr() as *mut _,
self.wnd_handle,
&raw_desc,
ptr::null(),
ptr::null_mut(),
swap_chain1.mut_void() as *mut *mut _,
)
match self.target {
SurfaceTarget::WndHandle(wnd_handle) => {
profiling::scope!("IDXGIFactory4::CreateSwapChainForHwnd");
self.factory.CreateSwapChainForHwnd(
device.present_queue.as_mut_ptr() as *mut _,
wnd_handle,
&raw_desc,
ptr::null(),
ptr::null_mut(),
swap_chain1.mut_void() as *mut *mut _,
)
}
SurfaceTarget::Visual(_) => {
profiling::scope!("IDXGIFactory4::CreateSwapChainForComposition");
self.factory.CreateSwapChainForComposition(
device.present_queue.as_mut_ptr() as *mut _,
&raw_desc,
ptr::null_mut(),
swap_chain1.mut_void() as *mut *mut _,
)
}
}
};
if let Err(err) = hr.into_result() {
@@ -633,6 +664,19 @@ impl crate::Surface<Api> for Surface {
return Err(crate::SurfaceError::Other("swap chain creation"));
}
match self.target {
SurfaceTarget::WndHandle(_) => {}
SurfaceTarget::Visual(visual) => {
if let Err(err) = visual.SetContent(swap_chain1.as_unknown()).into_result()
{
log::error!("Unable to SetContent: {}", err);
return Err(crate::SurfaceError::Other(
"IDCompositionVisual::SetContent",
));
}
}
}
match swap_chain1.cast::<dxgi1_4::IDXGISwapChain3>().into_result() {
Ok(swap_chain3) => {
swap_chain1.destroy();
@@ -646,13 +690,18 @@ impl crate::Surface<Api> for Surface {
}
};
// Disable automatic Alt+Enter handling by DXGI.
const DXGI_MWA_NO_WINDOW_CHANGES: u32 = 1;
const DXGI_MWA_NO_ALT_ENTER: u32 = 2;
self.factory.MakeWindowAssociation(
self.wnd_handle,
DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER,
);
match self.target {
SurfaceTarget::WndHandle(wnd_handle) => {
// Disable automatic Alt+Enter handling by DXGI.
const DXGI_MWA_NO_WINDOW_CHANGES: u32 = 1;
const DXGI_MWA_NO_ALT_ENTER: u32 = 2;
self.factory.MakeWindowAssociation(
wnd_handle,
DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER,
);
}
SurfaceTarget::Visual(_) => {}
}
swap_chain.SetMaximumFrameLatency(config.swap_chain_size);
let waitable = swap_chain.GetFrameLatencyWaitableObject();

View File

@@ -163,6 +163,23 @@ impl Context {
}
}
#[cfg(target_os = "windows")]
pub unsafe fn create_surface_from_visual(
self: &Arc<Self>,
visual: *mut std::ffi::c_void,
) -> crate::Surface {
let id = self
.0
.instance_create_surface_from_visual(visual, PhantomData);
crate::Surface {
context: Arc::clone(self),
id: Surface {
id,
configured_device: Mutex::default(),
},
}
}
fn handle_error(
&self,
sink_mutex: &Mutex<ErrorSinkRaw>,

View File

@@ -1513,6 +1513,16 @@ impl Instance {
self.context.create_surface_from_core_animation_layer(layer)
}
/// Creates a surface from `IDCompositionVisual`.
///
/// # Safety
///
/// - visual must be a valid IDCompositionVisual to create a surface upon.
#[cfg(target_os = "windows")]
pub unsafe fn create_surface_from_visual(&self, visual: *mut std::ffi::c_void) -> Surface {
self.context.create_surface_from_visual(visual)
}
/// Creates a surface from a `web_sys::HtmlCanvasElement`.
///
/// # Safety