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

Improved UI camera mapping #17244

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
859ef04
Added `UiCameraMap` and `UiCameraMapper` to wrap UI camera queries an…
ickshonpe Jan 8, 2025
9564393
Use closure for mapper
ickshonpe Jan 8, 2025
f6c3712
Removed closure implementation.
ickshonpe Jan 8, 2025
dceba52
use 'w not 's lifetime param
ickshonpe Jan 8, 2025
ed61de6
Added `current_camera` method to `UiCameraMapper`
ickshonpe Jan 8, 2025
66c4b40
Merge branch 'main' into ui-camera-mapper
ickshonpe Jan 8, 2025
15e525a
Fixed syntax errors and imports after merge
ickshonpe Jan 8, 2025
f05cab8
Merge branch 'main' into ui-camera-mapper
ickshonpe Jan 10, 2025
568308b
Merge branch 'main' into ui-camera-mapper
ickshonpe Jan 11, 2025
d650992
Removed unused import
ickshonpe Jan 11, 2025
fded3a8
removed unnecessary closure
ickshonpe Jan 11, 2025
2c49c5c
Replaced the `new` function on `UiCameraMapper` with a `get_mapper` f…
ickshonpe Jan 13, 2025
dc6646d
Merge branch 'main' into ui-camera-mapper
ickshonpe Jan 13, 2025
5e82432
Fixed debug overlay
ickshonpe Jan 13, 2025
d2df3e7
Merge branch 'main' into ui-camera-mapper
ickshonpe Jan 14, 2025
2bd2f8d
clippy fixes
ickshonpe Jan 14, 2025
6c8f827
use `UiCameraMap` in `measure_text_system`
ickshonpe Jan 16, 2025
4a61d4a
Fixed imports
ickshonpe Jan 16, 2025
2d2b03a
Merge branch 'main' into ui-camera-mapper
ickshonpe Jan 22, 2025
6cb83c9
Fixed errors from merge
ickshonpe Jan 22, 2025
864b1bb
Fixed another import
ickshonpe Jan 22, 2025
de967ee
Fixed text scalefactor
ickshonpe Jan 22, 2025
11fd6c7
Removed debug print
ickshonpe Jan 22, 2025
398f3fa
Removed redundant closure
ickshonpe Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 14 additions & 16 deletions crates/bevy_ui/src/render/box_shadow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use core::{hash::Hash, ops::Range};

use crate::{
BoxShadow, BoxShadowSamples, CalculatedClip, ComputedNode, DefaultUiCamera, RenderUiSystem,
BoxShadow, BoxShadowSamples, CalculatedClip, ComputedNode, RenderUiSystem,
ResolvedBorderRadius, TransparentUi, UiTargetCamera, Val,
};
use bevy_app::prelude::*;
Expand All @@ -27,14 +27,14 @@ use bevy_render::{
render_phase::*,
render_resource::{binding_types::uniform_buffer, *},
renderer::{RenderDevice, RenderQueue},
sync_world::{RenderEntity, TemporaryRenderEntity},
sync_world::TemporaryRenderEntity,
view::*,
Extract, ExtractSchedule, Render, RenderSet,
};
use bevy_transform::prelude::GlobalTransform;
use bytemuck::{Pod, Zeroable};

use super::{stack_z_offsets, UiCameraView, QUAD_INDICES, QUAD_VERTEX_POSITIONS};
use super::{stack_z_offsets, UiCameraMap, UiCameraView, QUAD_INDICES, QUAD_VERTEX_POSITIONS};

pub const BOX_SHADOW_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(17717747047134343426);

