Skip to content

Commit cf08e7a

Browse files
committed
proper prehashing
1 parent a5521f5 commit cf08e7a

File tree

3 files changed

+165
-43
lines changed

3 files changed

+165
-43
lines changed

crates/bevy_render/src/mesh/mesh/mod.rs

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,8 @@ use bevy_core::cast_slice;
1010
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
1111
use bevy_math::*;
1212
use bevy_reflect::TypeUuid;
13-
use bevy_utils::{AHasher, EnumVariantMeta};
14-
use std::{
15-
collections::BTreeMap,
16-
hash::{Hash, Hasher},
17-
};
13+
use bevy_utils::{EnumVariantMeta, Hashed};
14+
use std::{collections::BTreeMap, hash::Hash};
1815
use wgpu::{
1916
util::BufferInitDescriptor, BufferUsages, IndexFormat, PrimitiveTopology, VertexAttribute,
2017
VertexFormat, VertexStepMode,
@@ -186,14 +183,14 @@ impl Mesh {
186183
accumulated_offset += data.attribute.format.get_size();
187184
}
188185

189-
MeshVertexBufferLayout::new(
190-
VertexBufferLayout {
186+
MeshVertexBufferLayout::new(InnerMeshVertexBufferLayout {
187+
layout: VertexBufferLayout {
191188
array_stride: accumulated_offset,
192189
step_mode: VertexStepMode::Vertex,
193190
attributes,
194191
},
195192
attribute_ids,
196-
)
193+
})
197194
}
198195

199196
/// Counts all vertices of the mesh.
@@ -390,26 +387,15 @@ impl From<MeshVertexAttribute> for MeshVertexAttributeId {
390387
}
391388
}
392389

