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 13 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions deno_webgpu/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
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
2 changes: 2 additions & 0 deletions wgpu-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
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 @@ -849,7 +849,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 @@ -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,
Expand Down
110 changes: 110 additions & 0 deletions wgpu-hal/src/dx12/dcomp.rs
Original file line number Diff line number Diff line change
@@ -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<InnerState>,
}

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<Self, crate::SurfaceError> {
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::<Dxgi::IDXGIDevice>().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,
})
}
}
26 changes: 19 additions & 7 deletions wgpu-hal/src/dx12/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -120,14 +121,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 @@ -457,6 +458,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 @@ -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<dcomp::DCompState>,
},
Visual(DirectComposition::IDCompositionVisual),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be combining these enums?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

honestly i dont know enough about it to say. i think what maybe would make sense is to have a Visual Enum one variant that just has Dcomp Visual and one that has the handle and other state needed

/// Borrowed, lifetime externally managed
SurfaceHandle(Foundation::HANDLE),
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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}");
Expand Down Expand Up @@ -1319,6 +1355,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 @@ -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 {
Expand All @@ -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,
}
}

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

Expand Down Expand Up @@ -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<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
Loading