Browse Source

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>
pull/20166/head
Nikita Tsukanov 2 months ago
committed by GitHub
parent
commit
4e530e4340
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 168
      api/Avalonia.nupkg.xml
  2. 5
      src/Avalonia.Base/Platform/IDrawingContextImpl.cs
  3. 6
      src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs
  4. 8
      src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
  5. 2
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs
  6. 23
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
  7. 7
      src/Skia/Avalonia.Skia/DrawingContextImpl.Effects.cs

168
api/Avalonia.nupkg.xml

@ -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>

5
src/Avalonia.Base/Platform/IDrawingContextImpl.cs

@ -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();
}

6
src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs

@ -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);

8
src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs

@ -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
}
});
}

2
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs

@ -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();

23
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs

@ -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
src/Skia/Avalonia.Skia/DrawingContextImpl.Effects.cs

@ -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);
}

Loading…
Cancel
Save