Browse Source

Optimize shape rendering (pen creation mostly).

pull/5997/head
Dariusz Komosinski 5 years ago
parent
commit
f2e93ecb87
  1. 26
      src/Avalonia.Controls/Shapes/Shape.cs
  2. 12
      src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs
  3. 2
      src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs
  4. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  5. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
  6. 103
      tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs
  7. 4
      tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
  8. 53
      tests/Avalonia.Benchmarks/Rendering/ShapeRendering.cs

26
src/Avalonia.Controls/Shapes/Shape.cs

@ -1,6 +1,7 @@
using System;
using Avalonia.Collections;
using Avalonia.Media;
using Avalonia.Media.Immutable;
#nullable enable
@ -199,8 +200,29 @@ namespace Avalonia.Controls.Shapes
if (geometry != null)
{
var pen = new Pen(Stroke, StrokeThickness, new DashStyle(StrokeDashArray, StrokeDashOffset),
StrokeLineCap, StrokeJoin);
var stroke = Stroke;
ImmutablePen? pen = null;
if (stroke != null)
{
var strokeDashArray = StrokeDashArray;
ImmutableDashStyle? dashStyle = null;
if (strokeDashArray != null && strokeDashArray.Count > 0)
{
dashStyle = new ImmutableDashStyle(strokeDashArray, StrokeDashOffset);
}
pen = new ImmutablePen(
stroke.ToImmutable(),
StrokeThickness,
dashStyle,
StrokeLineCap,
StrokeJoin);
}
context.DrawGeometry(Fill, pen, geometry);
}
}

12
src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs

@ -10,6 +10,8 @@ namespace Avalonia.Media.Immutable
/// </summary>
public class ImmutableDashStyle : IDashStyle, IEquatable<IDashStyle>
{
private readonly double[] _dashes;
/// <summary>
/// Initializes a new instance of the <see cref="ImmutableDashStyle"/> class.
/// </summary>
@ -17,12 +19,12 @@ namespace Avalonia.Media.Immutable
/// <param name="offset">The dash sequence offset.</param>
public ImmutableDashStyle(IEnumerable<double> dashes, double offset)
{
Dashes = (IReadOnlyList<double>)dashes?.ToList() ?? Array.Empty<double>();
_dashes = dashes?.ToArray() ?? Array.Empty<double>();
Offset = offset;
}
/// <inheritdoc/>
public IReadOnlyList<double> Dashes { get; }
public IReadOnlyList<double> Dashes => _dashes;
/// <inheritdoc/>
public double Offset { get; }
@ -56,9 +58,9 @@ namespace Avalonia.Media.Immutable
var hashCode = 717868523;
hashCode = hashCode * -1521134295 + Offset.GetHashCode();
if (Dashes != null)
if (_dashes != null)
{
foreach (var i in Dashes)
foreach (var i in _dashes)
{
hashCode = hashCode * -1521134295 + i.GetHashCode();
}
@ -69,7 +71,7 @@ namespace Avalonia.Media.Immutable
private static bool SequenceEqual(IReadOnlyList<double> left, IReadOnlyList<double> right)
{
if (left == right)
if (ReferenceEquals(left, right))
{
return true;
}

2
src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs

@ -23,7 +23,7 @@ namespace Avalonia.Media.Immutable
ImmutableDashStyle dashStyle = null,
PenLineCap lineCap = PenLineCap.Flat,
PenLineJoin lineJoin = PenLineJoin.Miter,
double miterLimit = 10.0) : this(new SolidColorBrush(color), thickness, dashStyle, lineCap, lineJoin, miterLimit)
double miterLimit = 10.0) : this(new ImmutableSolidColorBrush(color), thickness, dashStyle, lineCap, lineJoin, miterLimit)
{
}

2
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@ -249,6 +249,8 @@ namespace Avalonia.Rendering.SceneGraph
{
var visualChildren = (IList<IVisual>) visual.VisualChildren;
node.TryPreallocateChildren(visualChildren.Count);
if (visualChildren.Count == 1)
{
var childNode = GetOrCreateChildNode(scene, visualChildren[0], node);

5
src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs

@ -358,6 +358,11 @@ namespace Avalonia.Rendering.SceneGraph
internal void TryPreallocateChildren(int count)
{
if (count == 0)
{
return;
}
EnsureChildrenCreated(count);
}

103
tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs

@ -0,0 +1,103 @@
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities;
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Benchmarks
{
internal class NullDrawingContextImpl : IDrawingContextImpl
{
public void Dispose()
{
}
public Matrix Transform { get; set; }
public void Clear(Color color)
{
}
public void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect,
BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
{
}
public void DrawBitmap(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
{
}
public void DrawLine(IPen pen, Point p1, Point p2)
{
}
public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry)
{
}
public void DrawRectangle(IBrush brush, IPen pen, RoundedRect rect, BoxShadows boxShadows = default)
{
}
public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)
{
}
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{
}
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return null;
}
public void PushClip(Rect clip)
{
}
public void PushClip(RoundedRect clip)
{
}
public void PopClip()
{
}
public void PushOpacity(double opacity)
{
}
public void PopOpacity()
{
}
public void PushOpacityMask(IBrush mask, Rect bounds)
{
}
public void PopOpacityMask()
{
}
public void PushGeometryClip(IGeometryImpl clip)
{
}
public void PopGeometryClip()
{
}
public void PushBitmapBlendMode(BitmapBlendingMode blendingMode)
{
}
public void PopBitmapBlendMode()
{
}
public void Custom(ICustomDrawOperation custom)
{
}
}
}

4
tests/Avalonia.Benchmarks/NullRenderingPlatform.cs

@ -18,12 +18,12 @@ namespace Avalonia.Benchmarks
public IGeometryImpl CreateEllipseGeometry(Rect rect)
{
throw new NotImplementedException();
return new MockStreamGeometryImpl();
}
public IGeometryImpl CreateLineGeometry(Point p1, Point p2)
{
throw new NotImplementedException();
return new MockStreamGeometryImpl();
}
public IGeometryImpl CreateRectangleGeometry(Rect rect)

53
tests/Avalonia.Benchmarks/Rendering/ShapeRendering.cs

@ -0,0 +1,53 @@
using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Avalonia.Platform;
using BenchmarkDotNet.Attributes;
namespace Avalonia.Benchmarks.Rendering
{
[MemoryDiagnoser]
public class ShapeRendering
{
private readonly DrawingContext _drawingContext;
private readonly Line _lineFill;
private readonly Line _lineFillAndStroke;
private readonly Line _lineNoBrushes;
private readonly Line _lineStroke;
public ShapeRendering()
{
_lineNoBrushes = new Line();
_lineStroke = new Line { Stroke = new SolidColorBrush() };
_lineFill = new Line { Fill = new SolidColorBrush() };
_lineFillAndStroke = new Line { Stroke = new SolidColorBrush(), Fill = new SolidColorBrush() };
_drawingContext = new DrawingContext(new NullDrawingContextImpl(), true);
AvaloniaLocator.CurrentMutable.Bind<IPlatformRenderInterface>().ToConstant(new NullRenderingPlatform());
}
[Benchmark]
public void Render_Line_NoBrushes()
{
_lineNoBrushes.Render(_drawingContext);
}
[Benchmark]
public void Render_Line_WithStroke()
{
_lineStroke.Render(_drawingContext);
}
[Benchmark]
public void Render_Line_WithFill()
{
_lineFill.Render(_drawingContext);
}
[Benchmark]
public void Render_Line_WithFillAndStroke()
{
_lineFillAndStroke.Render(_drawingContext);
}
}
}
Loading…
Cancel
Save