diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d9b5cceb4..1251b71194 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,14 @@ Bottom level categories: ### New Features +#### DX12 + +- Add Dcomp support to DX12 backend. By @n1ght-hunter in [#7550](https://github.com/gfx-rs/wgpu/pull/7550). +```diff +-pub struct Dx12BackendOptions { pub shader_compiler: Dx12Compiler } ++pub struct Dx12BackendOptions { pub shader_compiler: Dx12Compiler, pub presentation_system: Dx12PresentationSystem } +``` + #### General - Add support for rendering to slices of 3D texture views and single layered 2D-Array texture views (this requires `VK_KHR_maintenance1` which should be widely available on newer drivers). By @teoxoy in [#7596](https://github.com/gfx-rs/wgpu/pull/7596) diff --git a/deno_webgpu/lib.rs b/deno_webgpu/lib.rs index 006a6e3643..d706749790 100644 --- a/deno_webgpu/lib.rs +++ b/deno_webgpu/lib.rs @@ -155,6 +155,7 @@ impl GPU { backend_options: wgpu_types::BackendOptions { dx12: wgpu_types::Dx12BackendOptions { shader_compiler: wgpu_types::Dx12Compiler::Fxc, + presentation_system: wgpu_types::Dx12PresentationSystem::default(), }, gl: wgpu_types::GlBackendOptions::default(), noop: wgpu_types::NoopBackendOptions::default(), diff --git a/tests/src/init.rs b/tests/src/init.rs index d2aac6f14e..0503605489 100644 --- a/tests/src/init.rs +++ b/tests/src/init.rs @@ -50,6 +50,7 @@ pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) -> backend_options: wgpu::BackendOptions { dx12: wgpu::Dx12BackendOptions { shader_compiler: dx12_shader_compiler, + ..Default::default() }, gl: wgpu::GlBackendOptions { fence_behavior: if cfg!(target_family = "wasm") { diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 65b767d612..6a33a1c4da 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -141,6 +141,8 @@ dx12 = [ "windows/Win32_Graphics_Direct3D_Dxc", "windows/Win32_Graphics_Direct3D", "windows/Win32_Graphics_Direct3D12", + "windows/Win32_Graphics_Direct3D11", + "windows/Win32_Graphics_Direct3D11on12", "windows/Win32_Graphics_DirectComposition", "windows/Win32_Graphics_Dxgi_Common", "windows/Win32_Security", diff --git a/wgpu-hal/examples/ray-traced-triangle/main.rs b/wgpu-hal/examples/ray-traced-triangle/main.rs index a8d3a77b91..7695c7c5b0 100644 --- a/wgpu-hal/examples/ray-traced-triangle/main.rs +++ b/wgpu-hal/examples/ray-traced-triangle/main.rs @@ -242,6 +242,7 @@ impl Example { backend_options: wgpu_types::BackendOptions { dx12: Dx12BackendOptions { shader_compiler: wgpu_types::Dx12Compiler::default_dynamic_dxc(), + ..Default::default() }, ..Default::default() }, diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index eff1fffd28..cf28179a31 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -849,7 +849,10 @@ impl crate::Adapter for super::Adapter { ) -> Option { let current_extent = { match surface.target { - SurfaceTarget::WndHandle(wnd_handle) => { + SurfaceTarget::WndHandle(wnd_handle) + | SurfaceTarget::VisualFromWndHandle { + handle: wnd_handle, .. + } => { let mut rect = Default::default(); if unsafe { WindowsAndMessaging::GetClientRect(wnd_handle, &mut rect) }.is_ok() { @@ -893,6 +896,7 @@ impl crate::Adapter for super::Adapter { composite_alpha_modes: match surface.target { SurfaceTarget::WndHandle(_) => vec![wgt::CompositeAlphaMode::Opaque], SurfaceTarget::Visual(_) + | SurfaceTarget::VisualFromWndHandle { .. } | SurfaceTarget::SurfaceHandle(_) | SurfaceTarget::SwapChainPanel(_) => vec![ wgt::CompositeAlphaMode::Auto, diff --git a/wgpu-hal/src/dx12/dcomp.rs b/wgpu-hal/src/dx12/dcomp.rs new file mode 100644 index 0000000000..6d66e1d9c3 --- /dev/null +++ b/wgpu-hal/src/dx12/dcomp.rs @@ -0,0 +1,110 @@ +use windows::Win32::{ + Foundation::HWND, + Graphics::{Direct3D11, Direct3D11on12, DirectComposition, Dxgi}, +}; +use windows_core::Interface as _; + +#[derive(Default)] +pub struct DCompState { + inner: Option, +} + +impl DCompState { + pub fn get_or_init( + &mut self, + hwnd: &HWND, + device: &super::Device, + ) -> Result<&mut InnerState, crate::SurfaceError> { + if self.inner.is_none() { + self.inner = Some(unsafe { InnerState::init(hwnd, device) }?); + } + Ok(self.inner.as_mut().unwrap()) + } +} + +pub struct InnerState { + pub visual: DirectComposition::IDCompositionVisual, + pub device: DirectComposition::IDCompositionDevice, + // Must be kept alive but is otherwise unused after initialization. + pub _target: DirectComposition::IDCompositionTarget, +} + +impl InnerState { + /// Creates a DirectComposition device and a target for the given window handle. + /// From a Direct3D 12 device, it creates a Direct3D 11 device and then a DirectComposition device. + pub unsafe fn init(hwnd: &HWND, device: &super::Device) -> Result { + let mut d3d11_device = None; + { + profiling::scope!("Direct3D11on12::D3D11On12CreateDevice"); + unsafe { + Direct3D11on12::D3D11On12CreateDevice( + &device.raw, + Direct3D11::D3D11_CREATE_DEVICE_BGRA_SUPPORT.0, + None, + None, + 0, + Some(&mut d3d11_device), + None, + None, + ) + } + .map_err(|err| { + log::error!("Direct3D11on12::D3D11On12CreateDevice failed: {err}"); + crate::SurfaceError::Other("Direct3D11on12::D3D11On12CreateDevice") + })?; + } + let d3d11_device = d3d11_device.unwrap(); + + let dxgi_device = { + profiling::scope!("IDXGIDevice::QueryInterface"); + d3d11_device.cast::().map_err(|err| { + log::error!("IDXGIDevice::QueryInterface failed: {err}"); + crate::SurfaceError::Other("IDXGIDevice::QueryInterface") + })? + }; + + let dcomp_device = { + profiling::scope!("DirectComposition::DCompositionCreateDevice"); + unsafe { + DirectComposition::DCompositionCreateDevice::< + _, + DirectComposition::IDCompositionDevice, + >(&dxgi_device) + } + .map_err(|err| { + log::error!("DirectComposition::DCompositionCreateDevice failed: {err}"); + crate::SurfaceError::Other("DirectComposition::DCompositionCreateDevice") + })? + }; + + let target = { + profiling::scope!("IDCompositionDevice::CreateTargetForHwnd"); + unsafe { dcomp_device.CreateTargetForHwnd(*hwnd, false) }.map_err(|err| { + log::error!("IDCompositionDevice::CreateTargetForHwnd failed: {err}"); + crate::SurfaceError::Other("IDCompositionDevice::CreateTargetForHwnd") + })? + }; + + let visual = { + profiling::scope!("IDCompositionDevice::CreateVisual"); + unsafe { dcomp_device.CreateVisual() }.map_err(|err| { + log::error!("IDCompositionDevice::CreateVisual failed: {err}"); + crate::SurfaceError::Other("IDCompositionDevice::CreateVisual") + })? + }; + + { + profiling::scope!("IDCompositionTarget::SetRoot"); + unsafe { target.SetRoot(&visual) }.map_err(|err| { + log::error!("IDCompositionTarget::SetRoot failed: {err}"); + crate::SurfaceError::Other("IDCompositionTarget::SetRoot") + })?; + } + + Ok(InnerState { + visual, + device: dcomp_device, + _target: target, + }) + } +} diff --git a/wgpu-hal/src/dx12/instance.rs b/wgpu-hal/src/dx12/instance.rs index 7a94bbadf4..17a36fa386 100644 --- a/wgpu-hal/src/dx12/instance.rs +++ b/wgpu-hal/src/dx12/instance.rs @@ -106,6 +106,7 @@ impl crate::Instance for super::Instance { factory, factory_media, library: Arc::new(lib_main), + presentation_system: desc.backend_options.dx12.presentation_system, _lib_dxgi: lib_dxgi, supports_allow_tearing, flags: desc.flags, @@ -120,14 +121,25 @@ impl crate::Instance for super::Instance { window_handle: raw_window_handle::RawWindowHandle, ) -> Result { match window_handle { - raw_window_handle::RawWindowHandle::Win32(handle) => Ok(super::Surface { - factory: self.factory.clone(), - factory_media: self.factory_media.clone(), + raw_window_handle::RawWindowHandle::Win32(handle) => { // https://github.com/rust-windowing/raw-window-handle/issues/171 - target: SurfaceTarget::WndHandle(Foundation::HWND(handle.hwnd.get() as *mut _)), - supports_allow_tearing: self.supports_allow_tearing, - swap_chain: RwLock::new(None), - }), + let handle = Foundation::HWND(handle.hwnd.get() as *mut _); + let target = match self.presentation_system { + wgt::Dx12PresentationSystem::Dxgi => SurfaceTarget::WndHandle(handle), + wgt::Dx12PresentationSystem::Dcomp => SurfaceTarget::VisualFromWndHandle { + handle, + dcomp_state: Default::default(), + }, + }; + + Ok(super::Surface { + factory: self.factory.clone(), + factory_media: self.factory_media.clone(), + target, + supports_allow_tearing: self.supports_allow_tearing, + swap_chain: RwLock::new(None), + }) + } _ => Err(crate::InstanceError::new(format!( "window handle {window_handle:?} is not a Win32 handle" ))), diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index 2c39b1cc59..cd1fbe2bc4 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -75,6 +75,7 @@ Otherwise, we pass a range corresponding only to the current bind group. mod adapter; mod command; mod conv; +mod dcomp; mod descriptor; mod device; mod instance; @@ -457,6 +458,7 @@ pub struct Instance { factory_media: Option, library: Arc, supports_allow_tearing: bool, + presentation_system: wgt::Dx12PresentationSystem, _lib_dxgi: DxgiLib, flags: wgt::InstanceFlags, memory_budget_thresholds: wgt::MemoryBudgetThresholds, @@ -530,6 +532,11 @@ struct SwapChain { enum SurfaceTarget { /// Borrowed, lifetime externally managed WndHandle(Foundation::HWND), + /// `handle` is borrowed, lifetime externally managed + VisualFromWndHandle { + handle: Foundation::HWND, + dcomp_state: Mutex, + }, Visual(DirectComposition::IDCompositionVisual), /// Borrowed, lifetime externally managed SurfaceHandle(Foundation::HANDLE), @@ -1235,7 +1242,9 @@ impl crate::Surface for Surface { Flags: flags.0 as u32, }; let swap_chain1 = match self.target { - SurfaceTarget::Visual(_) | SurfaceTarget::SwapChainPanel(_) => { + SurfaceTarget::Visual(_) + | SurfaceTarget::VisualFromWndHandle { .. } + | SurfaceTarget::SwapChainPanel(_) => { profiling::scope!("IDXGIFactory2::CreateSwapChainForComposition"); unsafe { self.factory.CreateSwapChainForComposition( @@ -1282,6 +1291,33 @@ impl crate::Surface for Surface { match &self.target { SurfaceTarget::WndHandle(_) | SurfaceTarget::SurfaceHandle(_) => {} + SurfaceTarget::VisualFromWndHandle { + handle, + dcomp_state, + } => { + let mut dcomp_state = dcomp_state.lock(); + let dcomp_state = dcomp_state.get_or_init(handle, device)?; + // Set the new swap chain as the content for the backing visual + // and commit the changes to the composition visual tree. + { + profiling::scope!("IDCompositionVisual::SetContent"); + unsafe { dcomp_state.visual.SetContent(&swap_chain1) }.map_err( + |err| { + log::error!("IDCompositionVisual::SetContent failed: {err}"); + crate::SurfaceError::Other("IDCompositionVisual::SetContent") + }, + )?; + } + + // Commit the changes to the composition device. + { + profiling::scope!("IDCompositionDevice::Commit"); + unsafe { dcomp_state.device.Commit() }.map_err(|err| { + log::error!("IDCompositionDevice::Commit failed: {err}"); + crate::SurfaceError::Other("IDCompositionDevice::Commit") + })?; + } + } SurfaceTarget::Visual(visual) => { if let Err(err) = unsafe { visual.SetContent(&swap_chain1) } { log::error!("Unable to SetContent: {err}"); @@ -1319,6 +1355,7 @@ impl crate::Surface for Surface { .into_device_result("MakeWindowAssociation")?; } SurfaceTarget::Visual(_) + | SurfaceTarget::VisualFromWndHandle { .. } | SurfaceTarget::SurfaceHandle(_) | SurfaceTarget::SwapChainPanel(_) => {} } diff --git a/wgpu-types/src/instance.rs b/wgpu-types/src/instance.rs index 5942691239..47ac0c1dea 100644 --- a/wgpu-types/src/instance.rs +++ b/wgpu-types/src/instance.rs @@ -331,6 +331,8 @@ impl GlBackendOptions { pub struct Dx12BackendOptions { /// Which DX12 shader compiler to use. pub shader_compiler: Dx12Compiler, + /// Presentation system to use. + pub presentation_system: Dx12PresentationSystem, } impl Dx12BackendOptions { @@ -340,8 +342,10 @@ impl Dx12BackendOptions { #[must_use] pub fn from_env_or_default() -> Self { let compiler = Dx12Compiler::from_env().unwrap_or_default(); + let presentation_system = Dx12PresentationSystem::from_env().unwrap_or_default(); Self { shader_compiler: compiler, + presentation_system, } } @@ -351,7 +355,11 @@ impl Dx12BackendOptions { #[must_use] pub fn with_env(self) -> Self { let shader_compiler = self.shader_compiler.with_env(); - Self { shader_compiler } + let presentation_system = self.presentation_system.with_env(); + Self { + shader_compiler, + presentation_system, + } } } @@ -401,6 +409,47 @@ impl NoopBackendOptions { } } +#[derive(Clone, Debug, Default, Copy, PartialEq, Eq)] +/// Selects which presentation system to use on DX12. +pub enum Dx12PresentationSystem { + /// Use the DXGI presentation system. + #[default] + Dxgi, + /// Use the DirectComposition presentation system. + Dcomp, +} + +impl Dx12PresentationSystem { + /// Choose which presentation system to use from the environment variable `WGPU_DX12_PRESENTATION_SYSTEM`. + /// + /// Valid values, case insensitive: + /// - `Dxgi` + /// - `Dcomp` + #[must_use] + pub fn from_env() -> Option { + let value = crate::env::var("WGPU_DX12_PRESENTATION_SYSTEM") + .as_deref()? + .to_lowercase(); + match value.as_str() { + "dcomp" => Some(Self::Dcomp), + "dxgi" => Some(Self::Dxgi), + _ => None, + } + } + + /// Takes the given presentation system, modifies it based on the `WGPU_DX12_PRESENTATION_SYSTEM` environment variable, and returns the result. + /// + /// See [`from_env`](Self::from_env) for more information. + #[must_use] + pub fn with_env(self) -> Self { + if let Some(presentation_system) = Self::from_env() { + presentation_system + } else { + self + } + } +} + /// DXC shader model. #[derive(Clone, Debug)] #[allow(missing_docs)]