Description
Bevy version
0.16.0
Relevant system information
AdapterInfo { name: "AMD Radeon RX 7900 XTX (RADV NAVI31)", vendor: 4098, device: 29772, device_type: DiscreteGpu, driver: "radv", driver_info: "Mesa 25.0.3", backend: Vulkan }
What you did
I have the following "concepts":
enum CameraOrder {
World,
ViewModel,
Ui,
}
impl From<CameraOrder> for isize {
fn from(order: CameraOrder) -> Self {
order as isize
}
}
bitflags! {
struct RenderLayer: u32 {
const DEFAULT = 0b00000001;
const VIEW_MODEL = 0b00000010;
const PARTICLES = 0b00000100;
const GIZMO3 = 0b0001000;
const UI = 0b0010000;
}
}
impl From<RenderLayer> for RenderLayers {
fn from(layer: RenderLayer) -> Self {
RenderLayers::from_iter(layer.iter().map(|l| (l.bits() >> 1) as usize))
}
}
Using these terms, my hierarchy is as follows:
- UI camera
- Player camera parent
- World model camera
- View model camera
where the relevant code look like this:
UI Camera
Full code: https://github.com/janhohenheim/foxtrot/blob/nondefault-ui-cam/src/ui_camera.rs#L18-L32
(
Name::new("UI Camera"),
Camera2d,
IsDefaultUiCamera,
Camera {
// The UI camera order is the highest.
order: CameraOrder::Ui.into(),
..default()
},
)
World Model Camera
(
Name::new("World Model Camera"),
Camera3d::default(),
Camera {
order: CameraOrder::World.into(),
hdr: true,
..default()
},
RenderLayers::from(
RenderLayer::DEFAULT | RenderLayer::PARTICLES,
),
Bloom::NATURAL,
#[cfg(feature = "native")]
(
Msaa::Off,
ScreenSpaceAmbientOcclusion::default(),
TemporalAntiAliasing::default(),
NormalPrepass,
ShadowFilteringMethod::Temporal,
),
)
View Model Camera
(
Name::new("View Model Camera"),
Camera3d::default(),
Camera {
// Bump the order to render on top of the world model.
order: CameraOrder::ViewModel.into(),
hdr: true,
..default()
},
// Only render objects belonging to the view model.
RenderLayers::from(RenderLayer::VIEW_MODEL),
)
The lights all belong to RenderLayer::DEFAULT | RenderLayer::VIEW_MODEL
:
Light setup
fn add_render_layers_to_point_light(trigger: Trigger<OnAdd, PointLight>, mut commands: Commands) {
let entity = trigger.target();
commands.entity(entity).insert(RenderLayers::from(
RenderLayer::DEFAULT | RenderLayer::VIEW_MODEL,
));
}
fn add_render_layers_to_spot_light(trigger: Trigger<OnAdd, SpotLight>, mut commands: Commands) {
let entity = trigger.target();
commands.entity(entity).insert(RenderLayers::from(
RenderLayer::DEFAULT | RenderLayer::VIEW_MODEL,
));
}
fn add_render_layers_to_directional_light(
trigger: Trigger<OnAdd, DirectionalLight>,
mut commands: Commands,
) {
let entity = trigger.target();
commands.entity(entity).insert(RenderLayers::from(
RenderLayer::DEFAULT | RenderLayer::VIEW_MODEL,
));
}
This all works fine. Now let's do one little change by moving the UI camera to a nondefault render layer:
fn spawn_ui_camera(mut commands: Commands) {
commands.spawn((
Name::new("UI Camera"),
UiCamera,
IsDefaultUiCamera,
Camera {
order: CameraOrder::Ui.into(),
..default()
},
// This line causes the issue
RenderLayers::from(RenderLayer::UI),
));
}
What went wrong
The lights flicker uncontrollably (warning for flashing lights!)
Screencast_From_2025-05-10_14-39-17.mp4
Interestingly, this only happens when moving close to my spot lights
Screencast_From_2025-05-10_14-43-52.mp4
Additional information
To see this in action, clone https://github.com/janhohenheim/foxtrot/tree/nondefault-ui-cam (note the branch) and run bevy run
.
This has only happened on native, but it's hard to test this on Wasm since there, the lights ignore most of the obstacles in their way anyways:
Instead, I get #19167 on web