Browse Source

Merge 20a56bc08c into 658afb8717

pull/20885/merge
Matt 16 hours ago
committed by GitHub
parent
commit
a277b4dadb
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      src/Avalonia.Base/Rendering/Composition/Drawing/CompositionRenderData.cs
  2. 15
      src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataBitmapNode.cs
  3. 23
      src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataEllipseNode.cs
  4. 23
      src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataGeometryNode.cs
  5. 13
      src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataGlyphRunNode.cs
  6. 23
      src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataLineNode.cs
  7. 106
      src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodePool.cs
  8. 106
      src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs
  9. 12
      src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataPushMatrixNode.cs
  10. 13
      src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataPushOpacityMaskNode.cs
  11. 18
      src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataRectangleNode.cs
  12. 152
      src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs
  13. 4
      src/Avalonia.Base/Rendering/Composition/Drawing/ServerCompositionRenderData.cs
  14. 6
      src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs

6
src/Avalonia.Base/Rendering/Composition/Drawing/CompositionRenderData.cs

@ -31,11 +31,9 @@ internal class CompositionRenderData : ICompositorSerializable, IDisposable
{
if (!_itemsSent)
{
foreach(var i in _items)
if (i is IDisposable disp)
disp.Dispose();
RenderDataItemPoolHelper.DisposeAndReturnToPool(_items);
}
_items.Dispose();
_itemsSent = false;
foreach(var r in _resources)

15
src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataBitmapNode.cs

@ -4,8 +4,12 @@ using Avalonia.Utilities;
namespace Avalonia.Rendering.Composition.Drawing.Nodes;
class RenderDataBitmapNode : IRenderDataItem, IDisposable
class RenderDataBitmapNode : IRenderDataItem, IDisposable, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataBitmapNode> s_pool = new();
public static RenderDataBitmapNode Get() => s_pool.Get();
public IRef<IBitmapImpl>? Bitmap { get; set; }
public double Opacity { get; set; }
public Rect SourceRect { get; set; }
@ -25,4 +29,13 @@ class RenderDataBitmapNode : IRenderDataItem, IDisposable
Bitmap?.Dispose();
Bitmap = null;
}
public void ReturnToPool()
{
Dispose();
Opacity = default;
SourceRect = default;
DestRect = default;
s_pool.Return(this);
}
}

23
src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataEllipseNode.cs

@ -4,10 +4,14 @@ using Avalonia.Platform;
namespace Avalonia.Rendering.Composition.Drawing.Nodes;
class RenderDataEllipseNode :RenderDataBrushAndPenNode
class RenderDataEllipseNode : RenderDataBrushAndPenNode, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataEllipseNode> s_pool = new();
public static RenderDataEllipseNode Get() => s_pool.Get();
public Rect Rect { get; set; }
bool Contains(double dx, double dy, double radiusX, double radiusY)
{
var rx2 = radiusX * radiusX;
@ -17,7 +21,7 @@ class RenderDataEllipseNode :RenderDataBrushAndPenNode
return distance <= rx2 * ry2;
}
public override bool HitTest(Point p)
{
var center = Rect.Center;
@ -50,7 +54,7 @@ class RenderDataEllipseNode :RenderDataBrushAndPenNode
return inStroke && !inInner;
}
return false;
}
@ -58,4 +62,13 @@ class RenderDataEllipseNode :RenderDataBrushAndPenNode
context.Context.DrawEllipse(ServerBrush, ServerPen, Rect);
public override Rect? Bounds => Rect.Inflate(ServerPen?.Thickness ?? 0);
}
public void ReturnToPool()
{
ServerBrush = null;
ServerPen = null;
ClientPen = null;
Rect = default;
s_pool.Return(this);
}
}

23
src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataGeometryNode.cs