Expand Down Expand Up @@ -236,7 +236,6 @@ pub struct ExtractedBoxShadows {
pub fn extract_shadows(
mut commands: Commands,
mut extracted_box_shadows: ResMut<ExtractedBoxShadows>,
default_ui_camera: Extract<DefaultUiCamera>,
camera_query: Extract<Query<(Entity, &Camera)>>,
box_shadow_query: Extract<
Query<(
Expand All @@ -249,28 +248,23 @@ pub fn extract_shadows(
Option<&UiTargetCamera>,
)>,
>,
mapping: Extract<Query<RenderEntity>>,
camera_map: Extract<UiCameraMap>,
) {
let default_camera_entity = default_ui_camera.get();
let mut camera_mapper = camera_map.get_mapper();

for (entity, uinode, transform, view_visibility, box_shadow, clip, camera) in &box_shadow_query
{
let Some(camera_entity) = camera.map(UiTargetCamera::entity).or(default_camera_entity)
else {
continue;
};

let Ok(extracted_camera_entity) = mapping.get(camera_entity) else {
continue;
};

// Skip if no visible shadows
if !view_visibility.get() || box_shadow.is_empty() || uinode.is_empty() {
continue;
}

let Some(extracted_camera_entity) = camera_mapper.map(camera) else {
continue;
};

let ui_physical_viewport_size = camera_query
.get(extracted_camera_entity)
.get(camera_mapper.current_camera())
.ok()
.and_then(|(_, c)| {
c.physical_viewport_size()
Expand Down Expand Up @@ -393,6 +387,10 @@ pub fn queue_shadows(
}
}

#[expect(
clippy::too_many_arguments,
reason = "Could be rewritten with less arguments using a QueryData-implementing struct, but doesn't need to be."
)]
pub fn prepare_shadows(
mut commands: Commands,
render_device: Res<RenderDevice>,
Expand Down
15 changes: 4 additions & 11 deletions crates/bevy_ui/src/render/debug_overlay.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::CalculatedClip;
use crate::ComputedNode;
use crate::DefaultUiCamera;
use crate::UiTargetCamera;
use bevy_asset::AssetId;
use bevy_color::Hsla;
Expand All @@ -12,7 +11,6 @@ use bevy_ecs::system::Res;
use bevy_ecs::system::ResMut;
use bevy_math::Rect;
use bevy_math::Vec2;
use bevy_render::sync_world::RenderEntity;
use bevy_render::sync_world::TemporaryRenderEntity;
use bevy_render::view::ViewVisibility;
use bevy_render::Extract;
Expand All @@ -23,6 +21,7 @@ use super::ExtractedUiItem;
use super::ExtractedUiNode;
use super::ExtractedUiNodes;
use super::NodeType;
use super::UiCameraMap;

/// Configuration for the UI debug overlay
#[derive(Resource)]
Expand Down Expand Up @@ -58,7 +57,6 @@ pub fn extract_debug_overlay(
mut commands: Commands,
debug_options: Extract<Res<UiDebugOptions>>,
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
default_ui_camera: Extract<DefaultUiCamera>,
uinode_query: Extract<
Query<(
Entity,
Expand All @@ -69,25 +67,20 @@ pub fn extract_debug_overlay(
Option<&UiTargetCamera>,
)>,
>,
mapping: Extract<Query<RenderEntity>>,
camera_map: Extract<UiCameraMap>,
) {
if !debug_options.enabled {
return;
}

let default_camera_entity = default_ui_camera.get();
let mut camera_mapper = camera_map.get_mapper();

for (entity, uinode, visibility, maybe_clip, transform, camera) in &uinode_query {
if !debug_options.show_hidden && !visibility.get() {
continue;
}

let Some(camera_entity) = camera.map(UiTargetCamera::entity).or(default_camera_entity)
else {
continue;
};

let Ok(extracted_camera_entity) = mapping.get(camera_entity) else {
let Some(extracted_camera_entity) = camera_mapper.map(camera) else {
continue;
};

Expand Down
118 changes: 72 additions & 46 deletions crates/bevy_ui/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d};
use bevy_ecs::entity::hash_map::EntityHashMap;
use bevy_ecs::prelude::*;
use bevy_ecs::system::SystemParam;
use bevy_image::prelude::*;
use bevy_math::{FloatOrd, Mat4, Rect, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4Swizzles};
use bevy_render::render_graph::{NodeRunError, RenderGraphContext};
Expand Down Expand Up @@ -251,6 +252,54 @@ impl ExtractedUiNodes {
}
}

#[derive(SystemParam)]
pub struct UiCameraMap<'w, 's> {
default: DefaultUiCamera<'w, 's>,
mapping: Query<'w, 's, RenderEntity>,
}

impl<'w, 's> UiCameraMap<'w, 's> {
/// Get the default camera and create the mapper
pub fn get_mapper(&'w self) -> UiCameraMapper<'w, 's> {
let default_camera_entity = self.default.get();
UiCameraMapper {
mapping: &self.mapping,
default_camera_entity,
camera_entity: Entity::PLACEHOLDER,
render_entity: Entity::PLACEHOLDER,
}
}
}

pub struct UiCameraMapper<'w, 's> {
mapping: &'w Query<'w, 's, RenderEntity>,
default_camera_entity: Option<Entity>,
camera_entity: Entity,
render_entity: Entity,
}

impl<'w, 's> UiCameraMapper<'w, 's> {
/// Returns the render entity corresponding to the given `UiTargetCamera` or the default camera if `None`.
pub fn map(&mut self, camera: Option<&UiTargetCamera>) -> Option<Entity> {
let camera_entity = camera
.map(UiTargetCamera::entity)
.or(self.default_camera_entity)?;
if self.camera_entity != camera_entity {
let Ok(new_render_camera_entity) = self.mapping.get(camera_entity) else {
return None;
};
self.render_entity = new_render_camera_entity;
self.camera_entity = camera_entity;
}

Some(self.render_entity)
}

pub fn current_camera(&self) -> Entity {
self.camera_entity
}
}

/// A [`RenderGraphNode`] that executes the UI rendering subgraph on the UI
/// view.
struct RunUiSubgraphOnUiViewNode;
Expand Down Expand Up @@ -279,7 +328,6 @@ impl RenderGraphNode for RunUiSubgraphOnUiViewNode {
pub fn extract_uinode_background_colors(
mut commands: Commands,
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
default_ui_camera: Extract<DefaultUiCamera>,
uinode_query: Extract<
Query<(
Entity,
Expand All @@ -291,26 +339,22 @@ pub fn extract_uinode_background_colors(
&BackgroundColor,
)>,
>,
mapping: Extract<Query<RenderEntity>>,
camera_map: Extract<UiCameraMap>,
) {
let default_camera_entity = default_ui_camera.get();
let mut camera_mapper = camera_map.get_mapper();

for (entity, uinode, transform, view_visibility, clip, camera, background_color) in
&uinode_query
{
let Some(camera_entity) = camera.map(UiTargetCamera::entity).or(default_camera_entity)
else {
continue;
};

let Ok(extracted_camera_entity) = mapping.get(camera_entity) else {
continue;
};

// Skip invisible backgrounds
if !view_visibility.get() || background_color.0.is_fully_transparent() {
continue;
}

let Some(extracted_camera_entity) = camera_mapper.map(camera) else {
continue;
};

extracted_uinodes.uinodes.insert(
commands.spawn(TemporaryRenderEntity).id(),
ExtractedUiNode {
Expand Down Expand Up @@ -342,7 +386,6 @@ pub fn extract_uinode_images(
mut commands: Commands,
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
texture_atlases: Extract<Res<Assets<TextureAtlasLayout>>>,
default_ui_camera: Extract<DefaultUiCamera>,
uinode_query: Extract<
Query<(
Entity,
Expand All @@ -354,19 +397,10 @@ pub fn extract_uinode_images(
&ImageNode,
)>,
>,
mapping: Extract<Query<RenderEntity>>,
camera_map: Extract<UiCameraMap>,
) {
let default_camera_entity = default_ui_camera.get();
let mut camera_mapper = camera_map.get_mapper();
for (entity, uinode, transform, view_visibility, clip, camera, image) in &uinode_query {
let Some(camera_entity) = camera.map(UiTargetCamera::entity).or(default_camera_entity)
else {
continue;
};

let Ok(extracted_camera_entity) = mapping.get(camera_entity) else {
continue;
};

// Skip invisible images
if !view_visibility.get()
|| image.color.is_fully_transparent()
Expand All @@ -376,6 +410,10 @@ pub fn extract_uinode_images(
continue;
}

let Some(extracted_camera_entity) = camera_mapper.map(camera) else {
continue;
};

let atlas_rect = image
.texture_atlas
.as_ref()
Expand Down Expand Up @@ -432,7 +470,6 @@ pub fn extract_uinode_images(
pub fn extract_uinode_borders(
mut commands: Commands,
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
default_ui_camera: Extract<DefaultUiCamera>,
uinode_query: Extract<
Query<(
Entity,
Expand All @@ -445,10 +482,11 @@ pub fn extract_uinode_borders(
AnyOf<(&BorderColor, &Outline)>,
)>,
>,
mapping: Extract<Query<RenderEntity>>,
camera_map: Extract<UiCameraMap>,
) {
let image = AssetId::<Image>::default();
let default_camera_entity = default_ui_camera.get();
let mut camera_mapper = camera_map.get_mapper();

for (
entity,
node,
Expand All @@ -460,22 +498,15 @@ pub fn extract_uinode_borders(
(maybe_border_color, maybe_outline),
) in &uinode_query
{
let Some(camera_entity) = maybe_camera
.map(UiTargetCamera::entity)
.or(default_camera_entity)
else {
continue;
};

let Ok(extracted_camera_entity) = mapping.get(camera_entity) else {
continue;
};

// Skip invisible borders and removed nodes
if !view_visibility.get() || node.display == Display::None {
continue;
}

let Some(extracted_camera_entity) = camera_mapper.map(maybe_camera) else {
continue;
};

// Don't extract borders with zero width along all edges
if computed_node.border() != BorderRect::ZERO {
if let Some(border_color) = maybe_border_color.filter(|bc| !bc.0.is_fully_transparent())
Expand Down Expand Up @@ -671,7 +702,6 @@ pub fn extract_ui_camera_view(
pub fn extract_text_sections(
mut commands: Commands,
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
default_ui_camera: Extract<DefaultUiCamera>,
texture_atlases: Extract<Res<Assets<TextureAtlasLayout>>>,
uinode_query: Extract<
Query<(
Expand All @@ -686,12 +716,12 @@ pub fn extract_text_sections(
)>,
>,
text_styles: Extract<Query<&TextColor>>,
mapping: Extract<Query<RenderEntity>>,
camera_map: Extract<UiCameraMap>,
) {
let mut start = 0;
let mut end = 1;

let default_ui_camera = default_ui_camera.get();
let mut camera_mapper = camera_map.get_mapper();
for (
entity,
uinode,
Expand All @@ -703,16 +733,12 @@ pub fn extract_text_sections(
text_layout_info,
) in &uinode_query
{
let Some(camera_entity) = camera.map(UiTargetCamera::entity).or(default_ui_camera) else {
continue;
};

// Skip if not visible or if size is set to zero (e.g. when a parent is set to `Display::None`)
if !view_visibility.get() || uinode.is_empty() {
continue;
}

let Ok(extracted_camera_entity) = mapping.get(camera_entity) else {
let Some(extracted_camera_entity) = camera_mapper.map(camera) else {
continue;
};

Expand Down
Loading
Loading