From 8c351970ce7b13fe29cfef8fb0b8c8796d0e887f Mon Sep 17 00:00:00 2001 From: Xiaopeng Li Date: Tue, 8 Feb 2022 03:57:57 +0800 Subject: [PATCH] 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 --- wgpu-core/src/instance.rs | 24 ++++++++++ wgpu-hal/Cargo.toml | 2 +- wgpu-hal/src/dx12/adapter.rs | 27 ++++++----- wgpu-hal/src/dx12/instance.rs | 4 +- wgpu-hal/src/dx12/mod.rs | 85 +++++++++++++++++++++++++++-------- wgpu/src/backend/direct.rs | 17 +++++++ wgpu/src/lib.rs | 10 +++++ 7 files changed, 137 insertions(+), 32 deletions(-) diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 08773aa72c..117123be18 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -523,6 +523,30 @@ impl Global { id.0 } + #[cfg(dx12)] + pub unsafe fn instance_create_surface_from_visual( + &self, + visual: *mut std::ffi::c_void, + id_in: Input, + ) -> 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(); diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 1cc6074a05..7f21eef494 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -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] diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 51d40c8671..d7064d0183 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -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 for super::Adapter { surface: &super::Surface, ) -> Option { 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, } }; diff --git a/wgpu-hal/src/dx12/instance.rs b/wgpu-hal/src/dx12/instance.rs index c00fce84ce..e430d48d3d 100644 --- a/wgpu-hal/src/dx12/instance.rs +++ b/wgpu-hal/src/dx12/instance.rs @@ -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 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), diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index b4dd295059..7e1ec8e884 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -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), +} + pub struct Surface { factory: native::WeakPtr, - wnd_handle: windef::HWND, + target: SurfaceTarget, swap_chain: Option, } @@ -617,15 +635,28 @@ impl crate::Surface 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 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::().into_result() { Ok(swap_chain3) => { swap_chain1.destroy(); @@ -646,13 +690,18 @@ impl crate::Surface 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(); diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index f7a16e1237..e0dfea8923 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -163,6 +163,23 @@ impl Context { } } + #[cfg(target_os = "windows")] + pub unsafe fn create_surface_from_visual( + self: &Arc, + 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, diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 414a68c6c5..ff23b63024 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -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