Skip to content

Commit

Permalink
feat(arrange_dirty_path): First working version of arrange dirty path…
Browse files Browse the repository at this point in the history
… on Skia & Wasm
  • Loading branch information
carldebilly committed Mar 22, 2022
1 parent a949650 commit 56b69c7
Show file tree
Hide file tree
Showing 10 changed files with 246 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@
<Slider Header="Iterations" x:Name="iterations" Minimum="2" Maximum="1000" Value="60" />
<TextBlock>Deepness=<Run Text="{Binding Value, ElementName=deepness}" />, Wideness=<Run Text="{Binding Value, ElementName=wideness}" />, Iterations=<Run Text="{Binding Value, ElementName=iterations}" /></TextBlock>
<StackPanel Orientation="Horizontal" Spacing="8">
<StackPanel Orientation="Horizontal" Spacing="4">
<ToggleButton x:Name="optimizeMeasure" Click="changeOptimizeMeasure">Use MEASURE_DIRTY_PATH</ToggleButton>
</StackPanel>
<ToggleButton x:Name="optimizeMeasure" Click="changeOptimizeMeasure">Use MEASURE_DIRTY_PATH</ToggleButton>
<ToggleButton x:Name="optimizeArrange" Click="changeOptimizeMeasure">Use ARRANGE_DIRTY_PATH</ToggleButton>
<Button Click="BuildUI1">Build 1</Button>
<Button Click="GoTest1">Test Invalidations</Button>
<Button Click="GoTest2">Test Resizes</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ public UIElement_MeasurePerf()

#if !NETFX_CORE
bool originalUseInvalidateMeasurePath = FeatureConfiguration.UIElement.UseInvalidateMeasurePath;
bool originalUseInvalidateArrangePath = FeatureConfiguration.UIElement.UseInvalidateArrangePath;

Loaded += (_, _) =>
{
optimizeMeasure.IsChecked = FeatureConfiguration.UIElement.UseInvalidateMeasurePath;
optimizeArrange.IsChecked = FeatureConfiguration.UIElement.UseInvalidateArrangePath;
};

Unloaded += (_, _) =>
{
FeatureConfiguration.UIElement.UseInvalidateMeasurePath = originalUseInvalidateMeasurePath;
FeatureConfiguration.UIElement.UseInvalidateArrangePath = originalUseInvalidateArrangePath;
};
#endif
}
Expand Down Expand Up @@ -197,6 +200,15 @@ private void changeOptimizeMeasure(object sender, RoutedEventArgs e)
{
FeatureConfiguration.UIElement.UseInvalidateMeasurePath = false;
}

if (optimizeArrange.IsChecked is true)
{
FeatureConfiguration.UIElement.UseInvalidateArrangePath = true;
}
else
{
FeatureConfiguration.UIElement.UseInvalidateArrangePath = false;
}
#endif
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Uno.UI/Extensions/ViewExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ internal static string GetLayoutDetails(this UIElement uiElement)
#else
#endif
.Append(uiElement.IsMeasureDirtyPathDisabled is true ? " MEASURE_DIRTY_PATH_DISABLED" : "")
.Append(uiElement.IsArrangeDirtyPathDisabled is true ? " ARRANGE_DIRTY_PATH_DISABLED" : "")
.Append(uiElement.IsArrangeDirty is true ? " ARRANGE_DIRTY" : "");

