Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize resetting gesture recognizers #19987

Merged
merged 8 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 35 additions & 29 deletions src/Controls/src/Core/View/View.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#nullable disable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using Microsoft.Maui;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Graphics;
Expand Down Expand Up @@ -96,19 +96,20 @@ protected internal View()
_gestureManager = new GestureManager(this);
_gestureRecognizers.CollectionChanged += (sender, args) =>
{
void AddItems(IEnumerable<IElementDefinition> elements)
void AddItems(IList newItems)
{
foreach (IElementDefinition item in elements)
foreach (IElementDefinition item in newItems)
{
ValidateGesture(item as IGestureRecognizer);
var gestureRecognizer = item as IGestureRecognizer;
ValidateGesture(gestureRecognizer);
item.Parent = this;
GestureController.CompositeGestureRecognizers.Add(item as IGestureRecognizer);
GestureController.CompositeGestureRecognizers.Add(gestureRecognizer);
}
}

void RemoveItems(IEnumerable<IElementDefinition> elements)
void RemoveItems(IList oldItems)
{
foreach (IElementDefinition item in elements)
foreach (IElementDefinition item in oldItems)
{
item.Parent = null;
GestureController.CompositeGestureRecognizers.Remove(item as IGestureRecognizer);
Expand All @@ -118,41 +119,46 @@ void RemoveItems(IEnumerable<IElementDefinition> elements)
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
AddItems(args.NewItems.OfType<IElementDefinition>());
AddItems(args.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
RemoveItems(args.OldItems.OfType<IElementDefinition>());
RemoveItems(args.OldItems);
break;
case NotifyCollectionChangedAction.Replace:
AddItems(args.NewItems.OfType<IElementDefinition>());
RemoveItems(args.OldItems.OfType<IElementDefinition>());
AddItems(args.NewItems);
RemoveItems(args.OldItems);
break;
case NotifyCollectionChangedAction.Reset:

List<IElementDefinition> remove = new List<IElementDefinition>();
List<IElementDefinition> add = new List<IElementDefinition>();

foreach (IElementDefinition item in _gestureRecognizers.OfType<IElementDefinition>())
foreach (IGestureRecognizer gestureRecognizer in _gestureRecognizers)
{
if (!_gestureRecognizers.Contains((IGestureRecognizer)item))
add.Add(item);
item.Parent = this;
if (gestureRecognizer is IElementDefinition item)
{
item.Parent = this;
}
}

foreach (IElementDefinition item in GestureController.CompositeGestureRecognizers.OfType<IElementDefinition>())
{
if (item == _recognizerForPointerOverState)
continue;
HashSet<IGestureRecognizer> compositeGestureRecognizers = new(GestureController.CompositeGestureRecognizers);

if (_gestureRecognizers.Contains((IGestureRecognizer)item))
item.Parent = this;
else
remove.Add(item);
foreach (IGestureRecognizer gestureRecognizer in compositeGestureRecognizers)
symbiogenesis marked this conversation as resolved.
Show resolved Hide resolved
{
if (gestureRecognizer is IElementDefinition item)
{
if (item == _recognizerForPointerOverState)
continue;

if (_gestureRecognizers.Contains(gestureRecognizer))
{
item.Parent = this;
}
else
{
item.Parent = null;
GestureController.CompositeGestureRecognizers.Remove(gestureRecognizer);
}
}
}

AddItems(add);
RemoveItems(remove);

break;
}
};
Expand Down
123 changes: 123 additions & 0 deletions src/Core/tests/Benchmarks/Benchmarks/GestureRecognizerBenchmarker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using BenchmarkDotNet.Attributes;
using Microsoft.Maui.Controls;

namespace Microsoft.Maui.Handlers.Benchmarks;

[MemoryDiagnoser]
public class GestureRecognizerBenchmarker
{
View[] _views = null;
IGestureRecognizer[] _gestureRecognizers = null;

[GlobalSetup]
public void Setup()
{
_views = [
new Border(), new BoxView(), new CarouselView(), new Grid(), new Entry(), new Picker(), new CollectionView(),
new CheckBox(), new DatePicker(), new Stepper(), new Slider(), new ActivityIndicator(), new Frame(),
new ContentView(), new ProgressBar(), new SearchBar(), new Switch(), new TimePicker(), new WebView(), new Button(),
];

_gestureRecognizers = [
new PointerGestureRecognizer(), new TapGestureRecognizer(), new PanGestureRecognizer(),
new SwipeGestureRecognizer(), new DragGestureRecognizer(), new DropGestureRecognizer(),
];
}

[Benchmark]
public void AddLotsOfGestureRecognizers()
{
var layout = new VerticalStackLayout();

AddGestureRecognizers(layout, _gestureRecognizers);

for (int i = 0; i < 100; i++)
{
var childLayout = new VerticalStackLayout();

AddGestureRecognizers(childLayout, _gestureRecognizers);

foreach (var view in _views)
{
AddGestureRecognizers(view, _gestureRecognizers);

childLayout.Add(view);
}

layout.Add(childLayout);
}
}


[Benchmark]
public void ClearLotsOfGestureRecognizers()
{
var layout = new VerticalStackLayout();

for (int i = 0; i < 1000; i++)
{
AddGestureRecognizers(layout, _gestureRecognizers);
}

layout.GestureRecognizers.Clear();
}


[Benchmark]
public void RemoveLotsOfGestureRecognizers()
{
var layout = new VerticalStackLayout();

foreach (var gestureRecognizer in _gestureRecognizers)
{
for (int i = 0; i < 1000; i++)
{
layout.GestureRecognizers.Remove(gestureRecognizer);
}
}
}

[Benchmark]
public void AddOneGestureRecognizer()
{
var gestureRecognizer = new PointerGestureRecognizer();

for (int i = 0; i < 1000; i++)
{
var button = new Button();
button.GestureRecognizers.Add(gestureRecognizer);
}
}

[Benchmark]
public void RemoveOneGestureRecognizer()
{
var gestureRecognizer = new PointerGestureRecognizer();

for (int i = 0; i < 1000; i++)
{
var button = new Button();
button.GestureRecognizers.Remove(gestureRecognizer);
}
}

[Benchmark]
public void ClearOneGestureRecognizer()
{
for (int i = 0; i < 1000; i++)
{
var button = new Button();
var gestureRecognizer = new PointerGestureRecognizer();
button.GestureRecognizers.Add(gestureRecognizer);
button.GestureRecognizers.Clear();
}
}

private static void AddGestureRecognizers(View view, params IGestureRecognizer[] gestureRecognizers)
{
foreach (var gestureRecognizer in gestureRecognizers)
{
view.GestureRecognizers.Add(gestureRecognizer);
}
}
}
Loading