1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
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<u32, wgt::BindGroupLayoutEntry>,
/// 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<H: Hasher>(&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 [`EntryMap`] 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<Self, binding_model::CreateBindGroupLayoutError> {
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<Item = u32> + '_ {
self.inner.keys().copied()
}
/// Iterator over all the [`wgt::BindGroupLayoutEntry`]s in this map.
pub fn values(&self) -> impl ExactSizeIterator<Item = &wgt::BindGroupLayoutEntry> + '_ {
self.inner.values()
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = (&u32, &wgt::BindGroupLayoutEntry)> + '_ {
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)
}
pub fn sort(&mut self) {
self.inner.sort_unstable_keys();
self.sorted = true;
}
}