@ -5,20 +5,24 @@ using Avalonia.Rendering.SceneGraph;
namespace Avalonia.Rendering.Composition.Drawing.Nodes;
class RenderDataGeometryNode : RenderDataBrushAndPenNode
class RenderDataGeometryNode : RenderDataBrushAndPenNode, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataGeometryNode> s_pool = new();
public static RenderDataGeometryNode Get() => s_pool.Get();
public IGeometryImpl? Geometry { get; set; }
public override bool HitTest(Point p)
{
if (Geometry == null)
return false;
return (ServerBrush != null // null check is safe
&& Geometry.FillContains(p)) ||
(ClientPen != null && Geometry.StrokeContains(ClientPen, p));
}
public override void Invoke(ref RenderDataNodeRenderContext context)
{
Debug.Assert(Geometry != null);
@ -26,4 +30,13 @@ class RenderDataGeometryNode : RenderDataBrushAndPenNode
}
public override Rect? Bounds => Geometry?.GetRenderBounds(ServerPen) ?? default;
}
public void ReturnToPool()
{
ServerBrush = null;
ServerPen = null;
ClientPen = null;
Geometry = null;
s_pool.Return(this);
}
}

13
src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataGlyphRunNode.cs

@ -6,8 +6,12 @@ using Avalonia.Utilities;
namespace Avalonia.Rendering.Composition.Drawing.Nodes;
class RenderDataGlyphRunNode : IRenderDataItemWithServerResources, IDisposable
class RenderDataGlyphRunNode : IRenderDataItemWithServerResources, IDisposable, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataGlyphRunNode> s_pool = new();
public static RenderDataGlyphRunNode Get() => s_pool.Get();
public IBrush? ServerBrush { get; set; }
// Dispose only happens once, so it's safe to have one reference
public IRef<IGlyphRunImpl>? GlyphRun { get; set; }
@ -32,4 +36,11 @@ class RenderDataGlyphRunNode : IRenderDataItemWithServerResources, IDisposable
GlyphRun?.Dispose();
GlyphRun = null;
}
public void ReturnToPool()
{
Dispose();
ServerBrush = null;
s_pool.Return(this);
}
}

23
src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataLineNode.cs

@ -5,13 +5,17 @@ using Avalonia.Rendering.SceneGraph;
namespace Avalonia.Rendering.Composition.Drawing.Nodes;
class RenderDataLineNode : IRenderDataItemWithServerResources
class RenderDataLineNode : IRenderDataItemWithServerResources, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataLineNode> s_pool = new();
public static RenderDataLineNode Get() => s_pool.Get();
public IPen? ServerPen { get; set; }
public IPen? ClientPen { get; set; }
public Point P1 { get; set; }
public Point P2 { get; set; }
public bool HitTest(Point p)
{
if (ClientPen == null)
@ -52,9 +56,9 @@ class RenderDataLineNode : IRenderDataItemWithServerResources
return Math.Abs(distance) <= halfThickness;
}
public void Invoke(ref RenderDataNodeRenderContext context)
public void Invoke(ref RenderDataNodeRenderContext context)
=> context.Context.DrawLine(ServerPen, P1, P2);
public Rect? Bounds => LineBoundsHelper.CalculateBounds(P1, P2, ServerPen!);
@ -62,4 +66,13 @@ class RenderDataLineNode : IRenderDataItemWithServerResources
{
collector.AddRenderDataServerResource(ServerPen);
}
}
public void ReturnToPool()
{
ServerPen = null;
ClientPen = null;
P1 = default;
P2 = default;
s_pool.Return(this);
}
}

