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 System;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Immutable;
#nullable enable #nullable enable
@ -199,8 +200,29 @@ namespace Avalonia.Controls.Shapes
if (geometry != null) if (geometry != null)
{ {
var pen = new Pen(Stroke, StrokeThickness, new DashStyle(StrokeDashArray, StrokeDashOffset), var stroke = Stroke;
StrokeLineCap, StrokeJoin);
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); context.DrawGeometry(Fill, pen, geometry);
} }
} }

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

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

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

@ -23,7 +23,7 @@ namespace Avalonia.Media.Immutable
ImmutableDashStyle dashStyle = null, ImmutableDashStyle dashStyle = null,
PenLineCap lineCap = PenLineCap.Flat, PenLineCap lineCap = PenLineCap.Flat,
PenLineJoin lineJoin = PenLineJoin.Miter, 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; var visualChildren = (IList<IVisual>) visual.VisualChildren;
node.TryPreallocateChildren(visualChildren.Count);
if (visualChildren.Count == 1) if (visualChildren.Count == 1)
{ {
var childNode = GetOrCreateChildNode(scene, visualChildren[0], node); 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) internal void TryPreallocateChildren(int count)
{ {
if (count == 0)
{
return;
}
EnsureChildrenCreated(count); 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) public IGeometryImpl CreateEllipseGeometry(Rect rect)
{ {
throw new NotImplementedException(); return new MockStreamGeometryImpl();
} }
public IGeometryImpl CreateLineGeometry(Point p1, Point p2) public IGeometryImpl CreateLineGeometry(Point p1, Point p2)
{ {
throw new NotImplementedException(); return new MockStreamGeometryImpl();
} }
public IGeometryImpl CreateRectangleGeometry(Rect rect) 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