UI z-ordering fix (#19691)
# Objective During the migration to required components a lot of things were changed around and somehow the draw order for some UI elements ended up depending on the system ordering in `RenderSystems::Queue`, which can sometimes result in the elements being drawn in the wrong order. Fixes #19674 ## Solution * Added some more `stack_z_offsets` constants and used them to enforce an explicit ordering. * Removed the `stack_index: u32` field from `ExtractedUiNodes` and replaced it with a `z_order: f32` field. These changes should fix all the ordering problems. ## Testing I added a nine-patched bordered node with a navy background color to the slice section of the `testbed_ui` example. The border should always be drawn above the background color.
This commit is contained in:
parent
5e8aa7986b
commit
a949867a1c
@ -1,8 +1,14 @@
|
||||
use super::ExtractedUiItem;
|
||||
use super::ExtractedUiNode;
|
||||
use super::ExtractedUiNodes;
|
||||
use super::NodeType;
|
||||
use super::UiCameraMap;
|
||||
use crate::shader_flags;
|
||||
use crate::ui_node::ComputedNodeTarget;
|
||||
use crate::ui_transform::UiGlobalTransform;
|
||||
use crate::CalculatedClip;
|
||||
use crate::ComputedNode;
|
||||
use crate::UiStack;
|
||||
use bevy_asset::AssetId;
|
||||
use bevy_color::Hsla;
|
||||
use bevy_ecs::entity::Entity;
|
||||
@ -18,12 +24,6 @@ use bevy_render::view::InheritedVisibility;
|
||||
use bevy_render::Extract;
|
||||
use bevy_sprite::BorderRect;
|
||||
|
||||
use super::ExtractedUiItem;
|
||||
use super::ExtractedUiNode;
|
||||
use super::ExtractedUiNodes;
|
||||
use super::NodeType;
|
||||
use super::UiCameraMap;
|
||||
|
||||
/// Configuration for the UI debug overlay
|
||||
#[derive(Resource)]
|
||||
pub struct UiDebugOptions {
|
||||
@ -68,6 +68,7 @@ pub fn extract_debug_overlay(
|
||||
&ComputedNodeTarget,
|
||||
)>,
|
||||
>,
|
||||
ui_stack: Extract<Res<UiStack>>,
|
||||
camera_map: Extract<UiCameraMap>,
|
||||
) {
|
||||
if !debug_options.enabled {
|
||||
@ -89,7 +90,7 @@ pub fn extract_debug_overlay(
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
||||
// Add a large number to the UI node's stack index so that the overlay is always drawn on top
|
||||
stack_index: uinode.stack_index + u32::MAX / 2,
|
||||
z_order: (ui_stack.uinodes.len() as u32 + uinode.stack_index()) as f32,
|
||||
color: Hsla::sequential_dispersed(entity.index()).into(),
|
||||
rect: Rect {
|
||||
min: Vec2::ZERO,
|
||||
|
@ -408,7 +408,11 @@ pub fn extract_gradients(
|
||||
if let Some(color) = gradient.get_single() {
|
||||
// With a single color stop there's no gradient, fill the node with the color
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
stack_index: uinode.stack_index,
|
||||
z_order: uinode.stack_index as f32
|
||||
+ match node_type {
|
||||
NodeType::Rect => stack_z_offsets::GRADIENT,
|
||||
NodeType::Border(_) => stack_z_offsets::BORDER_GRADIENT,
|
||||
},
|
||||
color: color.into(),
|
||||
rect: Rect {
|
||||
min: Vec2::ZERO,
|
||||
@ -629,7 +633,13 @@ pub fn queue_gradient(
|
||||
draw_function,
|
||||
pipeline,
|
||||
entity: (gradient.render_entity, gradient.main_entity),
|
||||
sort_key: FloatOrd(gradient.stack_index as f32 + stack_z_offsets::GRADIENT),
|
||||
sort_key: FloatOrd(
|
||||
gradient.stack_index as f32
|
||||
+ match gradient.node_type {
|
||||
NodeType::Rect => stack_z_offsets::GRADIENT,
|
||||
NodeType::Border(_) => stack_z_offsets::BORDER_GRADIENT,
|
||||
},
|
||||
),
|
||||
batch_range: 0..0,
|
||||
extra_index: PhaseItemExtraIndex::None,
|
||||
index,
|
||||
|
@ -81,7 +81,7 @@ pub mod graph {
|
||||
}
|
||||
}
|
||||
|
||||
/// Z offsets of "extracted nodes" for a given entity. These exist to allow rendering multiple "extracted nodes"
|
||||
/// Local Z offsets of "extracted nodes" for a given entity. These exist to allow rendering multiple "extracted nodes"
|
||||
/// for a given source entity (ex: render both a background color _and_ a custom material for a given node).
|
||||
///
|
||||
/// When possible these offsets should be defined in _this_ module to ensure z-index coordination across contexts.
|
||||
@ -97,10 +97,13 @@ pub mod graph {
|
||||
/// a positive offset on a node below.
|
||||
pub mod stack_z_offsets {
|
||||
pub const BOX_SHADOW: f32 = -0.1;
|
||||
pub const TEXTURE_SLICE: f32 = 0.0;
|
||||
pub const NODE: f32 = 0.0;
|
||||
pub const GRADIENT: f32 = 0.1;
|
||||
pub const MATERIAL: f32 = 0.18267;
|
||||
pub const BACKGROUND_COLOR: f32 = 0.0;
|
||||
pub const BORDER: f32 = 0.01;
|
||||
pub const GRADIENT: f32 = 0.02;
|
||||
pub const BORDER_GRADIENT: f32 = 0.03;
|
||||
pub const IMAGE: f32 = 0.04;
|
||||
pub const MATERIAL: f32 = 0.05;
|
||||
pub const TEXT: f32 = 0.06;
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
|
||||
@ -213,7 +216,7 @@ fn get_ui_graph(render_app: &mut SubApp) -> RenderGraph {
|
||||
}
|
||||
|
||||
pub struct ExtractedUiNode {
|
||||
pub stack_index: u32,
|
||||
pub z_order: f32,
|
||||
pub color: LinearRgba,
|
||||
pub rect: Rect,
|
||||
pub image: AssetId<Image>,
|
||||
@ -374,7 +377,7 @@ pub fn extract_uinode_background_colors(
|
||||
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
||||
stack_index: uinode.stack_index,
|
||||
z_order: uinode.stack_index as f32 + stack_z_offsets::BACKGROUND_COLOR,
|
||||
color: background_color.0.into(),
|
||||
rect: Rect {
|
||||
min: Vec2::ZERO,
|
||||
@ -460,8 +463,8 @@ pub fn extract_uinode_images(
|
||||
};
|
||||
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
z_order: uinode.stack_index as f32 + stack_z_offsets::IMAGE,
|
||||
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
||||
stack_index: uinode.stack_index,
|
||||
color: image.color.into(),
|
||||
rect,
|
||||
clip: clip.map(|clip| clip.clip),
|
||||
@ -558,7 +561,7 @@ pub fn extract_uinode_borders(
|
||||
completed_flags |= border_flags;
|
||||
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
stack_index: computed_node.stack_index,
|
||||
z_order: computed_node.stack_index as f32 + stack_z_offsets::BORDER,
|
||||
color,
|
||||
rect: Rect {
|
||||
max: computed_node.size(),
|
||||
@ -591,8 +594,8 @@ pub fn extract_uinode_borders(
|
||||
{
|
||||
let outline_size = computed_node.outlined_node_size();
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
z_order: computed_node.stack_index as f32 + stack_z_offsets::BORDER,
|
||||
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
||||
stack_index: computed_node.stack_index,
|
||||
color: outline.color.into(),
|
||||
rect: Rect {
|
||||
max: outline_size,
|
||||
@ -782,8 +785,8 @@ pub fn extract_viewport_nodes(
|
||||
};
|
||||
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
z_order: uinode.stack_index as f32 + stack_z_offsets::IMAGE,
|
||||
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
||||
stack_index: uinode.stack_index,
|
||||
color: LinearRgba::WHITE,
|
||||
rect: Rect {
|
||||
min: Vec2::ZERO,
|
||||
@ -885,8 +888,8 @@ pub fn extract_text_sections(
|
||||
.map(|text_color| LinearRgba::from(text_color.0))
|
||||
.unwrap_or_default();
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
z_order: uinode.stack_index as f32 + stack_z_offsets::TEXT,
|
||||
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
||||
stack_index: uinode.stack_index,
|
||||
color,
|
||||
image: atlas_info.texture.id(),
|
||||
clip: clip.map(|clip| clip.clip),
|
||||
@ -966,8 +969,8 @@ pub fn extract_text_shadows(
|
||||
info.span_index != *span_index || info.atlas_info.texture != atlas_info.texture
|
||||
}) {
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
z_order: uinode.stack_index as f32 + stack_z_offsets::TEXT,
|
||||
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
||||
stack_index: uinode.stack_index,
|
||||
color: shadow.color.into(),
|
||||
image: atlas_info.texture.id(),
|
||||
clip: clip.map(|clip| clip.clip),
|
||||
@ -1023,8 +1026,8 @@ pub fn extract_text_background_colors(
|
||||
};
|
||||
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
z_order: uinode.stack_index as f32 + stack_z_offsets::TEXT,
|
||||
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
||||
stack_index: uinode.stack_index,
|
||||
color: text_background_color.0.to_linear(),
|
||||
rect: Rect {
|
||||
min: Vec2::ZERO,
|
||||
@ -1167,7 +1170,7 @@ pub fn queue_uinodes(
|
||||
draw_function,
|
||||
pipeline,
|
||||
entity: (extracted_uinode.render_entity, extracted_uinode.main_entity),
|
||||
sort_key: FloatOrd(extracted_uinode.stack_index as f32 + stack_z_offsets::NODE),
|
||||
sort_key: FloatOrd(extracted_uinode.z_order),
|
||||
index,
|
||||
// batch_range will be calculated in prepare_uinodes
|
||||
batch_range: 0..0,
|
||||
|
@ -366,9 +366,7 @@ pub fn queue_ui_slices(
|
||||
draw_function,
|
||||
pipeline,
|
||||
entity: (extracted_slicer.render_entity, extracted_slicer.main_entity),
|
||||
sort_key: FloatOrd(
|
||||
extracted_slicer.stack_index as f32 + stack_z_offsets::TEXTURE_SLICE,
|
||||
),
|
||||
sort_key: FloatOrd(extracted_slicer.stack_index as f32 + stack_z_offsets::IMAGE),
|
||||
batch_range: 0..0,
|
||||
extra_index: PhaseItemExtraIndex::None,
|
||||
index,
|
||||
|
@ -484,6 +484,26 @@ mod slice {
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
parent.spawn((
|
||||
ImageNode {
|
||||
image: asset_server
|
||||
.load("textures/fantasy_ui_borders/panel-border-010.png"),
|
||||
image_mode: NodeImageMode::Sliced(TextureSlicer {
|
||||
border: BorderRect::all(22.0),
|
||||
center_scale_mode: SliceScaleMode::Stretch,
|
||||
sides_scale_mode: SliceScaleMode::Stretch,
|
||||
max_corner_scale: 1.0,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
Node {
|
||||
width: Val::Px(100.),
|
||||
height: Val::Px(100.),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(bevy::color::palettes::css::NAVY.into()),
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
34
release-content/migration-guides/stack_z_offsets_changes.md
Normal file
34
release-content/migration-guides/stack_z_offsets_changes.md
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
title: Fixed UI draw order and `stack_z_offsets` changes
|
||||
pull_requests: [19691]
|
||||
---
|
||||
|
||||
The draw order of some renderable UI elements relative to others wasn't fixed and depended on system ordering.
|
||||
In particular the ordering of background colors and texture sliced images was sometimes swapped.
|
||||
|
||||
The UI draw order is now fixed.
|
||||
The new order is (back-to-front):
|
||||
|
||||
1. Box shadows
|
||||
|
||||
2. Node background colors
|
||||
|
||||
3. Node borders
|
||||
|
||||
4. Gradients
|
||||
|
||||
5. Border Gradients
|
||||
|
||||
6. Images (including texture-sliced images)
|
||||
|
||||
7. Materials
|
||||
|
||||
8. Text (including text shadows)
|
||||
|
||||
The values of the `stack_z_offsets` constants have been updated to enforce the new ordering. Other changes:
|
||||
|
||||
* `NODE` is renamed to `BACKGROUND_COLOR`
|
||||
|
||||
* `TEXTURE_SLICE` is removed, use `IMAGE`.
|
||||
|
||||
* New `BORDER`, `BORDER_GRADIENT` and `TEXT` constants.
|
Loading…
Reference in New Issue
Block a user