106
src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodePool.cs

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace Avalonia.Rendering.Composition.Drawing.Nodes;
internal interface IRenderDataNodePool
{
void Reduce();
}
/// <summary>
/// Manages a single cleanup timer shared by all <see cref="RenderDataNodePool{T}"/> instances.
/// Uses weak references so pools that go out of scope can be garbage collected.
/// </summary>
internal static class RenderDataNodePoolCleanup
{
private static readonly List<WeakReference<IRenderDataNodePool>> s_pools = new();
private static Timer? s_timer;
public static void Register(IRenderDataNodePool pool)
{
lock (s_pools)
{
s_pools.Add(new WeakReference<IRenderDataNodePool>(pool));
s_timer ??= new Timer(_ => RunCleanup(), null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
}
}
private static void RunCleanup()
{
lock (s_pools)
{
for (var i = s_pools.Count - 1; i >= 0; i--)
{
if (s_pools[i].TryGetTarget(out var pool))
pool.Reduce();
else
s_pools.RemoveAt(i);
}
}
}
}
/// <summary>
/// Object pool for render data nodes with gradual reclamation during idle periods.
/// While the pool is actively used, items are retained for reuse. Once activity stops,
/// a shared timer gradually releases a third of pooled items per cycle until empty.
/// </summary>
internal sealed class RenderDataNodePool<T> : IRenderDataNodePool where T : class, new()
{
private T[] _items = Array.Empty<T>();
private int _count;
private bool _active;
public RenderDataNodePool()
{
RenderDataNodePoolCleanup.Register(this);
}
public T Get()
{
lock (_items)
{
_active = true;
if (_count > 0)
return _items[--_count];
}
return new T();
}
public void Return(T item)
{
lock (_items)
{
_active = true;
if (_count == _items.Length)
Array.Resize(ref _items, Math.Max(4, _items.Length * 2));
_items[_count++] = item;
}
}
public void Reduce()
{
lock (_items)
{
if (_active)
{
_active = false;
return;
}
if (_count == 0)
return;
var release = Math.Max(1, _count / 3);
var newCount = _count - release;
Array.Clear(_items, newCount, release);
_count = newCount;
}
}
}

106
src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs

@ -53,6 +53,14 @@ struct RenderDataNodeRenderContext : IDisposable
}
}
/// <summary>
/// Implemented by render data nodes that support object pooling to reduce GC pressure.
/// </summary>
interface IPoolableRenderDataItem
{
void ReturnToPool();
}
interface IRenderDataItem
{
/// <summary>
@ -78,8 +86,12 @@ interface IRenderDataItem
bool HitTest(Point p);
}
class RenderDataCustomNode : IRenderDataItem, IDisposable
class RenderDataCustomNode : IRenderDataItem, IDisposable, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataCustomNode> s_pool = new();
public static RenderDataCustomNode Get() => s_pool.Get();
public ICustomDrawOperation? Operation { get; set; }
public bool HitTest(Point p) => Operation?.HitTest(p) ?? false;
public void Invoke(ref RenderDataNodeRenderContext context) => Operation?.Render(new(context.Context, false));
@ -91,6 +103,12 @@ class RenderDataCustomNode : IRenderDataItem, IDisposable
Operation?.Dispose();
Operation = null;
}
public void ReturnToPool()
{
Dispose();
s_pool.Return(this);
}
}
abstract class RenderDataPushNode : IRenderDataItem, IDisposable
@ -143,8 +161,12 @@ abstract class RenderDataPushNode : IRenderDataItem, IDisposable
}
}
class RenderDataClipNode : RenderDataPushNode
class RenderDataClipNode : RenderDataPushNode, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataClipNode> s_pool = new();
public static RenderDataClipNode Get() => s_pool.Get();
public RoundedRect Rect { get; set; }
public override void Push(ref RenderDataNodeRenderContext context) =>
context.Context.PushClip(Rect);
@ -158,13 +180,23 @@ class RenderDataClipNode : RenderDataPushNode
return false;
return base.HitTest(p);
}
public void ReturnToPool()
{
Rect = default;
s_pool.Return(this);
}
}
class RenderDataGeometryClipNode : RenderDataPushNode
class RenderDataGeometryClipNode : RenderDataPushNode, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataGeometryClipNode> s_pool = new();
public static RenderDataGeometryClipNode Get() => s_pool.Get();
public IGeometryImpl? Geometry { get; set; }
public bool Contains(Point p) => Geometry?.FillContains(p) ?? false;
public override void Push(ref RenderDataNodeRenderContext context)
{
if (Geometry != null)
@ -183,10 +215,20 @@ class RenderDataGeometryClipNode : RenderDataPushNode
return false;
return base.HitTest(p);
}
public void ReturnToPool()
{
Geometry = null;
s_pool.Return(this);
}
}
class RenderDataOpacityNode : RenderDataPushNode
class RenderDataOpacityNode : RenderDataPushNode, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataOpacityNode> s_pool = new();
public static RenderDataOpacityNode Get() => s_pool.Get();
public double Opacity { get; set; }
public override void Push(ref RenderDataNodeRenderContext context)
{
@ -199,6 +241,12 @@ class RenderDataOpacityNode : RenderDataPushNode
if (Opacity != 1)
context.Context.PopOpacity();
}
public void ReturnToPool()
{
Opacity = default;
s_pool.Return(this);
}
}
abstract class RenderDataBrushAndPenNode : IRenderDataItemWithServerResources
@ -218,8 +266,12 @@ abstract class RenderDataBrushAndPenNode : IRenderDataItemWithServerResources
public abstract bool HitTest(Point p);
}
class RenderDataRenderOptionsNode : RenderDataPushNode
class RenderDataRenderOptionsNode : RenderDataPushNode, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataRenderOptionsNode> s_pool = new();
public static RenderDataRenderOptionsNode Get() => s_pool.Get();
public RenderOptions RenderOptions { get; set; }
public override void Push(ref RenderDataNodeRenderContext context)
@ -231,10 +283,20 @@ class RenderDataRenderOptionsNode : RenderDataPushNode
{
context.Context.PopRenderOptions();
}
public void ReturnToPool()
{
RenderOptions = default;
s_pool.Return(this);
}
}
class RenderDataTextOptionsNode : RenderDataPushNode
class RenderDataTextOptionsNode : RenderDataPushNode, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataTextOptionsNode> s_pool = new();
public static RenderDataTextOptionsNode Get() => s_pool.Get();
public TextOptions TextOptions { get; set; }
public override void Push(ref RenderDataNodeRenderContext context)
@ -246,4 +308,34 @@ class RenderDataTextOptionsNode : RenderDataPushNode
{
context.Context.PopTextOptions();
}
public void ReturnToPool()
{
TextOptions = default;
s_pool.Return(this);
}
}
static class RenderDataItemPoolHelper
{
/// <summary>
/// Disposes disposable items and returns poolable items to their object pools.
/// Recurses into push node children.
/// </summary>
public static void DisposeAndReturnToPool(PooledInlineList<IRenderDataItem> items)
{
foreach (var item in items)
{
if (item is RenderDataPushNode pushNode)
{
DisposeAndReturnToPool(pushNode.Children);
pushNode.Children.Dispose();
}
if (item is IPoolableRenderDataItem poolable)
poolable.ReturnToPool();
else if (item is IDisposable disp)
disp.Dispose();
}
}
}

