Skip to content

Add bindless support for 2D materials.#24833

Open
pcwalton wants to merge 4 commits into
bevyengine:mainfrom
pcwalton:bind-group-allocator-2d
Open

Add bindless support for 2D materials.#24833
pcwalton wants to merge 4 commits into
bevyengine:mainfrom
pcwalton:bind-group-allocator-2d

Conversation

@pcwalton

@pcwalton pcwalton commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

At the moment, Bevy must issue a separate drawcall and bind group switch for each 2D object with a different texture, incurring significant wgpu and driver overhead. To address this problem, this commit ports the bindless support that the 3D PBR pipeline has long enjoyed to the 2D pipeline as well. Bindless allows Bevy to batch 2D meshes into single drawcalls as long as they share the same mesh, even if their materials (including textures) differ.

To implement bindless, this patch moves the efficient material bind group allocator from bevy_pbr to bevy_render. Like the 3D PBR pipeline, the code in this PR checks to see whether bindless is supported on the current platform and automatically falls back to non-bindless if it isn't.

Because the logic needed to support bindless is tightly intertwined with the logic needed to prepare pipelines, this PR also overhauls specialize_material2d_meshes. That function is now very similar to the one in the 3D pipeline. At some point, it'd probably be best to merge the core logic of this function for the 2D and 3D pipelines together. However, I opted to leave that to a follow-up for two reasons: (1) it's easier to verify in review that the 2D and 3D functions are the same than to verify that any added layers of abstraction are correct; (2) it might (or might not) be best to wait until multi-draw indirect is added to the 2D pipeline to do the refactoring.

Per review request, I've left the migration of ColorMaterial and SpriteMaterial over to bindless to a follow-up. With this follow-up (not in this PR), on the bevymark example with --waves 1 --per-wave 10000 --vary-per-instance --material-texture-count 1000, I observed:

  • With the dedicated sprite rendering path, which doesn't use bindless, I get 21.5 ms/frame, or 47 FPS.

  • With the SpriteMaterial path, with the follow-up patch that adds bindless support to that material, I get 17.4 ms/frame, or 67 FPS.

  • With the SpriteMaterial path on main, I get 29.7 ms/frame, or 38.2 FPS.

The follow-up patch is therefore a 1.71× speedup on that workload for SpriteMaterial and a 1.24× speedup over the dedicated sprite rendering path.

Note that, in that follow-up patch, order to see the changes in action, I had to change bevymark to avoid using the same seed for all its parallel RNGs. Without that change, the fact that each RNG uses the same seed means that each texture effectively gets its own Z layer, causing the benchmark to degenerate into the optimal scenario for batching in the non-bindless case. The follow-up PR scrambles the RNG seeds to avoid this pathological, unrealistic behavior.

@pcwalton pcwalton requested review from IceSentry and kfc35 July 1, 2026 22:23
@pcwalton pcwalton added the A-Rendering Drawing game state to the screen label Jul 1, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in Rendering Jul 1, 2026
@pcwalton pcwalton added the C-Performance A change motivated by improving speed, memory usage or compile times label Jul 1, 2026
@pcwalton pcwalton force-pushed the bind-group-allocator-2d branch from 87906a6 to c889703 Compare July 1, 2026 22:25
Comment thread crates/bevy_render/src/material_bind_groups.rs Outdated
/// The material bind group allocator is infrastructure for bindless resources.
/// It packs multiple materials into a small number of bind groups, allowing
/// Bevy to render large parts of the scene with a small number of drawcalls.
pub struct MaterialBindGroupsPlugin;

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.

Could you split this PR - one to move material bind groups from pbr to render

The other to update colour and sprite materials to use it

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.

OK, I ripped the ColorMaterial and SpriteMaterial update out of this PR. It’ll be a follow-up.

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.

Cheers, it just makes it easier to follow

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

The generated examples/README.md is out of sync with the example metadata in Cargo.toml or the example readme template. Please run cargo run -p build-templated-pages -- update examples to update it, and commit the file change.

At the moment, Bevy must issue a separate drawcall and bind group switch
for each 2D object with a different texture, incurring significant
`wgpu` and driver overhead. To address this problem, this commit ports
the bindless support that the 3D PBR pipeline has long enjoyed to the 2D
pipeline as well. Bindless allows Bevy to batch 2D meshes into single
drawcalls as long as they share the same mesh, even if their materials
(including textures) differ.

To implement bindless, this patch moves the efficient material bind
group allocator from `bevy_pbr` to `bevy_render`. Like the 3D PBR
pipeline, the code in this PR checks to see whether bindless is
supported on the current platform and automatically falls back to
non-bindless if it isn't.

Because the logic needed to support bindless is tightly intertwined with
the logic needed to prepare pipelines, this PR also overhauls
`specialize_material2d_meshes`. That function is now very similar to the
one in the 3D pipeline. At some point, it'd probably be best to merge
the core logic of this function for the 2D and 3D pipelines together.
However, I opted to leave that to a follow-up for two reasons: (1) it's
easier to verify in review that the 2D and 3D functions are the same
than to verify that any added layers of abstraction are correct; (2) it
might (or might not) be best to wait until multi-draw indirect is added
to the 2D pipeline to do the refactoring.

Per review request, I've left the migration of `ColorMaterial` and
`SpriteMaterial` over to bindless to a follow-up. With this follow-up
(*not in this PR*), on the `bevymark` example with `--waves 1 --per-wave
10000 --vary-per-instance --material-texture-count 1000`, I observed:

* With the dedicated sprite rendering path, which doesn't use bindless,
  I get 21.5 ms/frame, or 47 FPS.

* With the `SpriteMaterial` path, with the follow-up patch that adds
  bindless support to that material, I get 17.4 ms/frame, or 67 FPS.

* With the `SpriteMaterial` path on `main`, I get 29.7 ms/frame, or 38.2
  FPS.

The follow-up patch is therefore a 1.71× speedup on that workload for
`SpriteMaterial` and a 1.24× speedup over the dedicated sprite rendering
path.

Note that, in that follow-up patch, order to see the changes in action,
I had to change `bevymark` to avoid using the same seed for all its
parallel RNGs. Without that change, the fact that each RNG uses the same
seed means that each texture effectively gets its own Z layer, causing
the benchmark to degenerate into the optimal scenario for batching in
the non-bindless case. The follow-up PR scrambles the RNG seeds to avoid
this pathological, unrealistic behavior.
@pcwalton pcwalton force-pushed the bind-group-allocator-2d branch from c889703 to 8561eed Compare July 2, 2026 04:34
@pcwalton pcwalton changed the title Make the built-in 2D materials bindless. Add bindless support for 2D materials. Jul 2, 2026
@pcwalton

pcwalton commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

check-advisories failure seems unrelated to this PR.

@@ -238,7 +237,7 @@ pub struct AlphaMask2dBinKey {
/// the ID of another type of asset.
pub asset_id: UntypedAssetId,
/// The ID of a bind group specific to the material.

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.

nit: the comment should refer to an index, not ID


/// A [`Plugin`] that supplies generic infrastructure for 2D materials.
#[derive(Default)]
pub struct Materials2dPlugin;

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.

nit: Material2dPlugin

@Zeophlite Zeophlite left a comment

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.

Thanks for taking the time to improve 2d

@Zeophlite Zeophlite added the S-Needs-Review Needs reviewer attention (from anyone!) to move forward label Jul 2, 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 S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

Status: Needs SME Triage

Development

Successfully merging this pull request may close these issues.

2 participants