Improve effect render performance by providing Skia with information about subscene bounds (#20191)

* Improve effect render performance by providing Skia with information about subscene bounds

* Fixed test

* apidiff

* Update API diff

---------

Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
This commit is contained in:
Nikita Tsukanov
2025-12-05 19:25:03 +05:00
committed by GitHub
parent 981a0106a6
commit 4e530e4340
7 changed files with 208 additions and 11 deletions
+168
View File
@@ -43,6 +43,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.Typeface@)</Target>
@@ -61,6 +67,72 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.CreatePreviewWithControl(System.Object)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.GetDataContext(Avalonia.Controls.Templates.IDataTemplate)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.GetPreviewWith(Avalonia.Controls.Templates.IDataTemplate)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.GetPreviewWith(Avalonia.Styling.IStyle)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetDataContext(Avalonia.Controls.Templates.IDataTemplate,System.Object)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.ResourceDictionary,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.Templates.IDataTemplate,Avalonia.Controls.Control)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.Templates.IDataTemplate,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Styling.IStyle,Avalonia.Controls.Control)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Styling.IStyle,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Media.Fonts.FontCollectionBase._glyphTypefaceCache</Target>
@@ -109,6 +181,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.Typeface@)</Target>
@@ -127,6 +205,72 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.CreatePreviewWithControl(System.Object)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.GetDataContext(Avalonia.Controls.Templates.IDataTemplate)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.GetPreviewWith(Avalonia.Controls.Templates.IDataTemplate)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.GetPreviewWith(Avalonia.Styling.IStyle)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetDataContext(Avalonia.Controls.Templates.IDataTemplate,System.Object)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.ResourceDictionary,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.Templates.IDataTemplate,Avalonia.Controls.Control)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.Templates.IDataTemplate,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Styling.IStyle,Avalonia.Controls.Control)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Styling.IStyle,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Dialogs.Internal.ManagedFileChooserFilterViewModel.#ctor(Avalonia.Platform.Storage.FilePickerFileType)</Target>
@@ -163,6 +307,24 @@
<Left>baseline/Avalonia/lib/netstandard2.0/Avalonia.Dialogs.dll</Left>
<Right>current/Avalonia/lib/netstandard2.0/Avalonia.Dialogs.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.IDrawingContextImplWithEffects.PopEffect</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect)</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(System.Nullable{Avalonia.Rect},Avalonia.Media.IEffect)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.String,System.Globalization.CultureInfo,Avalonia.Media.Typeface@)</Target>
@@ -253,6 +415,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(System.Nullable{Avalonia.Rect},Avalonia.Media.IEffect)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.String,System.Globalization.CultureInfo,Avalonia.Media.Typeface@)</Target>
@@ -201,9 +201,10 @@ namespace Avalonia.Platform
object? GetFeature(Type t);
}
public interface IDrawingContextImplWithEffects
[PrivateApi]
public interface IDrawingContextImplWithEffects : IDrawingContextImpl
{
void PushEffect(IEffect effect);
void PushEffect(Rect? clipRect, IEffect effect);
void PopEffect();
}
@@ -48,6 +48,10 @@ internal partial class CompositorDrawingContextProxy
[FieldOffset(0)] public bool IsRoundRect;
[FieldOffset(4)] public RoundedRect RoundRect;
[FieldOffset(4)] public Rect NormalRect;
// PushEffect
[FieldOffset(0)]
public Rect? EffectClipRect;
}
struct PendingCommand
@@ -140,7 +144,7 @@ internal partial class CompositorDrawingContextProxy
else if (cmd.Type == PendingCommandType.PushEffect)
{
if (_impl is IDrawingContextImplWithEffects effects)
effects.PushEffect(cmd.ObjectUnion.Effect!);
effects.PushEffect(cmd.DataUnion.EffectClipRect, cmd.ObjectUnion.Effect!);
}
else if (cmd.Type == PendingCommandType.PushRenderOptions)
_impl.PushRenderOptions(cmd.DataUnion.RenderOptions);
@@ -285,14 +285,18 @@ internal partial class CompositorDrawingContextProxy : IDrawingContextImpl,
_impl.DrawRectangle(new ImmutableSolidColorBrush(material.FallbackColor), null, rect);
}
public void PushEffect(IEffect effect)
public void PushEffect(Rect? clipRect, IEffect effect)
{
AddCommand(new()
{
Type = PendingCommandType.PushEffect,
ObjectUnion =
{
Effect = effect
Effect = effect,
},
DataUnion =
{
EffectClipRect = clipRect
}
});
}
@@ -65,6 +65,8 @@ namespace Avalonia.Rendering.Composition.Server
return new(_transformedContentBounds, oldInvalidated, newInvalidated);
}
protected override LtrbRect GetEffectBounds() => _transformedContentBounds ?? default;
void AddEffectPaddedDirtyRect(IImmutableEffect effect, LtrbRect transformedBounds)
{
var padding = effect.GetEffectOutputPadding();
@@ -62,9 +62,8 @@ namespace Avalonia.Rendering.Composition.Server
if (applyRenderOptions)
canvas.PushRenderOptions(RenderOptions);
if (Effect != null)
canvas.PushEffect(Effect);
var needPopEffect = PushEffect(canvas);
if (Opacity != 1)
canvas.PushOpacity(Opacity, ClipToBounds ? boundsRect : null);
if (ClipToBounds && !HandlesClipToBounds)
@@ -87,12 +86,28 @@ namespace Avalonia.Rendering.Composition.Server
if (Opacity != 1)
canvas.PopOpacity();
if (Effect != null)
if (needPopEffect)
canvas.PopEffect();
if(applyRenderOptions)
canvas.PopRenderOptions();
}
protected virtual LtrbRect GetEffectBounds() => TransformedOwnContentBounds;
private bool PushEffect(CompositorDrawingContextProxy canvas)
{
if (Effect == null)
return false;
var clip = GetEffectBounds();
if (clip.IsZeroSize)
return false;
var oldMatrix = canvas.Transform;
canvas.Transform = Matrix.Identity;
canvas.PushEffect(GetEffectBounds().ToRect(), Effect!);
canvas.Transform = oldMatrix;
return true;
}
protected virtual bool HandlesClipToBounds => false;
private ReadbackData _readback0, _readback1, _readback2;
@@ -7,13 +7,16 @@ namespace Avalonia.Skia;
partial class DrawingContextImpl
{
public void PushEffect(IEffect effect)
public void PushEffect(Rect? effectClipRect, IEffect effect)
{
CheckLease();
using var filter = CreateEffect(effect);
var paint = SKPaintCache.Shared.Get();
paint.ImageFilter = filter;
Canvas.SaveLayer(paint);
if (effectClipRect.HasValue)
Canvas.SaveLayer(effectClipRect.Value.ToSKRect(), paint);
else
Canvas.SaveLayer(paint);
SKPaintCache.Shared.ReturnReset(paint);
}