Skip to content

Add dcomp on windows #7550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e6d7e5f
add dx12 dcomp option to backend options
n1ght-hunter Apr 13, 2025
c328699
impl dcomp for the dx12 backend
n1ght-hunter Apr 13, 2025
3f91a00
Merge branch 'gfx-rs:trunk' into dev/windows-os-transparent
n1ght-hunter Apr 13, 2025
b8131bf
Merge branch 'gfx-rs:trunk' into dev/windows-os-transparent
n1ght-hunter Apr 16, 2025
1f70a9a
Add Dcomp support to DX12 backend in changelog
n1ght-hunter Apr 16, 2025
60a747c
move decomp to another module
n1ght-hunter Apr 23, 2025
8b012f3
refactor: replace use_dcomp with presentation_system in Dx12BackendOp…
n1ght-hunter Apr 23, 2025
91d98af
refactor: enhance Dx12PresentationSystem with Copy, PartialEq, and Eq…
n1ght-hunter Apr 23, 2025
bae60c3
refactor: replace use_dcomp with presentation_system in Dx12 backend …
n1ght-hunter Apr 23, 2025
194a685
Merge branch 'trunk' into dev/windows-os-transparent
n1ght-hunter Apr 23, 2025
32ce523
refactor: update Dx12BackendOptions to use presentation_system instea…
n1ght-hunter Apr 23, 2025
157c1c6
Merge branch 'dev/windows-os-transparent' of https://github.com/n1ght…
n1ght-hunter Apr 23, 2025
cd41a6e
Merge branch 'trunk' into dev/windows-os-transparent
n1ght-hunter Apr 27, 2025
73579d3
refactor(dx12): update DirectComposition device creation to use DComp…
n1ght-hunter Aug 1, 2025
cae67ca
Merge branch 'trunk' into dev/windows-os-transparent
n1ght-hunter Aug 1, 2025
830bf2b
cleanup dcomp create device types
n1ght-hunter Aug 1, 2025
f40d9b5
clean dcomp code
n1ght-hunter Aug 1, 2025
2302705
refactor(dx12): remove unused Direct3D 11 dependencies from Cargo.toml
n1ght-hunter Aug 1, 2025
504dfa1
refactor(dx12): remove unnecessary device parameter from DirectCompos…
n1ght-hunter Aug 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions deno_webgpu/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
1 change: 1 addition & 0 deletions tests/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down
1 change: 1 addition & 0 deletions wgpu-hal/examples/ray-traced-triangle/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ impl<A: hal::Api> Example<A> {
backend_options: wgpu_types::BackendOptions {
dx12: Dx12BackendOptions {
shader_compiler: wgpu_types::Dx12Compiler::default_dynamic_dxc(),
..Default::default()
},
..Default::default()
},
Expand Down
6 changes: 5 additions & 1 deletion wgpu-hal/src/dx12/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,10 @@ impl crate::Adapter for super::Adapter {
) -> Option<crate::SurfaceCapabilities> {
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()
{
Expand Down Expand Up @@ -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,
Expand Down
70 changes: 70 additions & 0 deletions wgpu-hal/src/dx12/dcomp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use windows::Win32::{Foundation::HWND, Graphics::DirectComposition};

#[derive(Default)]
pub struct DCompState {
inner: Option<InnerState>,
}

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<Self, crate::SurfaceError> {
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,
})
}
}
26 changes: 19 additions & 7 deletions wgpu-hal/src/dx12/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -118,14 +119,25 @@ impl crate::Instance for super::Instance {
window_handle: raw_window_handle::RawWindowHandle,
) -> Result<super::Surface, crate::InstanceError> {
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"
))),
Expand Down
39 changes: 38 additions & 1 deletion wgpu-hal/src/dx12/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -460,6 +461,7 @@ pub struct Instance {
factory_media: Option<Dxgi::IDXGIFactoryMedia>,
library: Arc<D3D12Lib>,
supports_allow_tearing: bool,
presentation_system: wgt::Dx12PresentationSystem,
_lib_dxgi: DxgiLib,
flags: wgt::InstanceFlags,
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
Expand Down Expand Up @@ -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<dcomp::DCompState>,
},
Visual(DirectComposition::IDCompositionVisual),
/// Borrowed, lifetime externally managed
SurfaceHandle(Foundation::HANDLE),
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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}");
Expand Down Expand Up @@ -1359,6 +1395,7 @@ impl crate::Surface for Surface {
.into_device_result("MakeWindowAssociation")?;
}
SurfaceTarget::Visual(_)
| SurfaceTarget::VisualFromWndHandle { .. }
| SurfaceTarget::SurfaceHandle(_)
| SurfaceTarget::SwapChainPanel(_) => {}
}
Expand Down
51 changes: 50 additions & 1 deletion wgpu-types/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
}
}

Expand All @@ -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,
}
}
}

Expand Down Expand Up @@ -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<Self> {
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)]
Expand Down