-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
049ec59
commit 6df2c93
Showing
4 changed files
with
269 additions
and
0 deletions.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
src/Controls/tests/TestCases.HostApp/Issues/RenderingPerformance.xaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?xml version="1.0" encoding="utf-8" ?> | ||
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | ||
xmlns:issues="clr-namespace:Maui.Controls.Sample.Issues" | ||
x:Class="Maui.Controls.Sample.Issues.RenderingPerformance" | ||
Title="RenderingPerformance"> | ||
|
||
<Grid Padding="24" RowDefinitions="Auto,*" RowSpacing="8"> | ||
<Button x:Name="StartButton" Clicked="ButtonClicked" Text="Start" AutomationId="StartButton" /> | ||
|
||
<ScrollView Grid.Row="1"> | ||
<ContentView> | ||
<VerticalStackLayout x:Name="BindableContainer" BindableLayout.ItemsSource="{Binding Models}"> | ||
<BindableLayout.ItemTemplate> | ||
<DataTemplate> | ||
<ContentView> | ||
<Grid RowDefinitions="Auto,Auto,Auto"> | ||
<Label Text="{Binding Header}" /> | ||
<Label Text="{Binding Content}" Grid.Row="1" /> | ||
<VerticalStackLayout Grid.Row="2" BindableLayout.ItemsSource="{Binding SubModels}"> | ||
<BindableLayout.ItemTemplate> | ||
<DataTemplate> | ||
<ContentView> | ||
<VerticalStackLayout> | ||
<Label Text="{Binding Header}" /> | ||
<issues:MeasuredLabel IsMeasured="{Binding IsMeasured}" Text="{Binding Content}" /> | ||
</VerticalStackLayout> | ||
</ContentView> | ||
</DataTemplate> | ||
</BindableLayout.ItemTemplate> | ||
</VerticalStackLayout> | ||
</Grid> | ||
</ContentView> | ||
</DataTemplate> | ||
</BindableLayout.ItemTemplate> | ||
</VerticalStackLayout> | ||
</ContentView> | ||
</ScrollView> | ||
</Grid> | ||
</ContentPage> |
197 changes: 197 additions & 0 deletions
197
src/Controls/tests/TestCases.HostApp/Issues/RenderingPerformance.xaml.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
using System; | ||
using System.Collections.ObjectModel; | ||
using System.Diagnostics; | ||
using System.Globalization; | ||
using Microsoft.Maui; | ||
using Microsoft.Maui.Controls; | ||
using Microsoft.Maui.Controls.Xaml; | ||
using ILayout = Microsoft.Maui.ILayout; | ||
|
||
namespace Maui.Controls.Sample.Issues; | ||
|
||
public class MeasuredLabel : Label | ||
{ | ||
private static readonly TimeSpan ArrangedThreshold = TimeSpan.FromSeconds(1); | ||
public static readonly BindableProperty IsMeasuredProperty = BindableProperty.Create(nameof(IsMeasured), typeof(bool), typeof(MeasuredLabel), false); | ||
|
||
public bool IsMeasured | ||
{ | ||
get => (bool)GetValue(IsMeasuredProperty); | ||
set => SetValue(IsMeasuredProperty, value); | ||
} | ||
|
||
public long? LastArrangedTicks { get; set; } | ||
|
||
public long? GetArrangeTicks() { | ||
if (LastArrangedTicks is { } ticks) | ||
{ | ||
var elapsed = Stopwatch.GetElapsedTime(ticks); | ||
if (elapsed > ArrangedThreshold) | ||
{ | ||
return ticks; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
|
||
public static class RenderingPerformanceExtensions | ||
{ | ||
public static MauiAppBuilder RenderingPerformanceAddMappers(this MauiAppBuilder builder) | ||
{ | ||
builder.ConfigureMauiHandlers(handlers => | ||
{ | ||
Microsoft.Maui.Handlers.LabelHandler.CommandMapper.AppendToMapping(nameof(IView.Frame), (handler, view, arg) => | ||
{ | ||
if (view is MeasuredLabel { IsMeasured: true } measuredLabel) | ||
{ | ||
measuredLabel.LastArrangedTicks = Stopwatch.GetTimestamp(); | ||
} | ||
}); | ||
}); | ||
|
||
return builder; | ||
} | ||
} | ||
|
||
[XamlCompilation(XamlCompilationOptions.Compile)] | ||
[Issue(IssueTracker.None, 0, "Rendering performance", PlatformAffected.All)] | ||
public partial class RenderingPerformance : ContentPage | ||
{ | ||
bool _firstRun = true; | ||
|
||
public List<Model> Models { get; set; } | ||
|
||
public RenderingPerformance() | ||
{ | ||
Models = GenerateMeasuredItem(); | ||
BindingContext = this; | ||
InitializeComponent(); | ||
} | ||
|
||
private async void ButtonClicked(object sender, EventArgs e) | ||
{ | ||
var capturedTimes = new List<int[]>(); | ||
|
||
var test1Models = GenerateItems(50, "Test1"); | ||
var test2Models = GenerateItems(25, "Test2"); | ||
var resetModel = GenerateMeasuredItem(); | ||
|
||
if (_firstRun) | ||
{ | ||
_firstRun = false; | ||
await GetArrangeTicksAsync(); | ||
} | ||
|
||
for (var i = 0; i < 6; i++) | ||
{ | ||
await Task.Delay(200); | ||
|
||
Models = test1Models; | ||
var startTicks = Stopwatch.GetTimestamp(); | ||
OnPropertyChanged(nameof(Models)); | ||
var endTicks = await Task.Run(GetArrangeTicksAsync); | ||
var t1 = (int)Stopwatch.GetElapsedTime(startTicks, endTicks).TotalMilliseconds; | ||
|
||
await Task.Delay(200); | ||
|
||
Models = test2Models; | ||
startTicks = Stopwatch.GetTimestamp(); | ||
OnPropertyChanged(nameof(Models)); | ||
endTicks = await Task.Run(GetArrangeTicksAsync); | ||
var t2 = (int)Stopwatch.GetElapsedTime(startTicks, endTicks).TotalMilliseconds; | ||
|
||
await Task.Delay(200); | ||
|
||
startTicks = Stopwatch.GetTimestamp(); | ||
BindableContainer.Clear(); | ||
Models = resetModel; | ||
OnPropertyChanged(nameof(Models)); | ||
endTicks = await Task.Run(GetArrangeTicksAsync); | ||
var t3 = (int)Stopwatch.GetElapsedTime(startTicks, endTicks).TotalMilliseconds; | ||
|
||
capturedTimes.Add([t1, t2, t3]); | ||
} | ||
|
||
var avg1 = (int)capturedTimes.Average(t => t[0]); | ||
var avg2 = (int)capturedTimes.Average(t => t[1]); | ||
var avg3 = (int)capturedTimes.Average(t => t[2]); | ||
StartButton.Text = $"{avg1},{avg2},{avg3}"; | ||
} | ||
|
||
async Task<long> GetArrangeTicksAsync() | ||
{ | ||
while (true) | ||
{ | ||
await Task.Delay(100); | ||
IView view = BindableContainer; | ||
while (true) | ||
{ | ||
if (view is ILayout { Count: > 0 } layout) | ||
{ | ||
view = layout[^1]; | ||
} | ||
else if (view is IContentView contentView) | ||
{ | ||
view = (IView)contentView.Content; | ||
} | ||
else | ||
{ | ||
break; | ||
} | ||
} | ||
|
||
if (view is MeasuredLabel measuredLabel && measuredLabel.GetArrangeTicks() is { } arrangeTicks) | ||
{ | ||
measuredLabel.LastArrangedTicks = null; | ||
return arrangeTicks; | ||
} | ||
} | ||
} | ||
|
||
static List<Model> GenerateItems(int count, string prefix) | ||
{ | ||
return | ||
[ | ||
..Enumerable.Range(0, count).Select(i => new Model | ||
{ | ||
Content = $"{prefix} Content {i}", | ||
Header = $"Header {i}", | ||
SubModels = Enumerable.Range(0, 10).Select(j => new SubModel | ||
{ | ||
Content = $"{prefix} SubContent {j}", Header = $"{prefix} SubHeader {j}" | ||
}).ToArray() | ||
}), | ||
..GenerateMeasuredItem() | ||
]; | ||
} | ||
|
||
static List<Model> GenerateMeasuredItem() | ||
{ | ||
return | ||
[ | ||
new Model | ||
{ | ||
Content = "Measured", | ||
Header = "Measured", | ||
SubModels = | ||
[ | ||
new SubModel { Content = "Measured", Header = "Measured", IsMeasured = true } | ||
] | ||
} | ||
]; | ||
} | ||
|
||
public class Model : SubModel | ||
{ | ||
public SubModel[] SubModels { get; set; } | ||
} | ||
|
||
public class SubModel | ||
{ | ||
public string Header { get; set; } | ||
public string Content { get; set; } | ||
public bool IsMeasured { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/RenderingPerformance.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using NUnit.Framework; | ||
using UITest.Appium; | ||
using UITest.Core; | ||
|
||
namespace Microsoft.Maui.TestCases.Tests.Issues | ||
{ | ||
public class RenderingPerformance : _IssuesUITest | ||
{ | ||
public RenderingPerformance(TestDevice device) : base(device) { } | ||
|
||
public override string Issue => "Rendering performance"; | ||
|
||
[Test] | ||
[Category(UITestCategories.Performance)] | ||
public async Task RenderingPerformanceRun() | ||
{ | ||
const string automationId = "StartButton"; | ||
|
||
var button = App.WaitForElement(automationId); | ||
App.Tap(automationId); | ||
|
||
App.WaitForTextToBePresentInElement(automationId, ","); | ||
|
||
var times = button.GetText()?.Split(',') ?? throw new ArgumentNullException("StartButton text is null"); | ||
|
||
var logFile = Path.Combine(Path.GetTempPath(), "RenderingPerformance.log"); | ||
await File.WriteAllTextAsync(logFile, @$"RenderingPerformance: [{times[0]}, {times[1]}, {times[2]}]"); | ||
TestContext.AddTestAttachment(logFile, "RenderingPerformance.log"); | ||
} | ||
} | ||
} |