Skip to content

Commit 3902b0c

Browse files
n1ght-huntercwfitzgeraldMarijnS95
authored
Add support for builtin DComp swapchains (#7550)
Co-authored-by: Connor Fitzgerald <[email protected]> Co-authored-by: Marijn Suijten <[email protected]>
1 parent 1791064 commit 3902b0c

File tree

6 files changed

+203
-11
lines changed

6 files changed

+203
-11
lines changed

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,27 @@ Available on `CommandEncoder`, `CommandBuffer`, `RenderPass`, and `ComputePass`.
6464

6565
By @cwfitzgerald in [#8125](https://github.com/gfx-rs/wgpu/pull/8125).
6666

67+
#### Builtin Support for DXGI swapchains on top of of DirectComposition Visuals in DX12
68+
69+
By enabling DirectComposition support, the dx12 backend can now support transparent windows.
70+
71+
This creates a single `IDCompositionVisual` over the entire window that is used by the mf`Surface`. If a user wants to manage the composition tree themselves, they should create their own device and composition, and pass the relevant visual down into `wgpu` via `SurfaceTargetUnsafe::CompositionVisual`.
72+
73+
```rust
74+
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
75+
backend_options: wgpu::BackendOptions {
76+
dx12: wgpu::Dx12BackendOptions {
77+
presentation_system: wgpu::Dx12SwapchainKind::DxgiFromVisual,
78+
..
79+
},
80+
..
81+
},
82+
..
83+
});
84+
```
85+
86+
By @n1ght-hunter in [#7550](https://github.com/gfx-rs/wgpu/pull/7550).
87+
6788
#### `EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE` has been merged into `EXPERIMENTAL_RAY_QUERY`
6889

6990
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.

wgpu-hal/src/dx12/adapter.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,10 @@ impl crate::Adapter for super::Adapter {
919919
) -> Option<crate::SurfaceCapabilities> {
920920
let current_extent = {
921921
match surface.target {
922-
SurfaceTarget::WndHandle(wnd_handle) => {
922+
SurfaceTarget::WndHandle(wnd_handle)
923+
| SurfaceTarget::VisualFromWndHandle {
924+
handle: wnd_handle, ..
925+
} => {
923926
let mut rect = Default::default();
924927
if unsafe { WindowsAndMessaging::GetClientRect(wnd_handle, &mut rect) }.is_ok()
925928
{
@@ -963,6 +966,7 @@ impl crate::Adapter for super::Adapter {
963966
composite_alpha_modes: match surface.target {
964967
SurfaceTarget::WndHandle(_) => vec![wgt::CompositeAlphaMode::Opaque],
965968
SurfaceTarget::Visual(_)
969+
| SurfaceTarget::VisualFromWndHandle { .. }
966970
| SurfaceTarget::SurfaceHandle(_)
967971
| SurfaceTarget::SwapChainPanel(_) => vec![
968972
wgt::CompositeAlphaMode::Auto,

wgpu-hal/src/dx12/dcomp.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use windows::Win32::{Foundation::HWND, Graphics::DirectComposition};
2+
3+
#[derive(Default)]
4+
pub struct DCompState {
5+
inner: Option<InnerState>,
6+
}
7+
8+
impl DCompState {
9+
/// This will create a DirectComposition device and a target for the window handle if not already initialized.
10+
/// If the device is already initialized, it will return the existing state.
11+
pub unsafe fn get_or_init(
12+
&mut self,
13+
hwnd: &HWND,
14+
) -> Result<&mut InnerState, crate::SurfaceError> {
15+
if self.inner.is_none() {
16+
self.inner = Some(unsafe { InnerState::init(hwnd) }?);
17+
}
18+
Ok(self.inner.as_mut().unwrap())
19+
}
20+
}
21+
22+
pub struct InnerState {
23+
pub visual: DirectComposition::IDCompositionVisual,
24+
pub device: DirectComposition::IDCompositionDevice,
25+
// Must be kept alive but is otherwise unused after initialization.
26+
pub _target: DirectComposition::IDCompositionTarget,
27+
}
28+
29+
impl InnerState {
30+
/// Creates a DirectComposition device and a target for the given window handle.
31+
pub unsafe fn init(hwnd: &HWND) -> Result<Self, crate::SurfaceError> {
32+
profiling::scope!("DCompState::init");
33+
let dcomp_device: DirectComposition::IDCompositionDevice = {
34+
unsafe { DirectComposition::DCompositionCreateDevice2(None) }.map_err(|err| {
35+
log::error!("DirectComposition::DCompositionCreateDevice failed: {err}");
36+
crate::SurfaceError::Other("DirectComposition::DCompositionCreateDevice")
37+
})?
38+
};
39+
40+
let target = unsafe { dcomp_device.CreateTargetForHwnd(*hwnd, false) }.map_err(|err| {
41+
log::error!("IDCompositionDevice::CreateTargetForHwnd failed: {err}");
42+
crate::SurfaceError::Other("IDCompositionDevice::CreateTargetForHwnd")
43+
})?;
44+
45+
let visual = unsafe { dcomp_device.CreateVisual() }.map_err(|err| {
46+
log::error!("IDCompositionDevice::CreateVisual failed: {err}");
47+
crate::SurfaceError::Other("IDCompositionDevice::CreateVisual")
48+
})?;
49+
50+
unsafe { target.SetRoot(&visual) }.map_err(|err| {
51+
log::error!("IDCompositionTarget::SetRoot failed: {err}");
52+
crate::SurfaceError::Other("IDCompositionTarget::SetRoot")
53+
})?;
54+
55+
Ok(InnerState {
56+
visual,
57+
device: dcomp_device,
58+
_target: target,
59+
})
60+
}
61+
}

wgpu-hal/src/dx12/instance.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ impl crate::Instance for super::Instance {
104104
factory,
105105
factory_media,
106106
library: Arc::new(lib_main),
107+
presentation_system: desc.backend_options.dx12.presentation_system,
107108
_lib_dxgi: lib_dxgi,
108109
supports_allow_tearing,
109110
flags: desc.flags,
@@ -119,15 +120,26 @@ impl crate::Instance for super::Instance {
119120
window_handle: raw_window_handle::RawWindowHandle,
120121
) -> Result<super::Surface, crate::InstanceError> {
121122
match window_handle {
122-
raw_window_handle::RawWindowHandle::Win32(handle) => Ok(super::Surface {
123-
factory: self.factory.clone(),
124-
factory_media: self.factory_media.clone(),
123+
raw_window_handle::RawWindowHandle::Win32(handle) => {
125124
// https://github.com/rust-windowing/raw-window-handle/issues/171
126-
target: SurfaceTarget::WndHandle(Foundation::HWND(handle.hwnd.get() as *mut _)),
127-
supports_allow_tearing: self.supports_allow_tearing,
128-
swap_chain: RwLock::new(None),
129-
options: self.options.clone(),
130-
}),
125+
let handle = Foundation::HWND(handle.hwnd.get() as *mut _);
126+
let target = match self.presentation_system {
127+
wgt::Dx12SwapchainKind::DxgiFromHwnd => SurfaceTarget::WndHandle(handle),
128+
wgt::Dx12SwapchainKind::DxgiFromVisual => SurfaceTarget::VisualFromWndHandle {
129+
handle,
130+
dcomp_state: Default::default(),
131+
},
132+
};
133+
134+
Ok(super::Surface {
135+
factory: self.factory.clone(),
136+
factory_media: self.factory_media.clone(),
137+
target,
138+
supports_allow_tearing: self.supports_allow_tearing,
139+
swap_chain: RwLock::new(None),
140+
options: self.options.clone(),
141+
})
142+
}
131143
_ => Err(crate::InstanceError::new(format!(
132144
"window handle {window_handle:?} is not a Win32 handle"
133145
))),

wgpu-hal/src/dx12/mod.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Otherwise, we pass a range corresponding only to the current bind group.
7575
mod adapter;
7676
mod command;
7777
mod conv;
78+
mod dcomp;
7879
mod descriptor;
7980
mod device;
8081
mod instance;
@@ -460,6 +461,7 @@ pub struct Instance {
460461
factory_media: Option<Dxgi::IDXGIFactoryMedia>,
461462
library: Arc<D3D12Lib>,
462463
supports_allow_tearing: bool,
464+
presentation_system: wgt::Dx12SwapchainKind,
463465
_lib_dxgi: DxgiLib,
464466
flags: wgt::InstanceFlags,
465467
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
@@ -542,6 +544,11 @@ struct SwapChain {
542544
enum SurfaceTarget {
543545
/// Borrowed, lifetime externally managed
544546
WndHandle(Foundation::HWND),
547+
/// `handle` is borrowed, lifetime externally managed
548+
VisualFromWndHandle {
549+
handle: Foundation::HWND,
550+
dcomp_state: Mutex<dcomp::DCompState>,
551+
},
545552
Visual(DirectComposition::IDCompositionVisual),
546553
/// Borrowed, lifetime externally managed
547554
SurfaceHandle(Foundation::HANDLE),
@@ -1297,7 +1304,9 @@ impl crate::Surface for Surface {
12971304
Flags: flags.0 as u32,
12981305
};
12991306
let swap_chain1 = match self.target {
1300-
SurfaceTarget::Visual(_) | SurfaceTarget::SwapChainPanel(_) => {
1307+
SurfaceTarget::Visual(_)
1308+
| SurfaceTarget::VisualFromWndHandle { .. }
1309+
| SurfaceTarget::SwapChainPanel(_) => {
13011310
profiling::scope!("IDXGIFactory2::CreateSwapChainForComposition");
13021311
unsafe {
13031312
self.factory.CreateSwapChainForComposition(
@@ -1344,6 +1353,33 @@ impl crate::Surface for Surface {
13441353

13451354
match &self.target {
13461355
SurfaceTarget::WndHandle(_) | SurfaceTarget::SurfaceHandle(_) => {}
1356+
SurfaceTarget::VisualFromWndHandle {
1357+
handle,
1358+
dcomp_state,
1359+
} => {
1360+
let mut dcomp_state = dcomp_state.lock();
1361+
let dcomp_state = unsafe { dcomp_state.get_or_init(handle) }?;
1362+
// Set the new swap chain as the content for the backing visual
1363+
// and commit the changes to the composition visual tree.
1364+
{
1365+
profiling::scope!("IDCompositionVisual::SetContent");
1366+
unsafe { dcomp_state.visual.SetContent(&swap_chain1) }.map_err(
1367+
|err| {
1368+
log::error!("IDCompositionVisual::SetContent failed: {err}");
1369+
crate::SurfaceError::Other("IDCompositionVisual::SetContent")
1370+
},
1371+
)?;
1372+
}
1373+
1374+
// Commit the changes to the composition device.
1375+
{
1376+
profiling::scope!("IDCompositionDevice::Commit");
1377+
unsafe { dcomp_state.device.Commit() }.map_err(|err| {
1378+
log::error!("IDCompositionDevice::Commit failed: {err}");
1379+
crate::SurfaceError::Other("IDCompositionDevice::Commit")
1380+
})?;
1381+
}
1382+
}
13471383
SurfaceTarget::Visual(visual) => {
13481384
if let Err(err) = unsafe { visual.SetContent(&swap_chain1) } {
13491385
log::error!("Unable to SetContent: {err}");
@@ -1381,6 +1417,7 @@ impl crate::Surface for Surface {
13811417
.into_device_result("MakeWindowAssociation")?;
13821418
}
13831419
SurfaceTarget::Visual(_)
1420+
| SurfaceTarget::VisualFromWndHandle { .. }
13841421
| SurfaceTarget::SurfaceHandle(_)
13851422
| SurfaceTarget::SwapChainPanel(_) => {}
13861423
}

wgpu-types/src/instance.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,8 @@ impl GlBackendOptions {
359359
pub struct Dx12BackendOptions {
360360
/// Which DX12 shader compiler to use.
361361
pub shader_compiler: Dx12Compiler,
362+
/// Presentation system to use.
363+
pub presentation_system: Dx12SwapchainKind,
362364
/// Whether to wait for the latency waitable object before acquiring the next swapchain image.
363365
pub latency_waitable_object: Dx12UseFrameLatencyWaitableObject,
364366
}
@@ -370,10 +372,12 @@ impl Dx12BackendOptions {
370372
#[must_use]
371373
pub fn from_env_or_default() -> Self {
372374
let compiler = Dx12Compiler::from_env().unwrap_or_default();
375+
let presentation_system = Dx12SwapchainKind::from_env().unwrap_or_default();
373376
let latency_waitable_object =
374377
Dx12UseFrameLatencyWaitableObject::from_env().unwrap_or_default();
375378
Self {
376379
shader_compiler: compiler,
380+
presentation_system,
377381
latency_waitable_object,
378382
}
379383
}
@@ -384,10 +388,11 @@ impl Dx12BackendOptions {
384388
#[must_use]
385389
pub fn with_env(self) -> Self {
386390
let shader_compiler = self.shader_compiler.with_env();
391+
let presentation_system = self.presentation_system.with_env();
387392
let latency_waitable_object = self.latency_waitable_object.with_env();
388-
389393
Self {
390394
shader_compiler,
395+
presentation_system,
391396
latency_waitable_object,
392397
}
393398
}
@@ -439,6 +444,58 @@ impl NoopBackendOptions {
439444
}
440445
}
441446

447+
#[derive(Clone, Debug, Default, Copy, PartialEq, Eq)]
448+
/// Selects which kind of swapchain to use on DX12.
449+
pub enum Dx12SwapchainKind {
450+
/// Use a DXGI swapchain made directly from the window's HWND.
451+
///
452+
/// This does not support transparency but has better support from developer tooling from RenderDoc.
453+
#[default]
454+
DxgiFromHwnd,
455+
/// Use a DXGI swapchain made from a DirectComposition visual made automatically from the window's HWND.
456+
///
457+
/// This creates a single [`IDCompositionVisual`] over the entire window that is used by the `Surface`.
458+
/// If a user wants to manage the composition tree themselves, they should create their own device and
459+
/// composition, and pass the relevant visual down via [`SurfaceTargetUnsafe::CompositionVisual`][CV].
460+
///
461+
/// This supports transparent windows, but does not have support from RenderDoc.
462+
///
463+
/// [`IDCompositionVisual`]: https://learn.microsoft.com/en-us/windows/win32/api/dcomp/nn-dcomp-idcompositionvisual
464+
/// [CV]: ../wgpu/struct.SurfaceTargetUnsafe.html#variant.CompositionVisual
465+
DxgiFromVisual,
466+
}
467+
468+
impl Dx12SwapchainKind {
469+
/// Choose which presentation system to use from the environment variable `WGPU_DX12_PRESENTATION_SYSTEM`.
470+
///
471+
/// Valid values, case insensitive:
472+
/// - `DxgiFromVisual` or `Visual`
473+
/// - `DxgiFromHwnd` or `Hwnd` for [`Self::DxgiFromHwnd`]
474+
#[must_use]
475+
pub fn from_env() -> Option<Self> {
476+
let value = crate::env::var("WGPU_DX12_PRESENTATION_SYSTEM")
477+
.as_deref()?
478+
.to_lowercase();
479+
match value.as_str() {
480+
"dxgifromvisual" | "visual" => Some(Self::DxgiFromVisual),
481+
"dxgifromhwnd" | "hwnd" => Some(Self::DxgiFromHwnd),
482+
_ => None,
483+
}
484+
}
485+
486+
/// Takes the given presentation system, modifies it based on the `WGPU_DX12_PRESENTATION_SYSTEM` environment variable, and returns the result.
487+
///
488+
/// See [`from_env`](Self::from_env) for more information.
489+
#[must_use]
490+
pub fn with_env(self) -> Self {
491+
if let Some(presentation_system) = Self::from_env() {
492+
presentation_system
493+
} else {
494+
self
495+
}
496+
}
497+
}
498+
442499
/// DXC shader model.
443500
#[derive(Clone, Debug)]
444501
#[allow(missing_docs)]

0 commit comments

Comments
 (0)