Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
What does the pull request do?
This PR consists of a series of micro optimizations to
Measure()
andArrange()
, reducing the number of branches and operations.These changes result in a measurable reduction in layout times.
For example, our
Measure
micro-benchmark, which measures and arranges a few thousandsStackPanel
s andButton
s, goes from 1.847 ms before this PR to 1.421 ms after (.NET 8, Windows, x64), nearly a 30% increase in this specific case.This doesn't mean that users should expect a 30% increase overall. In practice, this depends on the visual tree complexity and controls used. For example,
TextBlock
is usually bottlenecked by text layouting. Also, it's important to keep in mind that layouting is only a part of the whole process of rendering controls to the screen.Changes
Layoutable.MeasureCore
MinMax
struct computes the expected min/max of a dimension (Width
orHeight
), but the result weren't reused inMeasureCore
like in WPF, effectively doing the work twice. This has been changed.Math.Min/Max
calls have been replaced with single comparisons. InMeasureCore
, one of the two operands is always the result and we don't care aboutNaN
or+0
/-0
ordering here.LayoutHelper.GetLayoutScale
TopLevel
for each measured visual. The validation has been moved toTopLevel
, once, instead.1.0
, it's normalized to exactly1.0
in theTopLevel
implementation. Thanks to that, we can easily check forscaling == 1.0
directly later instead of using the more expensiveMath.IsOne()
everywhere.LayoutHelper.RoundLayoutSize/Thickness
The complexity of these methods – used when
UseLayoutRounding
is enabled (the default) – has been reduced:scaleX
andscaleY
.1.0
exactly instead of usingMathUtilities.IsOne()
, since it's guaranteed to have been normalized by theTopLevel
(see above).NaN
,∞
andDouble.MaxValue
aren't checked anymore. The DPI scale is guaranteed to be valid. The only way to get to infinity is to have an invalid value in the first place (or a scale so large that nothing would render correctly anyways).Visual.VisualRoot
This property is accessed for each measure, arrange, bounds invalidation, visibility change and more...
It's been simplified to a single unconditional field access, set on appropriate paths, instead of always checking for a self implementation of
IRenderRoot
.