diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d9558c36b..f75905b8fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,16 @@ Bottom level categories: ### Major Changes +#### 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 + #### `EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE` has been merged into `EXPERIMENTAL_RAY_QUERY` We have merged the acceleration structure feature into the `RayQuery` feature. This is to help work around an AMD driver bug and reduce the feature complexity of ray tracing. In the future when ray tracing pipelines are implemented, if either feature is enabled, acceleration structures will be available. diff --git a/deno_webgpu/lib.rs b/deno_webgpu/lib.rs index 6ce31a29c5..42b3f7fb37 100644 --- a/deno_webgpu/lib.rs +++ b/deno_webgpu/lib.rs @@ -158,6 +158,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 bee5af689c..0a33f969f2 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/examples/ray-traced-triangle/main.rs b/wgpu-hal/examples/ray-traced-triangle/main.rs index 2947d1a601..43cb405ef1 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 22d52be4d7..29b7402310 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -886,7 +886,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() { @@ -930,6 +933,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..a9a8da83c5 --- /dev/null +++ b/wgpu-hal/src/dx12/dcomp.rs @@ -0,0 +1,70 @@ +use windows::Win32::{Foundation::HWND, Graphics::DirectComposition}; + +#[derive(Default)] +pub struct DCompState { + inner: Option, +} + +impl DCompState { + /// This will create a DirectComposition device and a target for the window handle if not already initialized. + /// If the device is already initialized, it will return the existing state. + pub unsafe fn get_or_init( + &mut self, + hwnd: &HWND, + ) -> Result<&mut InnerState, crate::SurfaceError> { + if self.inner.is_none() { + self.inner = Some(unsafe { InnerState::init(hwnd) }?); + } + 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. + pub unsafe fn init(hwnd: &HWND) -> Result { + let dcomp_device: DirectComposition::IDCompositionDevice = { + profiling::scope!("DirectComposition::DCompositionCreateDevice"); + unsafe { DirectComposition::DCompositionCreateDevice2(None) }.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 9055b5407f..8c24786b4a 100644 --- a/wgpu-hal/src/dx12/instance.rs +++ b/wgpu-hal/src/dx12/instance.rs @@ -104,6 +104,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, @@ -118,14 +119,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 a102006acb..4bcb41ea81 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; @@ -460,6 +461,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, @@ -538,6 +540,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), @@ -1275,7 +1282,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( @@ -1322,6 +1331,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 = unsafe { dcomp_state.get_or_init(handle) }?; + // 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}"); @@ -1359,6 +1395,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 79e3f7b252..91d0293564 100644 --- a/wgpu-types/src/instance.rs +++ b/wgpu-types/src/instance.rs @@ -359,6 +359,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 { @@ -368,8 +370,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, } } @@ -379,7 +383,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, + } } } @@ -429,6 +437,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)]