`GpuComponentArrayBuffer` panics when two instances leave the array in one frame, or one comes and goes in a frame

オープン
#26,316 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 2 日以内に返信

まだ誰も着手していません。

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
70/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
rust
領域
game-dev

調査の方向性

Start with crates/bevy_render/src/gpu_component_array_buffer.rs, especially update_components and GpuComponentArray::remove; the issue gives two reproductions and the failing assertions or command error. Trace how removals and swap-removal handle multiple entities and components added then removed before the system runs. Done means both reproductions and arbitrary same-frame removal orders complete without panicking.

索引モデルが issue の本文から書いたものです。

説明

A-Rendering C-Bug D-Straightforward S-Ready-For-Implementation

Bevy version

0.20.0 (crates.io). crates/bevy_render/src/gpu_component_array_buffer.rs on main at
fd9e7609109758a2d9af401345f100be9442e9f6 (2026-10-24) is byte-identical, so main has both bugs.

What went wrong

update_components::<C> (gpu_component_array_buffer.rs:130-220) has two reachable panics.

1. A command to a despawned entity. A removed entity's entry is swap-removed: the last entry moves into the gap,
and a command inserts the moved entity's new MeshTag:

if let Some((displaced_entity, displaced_entity_new_tag)) = component_array.remove(&mut buffer, entity) {
    commands.entity(displaced_entity).insert(MeshTag::with_type::<GpuComponentArray<C>>(displaced_entity_new_tag));
}

Sometimes the displaced entity was despawned in the same frame, and its own removal comes later in
RemovedComponents. Then the command fails, and the default error handler panics:

Encountered an error in command `insert<MeshTag>`: Entity despawned: The entity with ID 544v0 is invalid; its index now has generation 1.

Common ways to remove two instances in one frame:

  • despawn a parent whose children carry the component (Children is linked_spawn);
  • despawn a batch of instances, for example on a scene or level change.

Unless the batch happens to go from the highest tag down, this panics.

2. An instance added and removed before the system ran. The component is inserted and then removed (or the entity
despawned) in the same frame, before update_components runs. Its removal is then read from RemovedComponents, but
the entity was never pushed. remove panics:

  • in a debug build: debug_assert!(!self.tag_to_entity.is_empty()) (:305) when the array is empty;
  • otherwise: .expect("Entity must have been present") (:314).

How to reproduce

use bevy::ecs::query::QueryItem;
use bevy::ecs::system::lifetimeless::Read;
use bevy::prelude::*;
use bevy::render::gpu_component_array_buffer::{GpuComponentArrayBuffer, GpuComponentArrayBufferPlugin};
use bevy::render::storage::ShaderBuffer;

#[derive(Component, Clone, Copy)]
struct Data(u32);

impl GpuComponentArrayBuffer for Data {
    type QueryData = Read<Self>;
    type QueryFilter = Changed<Self>;
    type Out = u32;

    fn extract_component(data: QueryItem<'_, '_, Self::QueryData>) -> Option<u32> {
        Some(data.0)
    }
}

fn app() -> App {
    let mut app = App::new();
    app.add_plugins((MinimalPlugins, AssetPlugin::default()))
        .init_asset::<ShaderBuffer>()
        .add_plugins(GpuComponentArrayBufferPlugin::<Data>::default());
    app
}

#[test]
fn two_instances_leaving_in_one_frame() {
    let mut app = app();
    let e: Vec<Entity> = (0..3).map(|i| app.world_mut().spawn(Data(i)).id()).collect();
    app.update(); // tags 0, 1, 2
    app.world_mut().despawn(e[0]); // its slot is filled with e[2]'s entry, and e[2] is re-tagged by a command...
    app.world_mut().despawn(e[2]); // ...but e[2] is gone too
    app.update(); // panics: `insert<MeshTag>`: Entity despawned
}

#[test]
fn an_instance_coming_and_going_in_one_frame() {
    let mut app = app();
    let e = app.world_mut().spawn(Data(0)).id();
    app.world_mut().entity_mut(e).remove::<Data>();
    app.update(); // panics in `GpuComponentArray::remove`
}

Both tests fail on bevy 0.20.0 (run 2026-10-25, a debug build without Bevy's debug feature, which names the command):

two_instances_leaving_in_one_frame: Encountered an error in command `<Enable the debug feature to see the name>`: Entity despawned: The entity with ID 33v0 is invalid; its index now has generation 1.
an_instance_coming_and_going_in_one_frame: gpu_component_array_buffer.rs:305:9: assertion failed: !self.tag_to_entity.is_empty()

Expected

Any set of entities can lose the component in one frame, in any order, and nothing panics.

主要言語
Rust
スター
49.7k
フォーク
5k
平均マージ
3日 19時間
マージ済み PR(30日)
134

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

bevyengine/bevy のほかの issue

bevyengine/bevy の issue をすべて見る

似ている issue

Rust の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。