From e128021aee0e2f9f157e34944f23c9f6ad03e83e Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 7 Jul 2021 01:39:32 -0400 Subject: [PATCH] hal/dx12: pipeline layout --- wgpu-hal/src/dx12/adapter.rs | 2 +- wgpu-hal/src/dx12/command.rs | 6 +- wgpu-hal/src/dx12/conv.rs | 33 +++++ wgpu-hal/src/dx12/device.rs | 235 +++++++++++++++++++++++++++++++++-- wgpu-hal/src/dx12/mod.rs | 44 ++++++- 5 files changed, 303 insertions(+), 17 deletions(-) diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index e733de4c9..00220b161 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -240,7 +240,7 @@ impl crate::Adapter for super::Adapter { ) .to_device_result("Queue creation")?; - let device = super::Device::new(self.device, queue, self.private_caps)?; + let device = super::Device::new(self.device, queue, self.private_caps, &self.library)?; Ok(crate::OpenDevice { device, queue: super::Queue { raw: queue }, diff --git a/wgpu-hal/src/dx12/command.rs b/wgpu-hal/src/dx12/command.rs index ef4e175cd..8eef9aff3 100644 --- a/wgpu-hal/src/dx12/command.rs +++ b/wgpu-hal/src/dx12/command.rs @@ -81,15 +81,15 @@ impl crate::CommandEncoder for super::CommandEncoder { unsafe fn set_bind_group( &mut self, - layout: &Resource, + layout: &super::PipelineLayout, index: u32, - group: &Resource, + group: &super::BindGroup, dynamic_offsets: &[wgt::DynamicOffset], ) { } unsafe fn set_push_constants( &mut self, - layout: &Resource, + layout: &super::PipelineLayout, stages: wgt::ShaderStages, offset: u32, data: &[u32], diff --git a/wgpu-hal/src/dx12/conv.rs b/wgpu-hal/src/dx12/conv.rs index a4266f235..c26869586 100644 --- a/wgpu-hal/src/dx12/conv.rs +++ b/wgpu-hal/src/dx12/conv.rs @@ -200,3 +200,36 @@ pub fn map_border_color(border_color: Option) -> [f32; Some(Sbc::OpaqueWhite) => [1.0; 4], } } + +pub fn map_visibility(visibility: wgt::ShaderStages) -> native::ShaderVisibility { + match visibility { + wgt::ShaderStages::VERTEX => native::ShaderVisibility::VS, + wgt::ShaderStages::FRAGMENT => native::ShaderVisibility::PS, + _ => native::ShaderVisibility::All, + } +} + +pub fn map_binding_type(ty: &wgt::BindingType) -> native::DescriptorRangeType { + use wgt::BindingType as Bt; + match *ty { + Bt::Sampler { .. } => native::DescriptorRangeType::Sampler, + Bt::Buffer { + ty: wgt::BufferBindingType::Uniform, + .. + } => native::DescriptorRangeType::CBV, + Bt::Buffer { + ty: wgt::BufferBindingType::Storage { read_only: true }, + .. + } + | Bt::Texture { .. } + | Bt::StorageTexture { + access: wgt::StorageTextureAccess::ReadOnly, + .. + } => native::DescriptorRangeType::SRV, + Bt::Buffer { + ty: wgt::BufferBindingType::Storage { read_only: false }, + .. + } + | Bt::StorageTexture { .. } => native::DescriptorRangeType::UAV, + } +} diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs index 9b489c337..aaf62ecfd 100644 --- a/wgpu-hal/src/dx12/device.rs +++ b/wgpu-hal/src/dx12/device.rs @@ -1,6 +1,6 @@ use super::{conv, descriptor, HResult as _}; use parking_lot::Mutex; -use std::{iter, mem, ptr}; +use std::{iter, mem, ptr, sync::Arc}; use winapi::{ shared::{dxgiformat, dxgitype, winerror}, um::{d3d12, d3d12sdklayers, synchapi, winbase}, @@ -21,6 +21,7 @@ impl super::Device { raw: native::Device, present_queue: native::CommandQueue, private_caps: super::PrivateCapabilities, + library: &Arc, ) -> Result { let mut idle_fence = native::Fence::null(); let hr = unsafe { @@ -57,6 +58,7 @@ impl super::Device { raw, native::DescriptorHeapType::Sampler, )), + library: Arc::clone(library), }) } @@ -713,24 +715,237 @@ impl crate::Device for super::Device { unsafe fn create_bind_group_layout( &self, desc: &crate::BindGroupLayoutDescriptor, - ) -> Result { - Ok(Resource) + ) -> Result { + Ok(super::BindGroupLayout { + entries: desc.entries.to_vec(), + }) + } + unsafe fn destroy_bind_group_layout(&self, _bg_layout: super::BindGroupLayout) { + // just drop } - unsafe fn destroy_bind_group_layout(&self, bg_layout: Resource) {} unsafe fn create_pipeline_layout( &self, desc: &crate::PipelineLayoutDescriptor, - ) -> Result { - Ok(Resource) + ) -> Result { + // Pipeline layouts are implemented as RootSignature for D3D12. + // + // Push Constants are implemented as root constants. + // + // Each descriptor set layout will be one table entry of the root signature. + // We have the additional restriction that SRV/CBV/UAV and samplers need to be + // separated, so each set layout will actually occupy up to 2 entries! + // SRV/CBV/UAV tables are added to the signature first, then Sampler tables, + // and finally dynamic uniform descriptors. + // + // Dynamic uniform buffers are implemented as root descriptors. + // This allows to handle the dynamic offsets properly, which would not be feasible + // with a combination of root constant and descriptor table. + // + // Root signature layout: + // Root Constants: Register: Offest/4, Space: 0 + // ... + // DescriptorTable0: Space: 1 (SrvCbvUav) + // DescriptorTable0: Space: 1 (Sampler) + // Root Descriptors 0 + // DescriptorTable1: Space: 2 (SrvCbvUav) + // Root Descriptors 1 + // ... + + let mut root_offset = 0u32; + let root_constants: &[()] = &[]; + + // Number of elements in the root signature. + let total_parameters = root_constants.len() + desc.bind_group_layouts.len() * 2; + // Guarantees that no re-allocation is done, and our pointers are valid + let mut parameters = Vec::with_capacity(total_parameters); + let mut parameter_offsets = Vec::with_capacity(total_parameters); + + let root_space_offset = if !root_constants.is_empty() { 1 } else { 0 }; + // Collect the whole number of bindings we will create upfront. + // It allows us to preallocate enough storage to avoid reallocation, + // which could cause invalid pointers. + let total_non_dynamic_entries = desc + .bind_group_layouts + .iter() + .flat_map(|bgl| { + bgl.entries.iter().map(|entry| match entry.ty { + wgt::BindingType::Buffer { + has_dynamic_offset: true, + .. + } => 0, + _ => 1, + }) + }) + .sum(); + let mut ranges = Vec::with_capacity(total_non_dynamic_entries); + + let mut root_elements = + arrayvec::ArrayVec::<[super::RootElement; crate::MAX_BIND_GROUPS]>::default(); + for (index, bgl) in desc.bind_group_layouts.iter().enumerate() { + let space = root_space_offset + index as u32; + let mut types = super::TableTypes::empty(); + let root_table_offset = root_offset as usize; + + let mut visibility_view_static = wgt::ShaderStages::empty(); + let mut visibility_view_dynamic = wgt::ShaderStages::empty(); + let mut visibility_sampler = wgt::ShaderStages::empty(); + for entry in bgl.entries.iter() { + match entry.ty { + wgt::BindingType::Sampler { .. } => visibility_sampler |= entry.visibility, + wgt::BindingType::Buffer { + has_dynamic_offset: true, + .. + } => visibility_view_dynamic |= entry.visibility, + _ => visibility_view_static |= entry.visibility, + } + } + + // SRV/CBV/UAV descriptor tables + let mut range_base = ranges.len(); + for entry in bgl.entries.iter() { + let range_ty = match entry.ty { + wgt::BindingType::Buffer { + has_dynamic_offset: true, + .. + } + | wgt::BindingType::Sampler { .. } => continue, + ref other => conv::map_binding_type(other), + }; + ranges.push(native::DescriptorRange::new( + range_ty, + entry.count.map_or(1, |count| count.get()), + native::Binding { + register: entry.binding, + space, + }, + d3d12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, + )); + } + if ranges.len() > range_base { + parameter_offsets.push(root_offset); + parameters.push(native::RootParameter::descriptor_table( + conv::map_visibility(visibility_view_static), + &ranges[range_base..], + )); + types |= super::TableTypes::SRV_CBV_UAV; + root_offset += 1; + } + + // Sampler descriptor tables + range_base = ranges.len(); + for entry in bgl.entries.iter() { + let range_ty = match entry.ty { + wgt::BindingType::Sampler { .. } => native::DescriptorRangeType::Sampler, + _ => continue, + }; + ranges.push(native::DescriptorRange::new( + range_ty, + entry.count.map_or(1, |count| count.get()), + native::Binding { + register: entry.binding, + space, + }, + d3d12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, + )); + } + if ranges.len() > range_base { + parameter_offsets.push(root_offset); + parameters.push(native::RootParameter::descriptor_table( + conv::map_visibility(visibility_sampler), + &ranges[range_base..], + )); + types |= super::TableTypes::SAMPLERS; + root_offset += 1; + } + + // Root (dynamic) descriptor tables + let dynamic_buffers_visibility = conv::map_visibility(visibility_view_dynamic); + for entry in bgl.entries.iter() { + let buffer_ty = match entry.ty { + wgt::BindingType::Buffer { + has_dynamic_offset: true, + ty, + .. + } => ty, + _ => continue, + }; + let binding = native::Binding { + register: entry.binding, + space, + }; + let param = match buffer_ty { + wgt::BufferBindingType::Uniform => { + native::RootParameter::cbv_descriptor(dynamic_buffers_visibility, binding) + } + wgt::BufferBindingType::Storage { read_only: true } => { + native::RootParameter::srv_descriptor(dynamic_buffers_visibility, binding) + } + wgt::BufferBindingType::Storage { read_only: false } => { + native::RootParameter::uav_descriptor(dynamic_buffers_visibility, binding) + } + }; + parameter_offsets.push(root_offset); + parameters.push(param); + root_offset += 2; // root view costs 2 words + } + + root_elements.push(super::RootElement { + types, + offset: root_table_offset, + }); + } + + // Ensure that we didn't reallocate! + debug_assert_eq!(ranges.len(), total_non_dynamic_entries); + assert_eq!(parameters.len(), parameter_offsets.len()); + + let (blob, error) = self + .library + .serialize_root_signature( + native::RootSignatureVersion::V1_0, + ¶meters, + &[], + native::RootSignatureFlags::ALLOW_IA_INPUT_LAYOUT, + ) + .map_err(|e| { + log::error!("Unable to find serialization function: {:?}", e); + crate::DeviceError::Lost + })? + .to_device_result("Root signature serialization")?; + + if !error.is_null() { + log::error!( + "Root signature serialization error: {:?}", + error.as_c_str().to_str().unwrap() + ); + error.destroy(); + return Err(crate::DeviceError::Lost); + } + + let raw = self + .raw + .create_root_signature(blob, 0) + .to_device_result("Root signature creation")?; + blob.destroy(); + + Ok(super::PipelineLayout { + raw, + parameter_offsets, + total_slots: root_offset, + elements: root_elements, + }) } - unsafe fn destroy_pipeline_layout(&self, pipeline_layout: Resource) {} + unsafe fn destroy_pipeline_layout(&self, pipeline_layout: super::PipelineLayout) { + pipeline_layout.raw.destroy(); + } + unsafe fn create_bind_group( &self, desc: &crate::BindGroupDescriptor, - ) -> Result { - Ok(Resource) + ) -> Result { + Ok(super::BindGroup {}) } - unsafe fn destroy_bind_group(&self, group: Resource) {} + unsafe fn destroy_bind_group(&self, group: super::BindGroup) {} unsafe fn create_shader_module( &self, diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index 62a620c3e..4ace6d4de 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -46,9 +46,9 @@ impl crate::Api for Api { type QuerySet = QuerySet; type Fence = Fence; - type BindGroupLayout = Resource; - type BindGroup = Resource; - type PipelineLayout = Resource; + type BindGroupLayout = BindGroupLayout; + type BindGroup = BindGroup; + type PipelineLayout = PipelineLayout; type ShaderModule = Resource; type RenderPipeline = Resource; type ComputePipeline = Resource; @@ -166,6 +166,8 @@ pub struct Device { dsv_pool: Mutex, srv_uav_pool: Mutex, sampler_pool: Mutex, + // library + library: Arc, } unsafe impl Send for Device {} @@ -241,6 +243,42 @@ pub struct Fence { unsafe impl Send for Fence {} unsafe impl Sync for Fence {} +pub struct BindGroupLayout { + /// Sorted list of entries. + entries: Vec, +} + +#[derive(Debug)] +pub struct BindGroup {} + +bitflags::bitflags! { + struct TableTypes: u8 { + const SRV_CBV_UAV = 0x1; + const SAMPLERS = 0x2; + } +} + +type RootSignatureOffset = usize; + +pub struct RootElement { + types: TableTypes, + offset: RootSignatureOffset, +} + +pub struct PipelineLayout { + raw: native::RootSignature, + /// A root offset per parameter. + parameter_offsets: Vec, + /// Total number of root slots occupied by the layout. + total_slots: u32, + // Storing for each associated bind group, which tables we created + // in the root signature. This is required for binding descriptor sets. + elements: arrayvec::ArrayVec<[RootElement; crate::MAX_BIND_GROUPS]>, +} + +unsafe impl Send for PipelineLayout {} +unsafe impl Sync for PipelineLayout {} + impl SwapChain { unsafe fn release_resources(self) -> native::WeakPtr { for resource in self.resources {