diff --git a/d3d12/.gitignore b/d3d12/.gitignore new file mode 100644 index 000000000..cbed4d71a --- /dev/null +++ b/d3d12/.gitignore @@ -0,0 +1,12 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +.idea/ diff --git a/d3d12/CHANGELOG.md b/d3d12/CHANGELOG.md new file mode 100644 index 000000000..6af566ae6 --- /dev/null +++ b/d3d12/CHANGELOG.md @@ -0,0 +1,32 @@ +# Change Log + +## v0.6.0 (2023-01-25) + - add helpers for IDXGIFactoryMedia + - add `create_swapchain_for_composition_surface_handle` + +## v0.5.0 (2022-07-01) + - add COM helpers + - enable D3D11 adapter use + +## v0.4.1 (2021-08-18) + - expose all indirect argument types + - expose methods for setting root constants + +## v0.4.0 (2021-04-29) + - update `libloading` to 0.7 + +## v0.3.1 (2020-07-07) + - create shader from IL + - fix default doc target + - debug impl for root descriptors + +## v0.3.0 (2019-11-01) + - resource transitions + - dynamic library loading + +## v0.2.2 (2019-10-04) + - add `D3DHeap` + - add root descriptor + +## v0.1.0 (2018-12-26) + - basic version diff --git a/d3d12/Cargo.toml b/d3d12/Cargo.toml new file mode 100644 index 000000000..dbb116eb9 --- /dev/null +++ b/d3d12/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "d3d12" +version = "0.7.0" +authors = [ + "gfx-rs developers", +] +description = "Low level D3D12 API wrapper" +repository = "https://github.com/gfx-rs/d3d12-rs" +keywords = ["windows", "graphics"] +license = "MIT OR Apache-2.0" +documentation = "https://docs.rs/d3d12" +categories = ["api-bindings", "graphics", "memory-management", "os::windows-apis"] +edition = "2018" + +[features] +implicit-link = [] + +[dependencies] +bitflags = "2" +# libloading 0.8 switches from `winapi` to `windows-sys`; permit either +libloading = { version = ">=0.7,<0.9", optional = true } + +[dependencies.winapi] +version = "0.3" +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/d3d12/README.md b/d3d12/README.md new file mode 100644 index 000000000..a1e1fd661 --- /dev/null +++ b/d3d12/README.md @@ -0,0 +1,6 @@ +# d3d12-rs +[![Crates.io](https://img.shields.io/crates/v/d3d12.svg)](https://crates.io/crates/d3d12) +[![Docs.rs](https://docs.rs/d3d12/badge.svg)](https://docs.rs/d3d12) +[![Matrix](https://img.shields.io/matrix/gfx:matrix.org)](https://matrix.to/#/#gfx:matrix.org) + +Rust wrapper for raw D3D12 access. diff --git a/d3d12/appveyor.yml b/d3d12/appveyor.yml new file mode 100644 index 000000000..b947c526b --- /dev/null +++ b/d3d12/appveyor.yml @@ -0,0 +1,29 @@ +skip_branch_with_pr: true +branches: + except: + - staging.tmp +environment: + global: + PATH: '%PATH%;C:\msys64\mingw64\bin;C:\msys64\usr\bin;%USERPROFILE%\.cargo\bin' + RUST_BACKTRACE: full + matrix: + - CHANNEL: stable + TARGET: x86_64-pc-windows-msvc + +skip_commits: + files: + - bors.toml + - '*.md' + +install: + - curl -sSf -o rustup-init.exe https://win.rustup.rs + - rustup-init -yv --default-toolchain %CHANNEL% --default-host %TARGET% + - rustc -vV + - cargo -vV + +build: false +test_script: + - cargo check + - cargo check --features libloading + - cargo check --features implicit-link + - cargo check --all-features diff --git a/d3d12/bors.toml b/d3d12/bors.toml new file mode 100644 index 000000000..fc646ce92 --- /dev/null +++ b/d3d12/bors.toml @@ -0,0 +1,5 @@ +status = [ + "continuous-integration/appveyor/branch" +] + +timeout_sec = 18000 # 5 hours diff --git a/d3d12/rustfmt.toml b/d3d12/rustfmt.toml new file mode 100644 index 000000000..e69de29bb diff --git a/d3d12/src/com.rs b/d3d12/src/com.rs new file mode 100644 index 000000000..66b338c9e --- /dev/null +++ b/d3d12/src/com.rs @@ -0,0 +1,259 @@ +use crate::D3DResult; +use std::{ + fmt, + hash::{Hash, Hasher}, + ops::Deref, + ptr, +}; +use winapi::{ctypes::c_void, um::unknwnbase::IUnknown, Interface}; + +#[repr(transparent)] +pub struct ComPtr(*mut T); + +impl ComPtr { + pub fn null() -> Self { + ComPtr(ptr::null_mut()) + } + + pub unsafe fn from_raw(raw: *mut T) -> Self { + if !raw.is_null() { + (&*(raw as *mut IUnknown)).AddRef(); + } + ComPtr(raw) + } + + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + pub fn as_ptr(&self) -> *const T { + self.0 + } + + pub fn as_mut_ptr(&self) -> *mut T { + self.0 + } + + 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 ComPtr { + pub unsafe fn as_unknown(&self) -> &IUnknown { + debug_assert!(!self.is_null()); + &*(self.0 as *mut IUnknown) + } + + pub unsafe fn cast(&self) -> D3DResult> + where + U: Interface, + { + debug_assert!(!self.is_null()); + let mut obj = ComPtr::::null(); + let hr = self + .as_unknown() + .QueryInterface(&U::uuidof(), obj.mut_void()); + (obj, hr) + } +} + +impl Clone for ComPtr { + fn clone(&self) -> Self { + debug_assert!(!self.is_null()); + unsafe { self.as_unknown().AddRef(); } + ComPtr(self.0) + } +} + +impl Drop for ComPtr { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { self.as_unknown().Release(); } + } + } +} + +impl Deref for ComPtr { + type Target = T; + fn deref(&self) -> &T { + debug_assert!(!self.is_null()); + unsafe { &*self.0 } + } +} + +impl fmt::Debug for ComPtr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ComPtr( ptr: {:?} )", self.0) + } +} + +impl PartialEq<*mut T> for ComPtr { + fn eq(&self, other: &*mut T) -> bool { + self.0 == *other + } +} + +impl PartialEq for ComPtr { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Hash for ComPtr { + fn hash(&self, state: &mut H) { + 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 (`ComPtr -> Self`) +/// - the as function (`&self -> Option>`) +/// - the unwrap function (`&self -> ComPtr` 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::ComPtr<$first_type>), + $( + $variant($crate::ComPtr<$type>) + ),+ + } + impl $name { + $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::ComPtr<$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::ComPtr<$type>) -> Self { + Self::$variant(value) + } + + // Returns Some if the value implements the interface otherwise returns None. + $vis fn $as_name(&self) -> Option<&$crate::ComPtr<$type>> { + match *self { + $( + Self::$prev_variant(_) => None, + )* + Self::$variant(ref v) => Some(v), + $( + Self::$next_variant(ref v) => { + // v is &ComPtr and se cast to &ComPtr + Some(unsafe { std::mem::transmute(v) }) + } + )* + } + } + + // Returns the interface if the value implements it, otherwise panics. + #[track_caller] + $vis fn $unwrap_name(&self) -> &$crate::ComPtr<$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 &ComPtr and se cast to &ComPtr + unsafe { std::mem::transmute(v) } + } + )* + } + } + }; +} diff --git a/d3d12/src/command_allocator.rs b/d3d12/src/command_allocator.rs new file mode 100644 index 000000000..b50ec00d4 --- /dev/null +++ b/d3d12/src/command_allocator.rs @@ -0,0 +1,14 @@ +//! Command Allocator + +use crate::com::ComPtr; +use winapi::um::d3d12; + +pub type CommandAllocator = ComPtr; + +impl CommandAllocator { + pub fn reset(&self) { + unsafe { + self.Reset(); + } + } +} diff --git a/d3d12/src/command_list.rs b/d3d12/src/command_list.rs new file mode 100644 index 000000000..015d5788b --- /dev/null +++ b/d3d12/src/command_list.rs @@ -0,0 +1,406 @@ +//! Graphics command list + +use crate::{ + com::ComPtr, resource::DiscardRegion, CommandAllocator, CpuDescriptor, DescriptorHeap, Format, + GpuAddress, GpuDescriptor, IndexCount, InstanceCount, PipelineState, Rect, Resource, RootIndex, + RootSignature, Subresource, VertexCount, VertexOffset, WorkGroupCount, HRESULT, +}; +use std::{mem, ptr}; +use winapi::um::d3d12; + +#[repr(u32)] +#[derive(Clone, Copy)] +pub enum CmdListType { + Direct = d3d12::D3D12_COMMAND_LIST_TYPE_DIRECT, + Bundle = d3d12::D3D12_COMMAND_LIST_TYPE_BUNDLE, + Compute = d3d12::D3D12_COMMAND_LIST_TYPE_COMPUTE, + Copy = d3d12::D3D12_COMMAND_LIST_TYPE_COPY, + // VideoDecode = d3d12::D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE, + // VideoProcess = d3d12::D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS, +} + +bitflags! { + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct ClearFlags: u32 { + const DEPTH = d3d12::D3D12_CLEAR_FLAG_DEPTH; + const STENCIL = d3d12::D3D12_CLEAR_FLAG_STENCIL; + } +} + +#[repr(transparent)] +pub struct IndirectArgument(d3d12::D3D12_INDIRECT_ARGUMENT_DESC); + +impl IndirectArgument { + pub fn draw() -> Self { + IndirectArgument(d3d12::D3D12_INDIRECT_ARGUMENT_DESC { + Type: d3d12::D3D12_INDIRECT_ARGUMENT_TYPE_DRAW, + ..unsafe { mem::zeroed() } + }) + } + + pub fn draw_indexed() -> Self { + IndirectArgument(d3d12::D3D12_INDIRECT_ARGUMENT_DESC { + Type: d3d12::D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED, + ..unsafe { mem::zeroed() } + }) + } + + pub fn dispatch() -> Self { + IndirectArgument(d3d12::D3D12_INDIRECT_ARGUMENT_DESC { + Type: d3d12::D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH, + ..unsafe { mem::zeroed() } + }) + } + + pub fn vertex_buffer(slot: u32) -> Self { + let mut desc = d3d12::D3D12_INDIRECT_ARGUMENT_DESC { + Type: d3d12::D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW, + ..unsafe { mem::zeroed() } + }; + *unsafe { desc.u.VertexBuffer_mut() } = + d3d12::D3D12_INDIRECT_ARGUMENT_DESC_VertexBuffer { Slot: slot }; + IndirectArgument(desc) + } + + pub fn constant(root_index: RootIndex, dest_offset_words: u32, count: u32) -> Self { + let mut desc = d3d12::D3D12_INDIRECT_ARGUMENT_DESC { + Type: d3d12::D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT, + ..unsafe { mem::zeroed() } + }; + *unsafe { desc.u.Constant_mut() } = d3d12::D3D12_INDIRECT_ARGUMENT_DESC_Constant { + RootParameterIndex: root_index, + DestOffsetIn32BitValues: dest_offset_words, + Num32BitValuesToSet: count, + }; + IndirectArgument(desc) + } + + pub fn constant_buffer_view(root_index: RootIndex) -> Self { + let mut desc = d3d12::D3D12_INDIRECT_ARGUMENT_DESC { + Type: d3d12::D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW, + ..unsafe { mem::zeroed() } + }; + *unsafe { desc.u.ConstantBufferView_mut() } = + d3d12::D3D12_INDIRECT_ARGUMENT_DESC_ConstantBufferView { + RootParameterIndex: root_index, + }; + IndirectArgument(desc) + } + + pub fn shader_resource_view(root_index: RootIndex) -> Self { + let mut desc = d3d12::D3D12_INDIRECT_ARGUMENT_DESC { + Type: d3d12::D3D12_INDIRECT_ARGUMENT_TYPE_SHADER_RESOURCE_VIEW, + ..unsafe { mem::zeroed() } + }; + *unsafe { desc.u.ShaderResourceView_mut() } = + d3d12::D3D12_INDIRECT_ARGUMENT_DESC_ShaderResourceView { + RootParameterIndex: root_index, + }; + IndirectArgument(desc) + } + + pub fn unordered_access_view(root_index: RootIndex) -> Self { + let mut desc = d3d12::D3D12_INDIRECT_ARGUMENT_DESC { + Type: d3d12::D3D12_INDIRECT_ARGUMENT_TYPE_UNORDERED_ACCESS_VIEW, + ..unsafe { mem::zeroed() } + }; + *unsafe { desc.u.UnorderedAccessView_mut() } = + d3d12::D3D12_INDIRECT_ARGUMENT_DESC_UnorderedAccessView { + RootParameterIndex: root_index, + }; + IndirectArgument(desc) + } +} + +#[repr(transparent)] +pub struct ResourceBarrier(d3d12::D3D12_RESOURCE_BARRIER); + +impl ResourceBarrier { + pub fn transition( + resource: Resource, + subresource: Subresource, + state_before: d3d12::D3D12_RESOURCE_STATES, + state_after: d3d12::D3D12_RESOURCE_STATES, + flags: d3d12::D3D12_RESOURCE_BARRIER_FLAGS, + ) -> Self { + let mut barrier = d3d12::D3D12_RESOURCE_BARRIER { + Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + Flags: flags, + ..unsafe { mem::zeroed() } + }; + unsafe { + *barrier.u.Transition_mut() = d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: resource.as_mut_ptr(), + Subresource: subresource, + StateBefore: state_before, + StateAfter: state_after, + }; + } + ResourceBarrier(barrier) + } +} + +pub type CommandSignature = ComPtr; +pub type CommandList = ComPtr; +pub type GraphicsCommandList = ComPtr; + +impl GraphicsCommandList { + pub fn as_list(&self) -> CommandList { + unsafe { CommandList::from_raw(self.as_mut_ptr() as *mut _) } + } + + pub fn close(&self) -> HRESULT { + unsafe { self.Close() } + } + + pub fn reset(&self, allocator: &CommandAllocator, initial_pso: PipelineState) -> HRESULT { + unsafe { self.Reset(allocator.as_mut_ptr(), initial_pso.as_mut_ptr()) } + } + + pub fn discard_resource(&self, resource: Resource, region: DiscardRegion) { + debug_assert!(region.subregions.start < region.subregions.end); + unsafe { + self.DiscardResource( + resource.as_mut_ptr(), + &d3d12::D3D12_DISCARD_REGION { + NumRects: region.rects.len() as _, + pRects: region.rects.as_ptr(), + FirstSubresource: region.subregions.start, + NumSubresources: region.subregions.end - region.subregions.start - 1, + }, + ); + } + } + + pub fn clear_depth_stencil_view( + &self, + dsv: CpuDescriptor, + flags: ClearFlags, + depth: f32, + stencil: u8, + rects: &[Rect], + ) { + let num_rects = rects.len() as _; + let rects = if num_rects > 0 { + rects.as_ptr() + } else { + ptr::null() + }; + unsafe { + self.ClearDepthStencilView(dsv, flags.bits(), depth, stencil, num_rects, rects); + } + } + + pub fn clear_render_target_view(&self, rtv: CpuDescriptor, color: [f32; 4], rects: &[Rect]) { + let num_rects = rects.len() as _; + let rects = if num_rects > 0 { + rects.as_ptr() + } else { + ptr::null() + }; + unsafe { + self.ClearRenderTargetView(rtv, &color, num_rects, rects); + } + } + + pub fn dispatch(&self, count: WorkGroupCount) { + unsafe { + self.Dispatch(count[0], count[1], count[2]); + } + } + + pub fn draw( + &self, + num_vertices: VertexCount, + num_instances: InstanceCount, + start_vertex: VertexCount, + start_instance: InstanceCount, + ) { + unsafe { + self.DrawInstanced(num_vertices, num_instances, start_vertex, start_instance); + } + } + + pub fn draw_indexed( + &self, + num_indices: IndexCount, + num_instances: InstanceCount, + start_index: IndexCount, + base_vertex: VertexOffset, + start_instance: InstanceCount, + ) { + unsafe { + self.DrawIndexedInstanced( + num_indices, + num_instances, + start_index, + base_vertex, + start_instance, + ); + } + } + + pub fn set_index_buffer(&self, gpu_address: GpuAddress, size: u32, format: Format) { + let ibv = d3d12::D3D12_INDEX_BUFFER_VIEW { + BufferLocation: gpu_address, + SizeInBytes: size, + Format: format, + }; + unsafe { + self.IASetIndexBuffer(&ibv); + } + } + + pub fn set_blend_factor(&self, factor: [f32; 4]) { + unsafe { + self.OMSetBlendFactor(&factor); + } + } + + pub fn set_stencil_reference(&self, reference: u32) { + unsafe { + self.OMSetStencilRef(reference); + } + } + + pub fn set_pipeline_state(&self, pso:&PipelineState) { + unsafe { + self.SetPipelineState(pso.as_mut_ptr()); + } + } + + pub fn execute_bundle(&self, bundle: GraphicsCommandList) { + unsafe { + self.ExecuteBundle(bundle.as_mut_ptr()); + } + } + + pub fn set_descriptor_heaps(&self, heaps: &[DescriptorHeap]) { + unsafe { + self.SetDescriptorHeaps( + heaps.len() as _, + heaps.as_ptr() as *mut &DescriptorHeap as *mut _, + ); + } + } + + pub fn set_compute_root_signature(&self, signature: &RootSignature) { + unsafe { + self.SetComputeRootSignature(signature.as_mut_ptr()); + } + } + + pub fn set_graphics_root_signature(&self, signature: &RootSignature) { + unsafe { + self.SetGraphicsRootSignature(signature.as_mut_ptr()); + } + } + + pub fn set_compute_root_descriptor_table( + &self, + root_index: RootIndex, + base_descriptor: GpuDescriptor, + ) { + unsafe { + self.SetComputeRootDescriptorTable(root_index, base_descriptor); + } + } + + pub fn set_compute_root_constant_buffer_view( + &self, + root_index: RootIndex, + buffer_location: GpuAddress, + ) { + unsafe { + self.SetComputeRootConstantBufferView(root_index, buffer_location); + } + } + + pub fn set_compute_root_shader_resource_view( + &self, + root_index: RootIndex, + buffer_location: GpuAddress, + ) { + unsafe { + self.SetComputeRootShaderResourceView(root_index, buffer_location); + } + } + + pub fn set_compute_root_unordered_access_view( + &self, + root_index: RootIndex, + buffer_location: GpuAddress, + ) { + unsafe { + self.SetComputeRootUnorderedAccessView(root_index, buffer_location); + } + } + + pub fn set_compute_root_constant( + &self, + root_index: RootIndex, + value: u32, + dest_offset_words: u32, + ) { + unsafe { + self.SetComputeRoot32BitConstant(root_index, value, dest_offset_words); + } + } + + pub fn set_graphics_root_descriptor_table( + &self, + root_index: RootIndex, + base_descriptor: GpuDescriptor, + ) { + unsafe { + self.SetGraphicsRootDescriptorTable(root_index, base_descriptor); + } + } + + pub fn set_graphics_root_constant_buffer_view( + &self, + root_index: RootIndex, + buffer_location: GpuAddress, + ) { + unsafe { + self.SetGraphicsRootConstantBufferView(root_index, buffer_location); + } + } + + pub fn set_graphics_root_shader_resource_view( + &self, + root_index: RootIndex, + buffer_location: GpuAddress, + ) { + unsafe { + self.SetGraphicsRootShaderResourceView(root_index, buffer_location); + } + } + + pub fn set_graphics_root_unordered_access_view( + &self, + root_index: RootIndex, + buffer_location: GpuAddress, + ) { + unsafe { + self.SetGraphicsRootUnorderedAccessView(root_index, buffer_location); + } + } + + pub fn set_graphics_root_constant( + &self, + root_index: RootIndex, + value: u32, + dest_offset_words: u32, + ) { + unsafe { + self.SetGraphicsRoot32BitConstant(root_index, value, dest_offset_words); + } + } + + pub fn resource_barrier(&self, barriers: &[ResourceBarrier]) { + unsafe { + self.ResourceBarrier(barriers.len() as _, barriers.as_ptr() as _) // matches representation + } + } +} diff --git a/d3d12/src/debug.rs b/d3d12/src/debug.rs new file mode 100644 index 000000000..3a6abc46b --- /dev/null +++ b/d3d12/src/debug.rs @@ -0,0 +1,43 @@ +use crate::com::ComPtr; +use winapi::um::d3d12sdklayers; +#[cfg(any(feature = "libloading", feature = "implicit-link"))] +use winapi::Interface as _; + +pub type Debug = ComPtr; + +#[cfg(feature = "libloading")] +impl crate::D3D12Lib { + pub fn get_debug_interface(&self) -> Result, libloading::Error> { + type Fun = extern "system" fn( + winapi::shared::guiddef::REFIID, + *mut *mut winapi::ctypes::c_void, + ) -> crate::HRESULT; + + let mut debug = Debug::null(); + let hr = unsafe { + let func: libloading::Symbol = self.lib.get(b"D3D12GetDebugInterface")?; + func(&d3d12sdklayers::ID3D12Debug::uuidof(), debug.mut_void()) + }; + + Ok((debug, hr)) + } +} + +impl Debug { + #[cfg(feature = "implicit-link")] + pub fn get_interface() -> crate::D3DResult { + let mut debug = Debug::null(); + let hr = unsafe { + winapi::um::d3d12::D3D12GetDebugInterface( + &d3d12sdklayers::ID3D12Debug::uuidof(), + debug.mut_void(), + ) + }; + + (debug, hr) + } + + pub fn enable_layer(&self) { + unsafe { self.EnableDebugLayer() } + } +} diff --git a/d3d12/src/descriptor.rs b/d3d12/src/descriptor.rs new file mode 100644 index 000000000..5bb1b5b35 --- /dev/null +++ b/d3d12/src/descriptor.rs @@ -0,0 +1,362 @@ +use crate::{com::ComPtr, Blob, D3DResult, Error, TextureAddressMode}; +use std::{fmt, mem, ops::Range}; +use winapi::{shared::dxgiformat, um::d3d12}; + +pub type CpuDescriptor = d3d12::D3D12_CPU_DESCRIPTOR_HANDLE; +pub type GpuDescriptor = d3d12::D3D12_GPU_DESCRIPTOR_HANDLE; + +#[derive(Clone, Copy, Debug)] +pub struct Binding { + pub space: u32, + pub register: u32, +} + +#[repr(u32)] +#[derive(Clone, Copy, Debug)] +pub enum DescriptorHeapType { + CbvSrvUav = d3d12::D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + Sampler = d3d12::D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, + Rtv = d3d12::D3D12_DESCRIPTOR_HEAP_TYPE_RTV, + Dsv = d3d12::D3D12_DESCRIPTOR_HEAP_TYPE_DSV, +} + +bitflags! { + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct DescriptorHeapFlags: u32 { + const SHADER_VISIBLE = d3d12::D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + } +} + +pub type DescriptorHeap = ComPtr; + +impl DescriptorHeap { + pub fn start_cpu_descriptor(&self) -> CpuDescriptor { + unsafe { self.GetCPUDescriptorHandleForHeapStart() } + } + + pub fn start_gpu_descriptor(&self) -> GpuDescriptor { + unsafe { self.GetGPUDescriptorHandleForHeapStart() } + } +} + +#[repr(u32)] +#[derive(Clone, Copy, Debug)] +pub enum ShaderVisibility { + All = d3d12::D3D12_SHADER_VISIBILITY_ALL, + VS = d3d12::D3D12_SHADER_VISIBILITY_VERTEX, + HS = d3d12::D3D12_SHADER_VISIBILITY_HULL, + DS = d3d12::D3D12_SHADER_VISIBILITY_DOMAIN, + GS = d3d12::D3D12_SHADER_VISIBILITY_GEOMETRY, + PS = d3d12::D3D12_SHADER_VISIBILITY_PIXEL, +} + +#[repr(u32)] +#[derive(Clone, Copy, Debug)] +pub enum DescriptorRangeType { + SRV = d3d12::D3D12_DESCRIPTOR_RANGE_TYPE_SRV, + UAV = d3d12::D3D12_DESCRIPTOR_RANGE_TYPE_UAV, + CBV = d3d12::D3D12_DESCRIPTOR_RANGE_TYPE_CBV, + Sampler = d3d12::D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, +} + +#[repr(transparent)] +pub struct DescriptorRange(d3d12::D3D12_DESCRIPTOR_RANGE); +impl DescriptorRange { + pub fn new(ty: DescriptorRangeType, count: u32, base_binding: Binding, offset: u32) -> Self { + DescriptorRange(d3d12::D3D12_DESCRIPTOR_RANGE { + RangeType: ty as _, + NumDescriptors: count, + BaseShaderRegister: base_binding.register, + RegisterSpace: base_binding.space, + OffsetInDescriptorsFromTableStart: offset, + }) + } +} + +impl fmt::Debug for DescriptorRange { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter + .debug_struct("DescriptorRange") + .field("range_type", &self.0.RangeType) + .field("num", &self.0.NumDescriptors) + .field("register_space", &self.0.RegisterSpace) + .field("base_register", &self.0.BaseShaderRegister) + .field("table_offset", &self.0.OffsetInDescriptorsFromTableStart) + .finish() + } +} + +#[repr(transparent)] +pub struct RootParameter(d3d12::D3D12_ROOT_PARAMETER); +impl RootParameter { + // TODO: DescriptorRange must outlive Self + pub fn descriptor_table(visibility: ShaderVisibility, ranges: &[DescriptorRange]) -> Self { + let mut param = d3d12::D3D12_ROOT_PARAMETER { + ParameterType: d3d12::D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE, + ShaderVisibility: visibility as _, + ..unsafe { mem::zeroed() } + }; + + *unsafe { param.u.DescriptorTable_mut() } = d3d12::D3D12_ROOT_DESCRIPTOR_TABLE { + NumDescriptorRanges: ranges.len() as _, + pDescriptorRanges: ranges.as_ptr() as *const _, + }; + + RootParameter(param) + } + + pub fn constants(visibility: ShaderVisibility, binding: Binding, num: u32) -> Self { + let mut param = d3d12::D3D12_ROOT_PARAMETER { + ParameterType: d3d12::D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, + ShaderVisibility: visibility as _, + ..unsafe { mem::zeroed() } + }; + + *unsafe { param.u.Constants_mut() } = d3d12::D3D12_ROOT_CONSTANTS { + ShaderRegister: binding.register, + RegisterSpace: binding.space, + Num32BitValues: num, + }; + + RootParameter(param) + } + + //TODO: should this be unsafe? + pub fn descriptor( + ty: d3d12::D3D12_ROOT_PARAMETER_TYPE, + visibility: ShaderVisibility, + binding: Binding, + ) -> Self { + let mut param = d3d12::D3D12_ROOT_PARAMETER { + ParameterType: ty, + ShaderVisibility: visibility as _, + ..unsafe { mem::zeroed() } + }; + + *unsafe { param.u.Descriptor_mut() } = d3d12::D3D12_ROOT_DESCRIPTOR { + ShaderRegister: binding.register, + RegisterSpace: binding.space, + }; + + RootParameter(param) + } + + pub fn cbv_descriptor(visibility: ShaderVisibility, binding: Binding) -> Self { + Self::descriptor(d3d12::D3D12_ROOT_PARAMETER_TYPE_CBV, visibility, binding) + } + + pub fn srv_descriptor(visibility: ShaderVisibility, binding: Binding) -> Self { + Self::descriptor(d3d12::D3D12_ROOT_PARAMETER_TYPE_SRV, visibility, binding) + } + + pub fn uav_descriptor(visibility: ShaderVisibility, binding: Binding) -> Self { + Self::descriptor(d3d12::D3D12_ROOT_PARAMETER_TYPE_UAV, visibility, binding) + } +} + +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 }, + SingleCbv(Binding), + SingleSrv(Binding), + SingleUav(Binding), + } + let kind = match self.0.ParameterType { + d3d12::D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE => unsafe { + let raw = self.0.u.DescriptorTable(); + Inner::Table(std::slice::from_raw_parts( + raw.pDescriptorRanges as *const _, + raw.NumDescriptorRanges as usize, + )) + }, + d3d12::D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS => unsafe { + let raw = self.0.u.Constants(); + Inner::Constants { + binding: Binding { + space: raw.RegisterSpace, + register: raw.ShaderRegister, + }, + num: raw.Num32BitValues, + } + }, + _ => unsafe { + let raw = self.0.u.Descriptor(); + let binding = Binding { + space: raw.RegisterSpace, + register: raw.ShaderRegister, + }; + match self.0.ParameterType { + d3d12::D3D12_ROOT_PARAMETER_TYPE_CBV => Inner::SingleCbv(binding), + d3d12::D3D12_ROOT_PARAMETER_TYPE_SRV => Inner::SingleSrv(binding), + d3d12::D3D12_ROOT_PARAMETER_TYPE_UAV => Inner::SingleUav(binding), + other => panic!("Unexpected type {:?}", other), + } + }, + }; + + formatter + .debug_struct("RootParameter") + .field("visibility", &self.0.ShaderVisibility) + .field("kind", &kind) + .finish() + } +} + +#[repr(u32)] +#[derive(Copy, Clone, Debug)] +pub enum StaticBorderColor { + TransparentBlack = d3d12::D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK, + OpaqueBlack = d3d12::D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK, + OpaqueWhite = d3d12::D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, +} + +#[repr(transparent)] +pub struct StaticSampler(d3d12::D3D12_STATIC_SAMPLER_DESC); +impl StaticSampler { + pub fn new( + visibility: ShaderVisibility, + binding: Binding, + filter: d3d12::D3D12_FILTER, + address_mode: TextureAddressMode, + mip_lod_bias: f32, + max_anisotropy: u32, + comparison_op: d3d12::D3D12_COMPARISON_FUNC, + border_color: StaticBorderColor, + lod: Range, + ) -> Self { + StaticSampler(d3d12::D3D12_STATIC_SAMPLER_DESC { + Filter: filter, + AddressU: address_mode[0], + AddressV: address_mode[1], + AddressW: address_mode[2], + MipLODBias: mip_lod_bias, + MaxAnisotropy: max_anisotropy, + ComparisonFunc: comparison_op, + BorderColor: border_color as _, + MinLOD: lod.start, + MaxLOD: lod.end, + ShaderRegister: binding.register, + RegisterSpace: binding.space, + ShaderVisibility: visibility as _, + }) + } +} + +#[repr(u32)] +#[derive(Copy, Clone, Debug)] +pub enum RootSignatureVersion { + V1_0 = d3d12::D3D_ROOT_SIGNATURE_VERSION_1_0, + V1_1 = d3d12::D3D_ROOT_SIGNATURE_VERSION_1_1, +} + +bitflags! { + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct RootSignatureFlags: u32 { + const ALLOW_IA_INPUT_LAYOUT = d3d12::D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; + const DENY_VS_ROOT_ACCESS = d3d12::D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS; + const DENY_HS_ROOT_ACCESS = d3d12::D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS; + const DENY_DS_ROOT_ACCESS = d3d12::D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS; + const DENY_GS_ROOT_ACCESS = d3d12::D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS; + const DENY_PS_ROOT_ACCESS = d3d12::D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS; + } +} + +pub type RootSignature = ComPtr; +pub type BlobResult = D3DResult<(Blob, Error)>; + +#[cfg(feature = "libloading")] +impl crate::D3D12Lib { + pub fn serialize_root_signature( + &self, + version: RootSignatureVersion, + parameters: &[RootParameter], + static_samplers: &[StaticSampler], + flags: RootSignatureFlags, + ) -> Result { + use winapi::um::d3dcommon::ID3DBlob; + type Fun = extern "system" fn( + *const d3d12::D3D12_ROOT_SIGNATURE_DESC, + d3d12::D3D_ROOT_SIGNATURE_VERSION, + *mut *mut ID3DBlob, + *mut *mut ID3DBlob, + ) -> crate::HRESULT; + + let desc = d3d12::D3D12_ROOT_SIGNATURE_DESC { + NumParameters: parameters.len() as _, + pParameters: parameters.as_ptr() as *const _, + NumStaticSamplers: static_samplers.len() as _, + pStaticSamplers: static_samplers.as_ptr() as _, + Flags: flags.bits(), + }; + + let mut blob = Blob::null(); + let mut error = Error::null(); + let hr = unsafe { + let func: libloading::Symbol = self.lib.get(b"D3D12SerializeRootSignature")?; + func( + &desc, + version as _, + blob.mut_void() as *mut *mut _, + error.mut_void() as *mut *mut _, + ) + }; + + Ok(((blob, error), hr)) + } +} + +impl RootSignature { + #[cfg(feature = "implicit-link")] + pub fn serialize( + version: RootSignatureVersion, + parameters: &[RootParameter], + static_samplers: &[StaticSampler], + flags: RootSignatureFlags, + ) -> BlobResult { + let mut blob = Blob::null(); + let mut error = Error::null(); + + let desc = d3d12::D3D12_ROOT_SIGNATURE_DESC { + NumParameters: parameters.len() as _, + pParameters: parameters.as_ptr() as *const _, + NumStaticSamplers: static_samplers.len() as _, + pStaticSamplers: static_samplers.as_ptr() as _, + Flags: flags.bits(), + }; + + let hr = unsafe { + d3d12::D3D12SerializeRootSignature( + &desc, + version as _, + blob.mut_void() as *mut *mut _, + error.mut_void() as *mut *mut _, + ) + }; + + ((blob, error), hr) + } +} + +#[repr(transparent)] +pub struct RenderTargetViewDesc(pub(crate) d3d12::D3D12_RENDER_TARGET_VIEW_DESC); + +impl RenderTargetViewDesc { + pub fn texture_2d(format: dxgiformat::DXGI_FORMAT, mip_slice: u32, plane_slice: u32) -> Self { + let mut desc = d3d12::D3D12_RENDER_TARGET_VIEW_DESC { + Format: format, + ViewDimension: d3d12::D3D12_RTV_DIMENSION_TEXTURE2D, + ..unsafe { mem::zeroed() } + }; + + *unsafe { desc.u.Texture2D_mut() } = d3d12::D3D12_TEX2D_RTV { + MipSlice: mip_slice, + PlaneSlice: plane_slice, + }; + + RenderTargetViewDesc(desc) + } +} diff --git a/d3d12/src/device.rs b/d3d12/src/device.rs new file mode 100644 index 000000000..517b858bb --- /dev/null +++ b/d3d12/src/device.rs @@ -0,0 +1,344 @@ +//! Device + +use crate::{ + com::ComPtr, + command_list::{CmdListType, CommandSignature, IndirectArgument}, + descriptor::{CpuDescriptor, DescriptorHeapFlags, DescriptorHeapType, RenderTargetViewDesc}, + heap::{Heap, HeapFlags, HeapProperties}, + pso, query, queue, Blob, CachedPSO, CommandAllocator, CommandQueue, D3DResult, DescriptorHeap, + Fence, GraphicsCommandList, NodeMask, PipelineState, QueryHeap, Resource, RootSignature, + Shader, TextureAddressMode, +}; +use std::ops::Range; +use winapi::{um::d3d12, Interface}; + +pub type Device = ComPtr; + +#[cfg(feature = "libloading")] +impl crate::D3D12Lib { +pub fn create_device( + &self, + adapter: &ComPtr, + feature_level: crate::FeatureLevel, + ) -> Result, libloading::Error> { + type Fun = extern "system" fn( + *mut winapi::um::unknwnbase::IUnknown, + winapi::um::d3dcommon::D3D_FEATURE_LEVEL, + winapi::shared::guiddef::REFGUID, + *mut *mut winapi::ctypes::c_void, + ) -> crate::HRESULT; + + let mut device = Device::null(); + let hr = unsafe { + let func: libloading::Symbol = self.lib.get(b"D3D12CreateDevice")?; + func( + adapter.as_unknown() as *const _ as *mut _, + feature_level as _, + &d3d12::ID3D12Device::uuidof(), + device.mut_void(), + ) + }; + + Ok((device, hr)) + } +} + +impl Device { + #[cfg(feature = "implicit-link")] + pub fn create( + adapter: ComPtr, + feature_level: crate::FeatureLevel, + ) -> D3DResult { + let mut device = Device::null(); + let hr = unsafe { + d3d12::D3D12CreateDevice( + adapter.as_unknown() as *const _ as *mut _, + feature_level as _, + &d3d12::ID3D12Device::uuidof(), + device.mut_void(), + ) + }; + + (device, hr) + } + + pub fn create_heap( + &self, + size_in_bytes: u64, + properties: HeapProperties, + alignment: u64, + flags: HeapFlags, + ) -> D3DResult { + let mut heap = Heap::null(); + + let desc = d3d12::D3D12_HEAP_DESC { + SizeInBytes: size_in_bytes, + Properties: properties.0, + Alignment: alignment, + Flags: flags.bits(), + }; + + let hr = unsafe { self.CreateHeap(&desc, &d3d12::ID3D12Heap::uuidof(), heap.mut_void()) }; + + (heap, hr) + } + + pub fn create_command_allocator(&self, list_type: CmdListType) -> D3DResult { + let mut allocator = CommandAllocator::null(); + let hr = unsafe { + self.CreateCommandAllocator( + list_type as _, + &d3d12::ID3D12CommandAllocator::uuidof(), + allocator.mut_void(), + ) + }; + + (allocator, hr) + } + + pub fn create_command_queue( + &self, + list_type: CmdListType, + priority: queue::Priority, + flags: queue::CommandQueueFlags, + node_mask: NodeMask, + ) -> D3DResult { + let desc = d3d12::D3D12_COMMAND_QUEUE_DESC { + Type: list_type as _, + Priority: priority as _, + Flags: flags.bits(), + NodeMask: node_mask, + }; + + let mut queue = CommandQueue::null(); + let hr = unsafe { + self.CreateCommandQueue( + &desc, + &d3d12::ID3D12CommandQueue::uuidof(), + queue.mut_void(), + ) + }; + + (queue, hr) + } + + pub fn create_descriptor_heap( + &self, + num_descriptors: u32, + heap_type: DescriptorHeapType, + flags: DescriptorHeapFlags, + node_mask: NodeMask, + ) -> D3DResult { + let desc = d3d12::D3D12_DESCRIPTOR_HEAP_DESC { + Type: heap_type as _, + NumDescriptors: num_descriptors, + Flags: flags.bits(), + NodeMask: node_mask, + }; + + let mut heap = DescriptorHeap::null(); + let hr = unsafe { + self.CreateDescriptorHeap( + &desc, + &d3d12::ID3D12DescriptorHeap::uuidof(), + heap.mut_void(), + ) + }; + + (heap, hr) + } + + pub fn get_descriptor_increment_size(&self, heap_type: DescriptorHeapType) -> u32 { + unsafe { self.GetDescriptorHandleIncrementSize(heap_type as _) } + } + + pub fn create_graphics_command_list( + &self, + list_type: CmdListType, + allocator: &CommandAllocator, + initial: PipelineState, + node_mask: NodeMask, + ) -> D3DResult { + let mut command_list = GraphicsCommandList::null(); + let hr = unsafe { + self.CreateCommandList( + node_mask, + list_type as _, + allocator.as_mut_ptr(), + initial.as_mut_ptr(), + &d3d12::ID3D12GraphicsCommandList::uuidof(), + command_list.mut_void(), + ) + }; + + (command_list, hr) + } + + pub fn create_query_heap( + &self, + heap_ty: query::QueryHeapType, + count: u32, + node_mask: NodeMask, + ) -> D3DResult { + let desc = d3d12::D3D12_QUERY_HEAP_DESC { + Type: heap_ty as _, + Count: count, + NodeMask: node_mask, + }; + + let mut query_heap = QueryHeap::null(); + let hr = unsafe { + self.CreateQueryHeap( + &desc, + &d3d12::ID3D12QueryHeap::uuidof(), + query_heap.mut_void(), + ) + }; + + (query_heap, hr) + } + + pub fn create_graphics_pipeline_state( + &self, + _root_signature: RootSignature, + _vs: Shader, + _ps: Shader, + _gs: Shader, + _hs: Shader, + _ds: Shader, + _node_mask: NodeMask, + _cached_pso: CachedPSO, + _flags: pso::PipelineStateFlags, + ) -> D3DResult { + unimplemented!() + } + + pub fn create_compute_pipeline_state( + &self, + root_signature: &RootSignature, + cs: Shader, + node_mask: NodeMask, + cached_pso: CachedPSO, + flags: pso::PipelineStateFlags, + ) -> D3DResult { + let mut pipeline = PipelineState::null(); + let desc = d3d12::D3D12_COMPUTE_PIPELINE_STATE_DESC { + pRootSignature: root_signature.as_mut_ptr(), + CS: *cs, + NodeMask: node_mask, + CachedPSO: *cached_pso, + Flags: flags.bits(), + }; + + let hr = unsafe { + self.CreateComputePipelineState( + &desc, + &d3d12::ID3D12PipelineState::uuidof(), + pipeline.mut_void(), + ) + }; + + (pipeline, hr) + } + + pub fn create_sampler( + &self, + sampler: CpuDescriptor, + filter: d3d12::D3D12_FILTER, + address_mode: TextureAddressMode, + mip_lod_bias: f32, + max_anisotropy: u32, + comparison_op: d3d12::D3D12_COMPARISON_FUNC, + border_color: [f32; 4], + lod: Range, + ) { + let desc = d3d12::D3D12_SAMPLER_DESC { + Filter: filter, + AddressU: address_mode[0], + AddressV: address_mode[1], + AddressW: address_mode[2], + MipLODBias: mip_lod_bias, + MaxAnisotropy: max_anisotropy, + ComparisonFunc: comparison_op, + BorderColor: border_color, + MinLOD: lod.start, + MaxLOD: lod.end, + }; + + unsafe { + self.CreateSampler(&desc, sampler); + } + } + + pub fn create_root_signature( + &self, + blob: Blob, + node_mask: NodeMask, + ) -> D3DResult { + let mut signature = RootSignature::null(); + let hr = unsafe { + self.CreateRootSignature( + node_mask, + blob.GetBufferPointer(), + blob.GetBufferSize(), + &d3d12::ID3D12RootSignature::uuidof(), + signature.mut_void(), + ) + }; + + (signature, hr) + } + + pub fn create_command_signature( + &self, + root_signature: RootSignature, + arguments: &[IndirectArgument], + stride: u32, + node_mask: NodeMask, + ) -> D3DResult { + let mut signature = CommandSignature::null(); + let desc = d3d12::D3D12_COMMAND_SIGNATURE_DESC { + ByteStride: stride, + NumArgumentDescs: arguments.len() as _, + pArgumentDescs: arguments.as_ptr() as *const _, + NodeMask: node_mask, + }; + + let hr = unsafe { + self.CreateCommandSignature( + &desc, + root_signature.as_mut_ptr(), + &d3d12::ID3D12CommandSignature::uuidof(), + signature.mut_void(), + ) + }; + + (signature, hr) + } + + pub fn create_render_target_view( + &self, + resource: Resource, + desc: &RenderTargetViewDesc, + descriptor: CpuDescriptor, + ) { + unsafe { + self.CreateRenderTargetView(resource.as_mut_ptr(), &desc.0 as *const _, descriptor); + } + } + + // TODO: interface not complete + pub fn create_fence(&self, initial: u64) -> D3DResult { + let mut fence = Fence::null(); + let hr = unsafe { + self.CreateFence( + initial, + d3d12::D3D12_FENCE_FLAG_NONE, + &d3d12::ID3D12Fence::uuidof(), + fence.mut_void(), + ) + }; + + (fence, hr) + } +} diff --git a/d3d12/src/dxgi.rs b/d3d12/src/dxgi.rs new file mode 100644 index 000000000..81e2aaa1e --- /dev/null +++ b/d3d12/src/dxgi.rs @@ -0,0 +1,377 @@ +use crate::{com::ComPtr, D3DResult, Resource, SampleDesc, HRESULT}; +use std::ptr; +use winapi::{ + shared::{ + dxgi, dxgi1_2, dxgi1_3, dxgi1_4, dxgi1_5, dxgi1_6, dxgiformat, dxgitype, minwindef::TRUE, + windef::HWND, + }, + um::{d3d12, dxgidebug, unknwnbase::IUnknown, winnt::HANDLE}, + Interface, +}; + +bitflags! { + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct FactoryCreationFlags: u32 { + const DEBUG = dxgi1_3::DXGI_CREATE_FACTORY_DEBUG; + } +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone)] +pub enum Scaling { + Stretch = dxgi1_2::DXGI_SCALING_STRETCH, + Identity = dxgi1_2::DXGI_SCALING_NONE, + Aspect = dxgi1_2::DXGI_SCALING_ASPECT_RATIO_STRETCH, +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone)] +pub enum SwapEffect { + Discard = dxgi::DXGI_SWAP_EFFECT_DISCARD, + Sequential = dxgi::DXGI_SWAP_EFFECT_SEQUENTIAL, + FlipDiscard = dxgi::DXGI_SWAP_EFFECT_FLIP_DISCARD, + FlipSequential = dxgi::DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone)] +pub enum AlphaMode { + Unspecified = dxgi1_2::DXGI_ALPHA_MODE_UNSPECIFIED, + Premultiplied = dxgi1_2::DXGI_ALPHA_MODE_PREMULTIPLIED, + Straight = dxgi1_2::DXGI_ALPHA_MODE_STRAIGHT, + Ignore = dxgi1_2::DXGI_ALPHA_MODE_IGNORE, + ForceDword = dxgi1_2::DXGI_ALPHA_MODE_FORCE_DWORD, +} + +pub type InfoQueue = ComPtr; + +pub type Adapter1 = ComPtr; +pub type Adapter2 = ComPtr; +pub type Adapter3 = ComPtr; +pub type Adapter4 = ComPtr; +crate::weak_com_inheritance_chain! { + #[derive(Debug, 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 = ComPtr; +pub type Factory2 = ComPtr; +pub type Factory3 = ComPtr; +pub type Factory4 = ComPtr; +pub type Factory5 = ComPtr; +pub type Factory6 = ComPtr; +crate::weak_com_inheritance_chain! { + #[derive(Debug, 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 FactoryMedia = ComPtr; + +pub type SwapChain = ComPtr; +pub type SwapChain1 = ComPtr; +pub type SwapChain2 = ComPtr; +pub type SwapChain3 = ComPtr; +crate::weak_com_inheritance_chain! { + #[derive(Debug, 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)] +pub struct DxgiLib { + lib: libloading::Library, +} + +#[cfg(feature = "libloading")] +impl DxgiLib { + pub fn new() -> Result { + unsafe { libloading::Library::new("dxgi.dll").map(|lib| DxgiLib { lib }) } + } + + pub fn create_factory2( + &self, + flags: FactoryCreationFlags, + ) -> Result, libloading::Error> { + type Fun = extern "system" fn( + winapi::shared::minwindef::UINT, + winapi::shared::guiddef::REFIID, + *mut *mut winapi::ctypes::c_void, + ) -> HRESULT; + + let mut factory = Factory4::null(); + let hr = unsafe { + let func: libloading::Symbol = self.lib.get(b"CreateDXGIFactory2")?; + func( + flags.bits(), + &dxgi1_4::IDXGIFactory4::uuidof(), + factory.mut_void(), + ) + }; + + 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 create_factory_media(&self) -> Result, libloading::Error> { + type Fun = extern "system" fn( + winapi::shared::guiddef::REFIID, + *mut *mut winapi::ctypes::c_void, + ) -> HRESULT; + + let mut factory = FactoryMedia::null(); + let hr = unsafe { + // https://learn.microsoft.com/en-us/windows/win32/api/dxgi1_3/nn-dxgi1_3-idxgifactorymedia + let func: libloading::Symbol = self.lib.get(b"CreateDXGIFactory1")?; + func(&dxgi1_3::IDXGIFactoryMedia::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, + winapi::shared::guiddef::REFIID, + *mut *mut winapi::ctypes::c_void, + ) -> HRESULT; + + let mut queue = InfoQueue::null(); + let hr = unsafe { + let func: libloading::Symbol = self.lib.get(b"DXGIGetDebugInterface1")?; + func(0, &dxgidebug::IDXGIInfoQueue::uuidof(), queue.mut_void()) + }; + Ok((queue, hr)) + } +} + +// TODO: strong types +pub struct SwapchainDesc { + pub width: u32, + pub height: u32, + pub format: dxgiformat::DXGI_FORMAT, + pub stereo: bool, + pub sample: SampleDesc, + pub buffer_usage: dxgitype::DXGI_USAGE, + pub buffer_count: u32, + pub scaling: Scaling, + pub swap_effect: SwapEffect, + 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: *mut IUnknown, + hwnd: HWND, + desc: &SwapchainDesc, + ) -> D3DResult { + let mut swap_chain = SwapChain1::null(); + let hr = unsafe { + self.CreateSwapChainForHwnd( + queue, + hwnd, + &desc.to_desc1(), + ptr::null(), + ptr::null_mut(), + swap_chain.mut_void() as *mut *mut _, + ) + }; + + (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 { + #[cfg(feature = "implicit-link")] + pub fn create(flags: FactoryCreationFlags) -> D3DResult { + let mut factory = Factory4::null(); + let hr = unsafe { + dxgi1_3::CreateDXGIFactory2( + flags.bits(), + &dxgi1_4::IDXGIFactory4::uuidof(), + factory.mut_void(), + ) + }; + + (factory, hr) + } + + 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 _) }; + + (adapter, hr) + } +} + +impl FactoryMedia { + pub fn create_swapchain_for_composition_surface_handle( + &self, + queue: *mut IUnknown, + surface_handle: HANDLE, + desc: &SwapchainDesc, + ) -> D3DResult { + let mut swap_chain = SwapChain1::null(); + let hr = unsafe { + self.CreateSwapChainForCompositionSurfaceHandle( + queue, + surface_handle, + &desc.to_desc1(), + ptr::null_mut(), + swap_chain.mut_void() as *mut *mut _, + ) + }; + + (swap_chain, hr) + } +} + +bitflags! { + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct SwapChainPresentFlags: u32 { + const DXGI_PRESENT_DO_NOT_SEQUENCE = dxgi::DXGI_PRESENT_DO_NOT_SEQUENCE; + const DXGI_PRESENT_TEST = dxgi::DXGI_PRESENT_TEST; + const DXGI_PRESENT_RESTART = dxgi::DXGI_PRESENT_RESTART; + const DXGI_PRESENT_DO_NOT_WAIT = dxgi::DXGI_PRESENT_DO_NOT_WAIT; + const DXGI_PRESENT_RESTRICT_TO_OUTPUT = dxgi::DXGI_PRESENT_RESTRICT_TO_OUTPUT; + const DXGI_PRESENT_STEREO_PREFER_RIGHT = dxgi::DXGI_PRESENT_STEREO_PREFER_RIGHT; + const DXGI_PRESENT_STEREO_TEMPORARY_MONO = dxgi::DXGI_PRESENT_STEREO_TEMPORARY_MONO; + const DXGI_PRESENT_USE_DURATION = dxgi::DXGI_PRESENT_USE_DURATION; + const DXGI_PRESENT_ALLOW_TEARING = dxgi::DXGI_PRESENT_ALLOW_TEARING; + } +} + +impl SwapChain { + pub fn get_buffer(&self, id: u32) -> D3DResult { + let mut resource = Resource::null(); + let hr = + unsafe { self.GetBuffer(id, &d3d12::ID3D12Resource::uuidof(), resource.mut_void()) }; + + (resource, hr) + } + + //TODO: replace by present_flags + pub fn present(&self, interval: u32, flags: u32) -> HRESULT { + unsafe { self.Present(interval, flags) } + } + + pub fn present_flags(&self, interval: u32, flags: SwapChainPresentFlags) -> HRESULT { + unsafe { self.Present(interval, flags.bits()) } + } +} + +impl SwapChain3 { + pub fn get_current_back_buffer_index(&self) -> u32 { + unsafe { self.GetCurrentBackBufferIndex() } + } +} diff --git a/d3d12/src/heap.rs b/d3d12/src/heap.rs new file mode 100644 index 000000000..8d081a213 --- /dev/null +++ b/d3d12/src/heap.rs @@ -0,0 +1,87 @@ +use crate::com::ComPtr; +use winapi::um::d3d12; + +pub type Heap = ComPtr; + +#[repr(u32)] +#[derive(Clone, Copy)] +pub enum HeapType { + Default = d3d12::D3D12_HEAP_TYPE_DEFAULT, + Upload = d3d12::D3D12_HEAP_TYPE_UPLOAD, + Readback = d3d12::D3D12_HEAP_TYPE_READBACK, + Custom = d3d12::D3D12_HEAP_TYPE_CUSTOM, +} + +#[repr(u32)] +#[derive(Clone, Copy)] +pub enum CpuPageProperty { + Unknown = d3d12::D3D12_CPU_PAGE_PROPERTY_UNKNOWN, + NotAvailable = d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE, + WriteCombine = d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE, + WriteBack = d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK, +} + +#[repr(u32)] +#[derive(Clone, Copy)] +pub enum MemoryPool { + Unknown = d3d12::D3D12_CPU_PAGE_PROPERTY_UNKNOWN, + L0 = d3d12::D3D12_MEMORY_POOL_L0, + L1 = d3d12::D3D12_MEMORY_POOL_L1, +} + +bitflags! { + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct HeapFlags: u32 { + const NONE = d3d12::D3D12_HEAP_FLAG_NONE; + const SHARED = d3d12::D3D12_HEAP_FLAG_SHARED; + const DENY_BUFFERS = d3d12::D3D12_HEAP_FLAG_DENY_BUFFERS; + const ALLOW_DISPLAY = d3d12::D3D12_HEAP_FLAG_ALLOW_DISPLAY; + const SHARED_CROSS_ADAPTER = d3d12::D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER; + const DENT_RT_DS_TEXTURES = d3d12::D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES; + const DENY_NON_RT_DS_TEXTURES = d3d12::D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES; + const HARDWARE_PROTECTED = d3d12::D3D12_HEAP_FLAG_HARDWARE_PROTECTED; + const ALLOW_WRITE_WATCH = d3d12::D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH; + const ALLOW_ALL_BUFFERS_AND_TEXTURES = d3d12::D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES; + const ALLOW_ONLY_BUFFERS = d3d12::D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS; + const ALLOW_ONLY_NON_RT_DS_TEXTURES = d3d12::D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES; + const ALLOW_ONLY_RT_DS_TEXTURES = d3d12::D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES; + } +} + +#[repr(transparent)] +pub struct HeapProperties(pub d3d12::D3D12_HEAP_PROPERTIES); +impl HeapProperties { + pub fn new( + heap_type: HeapType, + cpu_page_property: CpuPageProperty, + memory_pool_preference: MemoryPool, + creation_node_mask: u32, + visible_node_mask: u32, + ) -> Self { + HeapProperties(d3d12::D3D12_HEAP_PROPERTIES { + Type: heap_type as _, + CPUPageProperty: cpu_page_property as _, + MemoryPoolPreference: memory_pool_preference as _, + CreationNodeMask: creation_node_mask, + VisibleNodeMask: visible_node_mask, + }) + } +} + +#[repr(transparent)] +pub struct HeapDesc(d3d12::D3D12_HEAP_DESC); +impl HeapDesc { + pub fn new( + size_in_bytes: u64, + properties: HeapProperties, + alignment: u64, + flags: HeapFlags, + ) -> Self { + HeapDesc(d3d12::D3D12_HEAP_DESC { + SizeInBytes: size_in_bytes, + Properties: properties.0, + Alignment: alignment, + Flags: flags.bits(), + }) + } +} diff --git a/d3d12/src/lib.rs b/d3d12/src/lib.rs new file mode 100644 index 000000000..8a68475de --- /dev/null +++ b/d3d12/src/lib.rs @@ -0,0 +1,121 @@ +#[macro_use] +extern crate bitflags; + +use std::{convert::TryFrom, ffi::CStr}; +use winapi::{ + shared::dxgiformat, + um::{d3d12, d3dcommon}, +}; + +mod com; +mod command_allocator; +mod command_list; +mod debug; +mod descriptor; +mod device; +mod dxgi; +mod heap; +mod pso; +mod query; +mod queue; +mod resource; +mod sync; + +pub use crate::com::*; +pub use crate::command_allocator::*; +pub use crate::command_list::*; +pub use crate::debug::*; +pub use crate::descriptor::*; +pub use crate::device::*; +pub use crate::dxgi::*; +pub use crate::heap::*; +pub use crate::pso::*; +pub use crate::query::*; +pub use crate::queue::*; +pub use crate::resource::*; +pub use crate::sync::*; + +pub use winapi::shared::winerror::HRESULT; + +pub type D3DResult = (T, HRESULT); +pub type GpuAddress = d3d12::D3D12_GPU_VIRTUAL_ADDRESS; +pub type Format = dxgiformat::DXGI_FORMAT; +pub type Rect = d3d12::D3D12_RECT; +pub type NodeMask = u32; + +/// Index into the root signature. +pub type RootIndex = u32; +/// Draw vertex count. +pub type VertexCount = u32; +/// Draw vertex base offset. +pub type VertexOffset = i32; +/// Draw number of indices. +pub type IndexCount = u32; +/// Draw number of instances. +pub type InstanceCount = u32; +/// Number of work groups. +pub type WorkGroupCount = [u32; 3]; + +pub type TextureAddressMode = [d3d12::D3D12_TEXTURE_ADDRESS_MODE; 3]; + +pub struct SampleDesc { + pub count: u32, + pub quality: u32, +} + +#[repr(u32)] +#[non_exhaustive] +pub enum FeatureLevel { + L9_1 = d3dcommon::D3D_FEATURE_LEVEL_9_1, + L9_2 = d3dcommon::D3D_FEATURE_LEVEL_9_2, + L9_3 = d3dcommon::D3D_FEATURE_LEVEL_9_3, + L10_0 = d3dcommon::D3D_FEATURE_LEVEL_10_0, + L10_1 = d3dcommon::D3D_FEATURE_LEVEL_10_1, + L11_0 = d3dcommon::D3D_FEATURE_LEVEL_11_0, + L11_1 = d3dcommon::D3D_FEATURE_LEVEL_11_1, + L12_0 = d3dcommon::D3D_FEATURE_LEVEL_12_0, + 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 = ComPtr; + +pub type Error = ComPtr; +impl Error { + pub unsafe fn as_c_str(&self) -> &CStr { + debug_assert!(!self.is_null()); + let data = self.GetBufferPointer(); + CStr::from_ptr(data as *const _ as *const _) + } +} + +#[cfg(feature = "libloading")] +#[derive(Debug)] +pub struct D3D12Lib { + lib: libloading::Library, +} + +#[cfg(feature = "libloading")] +impl D3D12Lib { + pub fn new() -> Result { + unsafe { libloading::Library::new("d3d12.dll").map(|lib| D3D12Lib { lib }) } + } +} diff --git a/d3d12/src/pso.rs b/d3d12/src/pso.rs new file mode 100644 index 000000000..2c19bd48d --- /dev/null +++ b/d3d12/src/pso.rs @@ -0,0 +1,162 @@ +//! Pipeline state + +use crate::{com::ComPtr, Blob, D3DResult, Error}; +use std::{ffi::{self, c_void}, ops::Deref, ptr, marker::PhantomData}; +use winapi::um::{d3d12, d3dcompiler}; + +bitflags! { + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct PipelineStateFlags: u32 { + const TOOL_DEBUG = d3d12::D3D12_PIPELINE_STATE_FLAG_TOOL_DEBUG; + } +} + +bitflags! { + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct ShaderCompileFlags: u32 { + const DEBUG = d3dcompiler::D3DCOMPILE_DEBUG; + const SKIP_VALIDATION = d3dcompiler::D3DCOMPILE_SKIP_VALIDATION; + const SKIP_OPTIMIZATION = d3dcompiler::D3DCOMPILE_SKIP_OPTIMIZATION; + const PACK_MATRIX_ROW_MAJOR = d3dcompiler::D3DCOMPILE_PACK_MATRIX_ROW_MAJOR; + const PACK_MATRIX_COLUMN_MAJOR = d3dcompiler::D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR; + const PARTIAL_PRECISION = d3dcompiler::D3DCOMPILE_PARTIAL_PRECISION; + // TODO: add missing flags + } +} + +#[derive(Copy, Clone)] +pub struct Shader<'a>(d3d12::D3D12_SHADER_BYTECODE, PhantomData<&'a c_void>); +impl<'a> Shader<'a> { + pub fn null() -> Self { + Shader(d3d12::D3D12_SHADER_BYTECODE { + BytecodeLength: 0, + pShaderBytecode: ptr::null(), + }, PhantomData) + } + + pub fn from_raw(data: &'a [u8]) -> Self { + Shader(d3d12::D3D12_SHADER_BYTECODE { + BytecodeLength: data.len() as _, + pShaderBytecode: data.as_ptr() as _, + }, PhantomData) + } + + // `blob` may not be null. + pub fn from_blob(blob: &'a Blob) -> Self { + Shader(d3d12::D3D12_SHADER_BYTECODE { + BytecodeLength: unsafe { blob.GetBufferSize() }, + pShaderBytecode: unsafe { blob.GetBufferPointer() }, + }, PhantomData) + } + + /// Compile a shader from raw HLSL. + /// + /// * `target`: example format: `ps_5_1`. + pub fn compile( + code: &[u8], + target: &ffi::CStr, + entry: &ffi::CStr, + flags: ShaderCompileFlags, + ) -> D3DResult<(Blob, Error)> { + let mut shader = Blob::null(); + let mut error = Error::null(); + + let hr = unsafe { + d3dcompiler::D3DCompile( + code.as_ptr() as *const _, + code.len(), + ptr::null(), // defines + ptr::null(), // include + ptr::null_mut(), + entry.as_ptr() as *const _, + target.as_ptr() as *const _, + flags.bits(), + 0, + shader.mut_void() as *mut *mut _, + error.mut_void() as *mut *mut _, + ) + }; + + ((shader, error), hr) + } +} + +impl<'a> Deref for Shader<'a> { + type Target = d3d12::D3D12_SHADER_BYTECODE; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Copy, Clone)] +pub struct CachedPSO<'a>(d3d12::D3D12_CACHED_PIPELINE_STATE, PhantomData<&'a c_void>); +impl<'a> CachedPSO<'a> { + pub fn null() -> Self { + CachedPSO(d3d12::D3D12_CACHED_PIPELINE_STATE { + CachedBlobSizeInBytes: 0, + pCachedBlob: ptr::null(), + }, PhantomData) + } + + // `blob` may not be null. + pub fn from_blob(blob: &'a Blob) -> Self { + CachedPSO(d3d12::D3D12_CACHED_PIPELINE_STATE { + CachedBlobSizeInBytes: unsafe { blob.GetBufferSize() }, + pCachedBlob: unsafe { blob.GetBufferPointer() }, + }, PhantomData) + } +} + +impl<'a> Deref for CachedPSO<'a> { + type Target = d3d12::D3D12_CACHED_PIPELINE_STATE; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub type PipelineState = ComPtr; + +#[repr(u32)] +pub enum Subobject { + RootSignature = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE, + VS = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS, + PS = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS, + DS = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS, + HS = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS, + GS = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS, + CS = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS, + StreamOutput = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT, + Blend = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND, + SampleMask = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK, + Rasterizer = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER, + DepthStencil = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL, + InputLayout = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT, + IBStripCut = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE, + PrimitiveTopology = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY, + RTFormats = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS, + DSFormat = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT, + SampleDesc = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC, + NodeMask = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK, + CachedPSO = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO, + Flags = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS, + DepthStencil1 = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1, + // ViewInstancing = d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING, +} + +/// Subobject of a pipeline stream description +#[repr(C)] +pub struct PipelineStateSubobject { + subobject_align: [usize; 0], // Subobjects must have the same alignment as pointers. + subobject_type: d3d12::D3D12_PIPELINE_STATE_SUBOBJECT_TYPE, + subobject: T, +} + +impl PipelineStateSubobject { + pub fn new(subobject_type: Subobject, subobject: T) -> Self { + PipelineStateSubobject { + subobject_align: [], + subobject_type: subobject_type as _, + subobject, + } + } +} diff --git a/d3d12/src/query.rs b/d3d12/src/query.rs new file mode 100644 index 000000000..a9dca262b --- /dev/null +++ b/d3d12/src/query.rs @@ -0,0 +1,15 @@ +use crate::com::ComPtr; +use winapi::um::d3d12; + +#[repr(u32)] +#[derive(Debug, Copy, Clone)] +pub enum QueryHeapType { + Occlusion = d3d12::D3D12_QUERY_HEAP_TYPE_OCCLUSION, + Timestamp = d3d12::D3D12_QUERY_HEAP_TYPE_TIMESTAMP, + PipelineStatistics = d3d12::D3D12_QUERY_HEAP_TYPE_PIPELINE_STATISTICS, + SOStatistics = d3d12::D3D12_QUERY_HEAP_TYPE_SO_STATISTICS, + // VideoDecodeStatistcs = d3d12::D3D12_QUERY_HEAP_TYPE_VIDEO_DECODE_STATISTICS, + // CopyQueueTimestamp = d3d12::D3D12_QUERY_HEAP_TYPE_COPY_QUEUE_TIMESTAMP, +} + +pub type QueryHeap = ComPtr; diff --git a/d3d12/src/queue.rs b/d3d12/src/queue.rs new file mode 100644 index 000000000..944cb6f32 --- /dev/null +++ b/d3d12/src/queue.rs @@ -0,0 +1,32 @@ +use crate::{com::ComPtr, sync::Fence, CommandList, HRESULT}; +use winapi::um::d3d12; + +#[repr(u32)] +pub enum Priority { + Normal = d3d12::D3D12_COMMAND_QUEUE_PRIORITY_NORMAL, + High = d3d12::D3D12_COMMAND_QUEUE_PRIORITY_HIGH, + GlobalRealtime = d3d12::D3D12_COMMAND_QUEUE_PRIORITY_GLOBAL_REALTIME, +} + +bitflags! { + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct CommandQueueFlags: u32 { + const DISABLE_GPU_TIMEOUT = d3d12::D3D12_COMMAND_QUEUE_FLAG_DISABLE_GPU_TIMEOUT; + } +} + +pub type CommandQueue = ComPtr; + +impl CommandQueue { + pub fn execute_command_lists(&self, command_lists: &[CommandList]) { + let command_lists = command_lists + .iter() + .map(CommandList::as_mut_ptr) + .collect::>(); + unsafe { self.ExecuteCommandLists(command_lists.len() as _, command_lists.as_ptr()) } + } + + pub fn signal(&self, fence: &Fence, value: u64) -> HRESULT { + unsafe { self.Signal(fence.as_mut_ptr(), value) } + } +} diff --git a/d3d12/src/resource.rs b/d3d12/src/resource.rs new file mode 100644 index 000000000..bdc669dd3 --- /dev/null +++ b/d3d12/src/resource.rs @@ -0,0 +1,53 @@ +//! GPU Resource + +use crate::{com::ComPtr, D3DResult, Rect}; +use std::{ops::Range, ptr}; +use winapi::um::d3d12; + +pub type Subresource = u32; + +pub struct DiscardRegion<'a> { + pub rects: &'a [Rect], + pub subregions: Range, +} + +pub type Resource = ComPtr; + +impl Resource { + /// + pub fn map( + &self, + subresource: Subresource, + read_range: Option>, + ) -> D3DResult<*mut ()> { + let mut ptr = ptr::null_mut(); + let read_range = read_range.map(|r| d3d12::D3D12_RANGE { + Begin: r.start, + End: r.end, + }); + let read = match read_range { + Some(ref r) => r as *const _, + None => ptr::null(), + }; + let hr = unsafe { self.Map(subresource, read, &mut ptr) }; + + (ptr as _, hr) + } + + pub fn unmap(&self, subresource: Subresource, write_range: Option>) { + let write_range = write_range.map(|r| d3d12::D3D12_RANGE { + Begin: r.start, + End: r.end, + }); + let write = match write_range { + Some(ref r) => r as *const _, + None => ptr::null(), + }; + + unsafe { self.Unmap(subresource, write) }; + } + + pub fn gpu_virtual_address(&self) -> u64 { + unsafe { self.GetGPUVirtualAddress() } + } +} diff --git a/d3d12/src/sync.rs b/d3d12/src/sync.rs new file mode 100644 index 000000000..fa5f09040 --- /dev/null +++ b/d3d12/src/sync.rs @@ -0,0 +1,39 @@ +use crate::{com::ComPtr, HRESULT}; +use std::ptr; +use winapi::um::{d3d12, synchapi, winnt}; + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct Event(pub winnt::HANDLE); +impl Event { + pub fn create(manual_reset: bool, initial_state: bool) -> Self { + Event(unsafe { + synchapi::CreateEventA( + ptr::null_mut(), + manual_reset as _, + initial_state as _, + ptr::null(), + ) + }) + } + + // TODO: return value + pub fn wait(&self, timeout_ms: u32) -> u32 { + unsafe { synchapi::WaitForSingleObject(self.0, timeout_ms) } + } +} + +pub type Fence = ComPtr; +impl Fence { + pub fn set_event_on_completion(&self, event: Event, value: u64) -> HRESULT { + unsafe { self.SetEventOnCompletion(value, event.0) } + } + + pub fn get_value(&self) -> u64 { + unsafe { self.GetCompletedValue() } + } + + pub fn signal(&self, value: u64) -> HRESULT { + unsafe { self.Signal(value) } + } +}