Skip to content

Optimized BindGroupLayoutCache to reduce unnecessary cloning#21757

Open
brianreavis wants to merge 5 commits into
bevyengine:mainfrom
brianreavis:pr-bindgrouplayoutcache-optimization
Open

Optimized BindGroupLayoutCache to reduce unnecessary cloning#21757
brianreavis wants to merge 5 commits into
bevyengine:mainfrom
brianreavis:pr-bindgrouplayoutcache-optimization

Conversation

@brianreavis

@brianreavis brianreavis commented Nov 5, 2025

Copy link
Copy Markdown
Contributor

Objective

Currently, BindGroupLayoutCache ends up cloning the BindGroupLayoutDescriptor and BindGroupLayout for every get. This PR removes unnecessary cloning.

Solution

  • Pin BindGroupLayoutCache values to a stable memory location and have pipeline_cache.get_bind_group_layout(...) return a reference to the BindGroupLayout instead of a clone.
  • In BindGroupLayoutCache::get(), operate on the hashmap using raw_entry_mut instead of entry so that we don't need to unnecessarily need to clone the BindGroupLayoutDescriptor when the BindGroupLayout is already in the cache.

Testing

  • I tested these changes on a local project.
  • When reviewing, jump to pipeline_cache.rs. Some unsafe is required in order to get a reference that outlives the mutex guard.

@brianreavis brianreavis added D-Trivial Nice and easy! A great choice to get started with Bevy C-Performance A change motivated by improving speed, memory usage or compile times D-Unsafe Touches with unsafe code in some way S-Needs-Review Needs reviewer attention (from anyone!) to move forward A-Rendering Drawing game state to the screen labels Nov 5, 2025
bgls: HashMap<BindGroupLayoutDescriptor, Pin<Box<BindGroupLayout>>>,
}

impl BindGroupLayoutCache {

@Zeophlite Zeophlite Nov 6, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewers, this file is the key part of the PR, the rest is mechanical

@alice-i-cecile alice-i-cecile added D-Straightforward Simple bug fixes and API improvements, docs, test and examples and removed D-Trivial Nice and easy! A great choice to get started with Bevy labels Nov 7, 2025

@tychedelia tychedelia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd kinda like to make sure the juice is worth the unsafe squeeze here. There are some situations where we do create bind groups eagerly every frame which I think would be the main source of performance slowdown. But the changes make mechanical sense on first pass. Will try to look at some benchmarks just to get a sense of where we were left post #21205

let boxed_layout_ptr = Pin::as_ref(boxed_layout).get_ref() as *const BindGroupLayout;

// SAFETY:
// - Cached `BindGroupLayout` entries are immutable: they're never replaced, modified, or evicted from the hashmap.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

People have complained to us about so-called "memory leaks" but I think it's pretty hard to generate unique permutations of bgls unless you're doing something truly really weird. In any case, this requirement seems quite conservative so we could cross the bridge if it mattered in practice, which it's unlikely to.

@brianreavis

Copy link
Copy Markdown
Contributor Author

Just ran a quick profile on a local project that does more bind group creation per frame than it should (yellow = this pull request, red = main):

image

@brianreavis brianreavis added this to the 0.18 milestone Nov 25, 2025
@brianreavis

Copy link
Copy Markdown
Contributor Author

Adding this to 0.18, given the get_bind_group_layout API is new and this PR changes its return type - so it'd be good to decide prior to that release. I'm in the camp that the performance gain is worth the unsafe, but it's minor in the grand scheme of things.

@JMS55 JMS55 removed their request for review November 25, 2025 20:22
@JMS55

JMS55 commented Nov 25, 2025

Copy link
Copy Markdown
Contributor

I don't have the time to review this atm, but I'm fine with the general idea. Users can always clone the BGL from the reference if they want.

Comment thread crates/bevy_render/src/render_resource/pipeline_cache.rs Outdated
@cart cart modified the milestones: 0.18, 0.19 Dec 17, 2025
@brianreavis brianreavis force-pushed the pr-bindgrouplayoutcache-optimization branch from b97070f to c339c31 Compare December 29, 2025 01:30
.lock()
.unwrap()
.get(&self.device, bind_group_layout_descriptor.clone())
) -> &BindGroupLayout {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the lifetime of the returned &BindGroupLayout is being created out of thin air here, it is important to be explicit with the lifetime relationships here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you have in mind? Something like this?

- pub fn get_bind_group_layout(
+ pub fn get_bind_group_layout<'a>(
-        &self,
+        &'a self,
        bind_group_layout_descriptor: &BindGroupLayoutDescriptor,
-    ) -> &BindGroupLayout {
+    ) -> &'a BindGroupLayout {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks correct to me. The important thing is that the resulting BGL does not outlive the immutable borrow on self, or it risks keeping a borrow past the Drop of the owning cache.

@cart cart added this to Rendering Feb 12, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in Rendering Feb 12, 2026
@cart cart removed this from Rendering Feb 12, 2026
@alice-i-cecile alice-i-cecile removed this from the 0.19 milestone Mar 16, 2026
@cart cart closed this May 5, 2026
@cart cart reopened this May 5, 2026
pull Bot pushed a commit to VitalyAnkh/bevy that referenced this pull request Jun 9, 2026
…tion (bevyengine#24503)

# Objective

Reduce `LayoutCache::get` and `BindGroupLayoutCache::get` heap
allocation
Part of this adopts bevyengine#21757

## Solution

Reduce `LayoutCache::get` allaction by using `SmallVec` with 8 elements
inlined on the stack.
Avoid cloning `BindGroupLayoutDescriptor` in `BindGroupLayoutCache::get`
(adopts bevyengine#21757).

bevyengine#21757 also avoids cloning `BindGroupLayout`, but I don't think it's
unnecessary because `BindGroupLayout` is cheap to clone (it just clones
id + Arc).

## Testing

CI
@beicause beicause added S-Nominated-To-Close A triage team member thinks this PR or issue should be closed out. and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jun 11, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in Rendering Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Rendering Drawing game state to the screen C-Performance A change motivated by improving speed, memory usage or compile times D-Straightforward Simple bug fixes and API improvements, docs, test and examples D-Unsafe Touches with unsafe code in some way S-Nominated-To-Close A triage team member thinks this PR or issue should be closed out.

Projects

Status: Needs SME Triage
Status: No status

Development

Successfully merging this pull request may close these issues.

8 participants