bevyengine / bevyengine/bevy

Panic / segfault when trying to create a custom bindless material with storage buffers.

Open
#18,853 4 comments 0 reactions 0 assignees View on GitHub
A-Rendering C-Bug I-Crash S-Needs-Investigation
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 16h
Merged PRs (30d)
171

Description

## Bevy version

Bevy 0.16.0-rc.5

## \[Optional\] Relevant system information

Asahi Linux, Apple M1 Pro

```
2025-04-15T14:42:33.261113Z INFO bevy_render::renderer: AdapterInfo { name: "Apple M1 Pro (G13S C0)", vendor: 65541, device: 0, device_type: IntegratedGpu, driver: "Honeykrisp", driver_info: "Mesa 25.1.0-asahi20250221", backend: Vulkan }
2025-04-15T14:42:33.398102Z INFO bevy_render::batching::gpu_preprocessing: GPU preprocessing is fully supported on this device.
```

## What you did

I tried to create my own bindless material, learning from the docs, the examples, and the StandardMaterial source code.

Here is my material:

```rust
#[derive(AsBindGroup, Clone, Asset, TypePath)]
#[data(0, TerrainMaterialUniform, binding_array(10))]
#[bindless]
pub struct TerrainMaterial {
mask_index: u32,
#[storage(1, read_only)]
mat_props: Handle,
#[storage(2, read_only)]
tex_props: Handle,
#[texture(3, dimension = "2d_array")]
#[sampler(4)]
texmasks: Handle,
#[texture(5, dimension = "2d_array")]
#[sampler(6)]
propmasks: Handle,
#[texture(7, dimension = "2d_array")]
#[sampler(8)]
textures_color: Handle,
#[texture(9, dimension = "2d_array")]
#[sampler(10)]
textures_normal: Handle,
}

impl Material for TerrainMaterial {
fn fragment_shader() -> ShaderRef {
"shaders/terrain.wgsl".into()
}
}

#[derive(ShaderType, Clone, Copy)]
struct TerrainMaterialUniform {
mask_index: u32,
}

impl From<&TerrainMaterial> for TerrainMaterialUniform {
fn from(value: &TerrainMaterial) -> Self {
Self {
mask_index: value.mask_index,
}
}
}
```

As you can see, I have the following:
- Some textures and samplers
- Global uniform data (like StandardMaterial does)
- Some custom storage buffers

I have not really written my shader code yet. I just made a simple dummy shader to test if bindless works:

```wgsl
#import bevy_pbr::forward_io::VertexOutput;

@fragment
fn fragment(input: VertexOutput) -> @location(0) vec4 {
#ifdef BINDLESS
return vec4(1.0, 0.0, 0.0, 1.0);
#else
return vec4(0.0, 0.0, 1.0, 1.0);
#endif
}
```

I spawned a simple plane mesh with my custom material to test. I expected to see it either solid red or solid blue, depending on the state of the BINDLESS shader def. Instead, I got a crash.

## What went wrong

The code compiles (no error from the `AsBindGroup` derive), but crashes at runtime with the following:

```
2025-04-15T14:42:36.203590Z ERROR bevy_pbr::material_bind_groups: Binding array wasn't present for buffer at index BindlessIndex(1)

thread 'Compute Task Pool (5)' panicked at c/bevy/crates/bevy_pbr/src/material_bind_groups.rs:825:13:
An allocation into an empty slab should always succeed
stack backtrace:
0: __rustc::rust_begin_unwind
at /rustc/092a284ba0421695f2032c947765429fd7095796/library/std/src/panicking.rs:697:5
1: core::panicking::panic_fmt
at /rustc/092a284ba0421695f2032c947765429fd7095796/library/core/src/panicking.rs:75:14
2: bevy_pbr::material_bind_groups::MaterialBindGroupBindlessAllocator::allocate_unprepared
3: as bevy_render::render_asset::RenderAsset>::prepare_asset
4: bevy_render::render_asset::prepare_assets
5: as bevy_ecs::system::system::System>::run_unsafe
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Encountered a panic in system `bevy_render::render_asset::prepare_assets>`!

thread 'Compute Task Pool (3)' panicked at c/crates/bevy_pbr/src/material_bind_groups.rs:707:18:
Bindings buffer must exist
stack backtrace:
0: __rustc::rust_begin_unwind
at /rustc/092a284ba0421695f2032c947765429fd7095796/library/std/src/panicking.rs:697:5
1: core::panicking::panic_fmt
at /rustc/092a284ba0421695f2032c947765429fd7095796/library/core/src/panicking.rs:75:14
2: core::panicking::panic_display
at /rustc/092a284ba0421695f2032c947765429fd7095796/library/core/src/panicking.rs:261:5
3: core::option::expect_failed
at /rustc/092a284ba0421695f2032c947765429fd7095796/library/core/src/option.rs:2024:5
4: alloc::vec::Vec::extend_trusted
5: as alloc::vec::spec_from_iter_nested::SpecFromIterNested>::from_iter
6: bevy_pbr::material_bind_groups::MaterialBindlessSlab::prepare_bind_group
7: bevy_pbr::material_bind_groups::MaterialBindlessSlab::prepare
8: as bevy_ecs::system::system::System>::run_unsafe
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Encountered a panic in system `bevy_pbr::material::prepare_material_bind_groups`!

[1] 479258 segmentation fault (core dumped) cargo run
```

## Additional information

If I comment out the storage buffer bindings (but leave everything else as is: the textures, samplers, global data uniform), everything works. The game runs and I get the mesh rendered solid red, indicating that bindless is enabled.

Contributor guide

Open the contributing guide

Research direction

The failure is in crates/bevy_pbr/src/material_bind_groups.rs, at the reported allocator and bind-group preparation paths; start by reproducing the custom TerrainMaterial with its two storage bindings and run cargo run. Done means the material no longer panics or segfaults and the dummy shader renders with storage buffers enabled.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
computer-graphics, game-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.