return sb.ToString();
Expand Down
8 changes: 7 additions & 1 deletion src/Uno.UI/FeatureConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -514,10 +514,16 @@ public static class UIElement
{
/// <summary>
/// Call the .MeasureOverride only on element explicitly invalidating
/// their measure and when the size changed.
/// their measure and when the available size is changing.
/// </summary>
public static bool UseInvalidateMeasurePath { get; set; } = true;

/// <summary>
/// Call the .ArrangeOverride only on element explicitly invalidating
/// their arrange and when the final rect is changing.
/// </summary>
public static bool UseInvalidateArrangePath { get; set; } = true;

/// <summary>
/// [DEPRECATED]
/// Not used anymore, does nothing.
Expand Down
33 changes: 33 additions & 0 deletions src/Uno.UI/UI/Xaml/FrameworkElementHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,38 @@ public static void SetUseMeasurePathDisabled(UIElement element, bool state = tru

public static bool GetUseMeasurePathDisabled(FrameworkElement element)
=> element.IsMeasureDirtyPathDisabled;

/// <summary>
/// This is the equivalent of <see cref="FeatureConfiguration.UIElement.UseInvalidateArrangePath"/>
/// but just for a specific element (and its descendants) in the visual tree.
/// </summary>
/// <remarks>
/// This will have no effect if <see cref="FeatureConfiguration.UIElement.UseInvalidateArrangePath"/>
/// is set to false.
/// </remarks>
public static void SetUseArrangePathDisabled(UIElement element, bool state = true, bool eager = true, bool invalidate = true)
{
element.IsArrangeDirtyPathDisabled = state;

if (eager)
{
using var children = element.GetChildren().GetEnumerator();
while (children.MoveNext())
{
if (children.Current is FrameworkElement child)
{
SetUseArrangePathDisabled(child, state, eager: true, invalidate);
}
}
}

if (invalidate)
{
element.InvalidateArrange();
}
}

public static bool GetUseArrangePathDisabled(FrameworkElement element)
=> element.IsArrangeDirtyPathDisabled;
}
}
2 changes: 1 addition & 1 deletion src/Uno.UI/UI/Xaml/ILayouterElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ internal static bool DoMeasure(this ILayouterElement element, Size availableSize
// If the child is dirty (or is a path to a dirty descendant child),
// We're remeasuring it.

if (child is UIElement { IsMeasureOrMeasureDirtyPath: true })
if (child is UIElement { IsMeasureDirtyOrMeasureDirtyPath: true })
{
var previousDesiredSize = LayoutInformation.GetDesiredSize(child);
element.Layouter.MeasureChild(child, LayoutInformation.GetAvailableSize(child));
Expand Down
63 changes: 59 additions & 4 deletions src/Uno.UI/UI/Xaml/UIElement.Layout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// "Managed Measure Dirty Path" means it's the responsibility of the
// managed code (Uno) to walk the tree and do the measure phase.
#define IMPLEMENTS_MANAGED_MEASURE_DIRTY_PATH
#define IMPLEMENTS_MANAGED_ARRANGE_DIRTY_PATH
#endif
using System;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -31,7 +32,7 @@ internal bool IsMeasureDirtyPath
#endif

#if IMPLEMENTS_MANAGED_MEASURE_DIRTY_PATH
internal bool IsMeasureOrMeasureDirtyPath
internal bool IsMeasureDirtyOrMeasureDirtyPath
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => IsAnyLayoutFlagsSet(LayoutFlag.MeasureDirty | LayoutFlag.MeasureDirtyPath);
Expand All @@ -40,7 +41,7 @@ internal bool IsMeasureOrMeasureDirtyPath
/// <summary>
/// This is for compatibility - not implemented yet on this platform
/// </summary>
internal bool IsMeasureOrMeasureDirtyPath
internal bool IsMeasureDirtyOrMeasureDirtyPath
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => IsMeasureDirty || IsMeasureDirtyPath;
Expand Down Expand Up @@ -68,6 +69,29 @@ internal bool IsArrangeDirty
}
#endif

#if IMPLEMENTS_MANAGED_ARRANGE_DIRTY_PATH
internal bool IsArrangeDirtyPath
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => IsAnyLayoutFlagsSet(LayoutFlag.ArrangeDirtyPath);
}

internal bool IsArrangeDirtyOrArrangeDirtyPath
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => IsAnyLayoutFlagsSet(LayoutFlag.ArrangeDirty | LayoutFlag.ArrangeDirtyPath);
}
#else
/// <summary>
/// This is for compatibility - not implemented yet on this platform
/// </summary>
internal bool IsArrangeDirtyOrArrangeDirtyPath
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => IsArrangeDirty || IsArrangeDirtyPath;
}
#endif

[Flags]
internal enum LayoutFlag : byte
{
Expand All @@ -84,7 +108,7 @@ internal enum LayoutFlag : byte
#endif

/// <summary>
/// Indicated the first measure has been done on the element after been connected to parent
/// Indicates that first measure has been done on the element after been connected to parent
/// </summary>
FirstMeasureDone = 0b0000_0100,

Expand All @@ -104,7 +128,27 @@ internal enum LayoutFlag : byte
ArrangeDirty = 0b0001_0000,
#endif

// ArrangeDirtyPath not implemented yet
#if IMPLEMENTS_MANAGED_ARRANGE_DIRTY_PATH
/// <summary>
/// Means the Arrange is dirty on at least one child of this element
/// </summary>
ArrangeDirtyPath = 0b0010_0000,

/// <summary>
/// Indicates that first arrange has been done on the element and we can use the
/// LayoutInformation.GetLayoutSlot() to get previous finalRect
/// </summary>
FirstArrangeDone = 0b0100_0000,
#endif

/// <summary>
/// Means the MeasureDirtyPath is disabled on this element.
/// </summary>
/// <remarks>
/// This flag is copied to children when they are attached, but can be re-enabled afterwards.
/// This flag is used during invalidation
/// </remarks>
ArrangeDirtyPathDisabled = 0b1000_0000,
}

private const LayoutFlag DEFAULT_STARTING_LAYOUTFLAGS = 0;
Expand All @@ -115,6 +159,10 @@ internal enum LayoutFlag : byte
#endif
#if !__ANDROID__
LayoutFlag.ArrangeDirty |
#endif
#if IMPLEMENTS_MANAGED_ARRANGE_DIRTY_PATH
LayoutFlag.ArrangeDirtyPath |
LayoutFlag.FirstArrangeDone |
#endif
LayoutFlag.FirstMeasureDone;

Expand Down Expand Up @@ -172,6 +220,13 @@ internal bool IsMeasureDirtyPathDisabled
set => SetLayoutFlags(LayoutFlag.MeasureDirtyPathDisabled, value);
}

internal bool IsArrangeDirtyPathDisabled
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => IsLayoutFlagSet(LayoutFlag.ArrangeDirtyPathDisabled);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetLayoutFlags(LayoutFlag.ArrangeDirtyPathDisabled, value);
}
}
}
Loading

0 comments on commit 56b69c7

Please sign in to comment.