# `prepare_windows` panics on transient swap chain error, crashes with cascade of GPU resource failures
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 22h
- Merged PRs (30d)
- 161
Description
## Bevy version and features
0.18.1, default features + `bevy_camera_controller` + `free_camera`.
## Relevant system information
- Rust: 1.93.1
- OS: Windows 11 Home China 10.0.26200
- `AdapterInfo { name: "NVIDIA GeForce RTX 5060 Laptop GPU", vendor: 4318, device: 11609, device_type: DiscreteGpu, driver: "NVIDIA", driver_info: "576.65", backend: Vulkan }`
## What you did
Run this app repeatedly. Most launches work fine. Occasionally (non-deterministic — sometimes it fails on two or even three launches in a row, sometimes not after 30 tries) it panics at startup.Note: this is not a minimal reproduction — the trigger frequency seemed to decrease as I simplified the scene, so I stopped reducing and kept the original code that reliably exhibits the issue.
```rust
use bevy::{
anti_alias::taa::TemporalAntiAliasing,
camera::Exposure,
camera_controller::free_camera::{FreeCamera, FreeCameraPlugin},
core_pipeline::{
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass},
tonemapping::Tonemapping,
},
light::{AtmosphereEnvironmentMapLight, VolumetricFog, VolumetricLight},
pbr::{
Atmosphere, DefaultOpaqueRendererMethod, ScatteringMedium,
ScreenSpaceAmbientOcclusion, ScreenSpaceAmbientOcclusionQualityLevel,
ScreenSpaceReflections,
},
post_process::bloom::{Bloom, BloomCompositeMode, BloomPrefilter},
prelude::*,
render::view::{ColorGrading, Hdr},
};
fn main() {
App::new()
.insert_resource(DefaultOpaqueRendererMethod::deferred())
.insert_resource(ClearColor(Color::BLACK))
.insert_resource(GlobalAmbientLight::NONE)
.add_plugins(DefaultPlugins)
.add_plugins(FreeCameraPlugin)
.add_systems(Startup, setup)
.run();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut>,
mut materials: ResMut>,
mut scattering_mediums: ResMut>,
) {
commands.spawn((
Mesh3d(meshes.add(Cuboid::default())),
MeshMaterial3d(materials.add(Color::srgb(0.4, 0.8, 0.3))),
Transform::from_xyz(0.0, 0.5, 0.0),
));
commands.spawn((
Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))),
MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
));
commands.spawn((
DirectionalLight {
illuminance: light_consts::lux::RAW_SUNLIGHT,
shadows_enabled: true,
..default()
},
Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -std::f32::consts::FRAC_PI_4)),
VolumetricLight,
));
commands
.spawn((
Camera3d::default(),
Transform::from_translation(Vec3::new(-2.0, 2.5, 5.0)).looking_at(Vec3::ZERO, Vec3::Y),
FreeCamera::default(),
Atmosphere::earthlike(scattering_mediums.add(ScatteringMedium::default())),
AtmosphereEnvironmentMapLight::default(),
Exposure { ev100: 13.0 },
ColorGrading::default(),
Hdr,
Msaa::Off,
DepthPrepass,
MotionVectorPrepass,
DeferredPrepass,
Tonemapping::TonyMcMapface,
))
.insert((
Bloom {
intensity: 0.12,
low_frequency_boost: 0.7,
low_frequency_boost_curvature: 0.95,
high_pass_frequency: 1.0,
prefilter: BloomPrefilter {
threshold: 1.0,
threshold_softness: 0.3,
},
composite_mode: BloomCompositeMode::Additive,
..Bloom::NATURAL
},
ScreenSpaceAmbientOcclusion {
quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Custom {
slice_count: 2,
samples_per_slice_side: 3,
},
constant_object_thickness: 0.25,
},
TemporalAntiAliasing::default(),
ScreenSpaceReflections::default(),
VolumetricFog {
ambient_intensity: 0.0,
..default()
},
));
}
```
## What went wrong
Expected: the app starts normally every time.
Actual: sporadic panic at startup:
```
thread 'Compute Task Pool (9)' panicked at bevy_render/src/view/window/mod.rs:304:17:
Couldn't get swap chain texture, operation unrecoverable: Acquiring a texture failed with a generic error. Check error callbacks for more information
```
Followed by a cascade of 10+ panics across Compute Task Pool threads:
```
thread 'main' panicked at bevy_render/src/render_resource/uniform_buffer.rs:312:18:
called `Option::unwrap()` on a `None` value
thread 'Compute Task Pool (19)' panicked at wgpu/src/backend/wgpu_core.rs:2169:18:
Error in Buffer::get_mapped_range: Validation Error
Caused by: Buffer with 'thickness_buffer' label is invalid
```
(Full trace trimmed — dozens of similar panics follow for various buffers and textures.)
## Additional information
- The failure is **non-deterministic**, pointing to a race condition rather than a resource limit (the "Too many textures" warning appears on every launch, including successful ones).
- The initial trigger is `surface.get_current_texture()` returning a "generic error" in `prepare_windows`.
- Once the first panic occurs, the GPU device enters a permanent error state, causing all subsequent GPU operations to fail — hence the cascade.
- Setting `WGPU_BACKEND=dx12` completely eliminates the issue (tested 60 consecutive launches with zero failures), i guess this is **Vulkan-specific**.
- The catch-all handler at `view/window/mod.rs:303` panics unconditionally, unlike `SurfaceError::Outdated` which already has graceful recovery (reconfigure + retry + skip frame).
- I attempted patching `prepare_windows` to handle this error the same way as `Outdated` (reconfigure + skip frame) and adding `run_if` conditions to skip `Queue`/`Prepare`/`Render` sets. The cascading panics were eliminated, but the device never recovered — the app entered an infinite skip-frame loop. A complete fix would likely require device loss detection and render context recreation.
Contributor guide
Assessment
This issue has not been assessed yet.