From ffe5e261da0a6cb85332b82ab310abd2a7e849f6 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Tue, 8 Mar 2022 23:15:03 -0500 Subject: [PATCH] Adds D3D11 DXGI Support and COM Helper Chain (#37) * Add older dxgi methods * Add create_swapchain_for_composition * Add DXGIFactory6 * Write COM inheritance chain macro * Add DXGIAdapter chain * Make accessing base of inheritance chain easy * Add `mut_self` to WeakPtr * Add TryFrom for FeatureLevel * Fix compile error on 1.51 * Comments --- Cargo.toml | 2 +- src/com.rs | 163 +++++++++++++++++++++++++++++++++++++++- src/descriptor.rs | 1 + src/dxgi.rs | 184 ++++++++++++++++++++++++++++++++++++---------- src/lib.rs | 22 +++++- 5 files changed, 329 insertions(+), 43 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7e41e6d831..0a41a3d753 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ libloading = { version = "0.7", optional = true } [dependencies.winapi] version = "0.3" -features = ["dxgi1_2","dxgi1_3","dxgi1_4","dxgidebug","d3d12","d3d12sdklayers","d3dcommon","d3dcompiler","dxgiformat","synchapi","winerror"] +features = ["dxgi1_2","dxgi1_3","dxgi1_4","dxgi1_5","dxgi1_6","dxgidebug","d3d12","d3d12sdklayers","d3dcommon","d3dcompiler","dxgiformat","synchapi","winerror"] [package.metadata.docs.rs] default-target = "x86_64-pc-windows-msvc" diff --git a/src/com.rs b/src/com.rs index 7007060a13..f3a85789ce 100644 --- a/src/com.rs +++ b/src/com.rs @@ -31,9 +31,13 @@ impl WeakPtr { self.0 } - pub unsafe fn mut_void(&mut self) -> *mut *mut c_void { + pub fn mut_void(&mut self) -> *mut *mut c_void { &mut self.0 as *mut *mut _ as *mut *mut _ } + + pub fn mut_self(&mut self) -> *mut *mut T { + &mut self.0 as *mut *mut _ + } } impl WeakPtr { @@ -100,3 +104,160 @@ impl Hash for WeakPtr { self.0.hash(state); } } + +/// Macro that allows generation of an easy to use enum for dealing with many different possible versions of a COM object. +/// +/// Give the variants so that parents come before children. This often manifests as going up in order (1 -> 2 -> 3). This is vital for safety. +/// +/// Three function names need to be attached to each variant. The examples are given for the MyComObject1 variant below: +/// - the from function (`WeakPtr -> Self`) +/// - the as function (`&self -> Option>`) +/// - the unwrap function (`&self -> WeakPtr` panicing on failure to cast) +/// +/// ```rust +/// # pub use d3d12::weak_com_inheritance_chain; +/// # mod actual { +/// # pub struct ComObject; impl winapi::Interface for ComObject { fn uuidof() -> winapi::shared::guiddef::GUID { todo!() } } +/// # pub struct ComObject1; impl winapi::Interface for ComObject1 { fn uuidof() -> winapi::shared::guiddef::GUID { todo!() } } +/// # pub struct ComObject2; impl winapi::Interface for ComObject2 { fn uuidof() -> winapi::shared::guiddef::GUID { todo!() } } +/// # } +/// weak_com_inheritance_chain! { +/// pub enum MyComObject { +/// MyComObject(actual::ComObject), from_my_com_object, as_my_com_object, my_com_object; // First variant doesn't use "unwrap" as it can never fail +/// MyComObject1(actual::ComObject1), from_my_com_object1, as_my_com_object1, unwrap_my_com_object1; +/// MyComObject2(actual::ComObject2), from_my_com_object2, as_my_com_object2, unwrap_my_com_object2; +/// } +/// } +/// ``` +#[macro_export] +macro_rules! weak_com_inheritance_chain { + // We first match a human readable enum style, before going into the recursive section. + // + // Internal calls to the macro have either the prefix + // - @recursion_logic for the recursion and termination + // - @render_members for the actual call to fill in the members. + ( + $(#[$meta:meta])* + $vis:vis enum $name:ident { + $first_variant:ident($first_type:ty), $first_from_name:ident, $first_as_name:ident, $first_unwrap_name:ident $(;)? + $($variant:ident($type:ty), $from_name:ident, $as_name:ident, $unwrap_name:ident);* $(;)? + } + ) => { + $(#[$meta])* + $vis enum $name { + $first_variant($crate::WeakPtr<$first_type>), + $( + $variant($crate::WeakPtr<$type>) + ),+ + } + impl $name { + $vis unsafe fn destroy(&self) { + match *self { + Self::$first_variant(v) => v.destroy(), + $( + Self::$variant(v) => v.destroy(), + )* + } + } + + $crate::weak_com_inheritance_chain! { + @recursion_logic, + $vis, + ; + $first_variant($first_type), $first_from_name, $first_as_name, $first_unwrap_name; + $($variant($type), $from_name, $as_name, $unwrap_name);* + } + } + + impl std::ops::Deref for $name { + type Target = $crate::WeakPtr<$first_type>; + fn deref(&self) -> &Self::Target { + self.$first_unwrap_name() + } + } + }; + + // This is the iteration case of the recursion. We instantiate the member functions for the variant we + // are currently at, recursing on ourself for the next variant. Note we only keep track of the previous + // variant name, not the functions names, as those are not needed. + ( + @recursion_logic, + $vis:vis, + $(,)? $($prev_variant:ident),* $(,)?; + $this_variant:ident($this_type:ty), $this_from_name:ident, $this_as_name:ident, $this_unwrap_name:ident $(;)? + $($next_variant:ident($next_type:ty), $next_from_name:ident, $next_as_name:ident, $next_unwrap_name:ident);* + ) => { + // Actually generate the members for this variant. Needs the previous and future variant names. + $crate::weak_com_inheritance_chain! { + @render_members, + $vis, + $this_from_name, $this_as_name, $this_unwrap_name; + $($prev_variant),*; + $this_variant($this_type); + $($next_variant),*; + } + + // Recurse on ourselves. If there is no future variants left, we'll hit the base case as the final expansion returns no tokens. + $crate::weak_com_inheritance_chain! { + @recursion_logic, + $vis, + $($prev_variant),* , $this_variant; + $($next_variant($next_type), $next_from_name, $next_as_name, $next_unwrap_name);* + } + }; + // Base case for recursion. There are no more variants left + ( + @recursion_logic, + $vis:vis, + $($prev_variant:ident),*; + ) => {}; + + + // This is where we generate the members using the given names. + ( + @render_members, + $vis:vis, + $from_name:ident, $as_name:ident, $unwrap_name:ident; + $($prev_variant:ident),*; + $variant:ident($type:ty); + $($next_variant:ident),*; + ) => { + // Construct this enum from weak pointer to this interface. For best usability, always use the highest constructor you can. This doesn't try to upcast. + $vis unsafe fn $from_name(value: $crate::WeakPtr<$type>) -> Self { + Self::$variant(value) + } + + // Returns Some if the value implements the interface otherwise returns None. + $vis fn $as_name(&self) -> Option<&$crate::WeakPtr<$type>> { + match *self { + $( + Self::$prev_variant(_) => None, + )* + Self::$variant(ref v) => Some(v), + $( + Self::$next_variant(ref v) => { + // v is &WeakPtr and se cast to &WeakPtr + Some(unsafe { std::mem::transmute(v) }) + } + )* + } + } + + // Returns the interface if the value implements it, otherwise panics. + #[track_caller] + $vis fn $unwrap_name(&self) -> &$crate::WeakPtr<$type> { + match *self { + $( + Self::$prev_variant(_) => panic!(concat!("Tried to unwrap a ", stringify!($prev_variant), " as a ", stringify!($variant))), + )* + Self::$variant(ref v) => &*v, + $( + Self::$next_variant(ref v) => { + // v is &WeakPtr and se cast to &WeakPtr + unsafe { std::mem::transmute(v) } + } + )* + } + } + }; +} diff --git a/src/descriptor.rs b/src/descriptor.rs index bfa0204127..02ca2e83c5 100644 --- a/src/descriptor.rs +++ b/src/descriptor.rs @@ -156,6 +156,7 @@ impl RootParameter { impl fmt::Debug for RootParameter { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { #[derive(Debug)] + #[allow(dead_code)] // False-positive enum Inner<'a> { Table(&'a [DescriptorRange]), Constants { binding: Binding, num: u32 }, diff --git a/src/dxgi.rs b/src/dxgi.rs index 67c7489b39..0a66dcd027 100644 --- a/src/dxgi.rs +++ b/src/dxgi.rs @@ -1,8 +1,11 @@ -use crate::{com::WeakPtr, CommandQueue, D3DResult, Resource, SampleDesc, HRESULT}; +use crate::{com::WeakPtr, D3DResult, Resource, SampleDesc, HRESULT}; use std::ptr; use winapi::{ - shared::{dxgi, dxgi1_2, dxgi1_3, dxgi1_4, dxgiformat, dxgitype, windef::HWND}, - um::{d3d12, dxgidebug}, + shared::{ + dxgi, dxgi1_2, dxgi1_3, dxgi1_4, dxgi1_5, dxgi1_6, dxgiformat, dxgitype, minwindef::TRUE, + windef::HWND, + }, + um::{d3d12, dxgidebug, unknwnbase::IUnknown}, Interface, }; @@ -39,13 +42,53 @@ pub enum AlphaMode { ForceDword = dxgi1_2::DXGI_ALPHA_MODE_FORCE_DWORD, } -pub type Adapter1 = WeakPtr; -pub type Factory2 = WeakPtr; -pub type Factory4 = WeakPtr; pub type InfoQueue = WeakPtr; + +pub type Adapter1 = WeakPtr; +pub type Adapter2 = WeakPtr; +pub type Adapter3 = WeakPtr; +pub type Adapter4 = WeakPtr; +crate::weak_com_inheritance_chain! { + #[derive(Debug, Copy, Clone, PartialEq, Hash)] + pub enum DxgiAdapter { + Adapter1(dxgi::IDXGIAdapter1), from_adapter1, as_adapter1, adapter1; + Adapter2(dxgi1_2::IDXGIAdapter2), from_adapter2, as_adapter2, unwrap_adapter2; + Adapter3(dxgi1_4::IDXGIAdapter3), from_adapter3, as_adapter3, unwrap_adapter3; + Adapter4(dxgi1_6::IDXGIAdapter4), from_adapter4, as_adapter4, unwrap_adapter4; + } +} + +pub type Factory1 = WeakPtr; +pub type Factory2 = WeakPtr; +pub type Factory3 = WeakPtr; +pub type Factory4 = WeakPtr; +pub type Factory5 = WeakPtr; +pub type Factory6 = WeakPtr; +crate::weak_com_inheritance_chain! { + #[derive(Debug, Copy, Clone, PartialEq, Hash)] + pub enum DxgiFactory { + Factory1(dxgi::IDXGIFactory1), from_factory1, as_factory1, factory1; + Factory2(dxgi1_2::IDXGIFactory2), from_factory2, as_factory2, unwrap_factory2; + Factory3(dxgi1_3::IDXGIFactory3), from_factory3, as_factory3, unwrap_factory3; + Factory4(dxgi1_4::IDXGIFactory4), from_factory4, as_factory4, unwrap_factory4; + Factory5(dxgi1_5::IDXGIFactory5), from_factory5, as_factory5, unwrap_factory5; + Factory6(dxgi1_6::IDXGIFactory6), from_factory6, as_factory6, unwrap_factory6; + } +} + pub type SwapChain = WeakPtr; pub type SwapChain1 = WeakPtr; +pub type SwapChain2 = WeakPtr; pub type SwapChain3 = WeakPtr; +crate::weak_com_inheritance_chain! { + #[derive(Debug, Copy, Clone, PartialEq, Hash)] + pub enum DxgiSwapchain { + SwapChain(dxgi::IDXGISwapChain), from_swap_chain, as_swap_chain, swap_chain; + SwapChain1(dxgi1_2::IDXGISwapChain1), from_swap_chain1, as_swap_chain1, unwrap_swap_chain1; + SwapChain2(dxgi1_3::IDXGISwapChain2), from_swap_chain2, as_swap_chain2, unwrap_swap_chain2; + SwapChain3(dxgi1_4::IDXGISwapChain3), from_swap_chain3, as_swap_chain3, unwrap_swap_chain3; + } +} #[cfg(feature = "libloading")] #[derive(Debug)] @@ -82,6 +125,21 @@ impl DxgiLib { Ok((factory, hr)) } + pub fn create_factory1(&self) -> Result, libloading::Error> { + type Fun = extern "system" fn( + winapi::shared::guiddef::REFIID, + *mut *mut winapi::ctypes::c_void, + ) -> HRESULT; + + let mut factory = Factory1::null(); + let hr = unsafe { + let func: libloading::Symbol = self.lib.get(b"CreateDXGIFactory1")?; + func(&dxgi::IDXGIFactory1::uuidof(), factory.mut_void()) + }; + + Ok((factory, hr)) + } + pub fn get_debug_interface1(&self) -> Result, libloading::Error> { type Fun = extern "system" fn( winapi::shared::minwindef::UINT, @@ -112,38 +170,80 @@ pub struct SwapchainDesc { pub alpha_mode: AlphaMode, pub flags: u32, } +impl SwapchainDesc { + pub fn to_desc1(&self) -> dxgi1_2::DXGI_SWAP_CHAIN_DESC1 { + dxgi1_2::DXGI_SWAP_CHAIN_DESC1 { + AlphaMode: self.alpha_mode as _, + BufferCount: self.buffer_count, + Width: self.width, + Height: self.height, + Format: self.format, + Flags: self.flags, + BufferUsage: self.buffer_usage, + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: self.sample.count, + Quality: self.sample.quality, + }, + Scaling: self.scaling as _, + Stereo: self.stereo as _, + SwapEffect: self.swap_effect as _, + } + } +} + +impl Factory1 { + pub fn create_swapchain( + &self, + queue: *mut IUnknown, + hwnd: HWND, + desc: &SwapchainDesc, + ) -> D3DResult { + let mut desc = dxgi::DXGI_SWAP_CHAIN_DESC { + BufferDesc: dxgitype::DXGI_MODE_DESC { + Width: desc.width, + Height: desc.width, + RefreshRate: dxgitype::DXGI_RATIONAL { + Numerator: 1, + Denominator: 60, + }, + Format: desc.format, + ScanlineOrdering: dxgitype::DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, + Scaling: dxgitype::DXGI_MODE_SCALING_UNSPECIFIED, + }, + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: desc.sample.count, + Quality: desc.sample.quality, + }, + BufferUsage: desc.buffer_usage, + BufferCount: desc.buffer_count, + OutputWindow: hwnd, + Windowed: TRUE, + SwapEffect: desc.swap_effect as _, + Flags: desc.flags, + }; + + let mut swapchain = SwapChain::null(); + let hr = + unsafe { self.CreateSwapChain(queue, &mut desc, swapchain.mut_void() as *mut *mut _) }; + + (swapchain, hr) + } +} impl Factory2 { // TODO: interface not complete pub fn create_swapchain_for_hwnd( &self, - queue: CommandQueue, + queue: *mut IUnknown, hwnd: HWND, desc: &SwapchainDesc, ) -> D3DResult { - let desc = dxgi1_2::DXGI_SWAP_CHAIN_DESC1 { - AlphaMode: desc.alpha_mode as _, - BufferCount: desc.buffer_count, - Width: desc.width, - Height: desc.height, - Format: desc.format, - Flags: desc.flags, - BufferUsage: desc.buffer_usage, - SampleDesc: dxgitype::DXGI_SAMPLE_DESC { - Count: desc.sample.count, - Quality: desc.sample.quality, - }, - Scaling: desc.scaling as _, - Stereo: desc.stereo as _, - SwapEffect: desc.swap_effect as _, - }; - let mut swap_chain = SwapChain1::null(); let hr = unsafe { self.CreateSwapChainForHwnd( - queue.as_mut_ptr() as *mut _, + queue, hwnd, - &desc, + &desc.to_desc1(), ptr::null(), ptr::null_mut(), swap_chain.mut_void() as *mut *mut _, @@ -152,6 +252,24 @@ impl Factory2 { (swap_chain, hr) } + + pub fn create_swapchain_for_composition( + &self, + queue: *mut IUnknown, + desc: &SwapchainDesc, + ) -> D3DResult { + let mut swap_chain = SwapChain1::null(); + let hr = unsafe { + self.CreateSwapChainForComposition( + queue, + &desc.to_desc1(), + ptr::null_mut(), + swap_chain.mut_void() as *mut *mut _, + ) + }; + + (swap_chain, hr) + } } impl Factory4 { @@ -169,10 +287,6 @@ impl Factory4 { (factory, hr) } - pub fn as_factory2(&self) -> Factory2 { - unsafe { Factory2::from_raw(self.as_mut_ptr() as *mut _) } - } - pub fn enumerate_adapters(&self, id: u32) -> D3DResult { let mut adapter = Adapter1::null(); let hr = unsafe { self.EnumAdapters1(id, adapter.mut_void() as *mut *mut _) }; @@ -214,17 +328,7 @@ impl SwapChain { } } -impl SwapChain1 { - pub fn as_swapchain0(&self) -> SwapChain { - unsafe { SwapChain::from_raw(self.as_mut_ptr() as *mut _) } - } -} - impl SwapChain3 { - pub fn as_swapchain0(&self) -> SwapChain { - unsafe { SwapChain::from_raw(self.as_mut_ptr() as *mut _) } - } - pub fn get_current_back_buffer_index(&self) -> u32 { unsafe { self.GetCurrentBackBufferIndex() } } diff --git a/src/lib.rs b/src/lib.rs index 49efc7ebcf..e52f9d9d46 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate bitflags; -use std::ffi::CStr; +use std::{convert::TryFrom, ffi::CStr}; use winapi::{ shared::dxgiformat, um::{d3d12, d3dcommon}, @@ -64,6 +64,7 @@ pub struct SampleDesc { } #[repr(u32)] +#[non_exhaustive] pub enum FeatureLevel { L9_1 = d3dcommon::D3D_FEATURE_LEVEL_9_1, L9_2 = d3dcommon::D3D_FEATURE_LEVEL_9_2, @@ -76,6 +77,25 @@ pub enum FeatureLevel { L12_1 = d3dcommon::D3D_FEATURE_LEVEL_12_1, } +impl TryFrom for FeatureLevel { + type Error = (); + + fn try_from(value: u32) -> Result { + Ok(match value { + d3dcommon::D3D_FEATURE_LEVEL_9_1 => Self::L9_1, + d3dcommon::D3D_FEATURE_LEVEL_9_2 => Self::L9_2, + d3dcommon::D3D_FEATURE_LEVEL_9_3 => Self::L9_3, + d3dcommon::D3D_FEATURE_LEVEL_10_0 => Self::L10_0, + d3dcommon::D3D_FEATURE_LEVEL_10_1 => Self::L10_1, + d3dcommon::D3D_FEATURE_LEVEL_11_0 => Self::L11_0, + d3dcommon::D3D_FEATURE_LEVEL_11_1 => Self::L11_1, + d3dcommon::D3D_FEATURE_LEVEL_12_0 => Self::L12_0, + d3dcommon::D3D_FEATURE_LEVEL_12_1 => Self::L12_1, + _ => return Err(()), + }) + } +} + pub type Blob = WeakPtr; pub type Error = WeakPtr;