bevy/examples/3d
ickshonpe 300fe4db4d
Store UI render target info locally per node (#17579)
# Objective

It's difficult to understand or make changes to the UI systems because
of how each system needs to individually track changes to scale factor,
windows and camera targets in local hashmaps, particularly for new
contributors. Any major change inevitably introduces new scale factor
bugs.

Instead of per-system resolution we can resolve the camera target info
for all UI nodes in a system at the start of `PostUpdate` and then store
it per-node in components that can be queried with change detection.

Fixes #17578
Fixes #15143

## Solution

Store the UI render target's data locally per node in a component that
is updated in `PostUpdate` before any other UI systems run.

This component can be then be queried with change detection so that UI
systems no longer need to have knowledge of cameras and windows and
don't require fragile custom change detection solutions using local
hashmaps.

## Showcase
Compare `measure_text_system` from main (which has a bug the causes it
to use the wrong scale factor when a node's camera target changes):
```
pub fn measure_text_system(
    mut scale_factors_buffer: Local<EntityHashMap<f32>>,
    mut last_scale_factors: Local<EntityHashMap<f32>>,
    fonts: Res<Assets<Font>>,
    camera_query: Query<(Entity, &Camera)>,
    default_ui_camera: DefaultUiCamera,
    ui_scale: Res<UiScale>,
    mut text_query: Query<
        (
            Entity,
            Ref<TextLayout>,
            &mut ContentSize,
            &mut TextNodeFlags,
            &mut ComputedTextBlock,
            Option<&UiTargetCamera>,
        ),
        With<Node>,
    >,
    mut text_reader: TextUiReader,
    mut text_pipeline: ResMut<TextPipeline>,
    mut font_system: ResMut<CosmicFontSystem>,
) {
    scale_factors_buffer.clear();

    let default_camera_entity = default_ui_camera.get();

    for (entity, block, content_size, text_flags, computed, maybe_camera) in &mut text_query {
        let Some(camera_entity) = maybe_camera
            .map(UiTargetCamera::entity)
            .or(default_camera_entity)
        else {
            continue;
        };
        let scale_factor = match scale_factors_buffer.entry(camera_entity) {
            Entry::Occupied(entry) => *entry.get(),
            Entry::Vacant(entry) => *entry.insert(
                camera_query
                    .get(camera_entity)
                    .ok()
                    .and_then(|(_, c)| c.target_scaling_factor())
                    .unwrap_or(1.0)
                    * ui_scale.0,
            ),
        };

        if last_scale_factors.get(&camera_entity) != Some(&scale_factor)
            || computed.needs_rerender()
            || text_flags.needs_measure_fn
            || content_size.is_added()
        {
            create_text_measure(
                entity,
                &fonts,
                scale_factor.into(),
                text_reader.iter(entity),
                block,
                &mut text_pipeline,
                content_size,
                text_flags,
                computed,
                &mut font_system,
            );
        }
    }
    core::mem::swap(&mut *last_scale_factors, &mut *scale_factors_buffer);
}
```

with `measure_text_system` from this PR (which always uses the correct
scale factor):
```
pub fn measure_text_system(
    fonts: Res<Assets<Font>>,
    mut text_query: Query<
        (
            Entity,
            Ref<TextLayout>,
            &mut ContentSize,
            &mut TextNodeFlags,
            &mut ComputedTextBlock,
            Ref<ComputedNodeTarget>,
        ),
        With<Node>,
    >,
    mut text_reader: TextUiReader,
    mut text_pipeline: ResMut<TextPipeline>,
    mut font_system: ResMut<CosmicFontSystem>,
) {
    for (entity, block, content_size, text_flags, computed, computed_target) in &mut text_query {
        // Note: the ComputedTextBlock::needs_rerender bool is cleared in create_text_measure().
        if computed_target.is_changed()
            || computed.needs_rerender()
            || text_flags.needs_measure_fn
            || content_size.is_added()
        {
            create_text_measure(
                entity,
                &fonts,
                computed_target.scale_factor.into(),
                text_reader.iter(entity),
                block,
                &mut text_pipeline,
                content_size,
                text_flags,
                computed,
                &mut font_system,
            );
        }
    }
}
```

## Testing

I removed an alarming number of tests from the `layout` module but they
were mostly to do with the deleted camera synchronisation logic. The
remaining tests should all pass now.

The most relevant examples are `multiple_windows` and `split_screen`,
the behaviour of both should be unchanged from main.

---------

Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-02-10 07:27:58 +00:00
..
3d_scene.rs Migrate cameras to required components (#15641) 2024-10-05 01:59:52 +00:00
3d_shapes.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
3d_viewport_to_world.rs Fix window close in example cause panic (#17533) 2025-01-28 05:37:23 +00:00
animated_material.rs aligning public apis of Time,Timer and Stopwatch (#15962) 2024-10-16 21:09:32 +00:00
anisotropy.rs Add a Sphere to anisotropy example (#17676) 2025-02-05 20:23:43 +00:00
anti_aliasing.rs Don't reëxport bevy_image from bevy_render (#16163) 2024-11-10 06:54:38 +00:00
atmosphere.rs Procedural atmospheric scattering (#16314) 2025-01-23 22:52:46 +00:00
atmospheric_fog.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
auto_exposure.rs Introduce support for mixed lighting by allowing lights to opt out of contributing diffuse light to lightmapped objects. (#16761) 2024-12-16 23:48:33 +00:00
blend_modes.rs Allow clippy::too_many_arguments to lint without warnings (#17249) 2025-01-09 07:26:15 +00:00
bloom_3d.rs Anamorphic Bloom (#17096) 2025-01-06 18:43:21 +00:00
camera_sub_view.rs simplify example, replace get_single to Single Query (#16187) 2024-11-01 18:25:42 +00:00
clearcoat.rs Don't reëxport bevy_image from bevy_render (#16163) 2024-11-10 06:54:38 +00:00
clustered_decals.rs Disable clustered decals on Metal. (#17554) 2025-01-27 05:39:07 +00:00
color_grading.rs Relationships (non-fragmenting, one-to-many) (#17398) 2025-01-18 22:20:30 +00:00
decal.rs Forward decals (port of bevy_contact_projective_decals) (#16600) 2025-01-15 02:31:30 +00:00
deferred_rendering.rs Allow clippy::too_many_arguments to lint without warnings (#17249) 2025-01-09 07:26:15 +00:00
depth_of_field.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
edit_material_on_gltf.rs Add edit_material_on_gltf example (#17677) 2025-02-05 22:45:20 +00:00
fog_volumes.rs Deprecate SpatialBundle (#15830) 2024-10-13 17:28:22 +00:00
fog.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
generate_custom_mesh.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
irradiance_volumes.rs Introduce support for mixed lighting by allowing lights to opt out of contributing diffuse light to lightmapped objects. (#16761) 2024-12-16 23:48:33 +00:00
lighting.rs Improved Spawn APIs and Bundle Effects (#17521) 2025-02-09 23:32:56 +00:00
lightmaps.rs Higher quality bicubic lightmap sampling (#16740) 2025-01-12 05:40:30 +00:00
lines.rs Migrate cameras to required components (#15641) 2024-10-05 01:59:52 +00:00
load_gltf_extras.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
load_gltf.rs aligning public apis of Time,Timer and Stopwatch (#15962) 2024-10-16 21:09:32 +00:00
mesh_ray_cast.rs Rename RayCastSettings to MeshRayCastSettings (#16703) 2024-12-10 03:27:42 +00:00
meshlet.rs Add external assets to .gitignore (#17388) 2025-01-17 01:20:14 +00:00
mixed_lighting.rs Parent -> ChildOf (#17427) 2025-01-20 22:13:29 +00:00
motion_blur.rs Improved Spawn APIs and Bundle Effects (#17521) 2025-02-09 23:32:56 +00:00
occlusion_culling.rs Implement experimental GPU two-phase occlusion culling for the standard 3D mesh pipeline. (#17413) 2025-01-27 05:02:46 +00:00
order_independent_transparency.rs Relationships (non-fragmenting, one-to-many) (#17398) 2025-01-18 22:20:30 +00:00
orthographic.rs Improve API for scaling orthographic cameras (#15969) 2024-10-17 17:50:06 +00:00
parallax_mapping.rs Don't reëxport bevy_image from bevy_render (#16163) 2024-11-10 06:54:38 +00:00
parenting.rs aligning public apis of Time,Timer and Stopwatch (#15962) 2024-10-16 21:09:32 +00:00
pbr.rs Fix confusing comment in pbr example (#16996) 2024-12-29 22:45:17 +00:00
pcss.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
post_processing.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
query_gltf_primitives.rs Cosmetic tweaks to query_gltf_primitives (#16102) 2024-10-27 19:06:19 +00:00
reflection_probes.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
render_to_texture.rs aligning public apis of Time,Timer and Stopwatch (#15962) 2024-10-16 21:09:32 +00:00
rotate_environment_map.rs Don't reëxport bevy_image from bevy_render (#16163) 2024-11-10 06:54:38 +00:00
scrolling_fog.rs Don't reëxport bevy_image from bevy_render (#16163) 2024-11-10 06:54:38 +00:00
shadow_biases.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
shadow_caster_receiver.rs Migrate cameras to required components (#15641) 2024-10-05 01:59:52 +00:00
skybox.rs Introduce support for mixed lighting by allowing lights to opt out of contributing diffuse light to lightmapped objects. (#16761) 2024-12-16 23:48:33 +00:00
specular_tint.rs Add support for specular tints and maps per the KHR_materials_specular glTF extension. (#14069) 2025-01-26 20:38:46 +00:00
spherical_area_lights.rs Migrate cameras to required components (#15641) 2024-10-05 01:59:52 +00:00
split_screen.rs Store UI render target info locally per node (#17579) 2025-02-10 07:27:58 +00:00
spotlight.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
ssao.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
ssr.rs Allow clippy::too_many_arguments to lint without warnings (#17249) 2025-01-09 07:26:15 +00:00
texture.rs Migrate cameras to required components (#15641) 2024-10-05 01:59:52 +00:00
tonemapping.rs Move hashbrown and foldhash out of bevy_utils (#17460) 2025-01-23 16:46:08 +00:00
transmission.rs Allow clippy::too_many_arguments to lint without warnings (#17249) 2025-01-09 07:26:15 +00:00
transparency_3d.rs aligning public apis of Time,Timer and Stopwatch (#15962) 2024-10-16 21:09:32 +00:00
two_passes.rs Migrate cameras to required components (#15641) 2024-10-05 01:59:52 +00:00
update_gltf_scene.rs aligning public apis of Time,Timer and Stopwatch (#15962) 2024-10-16 21:09:32 +00:00
vertex_colors.rs Migrate cameras to required components (#15641) 2024-10-05 01:59:52 +00:00
visibility_range.rs Parent -> ChildOf (#17427) 2025-01-20 22:13:29 +00:00
volumetric_fog.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
wireframe.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00