Browse Source

Merge pull request #9441 from DJGosnell/render-loop-skpaint-cache

Render Loop SKPaint Cache
fixes/win32-maximize-not-applied-initially-when-height-set
Max Katz 4 years ago
committed by GitHub
parent
commit
061beb6927
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 82
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  2. 13
      src/Skia/Avalonia.Skia/GeometryImpl.cs
  3. 76
      src/Skia/Avalonia.Skia/SKPaintCache.cs

82
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -35,9 +35,9 @@ namespace Avalonia.Skia
private GRContext _grContext;
public GRContext GrContext => _grContext;
private ISkiaGpu _gpu;
private readonly SKPaint _strokePaint = new SKPaint();
private readonly SKPaint _fillPaint = new SKPaint();
private readonly SKPaint _boxShadowPaint = new SKPaint();
private readonly SKPaint _strokePaint = SKPaintCache.Get();
private readonly SKPaint _fillPaint = SKPaintCache.Get();
private readonly SKPaint _boxShadowPaint = SKPaintCache.Get();
private static SKShader s_acrylicNoiseShader;
private readonly ISkiaGpuRenderSession _session;
private bool _leased = false;
@ -187,17 +187,13 @@ namespace Avalonia.Skia
var s = sourceRect.ToSKRect();
var d = destRect.ToSKRect();
using (var paint =
new SKPaint
{
Color = new SKColor(255, 255, 255, (byte)(255 * opacity * _currentOpacity))
})
{
paint.FilterQuality = bitmapInterpolationMode.ToSKFilterQuality();
paint.BlendMode = _currentBlendingMode.ToSKBlendMode();
var paint = SKPaintCache.Get();
paint.Color = new SKColor(255, 255, 255, (byte)(255 * opacity * _currentOpacity));
paint.FilterQuality = bitmapInterpolationMode.ToSKFilterQuality();
paint.BlendMode = _currentBlendingMode.ToSKBlendMode();
drawableImage.Draw(this, s, d, paint);
}
drawableImage.Draw(this, s, d, paint);
SKPaintCache.ReturnReset(paint);
}
/// <inheritdoc />
@ -561,6 +557,11 @@ namespace Avalonia.Skia
CheckLease();
try
{
// Return leased paints.
SKPaintCache.ReturnReset(_strokePaint);
SKPaintCache.ReturnReset(_fillPaint);
SKPaintCache.ReturnReset(_boxShadowPaint);
if (_grContext != null)
{
Monitor.Exit(_grContext);
@ -620,26 +621,33 @@ namespace Avalonia.Skia
public void PushOpacityMask(IBrush mask, Rect bounds)
{
CheckLease();
// TODO: This should be disposed
var paint = new SKPaint();
var paint = SKPaintCache.Get();
Canvas.SaveLayer(paint);
_maskStack.Push(CreatePaint(paint, mask, bounds.Size, true));
_maskStack.Push(CreatePaint(paint, mask, bounds.Size));
}
/// <inheritdoc />
public void PopOpacityMask()
{
CheckLease();
using (var paint = new SKPaint { BlendMode = SKBlendMode.DstIn })
var paint = SKPaintCache.Get();
paint.BlendMode = SKBlendMode.DstIn;
Canvas.SaveLayer(paint);
SKPaintCache.ReturnReset(paint);
PaintWrapper paintWrapper;
using (paintWrapper = _maskStack.Pop())
{
Canvas.SaveLayer(paint);
using (var paintWrapper = _maskStack.Pop())
{
Canvas.DrawPaint(paintWrapper.Paint);
}
Canvas.Restore();
Canvas.DrawPaint(paintWrapper.Paint);
}
// Return the paint wrapper's paint less the reset since the paint is already reset in the Dispose method above.
SKPaintCache.Return(paintWrapper.Paint);
Canvas.Restore();
Canvas.Restore();
}
@ -974,9 +982,9 @@ namespace Avalonia.Skia
);
}
internal PaintWrapper CreateAcrylicPaint (SKPaint paint, IExperimentalAcrylicMaterial material, bool disposePaint = false)
internal PaintWrapper CreateAcrylicPaint (SKPaint paint, IExperimentalAcrylicMaterial material)
{
var paintWrapper = new PaintWrapper(paint, disposePaint);
var paintWrapper = new PaintWrapper(paint);
paint.IsAntialias = true;
@ -1023,11 +1031,10 @@ namespace Avalonia.Skia
/// <param name="paint">The paint to wrap.</param>
/// <param name="brush">Source brush.</param>
/// <param name="targetSize">Target size.</param>
/// <param name="disposePaint">Optional dispose of the supplied paint.</param>
/// <returns>Paint wrapper for given brush.</returns>
internal PaintWrapper CreatePaint(SKPaint paint, IBrush brush, Size targetSize, bool disposePaint = false)
internal PaintWrapper CreatePaint(SKPaint paint, IBrush brush, Size targetSize)
{
var paintWrapper = new PaintWrapper(paint, disposePaint);
var paintWrapper = new PaintWrapper(paint);
paint.IsAntialias = true;
@ -1080,9 +1087,8 @@ namespace Avalonia.Skia
/// <param name="paint">The paint to wrap.</param>
/// <param name="pen">Source pen.</param>
/// <param name="targetSize">Target size.</param>
/// <param name="disposePaint">Optional dispose of the supplied paint.</param>
/// <returns></returns>
private PaintWrapper CreatePaint(SKPaint paint, IPen pen, Size targetSize, bool disposePaint = false)
private PaintWrapper CreatePaint(SKPaint paint, IPen pen, Size targetSize)
{
// In Skia 0 thickness means - use hairline rendering
// and for us it means - there is nothing rendered.
@ -1091,7 +1097,7 @@ namespace Avalonia.Skia
return default;
}
var rv = CreatePaint(paint, pen.Brush, targetSize, disposePaint);
var rv = CreatePaint(paint, pen.Brush, targetSize);
paint.IsStroke = true;
paint.StrokeWidth = (float) pen.Thickness;
@ -1206,16 +1212,14 @@ namespace Avalonia.Skia
{
//We are saving memory allocations there
public readonly SKPaint Paint;
private readonly bool _disposePaint;
private IDisposable _disposable1;
private IDisposable _disposable2;
private IDisposable _disposable3;
public PaintWrapper(SKPaint paint, bool disposePaint)
public PaintWrapper(SKPaint paint)
{
Paint = paint;
_disposePaint = disposePaint;
_disposable1 = null;
_disposable2 = null;
@ -1263,15 +1267,7 @@ namespace Avalonia.Skia
/// <inheritdoc />
public void Dispose()
{
if (_disposePaint)
{
Paint?.Dispose();
}
else
{
Paint?.Reset();
}
Paint?.Reset();
_disposable1?.Dispose();
_disposable2?.Dispose();
_disposable3?.Dispose();

13
src/Skia/Avalonia.Skia/GeometryImpl.cs

@ -81,15 +81,14 @@ namespace Avalonia.Skia
}
else
{
using (var paint = new SKPaint())
{
paint.IsStroke = true;
paint.StrokeWidth = strokeWidth;
var paint = SKPaintCache.Get();
paint.IsStroke = true;
paint.StrokeWidth = strokeWidth;
paint.GetFillPath(EffectivePath, strokePath);
paint.GetFillPath(EffectivePath, strokePath);
SKPaintCache.ReturnReset(paint);
_pathCache.Cache(strokePath, strokeWidth, strokePath.TightBounds.ToAvaloniaRect());
}
_pathCache.Cache(strokePath, strokeWidth, strokePath.TightBounds.ToAvaloniaRect());
}
}

76
src/Skia/Avalonia.Skia/SKPaintCache.cs

@ -0,0 +1,76 @@
using System.Collections.Concurrent;
using SkiaSharp;
namespace Avalonia.Skia
{
/// <summary>
/// Cache for SKPaints.
/// </summary>
internal static class SKPaintCache
{
private static ConcurrentBag<SKPaint> s_cachedPaints;
static SKPaintCache()
{
s_cachedPaints = new ConcurrentBag<SKPaint>();
}
/// <summary>
/// Gets a SKPaint for usage.
/// </summary>
/// <remarks>
/// If a SKPaint is in the cache, that existing SKPaint will be returned.
/// Otherwise a new SKPaint will be created.
/// </remarks>
/// <returns></returns>
public static SKPaint Get()
{
if (!s_cachedPaints.TryTake(out var paint))
{
paint = new SKPaint();
}
return paint;
}
/// <summary>
/// Returns a SKPaint for reuse later.
/// </summary>
/// <remarks>
/// Do not use the paint further.
/// Do not return the same paint multiple times as that will break the cache.
/// </remarks>
/// <param name="paint"></param>
public static void Return(SKPaint paint)
{
s_cachedPaints.Add(paint);
}
/// <summary>
/// Returns a SKPaint and resets it for reuse later.
/// </summary>
/// <remarks>
/// Do not use the paint further.
/// Do not return the same paint multiple times as that will break the cache.
/// Uses SKPaint.Reset() for reuse later.
/// </remarks>
/// <param name="paint"></param>
public static void ReturnReset(SKPaint paint)
{
paint.Reset();
s_cachedPaints.Add(paint);
}
/// <summary>
/// Clears and disposes all cached paints.
/// </summary>
public static void Clear()
{
while (s_cachedPaints.TryTake(out var paint))
{
paint.Dispose();
}
}
}
}
Loading…
Cancel
Save