[WinUI] Allocate less when updating gestures#21450
Conversation
|
Hey there @MartyIX! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
| } | ||
| } | ||
|
|
||
| public static bool HasAnyGesturesFor<T>(this IEnumerable<IGestureRecognizer>? gestures, Func<T, bool>? predicate = null) where T : GestureRecognizer |
|
|
||
| _container.CanDrag = gestures.GetGesturesFor<DragGestureRecognizer>() | ||
| .FirstOrDefault()?.CanDrag ?? false; | ||
| bool canDrag = gestures.GetGesturesFor<DragGestureRecognizer>().FirstOrDefault()?.CanDrag ?? false; |
There was a problem hiding this comment.
Store value in a local variable, so we save 1 interop call on L692 (i.e. if (_container.CanDrag) vs. if (canDrag)) and L698.
There was a problem hiding this comment.
Can this use your new HasAnyGesturesFor() method? And check CanDrag inside the predicate?
There was a problem hiding this comment.
I have come up with acadf48. Not really sure if that is what you had in mind.
| children?.GetChildGesturesFor<TapGestureRecognizer>().ToList(); | ||
|
|
||
| if (gestures.GetGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1).Any() | ||
| if (gestures.HasAnyGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1) |
There was a problem hiding this comment.
This should save 1 allocation.
| bool hasPanGesture = gestures.GetGesturesFor<PanGestureRecognizer>().GetEnumerator().MoveNext(); | ||
| bool hasSwipeGesture = gestures.HasAnyGesturesFor<SwipeGestureRecognizer>(); | ||
| bool hasPinchGesture = gestures.HasAnyGesturesFor<PinchGestureRecognizer>(); | ||
| bool hasPanGesture = gestures.HasAnyGesturesFor<PanGestureRecognizer>(); |
There was a problem hiding this comment.
3 allocations here.
There was a problem hiding this comment.
So one thing to consider is this would still iterate over the gestures list three times. How many are normally in this list? If it's usually like 1-5, that might be OK.
There was a problem hiding this comment.
I think the list contains only a handful of items as you said.
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
| bool hasPanGesture = gestures.GetGesturesFor<PanGestureRecognizer>().GetEnumerator().MoveNext(); | ||
| bool hasSwipeGesture = gestures.HasAnyGesturesFor<SwipeGestureRecognizer>(); | ||
| bool hasPinchGesture = gestures.HasAnyGesturesFor<PinchGestureRecognizer>(); | ||
| bool hasPanGesture = gestures.HasAnyGesturesFor<PanGestureRecognizer>(); |
There was a problem hiding this comment.
So one thing to consider is this would still iterate over the gestures list three times. How many are normally in this list? If it's usually like 1-5, that might be OK.
| } | ||
| } | ||
|
|
||
| public static bool HasAnyGesturesFor<T>(this IEnumerable<IGestureRecognizer>? gestures, Func<T, bool>? predicate = null) where T : GestureRecognizer |
There was a problem hiding this comment.
Can we make the new method(s) internal?
|
|
||
| _container.CanDrag = gestures.GetGesturesFor<DragGestureRecognizer>() | ||
| .FirstOrDefault()?.CanDrag ?? false; | ||
| bool canDrag = gestures.GetGesturesFor<DragGestureRecognizer>().FirstOrDefault()?.CanDrag ?? false; |
There was a problem hiding this comment.
Can this use your new HasAnyGesturesFor() method? And check CanDrag inside the predicate?
| return false; | ||
| } | ||
|
|
||
| predicate ??= x => true; |
There was a problem hiding this comment.
Could we just do a null check inside the foreach?
if (item is T gesture && (predicate is null || predicate(gesture)))
{
return true;
}This way we aren't creating a lambda like x => true.
|
Main: Maui.Controls.Sample.Sandbox.exe_20240326_202255.speedscope.json -> UNMANAGED_CODE_TIME: 1:26 (1 minute 26 seconds) PR: Maui.Controls.Sample.Sandbox.exe_20240326_202543.speedscope.json -> UNMANAGED_CODE_TIME: 54.21s On sample code: MainPage.xaml <ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.MainPage"
x:DataType="local:MainPage"
xmlns:local="clr-namespace:Maui.Controls.Sample">
<VerticalStackLayout x:Name="myLayout">
<Button Text="Add" Clicked="Button_Clicked"/>
<Label Text="Drag me">
<Label.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Label.GestureRecognizers>
</Label>
</VerticalStackLayout>
</ContentPage>MainPage.xaml.cs using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
namespace Maui.Controls.Sample
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private void DragGestureRecognizer_DragStarting(object? sender, DragStartingEventArgs e)
{
}
private void Button_Clicked(object sender, EventArgs e)
{
for (int i = 0; i < 10000; i++)
{
// Add the following XAML.
/*
<Rectangle Stroke="Red" Fill="DarkBlue" StrokeThickness="4" HeightRequest="200" WidthRequest="200">
<Rectangle.GestureRecognizers>
<DragGestureRecognizer DragStarting="DragGestureRecognizer_DragStarting" />
</Rectangle.GestureRecognizers>
</Rectangle>
*/
Microsoft.Maui.Controls.Shapes.Rectangle rectangle = new()
{
Fill = Colors.DarkBlue,
Stroke = Colors.Red,
StrokeThickness = 4,
WidthRequest = 200,
HeightRequest = 200,
};
DragGestureRecognizer recognizer = new();
recognizer.DragStarting += DragGestureRecognizer_DragStarting;
rectangle.GestureRecognizers.Add(recognizer);
myLayout.Add(rectangle);
}
}
}
} |
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
jonathanpeppers
left a comment
There was a problem hiding this comment.
The changes make sense to me. 👍
But I think someone most familiar with Windows gestures on the MAUI team should also review.
|
@mattleibow Could you please take a look? |
Description of Change
This PR adds a few very basic optimizations to improve performance of
GesturePlatformManager.UpdateDragAndDropGestureRecognizersand gestures in general.Measurements
Using commit e984695, I did:
Speedscope files:
Maui.Controls.Sample.Sandbox.exe_20240326_114219.speedscope.MAIN.json
Maui.Controls.Sample.Sandbox.exe_20240326_114435.speedscope.PR.json
Results
main:

PR:

Unfortunately, the results are not stable. So the results are not meaningful. I think one would have to start the application and then create gestures in code to measure it better. Any other ideas?
Issues Fixed
Contributes to #21449
See also: #21377