Fix debug-profile slider stutter (#25500) - #25536
alice-i-cecile merged 8 commits into
Conversation
EditableText re-triggered intrinsic-size recomputation and a full Taffy layout pass every frame regardless of whether its rendered size had actually changed, and ColorSlider/ColorPlane moved their thumb via Node::left/top (layout-affecting properties), forcing a relayout on every frame during drag. In debug builds this made the color slider/plane update in visible discrete steps instead of smoothly. - Cache the inputs that determine EditableText's intrinsic size (visible_width/visible_lines) and only recompute when they change. - Move ColorSlider and ColorPlane thumb positioning to UiTransform, which doesn't invalidate layout, instead of Node::left/top. - Split ColorPlane's thumb positioning into its own unfiltered system so it keeps tracking the container's resized size every frame, while update_plane_color keeps its Changed filter to avoid needlessly mutating Assets<ColorPlaneMaterial>. Debug-profile FPS during continuous slider drag went from ~11-14 FPS to ~59-63 FPS on Linux + NVIDIA RTX 4070 + Vulkan.
|
The macOS build job did not report a test failure; it was cancelled after reaching the workflow’s 40-minute timeout while running cargo run -p ci -- test. Ubuntu and Windows builds passed, as did run-examples-macos-metal. I’ll update the branch against current main to trigger a fresh run. If macOS times out again, could a maintainer please rerun the failed job? |
…sue-25500-slider-debug-perf # Conflicts: # crates/bevy_feathers/src/controls/color_slider.rs
cookie1170
left a comment
There was a problem hiding this comment.
tested it for myself, debug-mode performance did improve a lot! sadly the normal slider is still laggy, though i imagine similar changes there might not work since it actually needs to change size
i'm not entirely sure how often this would matter in real setups, however, since even the quick start guide recommends you compile bevy with opt 3
Slider dragging in feathers_gallery causes visible frame drops in debug builds (bevyengine#25500): the numeric caption's ContentSize is marked Changed every frame even when its measured size is identical, forcing ui_layout_system to re-sync the node with Taffy on every frame. Cache the last computed NoWrap intrinsic size on TextNodeFlags and only update ContentSize when it actually changes. Also switch the slider's numeric caption to TextLayout::no_wrap() so it takes this cheaper fixed- measure path instead of the wrapping text-measure path.
|
@cookie1170 Thanks for testing and pointing out the normal slider - it was using a different code path and was indeed still triggering a full UI layout while dragging. |
…r-debug-perf # Conflicts: # crates/bevy_feathers/src/controls/slider.rs
…sue-25500-slider-debug-perf # Conflicts: # crates/bevy_ui/src/widget/text_input_layout.rs
|
I'd like to merge this for 0.20: are you able to resolve merge conflicts? :) |
…sue-25500-slider-debug-perf # Conflicts: # crates/bevy_feathers/src/controls/color_plane.rs # crates/bevy_feathers/src/controls/color_slider.rs # crates/bevy_feathers/src/controls/slider.rs
|
Sure - I've resolved the merge confilcts against the lastest main and all the tests passed. |
Bug introduced in interaction of bevyengine#25536 with this PR.
# Objective - Fixes bevyengine#25500. - On debug builds, dragging the color slider/color plane in `feathers_gallery` updated in visible discrete steps instead of smoothly. Measured on Linux + NVIDIA RTX 4070 + Vulkan: out of 138 consecutive frames during a slow, controlled drag, only 86 states were unique (52 frames repeated the previous state) in debug, versus 129/138 unique in release. The slider's *value* updated correctly the whole time — this was a rendering smoothness problem, not an input problem. - Two causes were found: - `EditableText` triggered a full intrinsic-size recomputation (and thus a full Taffy layout pass) every frame, regardless of whether its rendered size had actually changed. - `ColorSlider` and `ColorPlane` moved their thumb every frame by writing `Node::left`/`Node::top`, which are layout-affecting properties and force a Taffy relayout on every frame during a drag. - Both are comparatively cheap in optimized builds but expensive enough in debug builds to visibly drop frames. ## Solution - `update_editable_text_content_size` now caches the inputs that actually determine `EditableText`'s intrinsic size (`visible_width`, `visible_lines`) in a new `EditableTextContentSizeState` component, and only recomputes when those specific fields change, instead of on every change to `EditableText` (which also churns from unrelated fields like cursor blink/edits). `EditableTextContentSizeState` is wired up as a required component of `bevy_text::EditableText` via `register_required_components` in `UiPlugin`, since `bevy_text` doesn't depend on `bevy_ui`. - `ColorSlider` and `ColorPlane` now position their thumb via `UiTransform` instead of `Node::left`/`Node::top`. `UiTransform` is applied after layout and doesn't invalidate Taffy, so moving the thumb no longer forces a relayout. - `ColorPlane`'s thumb positioning was split out of `update_plane_color` into its own system, `update_plane_thumb_position`, which runs unconditionally every frame so the thumb keeps tracking the parent node's actual size (e.g. on window resize) even when the color value hasn't changed. `update_plane_color` keeps its original `Changed` filter, since it mutates `Assets<ColorPlaneMaterial>`, which is comparatively expensive to touch every frame. ## Testing - `cargo test -p bevy_ui --lib`: 66/66 passed. - `cargo test -p bevy_feathers --lib`: 10/10 passed. - `cargo fmt --check` and `git diff --check` pass. - Manually verified in `feathers_gallery` (debug profile) that the color slider and color plane thumbs track the mouse correctly and land at the right position during and after a drag. - Manually verified that resizing the window while the color plane is visible keeps its thumb tracking the container's size correctly, both with and without a value change in between. - Measured debug-profile FPS during continuous slider drag: ~11-14 FPS before this fix, ~59-63 FPS after, both at rest and while continuously dragging. - Tested on Linux + NVIDIA RTX 4070 + Vulkan only. Not tested on Windows/macOS or other GPU vendors; the fix is platform-agnostic (it removes unnecessary layout/material work), so I don't expect platform-specific regressions, but review/testing on other platforms is welcome.
# Objective - Fixes bevyengine#25500. - On debug builds, dragging the color slider/color plane in `feathers_gallery` updated in visible discrete steps instead of smoothly. Measured on Linux + NVIDIA RTX 4070 + Vulkan: out of 138 consecutive frames during a slow, controlled drag, only 86 states were unique (52 frames repeated the previous state) in debug, versus 129/138 unique in release. The slider's *value* updated correctly the whole time — this was a rendering smoothness problem, not an input problem. - Two causes were found: - `EditableText` triggered a full intrinsic-size recomputation (and thus a full Taffy layout pass) every frame, regardless of whether its rendered size had actually changed. - `ColorSlider` and `ColorPlane` moved their thumb every frame by writing `Node::left`/`Node::top`, which are layout-affecting properties and force a Taffy relayout on every frame during a drag. - Both are comparatively cheap in optimized builds but expensive enough in debug builds to visibly drop frames. ## Solution - `update_editable_text_content_size` now caches the inputs that actually determine `EditableText`'s intrinsic size (`visible_width`, `visible_lines`) in a new `EditableTextContentSizeState` component, and only recomputes when those specific fields change, instead of on every change to `EditableText` (which also churns from unrelated fields like cursor blink/edits). `EditableTextContentSizeState` is wired up as a required component of `bevy_text::EditableText` via `register_required_components` in `UiPlugin`, since `bevy_text` doesn't depend on `bevy_ui`. - `ColorSlider` and `ColorPlane` now position their thumb via `UiTransform` instead of `Node::left`/`Node::top`. `UiTransform` is applied after layout and doesn't invalidate Taffy, so moving the thumb no longer forces a relayout. - `ColorPlane`'s thumb positioning was split out of `update_plane_color` into its own system, `update_plane_thumb_position`, which runs unconditionally every frame so the thumb keeps tracking the parent node's actual size (e.g. on window resize) even when the color value hasn't changed. `update_plane_color` keeps its original `Changed` filter, since it mutates `Assets<ColorPlaneMaterial>`, which is comparatively expensive to touch every frame. ## Testing - `cargo test -p bevy_ui --lib`: 66/66 passed. - `cargo test -p bevy_feathers --lib`: 10/10 passed. - `cargo fmt --check` and `git diff --check` pass. - Manually verified in `feathers_gallery` (debug profile) that the color slider and color plane thumbs track the mouse correctly and land at the right position during and after a drag. - Manually verified that resizing the window while the color plane is visible keeps its thumb tracking the container's size correctly, both with and without a value change in between. - Measured debug-profile FPS during continuous slider drag: ~11-14 FPS before this fix, ~59-63 FPS after, both at rest and while continuously dragging. - Tested on Linux + NVIDIA RTX 4070 + Vulkan only. Not tested on Windows/macOS or other GPU vendors; the fix is platform-agnostic (it removes unnecessary layout/material work), so I don't expect platform-specific regressions, but review/testing on other platforms is welcome.
# Objective PR bevyengine#25536 fixed performance for the color sliders and plane, but not the wheel as it had not merged yet. ## Solution This PR replicates the solution by using `UiTransform` instead of `Node::left`/`Node::top` to position the thumbs. This avoids a Taffy relayout. ## Testing Ran `feathers_example`, thumbs are still positioned correctly. Performance is slightly better on my machine, around 50fps rather than 45.
# Objective PR bevyengine#25536 fixed performance for the color sliders and plane, but not the wheel as it had not merged yet. ## Solution This PR replicates the solution by using `UiTransform` instead of `Node::left`/`Node::top` to position the thumbs. This avoids a Taffy relayout. ## Testing Ran `feathers_example`, thumbs are still positioned correctly. Performance is slightly better on my machine, around 50fps rather than 45.
# Objective PR #25536 fixed performance for the color sliders and plane, but not the wheel as it had not merged yet. ## Solution This PR replicates the solution by using `UiTransform` instead of `Node::left`/`Node::top` to position the thumbs. This avoids a Taffy relayout. ## Testing Ran `feathers_example`, thumbs are still positioned correctly. Performance is slightly better on my machine, around 50fps rather than 45.
Objective
feathers_gallerypoor performance in debug mode #25500.feathers_galleryupdated in visible discrete steps instead of smoothly. Measured on Linux + NVIDIA RTX 4070 + Vulkan: out of 138 consecutive frames during a slow, controlled drag, only 86 states were unique (52 frames repeated the previous state) in debug, versus 129/138 unique in release. The slider's value updated correctly the whole time — this was a rendering smoothness problem, not an input problem.EditableTexttriggered a full intrinsic-size recomputation (and thus a full Taffy layout pass) every frame, regardless of whether its rendered size had actually changed.ColorSliderandColorPlanemoved their thumb every frame by writingNode::left/Node::top, which are layout-affecting properties and force a Taffy relayout on every frame during a drag.Solution
update_editable_text_content_sizenow caches the inputs that actually determineEditableText's intrinsic size (visible_width,visible_lines) in a newEditableTextContentSizeStatecomponent, and only recomputes when those specific fields change, instead of on every change toEditableText(which also churns from unrelated fields like cursor blink/edits).EditableTextContentSizeStateis wired up as a required component ofbevy_text::EditableTextviaregister_required_componentsinUiPlugin, sincebevy_textdoesn't depend onbevy_ui.ColorSliderandColorPlanenow position their thumb viaUiTransforminstead ofNode::left/Node::top.UiTransformis applied after layout and doesn't invalidate Taffy, so moving the thumb no longer forces a relayout.ColorPlane's thumb positioning was split out ofupdate_plane_colorinto its own system,update_plane_thumb_position, which runs unconditionally every frame so the thumb keeps tracking the parent node's actual size (e.g. on window resize) even when the color value hasn't changed.update_plane_colorkeeps its originalChangedfilter, since it mutatesAssets<ColorPlaneMaterial>, which is comparatively expensive to touch every frame.Testing
cargo test -p bevy_ui --lib: 66/66 passed.cargo test -p bevy_feathers --lib: 10/10 passed.cargo fmt --checkandgit diff --checkpass.feathers_gallery(debug profile) that the color slider and color plane thumbs track the mouse correctly and land at the right position during and after a drag.