Skip to content

Commit

Permalink
Merge pull request #12568 from JetBrains/fixes/clip-bounds-invalidate
Browse files Browse the repository at this point in the history
Enhanced Clipping and Rendered Visuals Tracking in ServerCompositionVisual
  • Loading branch information
kekekeks committed Aug 18, 2023
2 parents 6019c51 + 8ecf014 commit c59e63e
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ namespace Avalonia.Rendering.Composition;

internal interface ICompositionTargetDebugEvents
{
int RenderedVisuals { get; }
void IncrementRenderedVisuals();
void RectInvalidated(Rect rc);
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ partial class ServerCompositionVisual
CompositionVisualChangedFields.Size
| CompositionVisualChangedFields.SizeAnimated
| CompositionVisualChangedFields.ClipToBounds
| CompositionVisualChangedFields.Clip
| CompositionVisualChangedFields.ClipToBoundsAnimated;

partial void OnFieldsDeserialized(CompositionVisualChangedFields changed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public void Render(CompositorDrawingContextProxy canvas, Rect currentTransformed
return;

Root!.RenderedVisuals++;
Root!.DebugEvents?.IncrementRenderedVisuals();

var boundsRect = new Rect(new Size(Size.X, Size.Y));

Expand Down Expand Up @@ -182,11 +183,24 @@ public virtual UpdateResult Update(ServerCompositionTarget root)

if (_clipSizeDirty || positionChanged)
{
_transformedClipBounds = ClipToBounds
? new Rect(new Size(Size.X, Size.Y))
.TransformToAABB(GlobalTransformMatrix)
: null;
Rect? transformedVisualBounds = null;
Rect? transformedClipBounds = null;

if (ClipToBounds)
transformedVisualBounds = new Rect(new Size(Size.X, Size.Y)).TransformToAABB(GlobalTransformMatrix);

if (Clip != null)
transformedClipBounds = Clip.Bounds.TransformToAABB(GlobalTransformMatrix);

if (transformedVisualBounds != null && transformedClipBounds != null)
_transformedClipBounds = transformedVisualBounds.Value.Intersect(transformedClipBounds.Value);
else if (transformedVisualBounds != null)
_transformedClipBounds = transformedVisualBounds;
else if (transformedClipBounds != null)
_transformedClipBounds = transformedClipBounds;
else
_transformedClipBounds = null;

_clipSizeDirty = false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Avalonia.Controls;
using Avalonia.Media;
using Xunit;

namespace Avalonia.Base.UnitTests.Rendering;
/// <summary>
/// Test class that verifies how clipping influences rendering in the compositor
/// </summary>
public class CompositorInvalidationClippingTests : CompositorTestsBase
{
[Fact]
// Test case: When the ClipToBounds is false, all visuals should be rendered
public void Siblings_Should_Be_Rendered_On_Invalidate_Without_ClipToBounds()
{
AssertRenderedVisuals(clipToBounds: false, clipGeometry: false, expectedRenderedVisualsCount: 4);
}

[Fact]
// Test case: When the ClipToBounds is true, only visuals within the clipped boundary should be rendered
public void Siblings_Should_Not_Be_Rendered_On_Invalidate_With_ClipToBounds()
{
AssertRenderedVisuals(clipToBounds: true, clipGeometry: false, expectedRenderedVisualsCount: 3);
}

[Fact]
// Test case: When the Clip is used, only visuals within the clip geometry should be rendered
public void Siblings_Should_Not_Be_Rendered_On_Invalidate_With_Clip()
{
AssertRenderedVisuals(clipToBounds: false, clipGeometry: true, expectedRenderedVisualsCount: 3);
}

private void AssertRenderedVisuals(bool clipToBounds, bool clipGeometry, int expectedRenderedVisualsCount)
{
using (var s = new CompositorCanvas())
{
//#1 visual is top level
//#2 visual is s.Canvas

//#3 visual is border1
s.Canvas.Children.Add(new Border()
{
[Canvas.LeftProperty] = 0, [Canvas.TopProperty] = 0,
Width = 20, Height = 10,
Background = Brushes.Red,
ClipToBounds = clipToBounds,
Clip = clipGeometry ? new RectangleGeometry(new Rect(new Size(20, 10))) : null
});

//#4 visual is border2
s.Canvas.Children.Add(new Border()
{
[Canvas.LeftProperty] = 30, [Canvas.TopProperty] = 50,
Width = 20, Height = 10,
Background = Brushes.Red,
ClipToBounds = clipToBounds,
Clip = clipGeometry ? new RectangleGeometry(new Rect(new Size(20, 10))) : null
});
s.RunJobs();
s.Events.Reset();

//invalidate border1
s.Canvas.Children[0].IsVisible = false;
s.RunJobs();

s.AssertRenderedVisuals(expectedRenderedVisualsCount);
}
}
}
17 changes: 16 additions & 1 deletion tests/Avalonia.UnitTests/CompositorTestServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ public void AssertRects(params Rect[] rects)
Events.Rects.Clear();
}

public void AssertRenderedVisuals(int renderVisuals)
{
RunJobs();
Assert.Equal(Events.RenderedVisuals, renderVisuals);
Events.Rects.Clear();
}

public void AssertHitTest(double x, double y, Func<Visual, bool> filter, params object[] expected)
=> AssertHitTest(new Point(x, y), filter, expected);

Expand All @@ -110,6 +117,13 @@ public class DebugEvents : ICompositionTargetDebugEvents
{
public List<Rect> Rects = new();

public int RenderedVisuals { get; private set; }

public void IncrementRenderedVisuals()
{
RenderedVisuals++;
}

public void RectInvalidated(Rect rc)
{
Rects.Add(rc);
Expand All @@ -118,6 +132,7 @@ public void RectInvalidated(Rect rc)
public void Reset()
{
Rects.Clear();
RenderedVisuals = 0;
}
}

Expand Down Expand Up @@ -218,4 +233,4 @@ public void CommitRequested(Compositor compositor)
{
Dispatcher.UIThread.Post(() => compositor.Commit(), DispatcherPriority.UiThreadRender);
}
}
}

0 comments on commit c59e63e

Please sign in to comment.