use std::hash::{Hash, Hasher}; use crate::{ binding_model::{self}, FastIndexMap, }; /// Where a given BGL came from. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Origin { /// The bind group layout was created by the user and is present in the BGL resource pool. Pool, /// The bind group layout was derived and is not present in the BGL resource pool. Derived, } /// A HashMap-like structure that stores a BindGroupLayouts [`wgt::BindGroupLayoutEntry`]s. /// /// It is hashable, so bind group layouts can be deduplicated. #[derive(Debug, Default, Clone, Eq)] pub struct EntryMap { /// We use a IndexMap here so that we can sort the entries by their binding index, /// guaranteeing that the hash of equivalent layouts will be the same. inner: FastIndexMap, /// We keep track of whether the map is sorted or not, so that we can assert that /// it is sorted, so that PartialEq and Hash will be stable. /// /// We only need sorted if it is used in a Hash or PartialEq, so we never need /// to actively sort it. sorted: bool, } impl PartialEq for EntryMap { fn eq(&self, other: &Self) -> bool { self.assert_sorted(); other.assert_sorted(); self.inner == other.inner } } impl Hash for EntryMap { fn hash(&self, state: &mut H) { self.assert_sorted(); // We don't need to hash the keys, since they are just extracted from the values. // // We know this is stable and will match the behavior of PartialEq as we ensure // that the array is sorted. for entry in self.inner.values() { entry.hash(state); } } } impl EntryMap { fn assert_sorted(&self) { assert!(self.sorted); } /// Create a new [`BindGroupLayoutEntryMap`] from a slice of [`wgt::BindGroupLayoutEntry`]s. /// /// Errors if there are duplicate bindings or if any binding index is greater than /// the device's limits. pub fn from_entries( device_limits: &wgt::Limits, entries: &[wgt::BindGroupLayoutEntry], ) -> Result { let mut inner = FastIndexMap::with_capacity_and_hasher(entries.len(), Default::default()); for entry in entries { if entry.binding >= device_limits.max_bindings_per_bind_group { return Err( binding_model::CreateBindGroupLayoutError::InvalidBindingIndex { binding: entry.binding, maximum: device_limits.max_bindings_per_bind_group, }, ); } if inner.insert(entry.binding, *entry).is_some() { return Err(binding_model::CreateBindGroupLayoutError::ConflictBinding( entry.binding, )); } } inner.sort_unstable_keys(); Ok(Self { inner, sorted: true, }) } /// Get the count of [`wgt::BindGroupLayoutEntry`]s in this map. pub fn len(&self) -> usize { self.inner.len() } /// Get the [`wgt::BindGroupLayoutEntry`] for the given binding index. pub fn get(&self, binding: u32) -> Option<&wgt::BindGroupLayoutEntry> { self.inner.get(&binding) } /// Iterator over all the binding indices in this map. pub fn indices(&self) -> impl ExactSizeIterator + '_ { self.inner.keys().copied() } /// Iterator over all the [`wgt::BindGroupLayoutEntry`]s in this map. pub fn values(&self) -> impl ExactSizeIterator + '_ { self.inner.values() } pub fn iter(&self) -> impl ExactSizeIterator + '_ { self.inner.iter() } pub fn is_empty(&self) -> bool { self.inner.is_empty() } pub fn contains_key(&self, key: u32) -> bool { self.inner.contains_key(&key) } pub fn entry(&mut self, key: u32) -> indexmap::map::Entry<'_, u32, wgt::BindGroupLayoutEntry> { self.sorted = false; self.inner.entry(key) } }