diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..5e6b53e92 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "d3d12-rs" +version = "0.1.0" +authors = ["msiglreith "] + +[dependencies] +bitflags = "1" +winapi = { version = "0.3", features = ["d3d12","d3dcommon","d3dcompiler","dxgiformat","winerror"] } diff --git a/src/com.rs b/src/com.rs new file mode 100644 index 000000000..9ecc6baba --- /dev/null +++ b/src/com.rs @@ -0,0 +1,98 @@ +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; +use std::ptr; +use winapi::ctypes::c_void; +use winapi::um::unknwnbase::IUnknown; +use winapi::Interface; +use D3DResult; + +#[repr(transparent)] +pub struct WeakPtr(*mut T); + +impl WeakPtr { + pub fn null() -> Self { + WeakPtr(ptr::null_mut()) + } + + 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 unsafe fn mut_void(&mut self) -> *mut *mut c_void { + &mut self.0 as *mut *mut _ as *mut *mut _ + } +} + +impl WeakPtr { + pub unsafe fn as_unknown(&self) -> &IUnknown { + debug_assert!(!self.is_null()); + &*(self.0 as *mut IUnknown) + } + + // Cast creates a new WeakPtr requiring explicit destroy call. + pub unsafe fn cast(&self) -> D3DResult> + where + U: Interface, + { + let mut obj = WeakPtr::::null(); + let hr = self + .as_unknown() + .QueryInterface(&U::uuidof(), obj.mut_void()); + (obj, hr) + } + + // Destroying one instance of the WeakPtr will invalidate all + // copies and clones. + pub unsafe fn destroy(&self) { + self.as_unknown().Release(); + } +} + +impl Clone for WeakPtr { + fn clone(&self) -> Self { + WeakPtr(self.0) + } +} + +impl Copy for WeakPtr {} + +impl Deref for WeakPtr { + type Target = T; + fn deref(&self) -> &T { + debug_assert!(!self.is_null()); + unsafe { &*self.0 } + } +} + +impl fmt::Debug for WeakPtr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "WeakPtr( ptr: {:?} )", self.0) + } +} + +impl PartialEq<*mut T> for WeakPtr { + fn eq(&self, other: &*mut T) -> bool { + self.0 == *other + } +} + +impl PartialEq for WeakPtr { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Hash for WeakPtr { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} diff --git a/src/command_allocator.rs b/src/command_allocator.rs new file mode 100644 index 000000000..93bf38cce --- /dev/null +++ b/src/command_allocator.rs @@ -0,0 +1,14 @@ +//! Command Allocator + +use com::WeakPtr; +use winapi::um::d3d12; + +pub type CommandAllocator = WeakPtr; + +impl CommandAllocator { + pub fn reset(&self) { + unsafe { + self.Reset(); + } + } +} diff --git a/src/command_list.rs b/src/command_list.rs new file mode 100644 index 000000000..77108e532 --- /dev/null +++ b/src/command_list.rs @@ -0,0 +1,218 @@ +//! Graphics command list + +use com::WeakPtr; +use resource::DiscardRegion; +use std::mem; +use winapi::um::d3d12; +use { + CommandAllocator, CpuDescriptor, DescriptorHeap, Format, GpuAddress, GpuDescriptor, IndexCount, + InstanceCount, PipelineState, Rect, Resource, RootSignature, VertexCount, VertexOffset, + WorkGroupCount, HRESULT, +}; + +#[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! { + 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() } + }) + } + + // TODO: missing variants +} + +pub type CommandSignature = WeakPtr; +pub type GraphicsCommandList = WeakPtr; + +impl GraphicsCommandList { + 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], + ) { + unsafe { + self.ClearDepthStencilView( + dsv, + flags.bits(), + depth, + stencil, + rects.len() as _, + rects.as_ptr(), + ); + } + } + + 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 mut ibv = d3d12::D3D12_INDEX_BUFFER_VIEW { + BufferLocation: gpu_address, + SizeInBytes: size, + Format: format, + }; + unsafe { + self.IASetIndexBuffer(&mut 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: u32, + base_descriptor: GpuDescriptor, + ) { + unsafe { + self.SetComputeRootDescriptorTable(root_index, base_descriptor); + } + } + + pub fn set_graphics_root_descriptor_table( + &self, + root_index: u32, + base_descriptor: GpuDescriptor, + ) { + unsafe { + self.SetGraphicsRootDescriptorTable(root_index, base_descriptor); + } + } +} diff --git a/src/descriptor.rs b/src/descriptor.rs new file mode 100644 index 000000000..d8e139c4a --- /dev/null +++ b/src/descriptor.rs @@ -0,0 +1,207 @@ +use com::WeakPtr; +use std::mem; +use std::ops::Range; +use winapi::um::d3d12; +use {Blob, D3DResult, Error, TextureAddressMode}; + +pub type CpuDescriptor = d3d12::D3D12_CPU_DESCRIPTOR_HANDLE; +pub type GpuDescriptor = d3d12::D3D12_GPU_DESCRIPTOR_HANDLE; + +#[repr(u32)] +#[derive(Clone, Copy)] +pub enum HeapType { + 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! { + pub struct HeapFlags: u32 { + const SHADER_VISIBLE = d3d12::D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + } +} + +pub type DescriptorHeap = WeakPtr; + +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)] +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)] +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_register: u32, + register_space: u32, + offset: u32, + ) -> Self { + DescriptorRange(d3d12::D3D12_DESCRIPTOR_RANGE { + RangeType: ty as _, + NumDescriptors: count, + BaseShaderRegister: base_register, + RegisterSpace: register_space, + OffsetInDescriptorsFromTableStart: offset, + }) + } +} + +#[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, + register: u32, + register_space: u32, + 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: register, + RegisterSpace: register_space, + Num32BitValues: num, + }; + + RootParameter(param) + } +} + +#[repr(u32)] +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, + register: u32, + register_space: u32, + + 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: register, + RegisterSpace: register_space, + ShaderVisibility: visibility as _, + }) + } +} + +#[repr(u32)] +pub enum RootSignatureVersion { + V1_0 = d3d12::D3D_ROOT_SIGNATURE_VERSION_1_0, + V1_1 = d3d12::D3D_ROOT_SIGNATURE_VERSION_1_1, +} + +bitflags! { + 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 = WeakPtr; + +impl RootSignature { + pub fn serialize( + version: RootSignatureVersion, + parameters: &[RootParameter], + static_samplers: &[StaticSampler], + flags: RootSignatureFlags, + ) -> D3DResult<(Blob, Error)> { + 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) + } +} diff --git a/src/device.rs b/src/device.rs new file mode 100644 index 000000000..5ae6a8017 --- /dev/null +++ b/src/device.rs @@ -0,0 +1,268 @@ +//! Device + +use com::WeakPtr; +use command_list::{CmdListType, CommandSignature, IndirectArgument}; +use descriptor::{CpuDescriptor, HeapFlags, HeapType}; +use std::ops::Range; +use winapi::um::d3d12; +use winapi::Interface; +use {pso, query, queue}; +use { + Blob, CachedPSO, CommandAllocator, CommandQueue, D3DResult, DescriptorHeap, FeatureLevel, + GraphicsCommandList, NodeMask, PipelineState, QueryHeap, RootSignature, Shader, + TextureAddressMode, +}; + +pub type Device = WeakPtr; + +impl Device { + pub fn create( + adapter: WeakPtr, + feature_level: 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_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: HeapType, + flags: HeapFlags, + 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: HeapType) -> 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::HeapType, + 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::ID3D12RootSignature::uuidof(), + signature.mut_void(), + ) + }; + + (signature, hr) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..036ff059e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,74 @@ +extern crate winapi; +#[macro_use] +extern crate bitflags; + +use std::ffi::CStr; +use winapi::shared::dxgiformat; +use winapi::um::{d3d12, d3dcommon}; + +mod com; +pub mod command_allocator; +pub mod command_list; +pub mod descriptor; +pub mod device; +pub mod pso; +pub mod query; +pub mod queue; +pub mod resource; +pub mod sync; + +pub use self::com::WeakPtr; +pub use self::command_allocator::CommandAllocator; +pub use self::command_list::{CommandSignature, GraphicsCommandList}; +pub use self::descriptor::{CpuDescriptor, DescriptorHeap, GpuDescriptor, RootSignature}; +pub use self::device::Device; +pub use self::pso::{CachedPSO, PipelineState, Shader}; +pub use self::query::QueryHeap; +pub use self::queue::CommandQueue; +pub use self::resource::{Heap, Resource}; +pub use self::sync::Fence; + +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; + +/// 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]; + +#[repr(u32)] +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, +} + +pub type Blob = self::com::WeakPtr; + +pub type Error = self::com::WeakPtr; +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 _) + } +} diff --git a/src/pso.rs b/src/pso.rs new file mode 100644 index 000000000..9feb379a4 --- /dev/null +++ b/src/pso.rs @@ -0,0 +1,164 @@ +//! Pipeline state + +use com::WeakPtr; +use std::ops::Deref; +use std::{ffi, ptr}; +use winapi::um::{d3d12, d3dcompiler}; +use {Blob, D3DResult, Error}; + +bitflags! { + pub struct PipelineStateFlags: u32 { + const TOOL_DEBUG = d3d12::D3D12_PIPELINE_STATE_FLAG_TOOL_DEBUG; + } +} + +bitflags! { + 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(d3d12::D3D12_SHADER_BYTECODE); +impl Shader { + pub fn null() -> Self { + Shader(d3d12::D3D12_SHADER_BYTECODE { + BytecodeLength: 0, + pShaderBytecode: ptr::null(), + }) + } + + // `blob` may not be null. + pub fn from_blob(blob: Blob) -> Self { + Shader(d3d12::D3D12_SHADER_BYTECODE { + BytecodeLength: unsafe { blob.GetBufferSize() }, + pShaderBytecode: unsafe { blob.GetBufferPointer() }, + }) + } + + /// 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 Deref for Shader { + type Target = d3d12::D3D12_SHADER_BYTECODE; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From> for Shader { + fn from(blob: Option) -> Self { + match blob { + Some(b) => Shader::from_blob(b), + None => Shader::null(), + } + } +} + +#[derive(Copy, Clone)] +pub struct CachedPSO(d3d12::D3D12_CACHED_PIPELINE_STATE); +impl CachedPSO { + pub fn null() -> Self { + CachedPSO(d3d12::D3D12_CACHED_PIPELINE_STATE { + CachedBlobSizeInBytes: 0, + pCachedBlob: ptr::null(), + }) + } + + // `blob` may not be null. + pub fn from_blob(blob: Blob) -> Self { + CachedPSO(d3d12::D3D12_CACHED_PIPELINE_STATE { + CachedBlobSizeInBytes: unsafe { blob.GetBufferSize() }, + pCachedBlob: unsafe { blob.GetBufferPointer() }, + }) + } +} + +impl Deref for CachedPSO { + type Target = d3d12::D3D12_CACHED_PIPELINE_STATE; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub type PipelineState = WeakPtr; + +#[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/src/query.rs b/src/query.rs new file mode 100644 index 000000000..1df33fd2f --- /dev/null +++ b/src/query.rs @@ -0,0 +1,15 @@ +use com::WeakPtr; +use winapi::um::d3d12; + +#[repr(u32)] +#[derive(Debug, Copy, Clone)] +pub enum HeapType { + 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 = WeakPtr; diff --git a/src/queue.rs b/src/queue.rs new file mode 100644 index 000000000..eb11504ea --- /dev/null +++ b/src/queue.rs @@ -0,0 +1,25 @@ +use com::WeakPtr; +use sync::Fence; +use winapi::um::d3d12; +use HRESULT; + +#[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! { + pub struct CommandQueueFlags: u32 { + const DISABLE_GPU_TIMEOUT = d3d12::D3D12_COMMAND_QUEUE_FLAG_DISABLE_GPU_TIMEOUT; + } +} + +pub type CommandQueue = WeakPtr; + +impl CommandQueue { + pub fn signal(&self, fence: Fence, value: u64) -> HRESULT { + unsafe { self.Signal(fence.as_mut_ptr(), value) } + } +} diff --git a/src/resource.rs b/src/resource.rs new file mode 100644 index 000000000..dd866a9f4 --- /dev/null +++ b/src/resource.rs @@ -0,0 +1,57 @@ +//! GPU Resource + +use com::WeakPtr; +use std::ops::Range; +use std::ptr; +use winapi::um::d3d12; +use {D3DResult, Rect}; + +pub type Subresource = u32; + +pub struct DiscardRegion<'a> { + pub rects: &'a [Rect], + pub subregions: Range, +} + +pub type Heap = WeakPtr; + +pub type Resource = WeakPtr; + +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/src/sync.rs b/src/sync.rs new file mode 100644 index 000000000..c10c73519 --- /dev/null +++ b/src/sync.rs @@ -0,0 +1,21 @@ +use com::WeakPtr; +use winapi::um::d3d12; +use winapi::um::winnt; +use HRESULT; + +pub type Event = winnt::HANDLE; +pub type Fence = WeakPtr; + +impl Fence { + pub fn set_event_on_completion(&self, event: Event, value: u64) -> HRESULT { + unsafe { self.SetEventOnCompletion(value, event) } + } + + pub fn get_value(&self) -> u64 { + unsafe { self.GetCompletedValue() } + } + + pub fn signal(&self, value: u64) -> HRESULT { + unsafe { self.Signal(value) } + } +}