1use core::hash::{Hash, Hasher};
23use crate::{
4 binding_model::{self},
5 FastIndexMap,
6};
78/// Where a given BGL came from.
9#[derive(Debug, Copy, Clone, PartialEq, Eq)]
10pub enum Origin {
11/// The bind group layout was created by the user and is present in the BGL resource pool.
12Pool,
13/// The bind group layout was derived and is not present in the BGL resource pool.
14Derived,
15}
1617/// A HashMap-like structure that stores a BindGroupLayouts [`wgt::BindGroupLayoutEntry`]s.
18///
19/// It is hashable, so bind group layouts can be deduplicated.
20#[derive(Debug, Default, Clone, Eq)]
21pub struct EntryMap {
22/// We use a IndexMap here so that we can sort the entries by their binding index,
23 /// guaranteeing that the hash of equivalent layouts will be the same.
24inner: FastIndexMap<u32, wgt::BindGroupLayoutEntry>,
25/// We keep track of whether the map is sorted or not, so that we can assert that
26 /// it is sorted, so that PartialEq and Hash will be stable.
27 ///
28 /// We only need sorted if it is used in a Hash or PartialEq, so we never need
29 /// to actively sort it.
30sorted: bool,
31}
3233impl PartialEq for EntryMap {
34fn eq(&self, other: &Self) -> bool {
35self.assert_sorted();
36 other.assert_sorted();
3738self.inner == other.inner
39 }
40}
4142impl Hash for EntryMap {
43fn hash<H: Hasher>(&self, state: &mut H) {
44self.assert_sorted();
4546// We don't need to hash the keys, since they are just extracted from the values.
47 //
48 // We know this is stable and will match the behavior of PartialEq as we ensure
49 // that the array is sorted.
50for entry in self.inner.values() {
51 entry.hash(state);
52 }
53 }
54}
5556impl EntryMap {
57fn assert_sorted(&self) {
58assert!(self.sorted);
59 }
6061/// Create a new [`EntryMap`] from a slice of [`wgt::BindGroupLayoutEntry`]s.
62 ///
63 /// Errors if there are duplicate bindings or if any binding index is greater than
64 /// the device's limits.
65pub fn from_entries(
66 device_limits: &wgt::Limits,
67 entries: &[wgt::BindGroupLayoutEntry],
68 ) -> Result<Self, binding_model::CreateBindGroupLayoutError> {
69let mut inner = FastIndexMap::with_capacity_and_hasher(entries.len(), Default::default());
70for entry in entries {
71if entry.binding >= device_limits.max_bindings_per_bind_group {
72return Err(
73 binding_model::CreateBindGroupLayoutError::InvalidBindingIndex {
74 binding: entry.binding,
75 maximum: device_limits.max_bindings_per_bind_group,
76 },
77 );
78 }
79if inner.insert(entry.binding, *entry).is_some() {
80return Err(binding_model::CreateBindGroupLayoutError::ConflictBinding(
81 entry.binding,
82 ));
83 }
84 }
85 inner.sort_unstable_keys();
8687Ok(Self {
88 inner,
89 sorted: true,
90 })
91 }
9293/// Get the count of [`wgt::BindGroupLayoutEntry`]s in this map.
94pub fn len(&self) -> usize {
95self.inner.len()
96 }
9798/// Get the [`wgt::BindGroupLayoutEntry`] for the given binding index.
99pub fn get(&self, binding: u32) -> Option<&wgt::BindGroupLayoutEntry> {
100self.inner.get(&binding)
101 }
102103/// Iterator over all the binding indices in this map.
104pub fn indices(&self) -> impl ExactSizeIterator<Item = u32> + '_ {
105self.inner.keys().copied()
106 }
107108/// Iterator over all the [`wgt::BindGroupLayoutEntry`]s in this map.
109pub fn values(&self) -> impl ExactSizeIterator<Item = &wgt::BindGroupLayoutEntry> + '_ {
110self.inner.values()
111 }
112113pub fn iter(&self) -> impl ExactSizeIterator<Item = (&u32, &wgt::BindGroupLayoutEntry)> + '_ {
114self.inner.iter()
115 }
116117pub fn is_empty(&self) -> bool {
118self.inner.is_empty()
119 }
120121pub fn contains_key(&self, key: u32) -> bool {
122self.inner.contains_key(&key)
123 }
124125pub fn entry(&mut self, key: u32) -> indexmap::map::Entry<'_, u32, wgt::BindGroupLayoutEntry> {
126self.sorted = false;
127self.inner.entry(key)
128 }
129130pub fn sort(&mut self) {
131self.inner.sort_unstable_keys();
132self.sorted = true;
133 }
134}