Browse Source

Some optimizations

pull/21310/head
Steven He 2 weeks ago
parent
commit
a94992be1a
  1. 55
      src/Avalonia.Base/Rendering/Composition/CompositionHitTestAabbTree.cs
  2. 31
      src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
  3. 25
      src/Avalonia.Base/Rendering/Composition/ContainerVisual.cs

55
src/Avalonia.Base/Rendering/Composition/CompositionHitTestAabbTree.cs

@ -7,6 +7,11 @@ namespace Avalonia.Rendering.Composition;
internal sealed class CompositionHitTestAabbTree
{
internal interface IQueryHitTester
{
CompositionVisual? HitTest(CompositionVisual visual);
}
private const int Null = -1;
private const double FatBoundsPadding = 1;
private static readonly CandidateComparer s_candidateComparer = new();
@ -124,6 +129,56 @@ internal sealed class CompositionHitTestAabbTree
_queryCandidates.Clear();
}
public CompositionVisual? QueryFirst<T>(Point point, ref T hitTest)
where T : struct, IQueryHitTester
{
_queryCandidates.Clear();
if (_root != Null)
{
var stackCount = 0;
PushQueryNode(ref stackCount, _root);
while (stackCount > 0)
{
var nodeIndex = _queryStack[--stackCount];
var node = _nodes[nodeIndex];
if (!node.Bounds.Contains(point))
continue;
if (node.IsLeaf)
{
if (node.Visual != null)
_queryCandidates.Add(new Candidate(node.Visual, node.Order));
}
else
{
PushQueryNode(ref stackCount, node.Child1);
PushQueryNode(ref stackCount, node.Child2);
}
}
}
foreach (var candidate in _unbounded)
_queryCandidates.Add(new Candidate(candidate.Key, candidate.Value));
_queryCandidates.Sort(s_candidateComparer);
foreach (var candidate in _queryCandidates)
{
var hit = hitTest.HitTest(candidate.Visual);
if (hit != null)
{
_queryCandidates.Clear();
return hit;
}
}
_queryCandidates.Clear();
return null;
}
private void Add(CompositionVisual visual, int order)
{
var state = GetBoundsState(visual, out var bounds);

31
src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs

@ -189,23 +189,12 @@ namespace Avalonia.Rendering.Composition
var queriedIndexedChildren = false;
if (cv.Children.Count >= CompositionContainerVisual.HitTestAabbTreeThreshold)
{
var candidates = RentHitTestChildCandidates(out var releaseToField);
try
var query = new FirstHitTestQuery(this, point, filter, resultFilter);
if (cv.TryQueryFirstHitTestChild(point, ref query, out var hit))
{
if (cv.TryQueryHitTestChildren(point, candidates))
{
queriedIndexedChildren = true;
foreach (var child in candidates)
{
var hit = HitTestFirstCore(child, point, filter, resultFilter);
if (hit != null)
return hit;
}
}
}
finally
{
ReleaseHitTestChildCandidates(candidates, releaseToField);
queriedIndexedChildren = true;
if (hit != null)
return hit;
}
}
@ -223,6 +212,16 @@ namespace Avalonia.Rendering.Composition
return visual.HitTest(point) && (resultFilter == null || resultFilter(visual)) ? visual : null;
}
private readonly struct FirstHitTestQuery(
CompositionTarget target,
Point point,
Func<CompositionVisual, bool>? filter,
Func<CompositionVisual, bool>? resultFilter) : CompositionHitTestAabbTree.IQueryHitTester
{
public CompositionVisual? HitTest(CompositionVisual visual) =>
target.HitTestFirstCore(visual, point, filter, resultFilter);
}
/// <summary>
/// Registers the composition target for explicit redraw
/// </summary>

25
src/Avalonia.Base/Rendering/Composition/ContainerVisual.cs

@ -8,7 +8,7 @@ namespace Avalonia.Rendering.Composition
/// </summary>
public partial class CompositionContainerVisual : CompositionVisual
{
internal static readonly int HitTestAabbTreeThreshold = CompositionHitTestAabbTree.IsEnabled ? 1 : int.MaxValue;
internal static readonly int HitTestAabbTreeThreshold = CompositionHitTestAabbTree.IsEnabled ? 32 : int.MaxValue;
private CompositionHitTestAabbTree? _hitTestChildren;
private bool _hitTestChildrenDirty = true;
@ -67,5 +67,28 @@ namespace Avalonia.Rendering.Composition
_hitTestChildren.Query(point, results);
return true;
}
internal bool TryQueryFirstHitTestChild<T>(Point point, ref T hitTest, out CompositionVisual? hit)
where T : struct, CompositionHitTestAabbTree.IQueryHitTester
{
if (Children.Count < HitTestAabbTreeThreshold)
{
_hitTestChildren?.Clear();
_hitTestChildren = null;
hit = null;
return false;
}
_hitTestChildren ??= new CompositionHitTestAabbTree();
if (_hitTestChildrenDirty)
{
_hitTestChildren.Rebuild(Children);
_hitTestChildrenDirty = false;
}
hit = _hitTestChildren.QueryFirst(point, ref hitTest);
return true;
}
}
}

Loading…
Cancel
Save