12
src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataPushMatrixNode.cs

@ -1,7 +1,11 @@
namespace Avalonia.Rendering.Composition.Drawing.Nodes;
class RenderDataPushMatrixNode : RenderDataPushNode
class RenderDataPushMatrixNode : RenderDataPushNode, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataPushMatrixNode> s_pool = new();
public static RenderDataPushMatrixNode Get() => s_pool.Get();
public Matrix Matrix { get; set; }
public override void Push(ref RenderDataNodeRenderContext context)
@ -24,4 +28,10 @@ class RenderDataPushMatrixNode : RenderDataPushNode
}
public override Rect? Bounds => base.Bounds?.TransformToAABB(Matrix);
public void ReturnToPool()
{
Matrix = default;
s_pool.Return(this);
}
}

13
src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataPushOpacityMaskNode.cs

@ -3,8 +3,12 @@ using Avalonia.Platform;
namespace Avalonia.Rendering.Composition.Drawing.Nodes;
class RenderDataOpacityMaskNode : RenderDataPushNode, IRenderDataItemWithServerResources
class RenderDataOpacityMaskNode : RenderDataPushNode, IRenderDataItemWithServerResources, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataOpacityMaskNode> s_pool = new();
public static RenderDataOpacityMaskNode Get() => s_pool.Get();
public IBrush? ServerBrush { get; set; }
public Rect BoundsRect { get; set; }
@ -22,4 +26,11 @@ class RenderDataOpacityMaskNode : RenderDataPushNode, IRenderDataItemWithServerR
public override void Pop(ref RenderDataNodeRenderContext context) =>
context.Context.PopOpacityMask();
public void ReturnToPool()
{
ServerBrush = null;
BoundsRect = default;
s_pool.Return(this);
}
}

18
src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataRectangleNode.cs

@ -2,8 +2,12 @@ using Avalonia.Media;
namespace Avalonia.Rendering.Composition.Drawing.Nodes;
class RenderDataRectangleNode : RenderDataBrushAndPenNode
class RenderDataRectangleNode : RenderDataBrushAndPenNode, IPoolableRenderDataItem
{
private static readonly RenderDataNodePool<RenderDataRectangleNode> s_pool = new();
public static RenderDataRectangleNode Get() => s_pool.Get();
public RoundedRect Rect { get; set; }
public BoxShadows BoxShadows { get; set; }
@ -21,7 +25,7 @@ class RenderDataRectangleNode : RenderDataBrushAndPenNode
var innerRoundedRect = Rect.Deflate(strokeThicknessAdjustment, strokeThicknessAdjustment);
return !innerRoundedRect.ContainsExclusive(p);
}
}
}
else
{
@ -43,4 +47,14 @@ class RenderDataRectangleNode : RenderDataBrushAndPenNode
context.Context.DrawRectangle(ServerBrush, ServerPen, Rect, BoxShadows);
public override Rect? Bounds => BoxShadows.TransformBounds(Rect.Rect).Inflate((ServerPen?.Thickness ?? 0) / 2);
public void ReturnToPool()
{
ServerBrush = null;
ServerPen = null;
ClientPen = null;
Rect = default;
BoxShadows = default;
s_pool.Return(this);
}
}

