Using simple approx round up in ui_layout_system (#14079)

# Objective

- built-in `f32::round `is slow 
- splits from #14064 
## Solution

- using a simple floor instead of round

## Testing

- I am not an expert on floating-point values, but I enumerated all f32
values to test for potential errors compared to the previous function.
[rust
playground](https://play.rust-lang.org/?version=stable&mode=release&edition=2021&gist=0d8ed5604499e7bd9c61ce57d47e8c06)

three cases where the behavior differs between the new and previous
functions:
| value  |  previous | new  |  
|---|---|---|
|  [-0.5,0) |  -0 | +0  |   
|  0.49999997  | 0  |  1 |   
| +-8388609 |  8388609   | 8388610  |   

## Performance


![image](https://github.com/bevyengine/bevy/assets/45868716/1910f342-e55b-4f5c-851c-24a142d5c72e)
This commit is contained in:
re0312 2024-07-03 20:48:34 +08:00 committed by GitHub
parent 160bcc787c
commit 2893fc3e8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -254,11 +254,11 @@ pub fn ui_layout_system(
absolute_location += layout_location;
let rounded_size = round_layout_coords(absolute_location + layout_size)
- round_layout_coords(absolute_location);
let rounded_size = approx_round_layout_coords(absolute_location + layout_size)
- approx_round_layout_coords(absolute_location);
let rounded_location =
round_layout_coords(layout_location) + 0.5 * (rounded_size - parent_size);
approx_round_layout_coords(layout_location) + 0.5 * (rounded_size - parent_size);
// only trigger change detection when the new values are different
if node.calculated_size != rounded_size || node.unrounded_size != layout_size {
@ -315,15 +315,8 @@ pub fn resolve_outlines_system(
#[inline]
/// Round `value` to the nearest whole integer, with ties (values with a fractional part equal to 0.5) rounded towards positive infinity.
fn round_ties_up(value: f32) -> f32 {
if value.fract() != -0.5 {
// The `round` function rounds ties away from zero. For positive numbers "away from zero" is towards positive infinity.
// So for all positive values, and negative values with a fractional part not equal to 0.5, `round` returns the correct result.
value.round()
} else {
// In the remaining cases, where `value` is negative and its fractional part is equal to 0.5, we use `ceil` to round it up towards positive infinity.
value.ceil()
}
fn approx_round_ties_up(value: f32) -> f32 {
(value + 0.5).floor()
}
#[inline]
@ -334,10 +327,10 @@ fn round_ties_up(value: f32) -> f32 {
/// Example: The width between bounds of -50.5 and 49.5 before rounding is 100, using:
/// - `f32::round`: width becomes 101 (rounds to -51 and 50).
/// - `round_ties_up`: width is 100 (rounds to -50 and 50).
fn round_layout_coords(value: Vec2) -> Vec2 {
fn approx_round_layout_coords(value: Vec2) -> Vec2 {
Vec2 {
x: round_ties_up(value.x),
y: round_ties_up(value.y),
x: approx_round_ties_up(value.x),
y: approx_round_ties_up(value.y),
}
}
@ -376,7 +369,7 @@ mod tests {
use bevy_window::WindowResolution;
use bevy_window::WindowScaleFactorChanged;
use crate::layout::round_layout_coords;
use crate::layout::approx_round_layout_coords;
use crate::layout::ui_surface::UiSurface;
use crate::prelude::*;
use crate::ui_layout_system;
@ -385,7 +378,10 @@ mod tests {
#[test]
fn round_layout_coords_must_round_ties_up() {
assert_eq!(round_layout_coords(vec2(-50.5, 49.5)), vec2(-50., 50.));
assert_eq!(
approx_round_layout_coords(vec2(-50.5, 49.5)),
vec2(-50., 50.)
);
}
// these window dimensions are easy to convert to and from percentage values