393-
#[derive(Debug, Clone)]
394-
pub struct MeshVertexBufferLayout {
390+
pub type MeshVertexBufferLayout = Hashed<InnerMeshVertexBufferLayout>;
391+
392+
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
393+
pub struct InnerMeshVertexBufferLayout {
395394
attribute_ids: Vec<MeshVertexAttributeId>,
396395
layout: VertexBufferLayout,
397-
hash: MeshVertexBufferLayoutHash,
398396
}
399397

400-
impl MeshVertexBufferLayout {
401-
pub fn new(layout: VertexBufferLayout, attribute_ids: Vec<MeshVertexAttributeId>) -> Self {
402-
let mut hasher = AHasher::default();
403-
let key = (layout, attribute_ids);
404-
key.hash(&mut hasher);
405-
406-
Self {
407-
layout: key.0,
408-
attribute_ids: key.1,
409-
hash: MeshVertexBufferLayoutHash(hasher.finish()),
410-
}
411-
}
412-
398+
impl InnerMeshVertexBufferLayout {
413399
#[inline]
414400
pub fn contains(&self, attribute_id: impl Into<MeshVertexAttributeId>) -> bool {
415401
self.attribute_ids.contains(&attribute_id.into())
@@ -454,16 +440,8 @@ impl MeshVertexBufferLayout {
454440
attributes,
455441
})
456442
}
457-
458-
#[inline]
459-
pub(crate) fn hash(&self) -> MeshVertexBufferLayoutHash {
460-
self.hash
461-
}
462443
}
463444

464-
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
465-
pub(crate) struct MeshVertexBufferLayoutHash(u64);
466-
467445
pub struct VertexAttributeDescriptor {
468446
pub shader_location: u32,
469447
pub id: MeshVertexAttributeId,

crates/bevy_render/src/render_resource/pipeline_specializer.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::{
2-
mesh::{MeshVertexBufferLayout, MeshVertexBufferLayoutHash},
2+
mesh::{InnerMeshVertexBufferLayout, MeshVertexBufferLayout},
33
render_resource::{CachedPipelineId, RenderPipelineCache, RenderPipelineDescriptor},
44
};
5-
use bevy_utils::HashMap;
5+
use bevy_utils::{HashMap, PreHashMap, PreHashMapExt};
66
use std::hash::Hash;
77

88
pub struct SpecializedPipelines<S: SpecializedPipeline> {
@@ -35,8 +35,9 @@ pub trait SpecializedPipeline {
3535
type Key: Clone + Hash + PartialEq + Eq;
3636
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor;
3737
}
38+
3839
pub struct SpecializedMeshPipelines<S: SpecializedMeshPipeline> {
39-
cache: HashMap<(S::Key, MeshVertexBufferLayoutHash), CachedPipelineId>,
40+
cache: PreHashMap<InnerMeshVertexBufferLayout, HashMap<S::Key, CachedPipelineId>>,
4041
}
4142

4243
impl<S: SpecializedMeshPipeline> Default for SpecializedMeshPipelines<S> {
@@ -48,20 +49,19 @@ impl<S: SpecializedMeshPipeline> Default for SpecializedMeshPipelines<S> {
4849
}
4950

5051
impl<S: SpecializedMeshPipeline> SpecializedMeshPipelines<S> {
52+
#[inline]
5153
pub fn specialize(
5254
&mut self,
5355
cache: &mut RenderPipelineCache,
5456
specialize_pipeline: &S,
5557
key: S::Key,
5658
layout: &MeshVertexBufferLayout,
5759
) -> CachedPipelineId {
58-
*self
59-
.cache
60-
.entry((key.clone(), layout.hash()))
61-
.or_insert_with(|| {
62-
let descriptor = specialize_pipeline.specialize(key, layout);
63-
cache.queue(descriptor)
64-
})
60+
let map = self.cache.get_or_insert_with(layout, Default::default);
61+
*map.entry(key.clone()).or_insert_with(|| {
62+
let descriptor = specialize_pipeline.specialize(key, layout);
63+
cache.queue(descriptor)
64+
})
6565
}
6666
}
6767

crates/bevy_utils/src/lib.rs

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@ pub mod label;
44
pub use ahash::AHasher;
55
pub use enum_variant_meta::*;
66
pub type Entry<'a, K, V> = hashbrown::hash_map::Entry<'a, K, V, RandomState>;
7+
pub use hashbrown;
8+
use hashbrown::hash_map::RawEntryMut;
79
pub use instant::{Duration, Instant};
810
pub use tracing;
911
pub use uuid::Uuid;
1012

1113
use ahash::RandomState;
12-
use std::{future::Future, pin::Pin};
14+
use std::{
15+
fmt::Debug,
16+
future::Future,
17+
hash::{BuildHasher, Hash, Hasher},
18+
marker::PhantomData,
19+
ops::Deref,
20+
pin::Pin,
21+
};
1322

1423
#[cfg(not(target_arch = "wasm32"))]
1524
pub type BoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
@@ -208,3 +217,138 @@ pub type StableHashSet<K> = hashbrown::HashSet<K, FixedState>;
208217
// StableHashSet::with_capacity_and_hasher(capacity, FixedState::default())
209218
// }
210219
// }
220+
221+
pub struct Hashed<V, H = FixedState> {
222+
hash: u64,
223+
value: V,
224+
marker: PhantomData<H>,
225+
}
226+
227+
impl<V: Hash, H: BuildHasher + Default> Hashed<V, H> {
228+
pub fn new(value: V) -> Self {
229+
let builder = H::default();
230+
let mut hasher = builder.build_hasher();
231+
value.hash(&mut hasher);
232+
Self {
233+
hash: hasher.finish(),
234+
value,
235+
marker: PhantomData,
236+
}
237+
}
238+
239+
#[inline]
240+
pub fn hash(&self) -> u64 {
241+
self.hash
242+
}
243+
}
244+
245+
impl<V, H> Hash for Hashed<V, H> {
246+
#[inline]
247+
fn hash<R: Hasher>(&self, state: &mut R) {
248+
state.write_u64(self.hash);
249+
}
250+
}
251+
252+
impl<V, H> Deref for Hashed<V, H> {
253+
type Target = V;
254+
255+
#[inline]
256+
fn deref(&self) -> &Self::Target {
257+
&self.value
258+
}
259+
}
260+
261+
impl<V: PartialEq, H> Hashed<V, H> {
262+
#[inline]
263+
pub fn fast_eq(&self, other: &Hashed<V, H>) -> bool {
264+
// Makes the common case of two values not been equal very fast
265+
self.hash == other.hash && self.value.eq(&other.value)
266+
}
267+
}
268+
269+
impl<V: PartialEq, H> PartialEq for Hashed<V, H> {
270+
#[inline]
271+
fn eq(&self, other: &Self) -> bool {
272+
self.value.eq(&other.value)
273+
}
274+
}
275+
276+
impl<V: Debug, H> Debug for Hashed<V, H> {
277+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278+
f.debug_struct("Hashed")
279+
.field("hash", &self.hash)
280+
.field("value", &self.value)
281+
.finish()
282+
}
283+
}
284+
285+
impl<V: Clone, H> Clone for Hashed<V, H> {
286+
#[inline]
287+
fn clone(&self) -> Self {
288+
Self {
289+
hash: self.hash.clone(),
290+
value: self.value.clone(),
291+
marker: PhantomData,
292+
}
293+
}
294+
}
295+
296+
impl<V: Eq, H> Eq for Hashed<V, H> {}
297+
298+
#[derive(Default)]
299+
pub struct PassHash;
300+
301+
impl BuildHasher for PassHash {
302+
type Hasher = PassHasher;
303+
304+
fn build_hasher(&self) -> Self::Hasher {
305+
PassHasher::default()
306+
}
307+
}
308+
309+
#[derive(Debug)]
310+
pub struct PassHasher {
311+
hash: u64,
312+
}
313+
314+
impl Default for PassHasher {
315+
fn default() -> Self {
316+
Self { hash: 0 }
317+
}
318+
}
319+
320+
impl Hasher for PassHasher {
321+
fn write(&mut self, _bytes: &[u8]) {
322+
panic!("cannot hash byte arrays using PassHasher");
323+
}
324+
325+
fn write_u64(&mut self, i: u64) {
326+
self.hash = i;
327+
}
328+
329+
fn finish(&self) -> u64 {
330+
self.hash
331+
}
332+
}
333+
334+
pub type PreHashMap<K, V> = hashbrown::HashMap<Hashed<K>, V, PassHash>;
335+
336+
pub trait PreHashMapExt<K, V> {
337+
fn get_or_insert_with<F: FnOnce() -> V>(&mut self, key: &Hashed<K>, func: F) -> &mut V;
338+
}
339+
340+
impl<K: Hash + Eq + PartialEq + Clone, V> PreHashMapExt<K, V> for PreHashMap<K, V> {
341+
#[inline]
342+
fn get_or_insert_with<F: FnOnce() -> V>(&mut self, key: &Hashed<K>, func: F) -> &mut V {
343+
let entry = self
344+
.raw_entry_mut()
345+
.from_key_hashed_nocheck(key.hash(), key);
346+
match entry {
347+
RawEntryMut::Occupied(entry) => entry.into_mut(),
348+
RawEntryMut::Vacant(entry) => {
349+
let (_, value) = entry.insert_hashed_nocheck(key.hash(), key.clone(), func());
350+
value
351+
}
352+
}
353+
}
354+
}

0 commit comments

Comments
 (0)