diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b9e652367..6ffbefdaeda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change Log +### backend-dx12-0.6.1 (18-08-2020) + - fix descriptor binding + ### backend-vulkan-0.6.1 (17-08-2020) - fix Android build diff --git a/src/backend/dx12/Cargo.toml b/src/backend/dx12/Cargo.toml index 5a6e14a445b..1bc6a161f8a 100644 --- a/src/backend/dx12/Cargo.toml +++ b/src/backend/dx12/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gfx-backend-dx12" -version = "0.6.0" +version = "0.6.1" description = "DirectX-12 API backend for gfx-rs" homepage = "https://github.com/gfx-rs/gfx" repository = "https://github.com/gfx-rs/gfx" diff --git a/src/backend/dx12/src/command.rs b/src/backend/dx12/src/command.rs index c11eb03adc6..38b24f25255 100644 --- a/src/backend/dx12/src/command.rs +++ b/src/backend/dx12/src/command.rs @@ -20,8 +20,8 @@ use winapi::{ use smallvec::SmallVec; use crate::{ - conv, descriptors_cpu, device, internal, resource as r, root_constants::RootConstant, - validate_line_width, Backend, Device, Shared, MAX_VERTEX_BUFFERS, + conv, descriptors_cpu, device, internal, resource as r, validate_line_width, Backend, Device, + Shared, MAX_VERTEX_BUFFERS, }; // Fixed size of the root signature. @@ -103,6 +103,15 @@ struct UserData { dirty_mask: u64, } +impl Default for UserData { + fn default() -> Self { + UserData { + data: [RootElement::Undefined; ROOT_SIGNATURE_SIZE], + dirty_mask: 0, + } + } +} + impl fmt::Debug for UserData { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("UserData") @@ -113,13 +122,6 @@ impl fmt::Debug for UserData { } impl UserData { - fn new() -> Self { - UserData { - data: [RootElement::Undefined; ROOT_SIGNATURE_SIZE], - dirty_mask: 0, - } - } - /// Write root constant values into the user data, overwriting virtual memory /// range [offset..offset + data.len()]. Changes are marked as dirty. fn set_constants(&mut self, offset: usize, data: &[u32]) { @@ -164,50 +166,38 @@ impl UserData { self.dirty_mask != 0 } - fn is_index_dirty(&self, i: usize) -> bool { + fn is_index_dirty(&self, i: u32) -> bool { ((self.dirty_mask >> i) & 1) == 1 } - /// Clear dirty flag. - fn clear_dirty(&mut self, i: usize) { - self.dirty_mask &= !(1 << i); - } - /// Mark all entries as dirty. fn dirty_all(&mut self) { self.dirty_mask = !0; } + + /// Mark all entries as clear up to the given i. + fn clear_up_to(&mut self, i: u32) { + self.dirty_mask &= !((1 << i) - 1); + } } -#[derive(Debug)] +#[derive(Debug, Default)] struct PipelineCache { - // Bound pipeline and root signature. + // Bound pipeline and layout info. // Changed on bind pipeline calls. - pipeline: Option<(native::PipelineState, native::RootSignature)>, - // Parameter slots of the current root signature. - num_parameter_slots: usize, - // - root_constants: Vec, + pipeline: Option<(native::PipelineState, Arc)>, + // Virtualized root signature user data of the shaders user_data: UserData, + temp_constants: Vec, + // Descriptor heap gpu handle offsets srv_cbv_uav_start: u64, sampler_start: u64, } impl PipelineCache { - fn new() -> Self { - PipelineCache { - pipeline: None, - num_parameter_slots: 0, - root_constants: Vec::new(), - user_data: UserData::new(), - srv_cbv_uav_start: 0, - sampler_start: 0, - } - } - fn bind_descriptor_sets<'a, I, J>( &mut self, layout: &r::PipelineLayout, @@ -243,34 +233,30 @@ impl PipelineCache { for (set, element) in sets.zip(layout.elements[first_set..].iter()) { let set = set.borrow(); - - let mut num_words = 0; - let table = &element.table; + let mut root_offset = element.table.offset; // Bind CBV/SRC/UAV descriptor tables - set.first_gpu_view.map(|gpu| { - assert!(table.ty.contains(r::SRV_CBV_UAV)); - + if let Some(gpu) = set.first_gpu_view { + assert!(element.table.ty.contains(r::SRV_CBV_UAV)); // Cast is safe as offset **must** be in u32 range. Unable to // create heaps with more descriptors. let table_gpu_offset = (gpu.ptr - srv_cbv_uav_start) as u32; - let table_offset = table.offset + num_words; self.user_data - .set_srv_cbv_uav_table(table_offset, table_gpu_offset); - num_words += 1; - }); + .set_srv_cbv_uav_table(root_offset, table_gpu_offset); + root_offset += 1; + } // Bind Sampler descriptor tables. - set.first_gpu_sampler.map(|gpu| { - assert!(table.ty.contains(r::SAMPLERS)); + if let Some(gpu) = set.first_gpu_sampler { + assert!(element.table.ty.contains(r::SAMPLERS)); + // Cast is safe as offset **must** be in u32 range. Unable to // create heaps with more descriptors. let table_gpu_offset = (gpu.ptr - sampler_start) as u32; - let table_offset = table.offset + num_words; self.user_data - .set_sampler_table(table_offset, table_gpu_offset); - num_words += 1; - }); + .set_sampler_table(root_offset, table_gpu_offset); + root_offset += 1; + } // Bind root descriptors // TODO: slow, can we move the dynamic descriptors into toplevel somehow during initialization? @@ -279,18 +265,93 @@ impl PipelineCache { // It's not valid to modify the descriptor sets during recording -> access if safe. let dynamic_descriptors = unsafe { &*binding.dynamic_descriptors.get() }; for descriptor in dynamic_descriptors { - let root_offset = table.offset + num_words; - self.user_data.set_descriptor_cbv( - root_offset, - descriptor.gpu_buffer_location + offsets.next().unwrap(), - ); - num_words += 2; + let gpu_offset = descriptor.gpu_buffer_location + offsets.next().unwrap(); + self.user_data.set_descriptor_cbv(root_offset, gpu_offset); + root_offset += 2; } } } [heap_srv_cbv_uav, heap_sampler] } + + fn flush_user_data( + &mut self, + mut constants_update: F, + mut table_update: G, + mut descriptor_cbv_update: H, + ) where + F: FnMut(u32, &[u32]), + G: FnMut(u32, d3d12::D3D12_GPU_DESCRIPTOR_HANDLE), + H: FnMut(u32, d3d12::D3D12_GPU_VIRTUAL_ADDRESS), + { + let user_data = &mut self.user_data; + if !user_data.is_dirty() { + return; + } + + let shared = match self.pipeline { + Some((_, ref shared)) => shared, + None => return, + }; + + for (i, &root_offset) in shared.parameter_offsets.iter().enumerate() { + if !user_data.is_index_dirty(root_offset) { + continue; + } + match user_data.data[root_offset as usize] { + RootElement::Constant(_) => { + let c = &shared.constants[i]; + debug_assert_eq!(root_offset, c.range.start); + self.temp_constants.clear(); + self.temp_constants.extend( + user_data.data[c.range.start as usize..c.range.end as usize] + .iter() + .map(|ud| match *ud { + RootElement::Constant(v) => v, + _ => { + warn!( + "Unset or mismatching root constant at index {:?} ({:?})", + c, ud + ); + 0 + } + }), + ); + constants_update(root_offset, &self.temp_constants); + } + RootElement::TableSrvCbvUav(offset) => { + let gpu = d3d12::D3D12_GPU_DESCRIPTOR_HANDLE { + ptr: self.srv_cbv_uav_start + offset as u64, + }; + table_update(root_offset, gpu); + } + RootElement::TableSampler(offset) => { + let gpu = d3d12::D3D12_GPU_DESCRIPTOR_HANDLE { + ptr: self.sampler_start + offset as u64, + }; + table_update(root_offset, gpu); + } + RootElement::DescriptorCbv { buffer } => { + debug_assert!(user_data.is_index_dirty(root_offset + 1)); + debug_assert_eq!( + user_data.data[root_offset as usize + 1], + RootElement::DescriptorPlaceholder + ); + + descriptor_cbv_update(root_offset, buffer); + } + RootElement::DescriptorPlaceholder | RootElement::Undefined => { + error!( + "Undefined user data element in the root signature at {}", + root_offset + ); + } + } + } + + user_data.clear_up_to(shared.total_slots); + } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -410,9 +471,9 @@ impl CommandBuffer { is_active: false, pass_cache: None, cur_subpass: !0, - gr_pipeline: PipelineCache::new(), + gr_pipeline: PipelineCache::default(), primitive_topology: d3dcommon::D3D_PRIMITIVE_TOPOLOGY_UNDEFINED, - comp_pipeline: PipelineCache::new(), + comp_pipeline: PipelineCache::default(), active_bindpoint: BindPoint::Graphics { internal: false }, active_descriptor_heaps: [native::DescriptorHeap::null(); 2], occlusion_query: None, @@ -463,9 +524,9 @@ impl CommandBuffer { self.pass_cache = None; self.cur_subpass = !0; - self.gr_pipeline = PipelineCache::new(); + self.gr_pipeline = PipelineCache::default(); self.primitive_topology = d3dcommon::D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; - self.comp_pipeline = PipelineCache::new(); + self.comp_pipeline = PipelineCache::default(); self.active_bindpoint = BindPoint::Graphics { internal: false }; self.active_descriptor_heaps = [native::DescriptorHeap::null(); 2]; self.occlusion_query = None; @@ -687,20 +748,22 @@ impl CommandBuffer { match self.active_bindpoint { BindPoint::Compute => { // Switch to graphics bind point - let (pipeline, _) = self + let &(pipeline, _) = self .gr_pipeline .pipeline + .as_ref() .expect("No graphics pipeline bound"); self.raw.set_pipeline_state(pipeline); } BindPoint::Graphics { internal: true } => { // Switch to graphics bind point - let (pipeline, signature) = self + let &(pipeline, ref shared) = self .gr_pipeline .pipeline + .as_ref() .expect("No graphics pipeline bound"); self.raw.set_pipeline_state(pipeline); - self.raw.set_graphics_root_signature(signature); + self.raw.set_graphics_root_signature(shared.signature); self.bind_descriptor_heaps(); } BindPoint::Graphics { internal: false } => {} @@ -710,8 +773,7 @@ impl CommandBuffer { let cmd_buffer = &mut self.raw; // Flush root signature data - Self::flush_user_data( - &mut self.gr_pipeline, + self.gr_pipeline.flush_user_data( |slot, data| unsafe { cmd_buffer.clone().SetGraphicsRoot32BitConstants( slot, @@ -729,9 +791,10 @@ impl CommandBuffer { match self.active_bindpoint { BindPoint::Graphics { internal } => { // Switch to compute bind point - let (pipeline, _) = self + let &(pipeline, _) = self .comp_pipeline .pipeline + .as_ref() .expect("No compute pipeline bound"); self.raw.set_pipeline_state(pipeline); @@ -743,8 +806,8 @@ impl CommandBuffer { // Rebind the graphics root signature as we come from an internal graphics. // Issuing a draw call afterwards would hide the information that we internally // changed the graphics root signature. - if let Some((_, signature)) = self.gr_pipeline.pipeline { - self.raw.set_graphics_root_signature(signature); + if let Some((_, ref shared)) = self.gr_pipeline.pipeline { + self.raw.set_graphics_root_signature(shared.signature); } } } @@ -752,8 +815,7 @@ impl CommandBuffer { } let cmd_buffer = &mut self.raw; - Self::flush_user_data( - &mut self.comp_pipeline, + self.comp_pipeline.flush_user_data( |slot, data| unsafe { cmd_buffer.clone().SetComputeRoot32BitConstants( slot, @@ -767,96 +829,6 @@ impl CommandBuffer { ); } - fn flush_user_data( - pipeline: &mut PipelineCache, - mut constants_update: F, - mut table_update: G, - mut descriptor_cbv_update: H, - ) where - F: FnMut(u32, &[u32]), - G: FnMut(u32, d3d12::D3D12_GPU_DESCRIPTOR_HANDLE), - H: FnMut(u32, d3d12::D3D12_GPU_VIRTUAL_ADDRESS), - { - let user_data = &mut pipeline.user_data; - if !user_data.is_dirty() { - return; - } - - let num_root_constant = pipeline.root_constants.len(); - let mut cur_index = 0; - // TODO: opt: Only set dirty root constants? - for (i, root_constant) in pipeline.root_constants.iter().enumerate() { - let num_constants = (root_constant.range.end - root_constant.range.start) as usize; - let mut data = Vec::new(); - for c in cur_index..cur_index + num_constants { - data.push(match user_data.data[c] { - RootElement::Constant(v) => v, - _ => { - warn!( - "Unset or mismatching root constant at index {:?} ({:?})", - c, user_data.data[c] - ); - 0 - } - }); - user_data.clear_dirty(c); - } - constants_update(i as _, &data); - cur_index += num_constants; - } - - // Flush descriptor tables & root descriptors - // Index in the user data array where tables are starting - let mut root_index = pipeline - .root_constants - .iter() - .fold(0, |sum, c| sum + c.range.end - c.range.start) - as usize; - - for i in num_root_constant..pipeline.num_parameter_slots { - if user_data.is_index_dirty(root_index) { - match user_data.data[root_index] { - RootElement::TableSrvCbvUav(offset) => { - let gpu = d3d12::D3D12_GPU_DESCRIPTOR_HANDLE { - ptr: pipeline.srv_cbv_uav_start + offset as u64, - }; - table_update(i as _, gpu); - user_data.clear_dirty(root_index); - root_index += 1; - } - RootElement::TableSampler(offset) => { - let gpu = d3d12::D3D12_GPU_DESCRIPTOR_HANDLE { - ptr: pipeline.sampler_start + offset as u64, - }; - table_update(i as _, gpu); - user_data.clear_dirty(root_index); - root_index += 1; - } - RootElement::DescriptorCbv { buffer } => { - debug_assert!(user_data.is_index_dirty(root_index + 1)); - debug_assert_eq!( - user_data.data[root_index + 1], - RootElement::DescriptorPlaceholder - ); - - descriptor_cbv_update(i as _, buffer); - - user_data.clear_dirty(root_index); - user_data.clear_dirty(root_index + 1); // skip placeholder - root_index += 2; - } - other => { - error!( - "Unexpected user data element in the root signature ({:?})", - (root_index, other) - ); - continue; - } - }; - } - } - } - fn transition_barrier( transition: d3d12::D3D12_RESOURCE_TRANSITION_BARRIER, ) -> d3d12::D3D12_RESOURCE_BARRIER { @@ -1960,13 +1932,12 @@ impl com::CommandBuffer for CommandBuffer { unsafe fn bind_graphics_pipeline(&mut self, pipeline: &r::GraphicsPipeline) { match self.gr_pipeline.pipeline { - Some((_, signature)) if signature == pipeline.signature => { + Some((_, ref shared)) if Arc::ptr_eq(shared, &pipeline.shared) => { // Same root signature, nothing to do } _ => { - self.raw.set_graphics_root_signature(pipeline.signature); - self.gr_pipeline.num_parameter_slots = pipeline.num_parameter_slots; - self.gr_pipeline.root_constants = pipeline.constants.clone(); + self.raw + .set_graphics_root_signature(pipeline.shared.signature); // All slots need to be rebound internally on signature change. self.gr_pipeline.user_data.dirty_all(); } @@ -1976,7 +1947,7 @@ impl com::CommandBuffer for CommandBuffer { self.primitive_topology = pipeline.topology; self.active_bindpoint = BindPoint::Graphics { internal: false }; - self.gr_pipeline.pipeline = Some((pipeline.raw, pipeline.signature)); + self.gr_pipeline.pipeline = Some((pipeline.raw, Arc::clone(&pipeline.shared))); self.vertex_bindings_remap = pipeline.vertex_bindings; self.set_vertex_buffers(); @@ -2015,13 +1986,12 @@ impl com::CommandBuffer for CommandBuffer { unsafe fn bind_compute_pipeline(&mut self, pipeline: &r::ComputePipeline) { match self.comp_pipeline.pipeline { - Some((_, signature)) if signature == pipeline.signature => { + Some((_, ref shared)) if Arc::ptr_eq(shared, &pipeline.shared) => { // Same root signature, nothing to do } _ => { - self.raw.set_compute_root_signature(pipeline.signature); - self.comp_pipeline.num_parameter_slots = pipeline.num_parameter_slots; - self.comp_pipeline.root_constants = pipeline.constants.clone(); + self.raw + .set_compute_root_signature(pipeline.shared.signature); // All slots need to be rebound internally on signature change. self.comp_pipeline.user_data.dirty_all(); } @@ -2029,7 +1999,7 @@ impl com::CommandBuffer for CommandBuffer { self.raw.set_pipeline_state(pipeline.raw); self.active_bindpoint = BindPoint::Compute; - self.comp_pipeline.pipeline = Some((pipeline.raw, pipeline.signature)); + self.comp_pipeline.pipeline = Some((pipeline.raw, Arc::clone(&pipeline.shared))); } unsafe fn bind_compute_descriptor_sets( diff --git a/src/backend/dx12/src/device.rs b/src/backend/dx12/src/device.rs index b5dbcb38a1a..3fbba1f9192 100644 --- a/src/backend/dx12/src/device.rs +++ b/src/backend/dx12/src/device.rs @@ -4,6 +4,7 @@ use std::{ ffi, mem, ops::Range, ptr, slice, + sync::Arc, }; use range_alloc::RangeAllocator; @@ -268,7 +269,11 @@ impl Device { layout: &r::PipelineLayout, ) -> Result<(), d::ShaderError> { // Move the descriptor sets away to yield for the root constants at "space0". - let space_offset = if layout.constants.is_empty() { 0 } else { 1 }; + let space_offset = if layout.shared.constants.is_empty() { + 0 + } else { + 1 + }; let shader_resources = ast.get_shader_resources().map_err(gen_query_error)?; if space_offset != 0 { @@ -405,6 +410,7 @@ impl Device { let stage_flag = stage.to_flag(); let root_constant_layout = layout + .shared .constants .iter() .filter_map(|constant| { @@ -1519,12 +1525,12 @@ impl d::Device for Device { let sets = sets.into_iter().collect::>(); - let mut root_offset = 0; + let mut root_offset = 0u32; let root_constants = root_constants::split(push_constant_ranges) .iter() .map(|constant| { assert!(constant.range.start <= constant.range.end); - root_offset += (constant.range.end - constant.range.start) as usize; + root_offset += constant.range.end - constant.range.start; RootConstant { stages: constant.stages, @@ -1542,6 +1548,7 @@ impl d::Device for Device { // Number of elements in the root signature. // Guarantees that no re-allocation is done, and our pointers are valid let mut parameters = Vec::with_capacity(root_constants.len() + sets.len() * 2); + let mut parameter_offsets = Vec::with_capacity(parameters.capacity()); // Convert root signature descriptions into root signature parameters. for root_constant in root_constants.iter() { @@ -1549,6 +1556,7 @@ impl d::Device for Device { "\tRoot constant set={} range {:?}", ROOT_CONSTANT_SPACE, root_constant.range ); + parameter_offsets.push(root_constant.range.start); parameters.push(native::RootParameter::constants( conv::map_shader_visibility(root_constant.stages), native::Binding { @@ -1638,6 +1646,7 @@ impl d::Device for Device { } } if ranges.len() > range_base { + parameter_offsets.push(root_offset); parameters.push(native::RootParameter::descriptor_table( visibility, &ranges[range_base..], @@ -1655,6 +1664,7 @@ impl d::Device for Device { } } if ranges.len() > range_base { + parameter_offsets.push(root_offset); parameters.push(native::RootParameter::descriptor_table( visibility, &ranges[range_base..], @@ -1674,8 +1684,9 @@ impl d::Device for Device { if content.contains(r::DescriptorContent::CBV) { descriptors.push(r::RootDescriptor { - offset: root_offset, + offset: root_offset as usize, }); + parameter_offsets.push(root_offset); parameters .push(native::RootParameter::cbv_descriptor(visibility, binding)); root_offset += 2; // root CBV costs 2 words @@ -1699,6 +1710,7 @@ impl d::Device for Device { // Ensure that we didn't reallocate! debug_assert_eq!(ranges.len(), total); + assert_eq!(parameters.len(), parameter_offsets.len()); // TODO: error handling let (signature_raw, error) = match self.library.serialize_root_signature( @@ -1725,10 +1737,13 @@ impl d::Device for Device { signature_raw.destroy(); Ok(r::PipelineLayout { - raw: signature, - constants: root_constants, + shared: Arc::new(r::PipelineShared { + signature, + constants: root_constants, + parameter_offsets, + total_slots: root_offset, + }), elements, - num_parameter_slots: parameters.len(), }) } @@ -1960,7 +1975,7 @@ impl d::Device for Device { // Setup pipeline description let pso_desc = d3d12::D3D12_GRAPHICS_PIPELINE_STATE_DESC { - pRootSignature: desc.layout.raw.as_mut_ptr(), + pRootSignature: desc.layout.shared.signature.as_mut_ptr(), VS: *vs.shader(), PS: *ps.shader(), GS: *gs.shader(), @@ -2065,10 +2080,8 @@ impl d::Device for Device { Ok(r::GraphicsPipeline { raw: pipeline, - signature: desc.layout.raw, - num_parameter_slots: desc.layout.num_parameter_slots, + shared: Arc::clone(&desc.layout.shared), topology, - constants: desc.layout.constants.clone(), vertex_bindings, baked_states, }) @@ -2092,7 +2105,7 @@ impl d::Device for Device { .map_err(|err| pso::CreationError::Shader(err))?; let (pipeline, hr) = self.raw.create_compute_pipeline_state( - desc.layout.raw, + desc.layout.shared.signature, native::Shader::from_blob(cs), 0, native::CachedPSO::null(), @@ -2106,11 +2119,10 @@ impl d::Device for Device { if winerror::SUCCEEDED(hr) { Ok(r::ComputePipeline { raw: pipeline, - signature: desc.layout.raw, - num_parameter_slots: desc.layout.num_parameter_slots, - constants: desc.layout.constants.clone(), + shared: Arc::clone(&desc.layout.shared), }) } else { + error!("Failed to build shader: {:x}", hr); Err(pso::CreationError::Other) } } @@ -3426,7 +3438,7 @@ impl d::Device for Device { } unsafe fn destroy_pipeline_layout(&self, layout: r::PipelineLayout) { - layout.raw.destroy(); + layout.shared.signature.destroy(); } unsafe fn destroy_graphics_pipeline(&self, pipeline: r::GraphicsPipeline) { diff --git a/src/backend/dx12/src/resource.rs b/src/backend/dx12/src/resource.rs index c792b1011da..d19e3a4a606 100644 --- a/src/backend/dx12/src/resource.rs +++ b/src/backend/dx12/src/resource.rs @@ -8,7 +8,7 @@ use range_alloc::RangeAllocator; use crate::{root_constants::RootConstant, Backend, MAX_VERTEX_BUFFERS}; -use std::{cell::UnsafeCell, collections::BTreeMap, fmt, ops::Range}; +use std::{cell::UnsafeCell, collections::BTreeMap, fmt, ops::Range, sync::Arc}; // ShaderModule is either a precompiled if the source comes from HLSL or // the SPIR-V module doesn't contain specialization constants or push constants @@ -97,10 +97,8 @@ pub struct VertexBinding { #[derive(Debug)] pub struct GraphicsPipeline { pub(crate) raw: native::PipelineState, - pub(crate) signature: native::RootSignature, // weak-ptr, owned by `PipelineLayout` - pub(crate) num_parameter_slots: usize, // signature parameter slots, see `PipelineLayout` + pub(crate) shared: Arc, pub(crate) topology: d3d12::D3D12_PRIMITIVE_TOPOLOGY, - pub(crate) constants: Vec, pub(crate) vertex_bindings: [Option; MAX_VERTEX_BUFFERS], pub(crate) baked_states: pso::BakedStates, } @@ -110,9 +108,7 @@ unsafe impl Sync for GraphicsPipeline {} #[derive(Debug)] pub struct ComputePipeline { pub(crate) raw: native::PipelineState, - pub(crate) signature: native::RootSignature, // weak-ptr, owned by `PipelineLayout` - pub(crate) num_parameter_slots: usize, // signature parameter slots, see `PipelineLayout` - pub(crate) constants: Vec, + pub(crate) shared: Arc, } unsafe impl Send for ComputePipeline {} @@ -150,19 +146,26 @@ pub struct RootElement { } #[derive(Debug)] -pub struct PipelineLayout { - pub(crate) raw: native::RootSignature, - // Disjunct, sorted vector of root constant ranges. +pub struct PipelineShared { + pub(crate) signature: native::RootSignature, + /// Disjunct, sorted vector of root constant ranges. pub(crate) constants: Vec, + /// A root offset per parameter. + pub(crate) parameter_offsets: Vec, + /// Total number of root slots occupied by the pipeline. + pub(crate) total_slots: u32, +} + +unsafe impl Send for PipelineShared {} +unsafe impl Sync for PipelineShared {} + +#[derive(Debug)] +pub struct PipelineLayout { + pub(crate) shared: Arc, // Storing for each associated descriptor set layout, which tables we created // in the root signature. This is required for binding descriptor sets. pub(crate) elements: Vec, - // Number of parameter slots in this layout, can be larger than number of tables. - // Required for updating the root signature when flushing user data. - pub(crate) num_parameter_slots: usize, } -unsafe impl Send for PipelineLayout {} -unsafe impl Sync for PipelineLayout {} #[derive(Debug, Clone)] pub struct Framebuffer {