Fix Camera::is_active and delayed spawned no cpu culling camera not rendering anything - #25690
Merged
alice-i-cecile merged 10 commits intoSep 11, 2026
Merged
Conversation
2 tasks
beicause
reviewed
Sep 8, 2026
beicause
left a comment
Member
There was a problem hiding this comment.
I ran an AI review. See CodingDaniel1#1.
I manually tested and this fixed the issue (the testing on the main branch always shows a black screen).
Fix render layer change propagation for GPU-culled meshes
stuartparmenter
self-requested a review
September 8, 2026 18:02
Contributor
Author
|
Latest commit fixes directional light shadow cascades changing path issue. By using the following code, im able to produce shadows missing issues on main, but not with this pr. When changing the cascade count using KeyP, you can see the shadows missing behavior on main. //! A simple 3D scene with light shining over a cube sitting on a plane.
use bevy::{
camera::visibility::NoCpuCulling,
light::{CascadeShadowConfig, CascadeShadowConfigBuilder},
prelude::*,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, scene.spawn())
.add_systems(Update, change_cascade)
.add_observer(use_no_cpu_culling)
.run();
}
/// set up a simple 3D scene
fn scene() -> impl SceneList {
bsn! {
#CircularBase
Mesh3d(asset_value(Circle::new(4.0)))
MeshMaterial3d::<StandardMaterial>(asset_value(Color::WHITE))
Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2))
--
#Cube
Mesh3d(asset_value(Cuboid::new(1.0, 1.0, 1.0)))
MeshMaterial3d::<StandardMaterial>(asset_value(Color::srgb_u8(124, 144, 255)))
Transform::from_xyz(0.0, 0.5, 0.0)
--
// PointLight {
// shadow_maps_enabled: true,
// }
// Transform::from_xyz(4.0, 8.0, 4.0)
// --
DirectionalLight {
shadow_maps_enabled: true
}
CascadeShadowConfigBuilder {
num_cascades: 4,
..Default::default()
}.build()
Transform::from_xyz(4.0, 8.0, 4.0).looking_at(Vec3::ZERO, Vec3::Y)
--
Camera3d
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y)
}
}
fn use_no_cpu_culling(add: On<Add<Mesh3d>>, mut commands: Commands) {
commands.entity(add.entity).insert(NoCpuCulling);
}
fn change_cascade(
query: Query<&mut CascadeShadowConfig, With<DirectionalLight>>,
mut cascade_count: Local<u32>,
keyboard: Res<ButtonInput<KeyCode>>,
) {
if keyboard.just_pressed(KeyCode::KeyP) {
*cascade_count += 1;
let cur_cascade_count = *cascade_count;
if *cascade_count > 4 {
*cascade_count = 1;
}
for mut config in query {
*config = CascadeShadowConfigBuilder {
num_cascades: cur_cascade_count as usize,
..default()
}
.build();
}
}
} |
stuartparmenter
approved these changes
Sep 9, 2026
beicause
approved these changes
Sep 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Objective
This is a followup pr for #25670
I have found issues related to toggling
is_activeat runtime causes the rendering to stop working, and this pr fixes that. While fixing on that, ive found another issues when usingNoCpuCullingon Mesh entity, if the camera is spawned after the entity got collected for rendering, then the camera wont render that entity. The reason whyNoCpuCullingon Mesh causes this but not on camera, is the gpu mesh collect pass looks forViewVisibilitychanges, and cpu culling system will trigger the change detection even if the camera is tagged withNoCpuCulling. But it wont trigger it when mesh haveNoCpuCulling.Solution
For the
is_activeissue, I removedRenderVisibleEntitiesfrom the render camera when its inactive, but dont remove it when the window is minized. This behaviour matches what bevy other places does, which most places dont care if window is minized.For the second issue, I check to see is
RenderVisibleEntitiesadded this frame incollect_gpu_culled_meshesand do a full table flush whenRenderVisibleEntitiesis confirmed to be new. This means a freshly spawned camera will pick up previously registered mesh, and is_active toggling camera will still do the same. Be aware that this is solely forMesh3dtagged withNoCpuCullingsince thats purpose of this function, anyMesh3dnot tagged withNoCpuCullingwill still go through the cpu collect pass instead.Testing
I used the following functions to toggle is_active field and use gpu culling path, put it in 3d_scene and ssao example. Live test it, the rendering stays the same when toggling is_active at runtime. But the issue remains on bevy/main.
Here is the full code snippet i used for testing both issues. The camera spawning will be delayed by 2sec. On main you will not see anything rendered, but with this pr, rendering is normal.