Repository navigation
Fix memory leak when freeing a bindless extended material by fixing inverted binary search condition - #25848
Merged
Conversation
Contributor
Author
|
Oops, it stole the commit message for the PR title, which wasn't quite the right uh mood. |
Zeophlite
approved these changes
Sep 19, 2026
alice-i-cecile
approved these changes
Sep 20, 2026
alice-i-cecile
left a comment
Member
There was a problem hiding this comment.
I've spent a few minutes reviewing the surrounding code and understanding how this works, and had Sonnet 5 review for algorithmic correctness too since stupid mistakes here are easy. All good!
gregcsokas
pushed a commit
to gregcsokas/bevy
that referenced
this pull request
Sep 21, 2026
…nverted binary search condition (bevyengine#25848) # Objective Fix a memory leak when freeing a bindless extended material. The old code used a binary search, as follows: ```rust self .bindless_index_tables .binary_search_by(|bindless_index_table| { if bindless_index < bindless_index_table.index_range.start { Ordering::Less } else if bindless_index >= bindless_index_table.index_range.end { Ordering::Greater } else { Ordering::Equal } }) ``` The problem is that [`binary_search_by` is supposed to go the other way around](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.binary_search_by): > The comparator function should return an order code that indicates whether **its argument** is `Less`, `Equal` or `Greater` the desired target. Emphasis added. The code above returned whether the _target_ (`bindless_index`) was less. This led to the function failing to find anything if there was more than one bindless binding, which caused lookup to fail in `free`, and so it was skipped instead of freed. ```rust // in MaterialBindlessSlab::free let Some(bindless_index_table) = self.get_bindless_index_table(bindless_index) else { continue; }; ``` ## Solution Swap the comparisons! ## Testing Two tests! First: I added unit tests for the binary search. It did not have any before. Secondly... I was actually paranoid that while all the docs said it was "sorted", and "sorted" conventionally means "sorted ascending" (as in `[T]::is_sorted`), maybe it idiosyncratically meant "sorted descending". So I manually tested that by just asserting that it was ascending at runtime when running the `extended_material_bindless` example. ```rust // in MaterialBindlessSlab::new if bindless_index_tables.len() > 1 { assert!(bindless_index_tables.is_sorted_by_key(|b| b.index_range.start)); } ``` This assertion failed if I negated it, so it really is sorted ascending, and the lookup really was backwards. --- ## AI Diclosure Both the code that originally triggered the bug, and the identification of the issue, came from an LLM. Which is pretty helpful, because debugging a GPU OOM sounds difficult. After that, I took over and the rest is human. I may not be a GPU expert, but I have taken CS 101 and I can understand what a broken binary search looks like. Based on the documented properties of the code, the original was broken, and I did my best to verify that the documentation matched reality. I believe I fully understand both the issue and the fix.
mockersf
pushed a commit
that referenced
this pull request
Sep 25, 2026
…nverted binary search condition (#25848) # Objective Fix a memory leak when freeing a bindless extended material. The old code used a binary search, as follows: ```rust self .bindless_index_tables .binary_search_by(|bindless_index_table| { if bindless_index < bindless_index_table.index_range.start { Ordering::Less } else if bindless_index >= bindless_index_table.index_range.end { Ordering::Greater } else { Ordering::Equal } }) ``` The problem is that [`binary_search_by` is supposed to go the other way around](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.binary_search_by): > The comparator function should return an order code that indicates whether **its argument** is `Less`, `Equal` or `Greater` the desired target. Emphasis added. The code above returned whether the _target_ (`bindless_index`) was less. This led to the function failing to find anything if there was more than one bindless binding, which caused lookup to fail in `free`, and so it was skipped instead of freed. ```rust // in MaterialBindlessSlab::free let Some(bindless_index_table) = self.get_bindless_index_table(bindless_index) else { continue; }; ``` ## Solution Swap the comparisons! ## Testing Two tests! First: I added unit tests for the binary search. It did not have any before. Secondly... I was actually paranoid that while all the docs said it was "sorted", and "sorted" conventionally means "sorted ascending" (as in `[T]::is_sorted`), maybe it idiosyncratically meant "sorted descending". So I manually tested that by just asserting that it was ascending at runtime when running the `extended_material_bindless` example. ```rust // in MaterialBindlessSlab::new if bindless_index_tables.len() > 1 { assert!(bindless_index_tables.is_sorted_by_key(|b| b.index_range.start)); } ``` This assertion failed if I negated it, so it really is sorted ascending, and the lookup really was backwards. --- ## AI Diclosure Both the code that originally triggered the bug, and the identification of the issue, came from an LLM. Which is pretty helpful, because debugging a GPU OOM sounds difficult. After that, I took over and the rest is human. I may not be a GPU expert, but I have taken CS 101 and I can understand what a broken binary search looks like. Based on the documented properties of the code, the original was broken, and I did my best to verify that the documentation matched reality. I believe I fully understand both the issue and the fix.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Objective
Fix a memory leak when freeing a bindless extended material.
The old code used a binary search, as follows:
The problem is that
binary_search_byis supposed to go the other way around:Emphasis added. The code above returned whether the target (
bindless_index) was less.This led to the function failing to find anything if there was more than one bindless binding, which caused lookup to fail in
free, and so it was skipped instead of freed.Solution
Swap the comparisons!
Testing
Two tests!
First: I added unit tests for the binary search. It did not have any before.
Secondly... I was actually paranoid that while all the docs said it was "sorted", and "sorted" conventionally means "sorted ascending" (as in
[T]::is_sorted), maybe it idiosyncratically meant "sorted descending". So I manually tested that by just asserting that it was ascending at runtime when running theextended_material_bindlessexample.This assertion failed if I negated it, so it really is sorted ascending, and the lookup really was backwards.
AI Diclosure
Both the code that originally triggered the bug, and the identification of the issue, came from an LLM. Which is pretty helpful, because debugging a GPU OOM sounds difficult. After that, I took over and the rest is human. I may not be a GPU expert, but I have taken CS 101 and I can understand what a broken binary search looks like. Based on the documented properties of the code, the original was broken, and I did my best to verify that the documentation matched reality. I believe I fully understand both the issue and the fix.