152
src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs

@ -124,13 +124,12 @@ internal class RenderDataDrawingContext : DrawingContext
if(pen == null)
return;
AddResource(pen);
Add(new RenderDataLineNode
{
ClientPen = pen,
ServerPen = pen.GetServer(_compositor),
P1 = p1,
P2 = p2
});
var lineNode = RenderDataLineNode.Get();
lineNode.ClientPen = pen;
lineNode.ServerPen = pen.GetServer(_compositor);
lineNode.P1 = p1;
lineNode.P2 = p2;
Add(lineNode);
}
protected override void DrawGeometryCore(IBrush? brush, IPen? pen, IGeometryImpl geometry)
@ -139,13 +138,12 @@ internal class RenderDataDrawingContext : DrawingContext
return;
AddResource(brush);
AddResource(pen);
Add(new RenderDataGeometryNode
{
ServerBrush = brush.GetServer(_compositor),
ServerPen = pen.GetServer(_compositor),
ClientPen = pen,
Geometry = geometry
});
var geoNode = RenderDataGeometryNode.Get();
geoNode.ServerBrush = brush.GetServer(_compositor);
geoNode.ServerPen = pen.GetServer(_compositor);
geoNode.ClientPen = pen;
geoNode.Geometry = geometry;
Add(geoNode);
}
protected override void DrawRectangleCore(IBrush? brush, IPen? pen, RoundedRect rrect, BoxShadows boxShadows = default)
@ -156,14 +154,13 @@ internal class RenderDataDrawingContext : DrawingContext
return;
AddResource(brush);
AddResource(pen);
Add(new RenderDataRectangleNode
{
ServerBrush = brush.GetServer(_compositor),
ServerPen = pen.GetServer(_compositor),
ClientPen = pen,
Rect = rrect,
BoxShadows = boxShadows
});
var rectNode = RenderDataRectangleNode.Get();
rectNode.ServerBrush = brush.GetServer(_compositor);
rectNode.ServerPen = pen.GetServer(_compositor);
rectNode.ClientPen = pen;
rectNode.Rect = rrect;
rectNode.BoxShadows = boxShadows;
Add(rectNode);
}
protected override void DrawEllipseCore(IBrush? brush, IPen? pen, Rect rect)
@ -175,51 +172,56 @@ internal class RenderDataDrawingContext : DrawingContext
return;
AddResource(brush);
AddResource(pen);
Add(new RenderDataEllipseNode
{
ServerBrush = brush.GetServer(_compositor),
ServerPen = pen.GetServer(_compositor),
ClientPen = pen,
Rect = rect,
});
var ellipseNode = RenderDataEllipseNode.Get();
ellipseNode.ServerBrush = brush.GetServer(_compositor);
ellipseNode.ServerPen = pen.GetServer(_compositor);
ellipseNode.ClientPen = pen;
ellipseNode.Rect = rect;
Add(ellipseNode);
}
public override void Custom(ICustomDrawOperation custom) => Add(new RenderDataCustomNode
public override void Custom(ICustomDrawOperation custom)
{
Operation = custom
});
var node = RenderDataCustomNode.Get();
node.Operation = custom;
Add(node);
}
public override void DrawGlyphRun(IBrush? foreground, GlyphRun? glyphRun)
{
if (foreground == null || glyphRun == null)
return;
AddResource(foreground);
Add(new RenderDataGlyphRunNode
{
ServerBrush = foreground.GetServer(_compositor),
GlyphRun = glyphRun.PlatformImpl.Clone()
});
var glyphNode = RenderDataGlyphRunNode.Get();
glyphNode.ServerBrush = foreground.GetServer(_compositor);
glyphNode.GlyphRun = glyphRun.PlatformImpl.Clone();
Add(glyphNode);
}
protected override void PushClipCore(RoundedRect rect) => Push(new RenderDataClipNode
protected override void PushClipCore(RoundedRect rect)
{
Rect = rect
});
var node = RenderDataClipNode.Get();
node.Rect = rect;
Push(node);
}
protected override void PushClipCore(Rect rect) => Push(new RenderDataClipNode
protected override void PushClipCore(Rect rect)
{
Rect = rect
});
var node = RenderDataClipNode.Get();
node.Rect = rect;
Push(node);
}
protected override void PushGeometryClipCore(Geometry? clip)
{
if (clip == null)
Push();
else
Push(new RenderDataGeometryClipNode
{
Geometry = clip?.PlatformImpl
});
{
var node = RenderDataGeometryClipNode.Get();
node.Geometry = clip?.PlatformImpl;
Push(node);
}
}
protected override void PushOpacityCore(double opacity)
@ -227,10 +229,11 @@ internal class RenderDataDrawingContext : DrawingContext
if (opacity == 1)
Push();
else
Push(new RenderDataOpacityNode
{
Opacity = opacity
});
{
var node = RenderDataOpacityNode.Get();
node.Opacity = opacity;
Push(node);
}
}
protected override void PushOpacityMaskCore(IBrush? mask, Rect bounds)
@ -240,11 +243,10 @@ internal class RenderDataDrawingContext : DrawingContext
else
{
AddResource(mask);
Push(new RenderDataOpacityMaskNode
{
ServerBrush = mask.GetServer(_compositor),
BoundsRect = bounds
});
var node = RenderDataOpacityMaskNode.Get();
node.ServerBrush = mask.GetServer(_compositor);
node.BoundsRect = bounds;
Push(node);
}
}
@ -253,21 +255,26 @@ internal class RenderDataDrawingContext : DrawingContext
if (matrix.IsIdentity)
Push();
else
Push(new RenderDataPushMatrixNode()
{
Matrix = matrix
});
{
var node = RenderDataPushMatrixNode.Get();
node.Matrix = matrix;
Push(node);
}
}
protected override void PushRenderOptionsCore(RenderOptions renderOptions) => Push(new RenderDataRenderOptionsNode()
protected override void PushRenderOptionsCore(RenderOptions renderOptions)
{
RenderOptions = renderOptions
});
var node = RenderDataRenderOptionsNode.Get();
node.RenderOptions = renderOptions;
Push(node);
}
protected override void PushTextOptionsCore(TextOptions textOptions) => Push(new RenderDataTextOptionsNode()
protected override void PushTextOptionsCore(TextOptions textOptions)
{
TextOptions = textOptions
});
var node = RenderDataTextOptionsNode.Get();
node.TextOptions = textOptions;
Push(node);
}
protected override void PopClipCore() => Pop<RenderDataClipNode>();
@ -287,13 +294,12 @@ internal class RenderDataDrawingContext : DrawingContext
{
if (source == null || sourceRect.IsEmpty() || destRect.IsEmpty())
return;
Add(new RenderDataBitmapNode
{
Bitmap = source.Clone(),
Opacity = opacity,
SourceRect = sourceRect,
DestRect = destRect
});
var bitmapNode = RenderDataBitmapNode.Get();
bitmapNode.Bitmap = source.Clone();
bitmapNode.Opacity = opacity;
bitmapNode.SourceRect = sourceRect;
bitmapNode.DestRect = destRect;
Add(bitmapNode);
}

4
src/Avalonia.Base/Rendering/Composition/Drawing/ServerCompositionRenderData.cs

@ -137,9 +137,7 @@ class ServerCompositionRenderData : SimpleServerRenderResource
foreach (var r in _referencedResources)
r.RemoveObserver(this);
_referencedResources.Dispose();
foreach(var i in _items)
if (i is IDisposable disp)
disp.Dispose();
RenderDataItemPoolHelper.DisposeAndReturnToPool(_items);
_items.Dispose();
}

6
src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs

@ -17,6 +17,12 @@ namespace Avalonia.Threading
}
}
public void Return(T obj)
{
lock (_stack)
_stack.Push(obj);
}
public void ReturnAndSetNull(ref T? obj)
{
if (obj == null)

Loading…
Cancel
Save