Skip to content

Commit eed2fed

Browse files
authored
Merge branch 'master' into feat-container-queries
2 parents 51f0889 + acd4653 commit eed2fed

File tree

26 files changed

+291
-24
lines changed

26 files changed

+291
-24
lines changed

Avalonia.sln

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
115115
build\SkiaSharp.props = build\SkiaSharp.props
116116
build\SourceGenerators.props = build\SourceGenerators.props
117117
build\SourceLink.props = build\SourceLink.props
118-
build\System.Memory.props = build\System.Memory.props
119118
build\TargetFrameworks.props = build\TargetFrameworks.props
120119
build\TrimmingEnable.props = build\TrimmingEnable.props
121120
build\UnitTests.NetFX.props = build\UnitTests.NetFX.props

build/Base.props

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
22
<!-- '!NET6_0_OR_GREATER' equivalent -->
3+
<ItemGroup Condition="!('$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '6.0')))">
4+
<PackageReference Include="System.Memory" Version="4.5.5" />
5+
</ItemGroup>
36
<ItemGroup Condition="!('$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '6.0')))">
47
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
58
</ItemGroup>
9+
<ItemGroup Condition="!('$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '8.0')))">
10+
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
11+
</ItemGroup>
612
</Project>

build/System.Memory.props

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/Avalonia.Base/Avalonia.Base.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
</ItemGroup>
1313
<Import Project="..\..\build\Base.props" />
1414
<Import Project="..\..\build\Binding.props" />
15-
<Import Project="..\..\build\System.Memory.props" />
1615
<Import Project="..\..\build\NullableEnable.props" />
1716
<Import Project="..\..\build\TrimmingEnable.props" />
1817
<Import Project="..\..\build\DevAnalyzers.props" />

src/Avalonia.Base/Controls/ResourceNodeExtensions.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using Avalonia.Diagnostics;
23
using Avalonia.Reactive;
34
using Avalonia.Styling;
45

