Skip to content

Commit

Permalink
Fixes bevyengine#12184: panic when no camera is present
Browse files Browse the repository at this point in the history
Created default values to be used in upsert_node, added flag to control warnings about no camera present and created unit test no_camera_ui.
  • Loading branch information
salvadorcarvalhinho committed Mar 30, 2024
1 parent c223fbb commit 8bbf915
Showing 1 changed file with 78 additions and 1 deletion.
79 changes: 78 additions & 1 deletion crates/bevy_ui/src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ pub struct LayoutContext {
}

impl LayoutContext {
fn default() -> Self {
Self {
scale_factor: 1.0,
physical_size: Vec2::ZERO,
min_size: 0.0,
max_size: 0.0,
}
}
/// create new a [`LayoutContext`] from the window's physical size and scale factor
fn new(scale_factor: f32, physical_size: Vec2) -> Self {
Self {
Expand All @@ -55,6 +63,7 @@ pub struct UiSurface {
camera_entity_to_taffy: EntityHashMap<EntityHashMap<taffy::node::Node>>,
camera_roots: EntityHashMap<Vec<RootNodePair>>,
taffy: Taffy,
no_camera: bool,
}

fn _assert_send_sync_ui_surface_impl_safe() {
Expand Down Expand Up @@ -82,6 +91,7 @@ impl Default for UiSurface {
camera_entity_to_taffy: Default::default(),
camera_roots: Default::default(),
taffy,
no_camera: false,
}
}
}
Expand Down Expand Up @@ -330,14 +340,18 @@ pub fn ui_layout_system(
);
continue;
};
ui_surface.no_camera = false;
let layout_info = camera_layout_info
.entry(camera_entity)
.or_insert_with(|| calculate_camera_layout_info(camera));
layout_info.root_nodes.push(entity);
}
None => {
if cameras.is_empty() {
warn!("No camera found to render UI to. To fix this, add at least one camera to the scene.");
if !ui_surface.no_camera {
ui_surface.no_camera = true;
warn!("No camera found to render UI to. To fix this, add at least one camera to the scene.");
}
} else {
warn!(
"Multiple cameras found, causing UI target ambiguity. \
Expand Down Expand Up @@ -366,6 +380,8 @@ pub fn ui_layout_system(
);
ui_surface.upsert_node(entity, &style, &layout_context);
}
} else {
ui_surface.upsert_node(entity, &Style::default(), &LayoutContext::default());
}
}
scale_factor_events.clear();
Expand Down Expand Up @@ -1209,4 +1225,65 @@ mod tests {
}
}
}

#[test]
fn no_camera_ui() {
let mut world = World::new();
world.init_resource::<UiScale>();
world.init_resource::<UiSurface>();
world.init_resource::<Events<WindowScaleFactorChanged>>();
world.init_resource::<Events<WindowResized>>();
// Required for the camera system
world.init_resource::<Events<WindowCreated>>();
world.init_resource::<Events<AssetEvent<Image>>>();
world.init_resource::<Assets<Image>>();
world.init_resource::<ManualTextureViews>();

// spawn a dummy primary window and camera
world.spawn((
Window {
resolution: WindowResolution::new(WINDOW_WIDTH, WINDOW_HEIGHT),
..default()
},
PrimaryWindow,
));

let mut ui_schedule = Schedule::default();
ui_schedule.add_systems(
(
// UI is driven by calculated camera target info, so we need to run the camera system first
bevy_render::camera::camera_system::<OrthographicProjection>,
update_target_camera_system,
apply_deferred,
ui_layout_system,
)
.chain(),
);

let ui_root = world
.spawn(NodeBundle {
style: Style {
width: Val::Percent(100.),
height: Val::Percent(100.),
..default()
},
..default()
})
.id();

let ui_child = world
.spawn(NodeBundle {
style: Style {
width: Val::Percent(100.),
height: Val::Percent(100.),
..default()
},
..default()
})
.id();

world.entity_mut(ui_root).add_child(ui_child);

ui_schedule.run(&mut world);
}
}

0 comments on commit 8bbf915

Please sign in to comment.