Panic in check_dir_light_mesh_visibility after switching to a DirectionalLight with more cascades
C-BugA-RenderingP-CrashS-Needs-Triage
## Bevy version and features
0.19.0, default features
## \[Optional\] Relevant system information
- rustc 1.96.1, Windows 11
- Not GPU-related — the panic is CPU-side in a visibility system.
## What you did
Used two shadow-casting `DirectionalLight`s with different cascade counts at different times: one screen with a 3-cascade light, then a state transition despawns it and another screen spawns 4-cascade lights (bevy default). The lights never exist simultaneously.
```rust
// screen A
commands.spawn((
DirectionalLight { shadow_maps_enabled: true, ..default() },
CascadeShadowConfigBuilder { num_cascades: 3, ..default() }.build(),
DespawnOnExit(GameState::A),
));
// screen B, entered later — default config = 4 cascades
commands.spawn(DirectionalLight { shadow_maps_enabled: true, ..default() });
```
## What went wrong
Expected: normal transition. Actual: intermittent panic on the first frames after the transition:
```
thread 'Compute Task Pool (0)' panicked at bevy_light-0.19.0\src\lib.rs:477:57:
index out of bounds: the len is 3 but the index is 3
Encountered a panic in system `bevy_light::check_dir_light_mesh_visibility`!
```
## Additional information
Cause (from reading `check_dir_light_mesh_visibility`): the per-thread scratch buffers `view_visible_entities_queue: Local<Parallel<Vec<Vec<Entity>>>>` persist across frames. They are resized to `view_frusta.len()` only inside the `par_iter` per-batch init closure, so a worker thread that receives no batch on a given frame keeps its old buffer size. The collection loop afterwards iterates **all** thread-local buffers and indexes them by the current light's cascade index — a stale 3-slot buffer indexed with cascade 3 of a 4-cascade light panics at line 477 (`thread_entity_queue[view_dest_index]`).
Consequences that match observed behavior:
- Only triggers going from a smaller cascade count to a larger one (oversized stale buffers are harmless).
- Intermittent, since it needs a worker thread to be idle during that frame's parallel iteration.
- Same-count lights never trigger it.
Workaround: give every shadow-casting `DirectionalLight` the same `num_cascades`.
Probable fix: resize/clear the thread-local buffers for all of `Parallel` before the collection loop, or index with `get_mut` and skip/grow stale entries.
关闭于 2026-07-02 2 条评论