@@ -75,12 +76,17 @@ public static bool TryFindResource(this IResourceHost control, object key, Theme
7576
control = control ?? throw new ArgumentNullException(nameof(control));
7677
key = key ?? throw new ArgumentNullException(nameof(key));
7778

78-
IResourceHost? current = control;
79+
using var activity = Diagnostic.FindingResource()?
80+
.AddTag(Diagnostic.Tags.Key, key)
81+
.AddTag(Diagnostic.Tags.ThemeVariant, theme);
82+
83+
var current = control;
7984

8085
while (current != null)
8186
{
8287
if (current.TryGetResource(key, theme, out value))
8388
{
89+
activity?.AddTag(Diagnostic.Tags.Result, true);
8490
return true;
8591
}
8692

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System.Diagnostics;
2+
3+
// ReSharper disable ExplicitCallerInfoArgument
4+
5+
namespace Avalonia.Diagnostics;
6+
7+
internal static partial class Diagnostic
8+
{
9+
private static ActivitySource? s_activitySource;
10+
11+
public static void InitActivitySource()
12+
{
13+
s_activitySource = new("Avalonia.Diagnostic.Source");
14+
}
15+
16+
private static Activity? StartActivity(string name) => s_activitySource?.StartActivity(name);
17+
18+
public static Activity? AttachingStyle() => StartActivity("Avalonia.AttachingStyle");
19+
public static Activity? FindingResource() => StartActivity("Avalonia.FindingResource");
20+
public static Activity? EvaluatingStyle() => StartActivity("Avalonia.EvaluatingStyle");
21+
public static Activity? MeasuringLayoutable() => StartActivity("Avalonia.MeasuringLayoutable");
22+
public static Activity? ArrangingLayoutable() => StartActivity("Avalonia.ArrangingLayoutable");
23+
public static Activity? PerformingHitTest() => StartActivity("Avalonia.PerformingHitTest");
24+
public static Activity? RaisingRoutedEvent() => StartActivity("Avalonia.RaisingRoutedEvent");
25+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
namespace Avalonia.Diagnostics;
2+
3+
internal static partial class Diagnostic
4+
{
5+
public static class Meters
6+
{
7+
public const string SecondsUnit = "s";
8+
public const string MillisecondsUnit = "ms";
9+
10+
public const string CompositorRenderPassName = "avalonia.comp.render.time";
11+
public const string CompositorRenderPassDescription = "Duration of the compositor render pass on render thread";
12+
public const string CompositorUpdatePassName = "avalonia.comp.update.time";
13+
public const string CompositorUpdatePassDescription = "Duration of the compositor update pass on render thread";
14+
15+
public const string LayoutMeasurePassName = "avalonia.ui.measure.time";
16+
public const string LayoutMeasurePassDescription = "Duration of layout measurement pass on UI thread";
17+
public const string LayoutArrangePassName = "avalonia.ui.arrange.time";
18+
public const string LayoutArrangePassDescription = "Duration of layout arrangement pass on UI thread";
19+
public const string LayoutRenderPassName = "avalonia.ui.render.time";
20+
public const string LayoutRenderPassDescription = "Duration of render recording pass on UI thread";
21+
public const string LayoutInputPassName = "avalonia.ui.input.time";
22+
public const string LayoutInputPassDescription = "Duration of input processing on UI thread";
23+
24+
public const string TotalEventHandleCountName = "avalonia.ui.event.handler.count";
25+
public const string TotalEventHandleCountDescription = "Number of event handlers currently registered in the application";
26+
public const string TotalEventHandleCountUnit = "{handler}";
27+
public const string TotalVisualCountName = "avalonia.ui.visual.count";
28+
public const string TotalVisualCountDescription = "Number of visual elements currently present in the visual tree";
29+
public const string TotalVisualCountUnit = "{visual}";
30+
public const string TotalDispatcherTimerCountName = "avalonia.ui.dispatcher.timer.count";
31+
public const string TotalDispatcherTimerCountDescription = "Number of active dispatcher timers in the application";
32+
public const string TotalDispatcherTimerCountUnit = "{timer}";
33+
}
34+
35+
public static class Tags
36+
{
37+
public const string Style = nameof(Style);
38+
public const string SelectorResult = nameof(SelectorResult);
39+
40+
public const string Key = nameof(Key);
41+
public const string ThemeVariant = nameof(ThemeVariant);
42+
public const string Result = nameof(Result);
43+
44+
public const string Activator = nameof(Activator);
45+
public const string IsActive = nameof(IsActive);
46+
public const string Selector = nameof(Selector);
47+
public const string Control = nameof(Control);
48+
49+
public const string RoutedEvent = nameof(RoutedEvent);
50+
}
51+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using System.Diagnostics;
2+
using System.Diagnostics.Metrics;
3+
using Avalonia.Interactivity;
4+
using Avalonia.Threading;
5+
using Avalonia.Utilities;
6+
7+
namespace Avalonia.Diagnostics;
8+
9+
internal static partial class Diagnostic
10+
{
11+
private static Histogram<double>? s_compositorRender;
12+
private static Histogram<double>? s_compositorUpdate;
13+
private static Histogram<double>? s_layoutMeasure;
14+
private static Histogram<double>? s_layoutArrange;
15+
private static Histogram<double>? s_layoutRender;
16+
private static Histogram<double>? s_layoutInput;
17+
18+
public static void InitMetrics()
19+
{
20+
// Metrics
21+
var meter = new Meter("Avalonia.Diagnostic.Meter");
22+
s_compositorRender = meter.CreateHistogram<double>(
23+
Meters.CompositorRenderPassName,
24+
Meters.MillisecondsUnit,
25+
Meters.CompositorRenderPassDescription);
26+
s_compositorUpdate = meter.CreateHistogram<double>(
27+
Meters.CompositorUpdatePassName,
28+
Meters.MillisecondsUnit,
29+
Meters.CompositorUpdatePassDescription);
30+
s_layoutMeasure = meter.CreateHistogram<double>(
31+
Meters.LayoutMeasurePassName,
32+
Meters.MillisecondsUnit,
33+
Meters.LayoutMeasurePassDescription);
34+
s_layoutArrange = meter.CreateHistogram<double>(
35+
Meters.LayoutArrangePassName,
36+
Meters.MillisecondsUnit,
37+
Meters.LayoutArrangePassDescription);
38+
s_layoutRender = meter.CreateHistogram<double>(
39+
Meters.LayoutRenderPassName,
40+
Meters.MillisecondsUnit,
41+
Meters.LayoutRenderPassDescription);
42+
s_layoutInput = meter.CreateHistogram<double>(
43+
Meters.LayoutInputPassName,
44+
Meters.MillisecondsUnit,
45+
Meters.LayoutInputPassDescription);
46+
meter.CreateObservableUpDownCounter(
47+
Meters.TotalEventHandleCountName,
48+
() => Interactive.TotalHandlersCount,
49+
Meters.TotalEventHandleCountUnit,
50+
Meters.TotalEventHandleCountDescription);
51+
meter.CreateObservableUpDownCounter(
52+
Meters.TotalVisualCountName,
53+
() => Visual.RootedVisualChildrenCount,
54+
Meters.TotalVisualCountUnit,
55+
Meters.TotalVisualCountDescription);
56+
meter.CreateObservableUpDownCounter(
57+
Meters.TotalDispatcherTimerCountName,
58+
() => DispatcherTimer.ActiveTimersCount,
59+
Meters.TotalDispatcherTimerCountUnit,
60+
Meters.TotalDispatcherTimerCountDescription);
61+
}
62+
63+
public static HistogramReportDisposable BeginCompositorRenderPass() => Begin(s_compositorRender);
64+
public static HistogramReportDisposable BeginCompositorUpdatePass() => Begin(s_compositorUpdate);
65+
public static HistogramReportDisposable BeginLayoutMeasurePass() => Begin(s_layoutMeasure);
66+
public static HistogramReportDisposable BeginLayoutArrangePass() => Begin(s_layoutArrange);
67+
public static HistogramReportDisposable BeginLayoutInputPass() => Begin(s_layoutInput);
68+
public static HistogramReportDisposable BeginLayoutRenderPass() => Begin(s_layoutRender);
69+
70+
private static HistogramReportDisposable Begin(Histogram<double>? histogram) => histogram is not null ? new(histogram) : default;
71+
72+
internal readonly ref struct HistogramReportDisposable
73+
{
74+
private readonly Histogram<double> _histogram;
75+
private readonly long _timestamp;
76+
77+
public HistogramReportDisposable(Histogram<double> histogram)
78+
{
79+
_histogram = histogram;
80+
if (histogram.Enabled)
81+
{
82+
_timestamp = Stopwatch.GetTimestamp();
83+
}
84+
}
85+
86+
public void Dispose()
87+
{
88+
if (_timestamp > 0)
89+
{
90+
_histogram.Record(StopwatchHelper.GetElapsedTimeMs(_timestamp));
91+
}
92+
}
93+
}
94+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
3+
namespace Avalonia.Diagnostics;
4+
5+
internal static partial class Diagnostic
6+
{
7+
public static bool IsEnabled { get; }
8+
9+
private static bool InitializeIsEnabled() => AppContext.TryGetSwitch("Avalonia.Diagnostics.Diagnostic.IsEnabled", out var isEnabled) && isEnabled;
10+
11+
static Diagnostic()
12+
{
13+
IsEnabled = InitializeIsEnabled();
14+
if (!IsEnabled)
15+
{
16+
return;
17+
}
18+
19+
InitActivitySource();
20+
InitMetrics();
21+
}
22+
}

src/Avalonia.Base/Interactivity/EventRoute.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using Avalonia.Collections.Pooled;
3+
using Avalonia.Diagnostics;
34

45
namespace Avalonia.Interactivity
56
{
@@ -80,6 +81,10 @@ public void RaiseEvent(Interactive source, RoutedEventArgs e)
8081

8182
e.Source = source;
8283

84+
using var _ = Diagnostic.RaisingRoutedEvent()?
85+
.AddTag(Diagnostic.Tags.Control, e.Source)
86+
.AddTag(Diagnostic.Tags.RoutedEvent, e.RoutedEvent);
87+
8388
if (_event.RoutingStrategies == RoutingStrategies.Direct)
8489
{
8590
e.Route = RoutingStrategies.Direct;

src/Avalonia.Base/Interactivity/Interactive.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace Avalonia.Interactivity
1212
/// </summary>
1313
public class Interactive : Layoutable
1414
{
15+
internal static int TotalHandlersCount { get; private set; }
1516
private Dictionary<RoutedEvent, List<EventSubscription>>? _eventHandlers;
1617

1718
/// <summary>
@@ -90,6 +91,7 @@ public void RemoveHandler(RoutedEvent routedEvent, Delegate handler)
9091
if (subscriptions[i].Handler == handler)
9192
{
9293
subscriptions.RemoveAt(i);
94+
TotalHandlersCount--;
9395
}
9496
}
9597
}
@@ -185,6 +187,7 @@ private void AddEventSubscription(RoutedEvent routedEvent, EventSubscription sub
185187
}
186188

187189
subscriptions.Add(subscription);
190+
TotalHandlersCount++;
188191
}
189192

190193
private void AddToEventRoute(RoutedEvent routedEvent, EventRoute route)

src/Avalonia.Base/Interactivity/RoutedEvent.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ internal void InvokeRouteFinished(RoutedEventArgs e)
103103
{
104104
_routeFinished.OnNext(e);
105105
}
106+
107+
public override string ToString()
108+
{
109+
return FormattableString.Invariant($"{OwnerType.Name}.{Name}");
110+
}
106111
}
107112

108113
public class RoutedEvent<TEventArgs> : RoutedEvent

src/Avalonia.Base/Layout/LayoutManager.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Buffers;
33
using System.Collections.Generic;
44
using System.Diagnostics;
5+
using Avalonia.Diagnostics;
56
using Avalonia.Logging;
67
using Avalonia.Media;
78
using Avalonia.Metadata;
@@ -246,6 +247,7 @@ private void InnerLayoutPass()
246247

247248
private void ExecuteMeasurePass()
248249
{
250+
using var _ = Diagnostic.BeginLayoutMeasurePass();
249251
while (_toMeasure.Count > 0)
250252
{
251253
var control = _toMeasure.Dequeue();
@@ -261,6 +263,7 @@ private void ExecuteMeasurePass()
261263

262264
private void ExecuteArrangePass()
263265
{
266+
using var _ = Diagnostic.BeginLayoutArrangePass();
264267
while (_toArrange.Count > 0)
265268
{
266269
var control = _toArrange.Dequeue();

src/Avalonia.Base/Layout/Layoutable.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using Avalonia.Diagnostics;
23
using Avalonia.Logging;
34
using Avalonia.Reactive;
45
using Avalonia.Styling;
@@ -368,6 +369,9 @@ public void Measure(Size availableSize)
368369

369370
if (!IsMeasureValid || _previousMeasure != availableSize)
370371
{
372+
using var activity = Diagnostic.MeasuringLayoutable()?
373+
.AddTag(Diagnostic.Tags.Control, this);
374+
371375
var previousDesiredSize = DesiredSize;
372376
var desiredSize = default(Size);
373377

@@ -418,6 +422,9 @@ public void Arrange(Rect rect)
418422

419423
if (!IsArrangeValid || _previousArrange != rect)
420424
{
425+
using var activity = Diagnostic.ArrangingLayoutable()?
426+
.AddTag(Diagnostic.Tags.Control, this);
427+
421428
Logger.TryGet(LogEventLevel.Verbose, LogArea.Layout)?.Log(this, "Arrange to {Rect} ", rect);
422429

423430
IsArrangeValid = true;

src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Threading.Tasks;
77
using Avalonia.Collections;
88
using Avalonia.Collections.Pooled;
9+
using Avalonia.Diagnostics;
910
using Avalonia.Media;
1011
using Avalonia.Rendering.Composition.Drawing;
1112
using Avalonia.Threading;
@@ -96,6 +97,8 @@ public void AddDirty(Visual visual)
9697
/// <inheritdoc/>
9798
public IEnumerable<Visual> HitTest(Point p, Visual? root, Func<Visual, bool>? filter)
9899
{
100+
using var _ = Diagnostic.PerformingHitTest();
101+
99102
CompositionVisual? rootVisual = null;
100103
if (root != null)
101104
{
@@ -198,7 +201,8 @@ private void Update()
198201
_updating = true;
199202
try
200203
{
201-
UpdateCore();
204+
using (Diagnostic.BeginLayoutRenderPass())
205+
UpdateCore();
202206
}
203207
finally
204208
{

0 commit comments

Comments
 (0)