Browse Source

[Text] Add pooling to shapedBuffer. works only with dispose.

pull/9954/head
Sergey Mikolaitis 3 years ago
parent
commit
39374451dd
  1. 2
      src/Avalonia.Base/Media/GlyphRun.cs
  2. 29
      src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs
  3. 8
      src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs
  4. 10
      src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
  5. 4
      src/Avalonia.Base/Media/TextFormatting/TextLine.cs
  6. 9
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  7. 9
      src/Avalonia.Base/Utilities/ArraySlice.cs
  8. 8
      tests/Avalonia.Benchmarks/Text/HugeTextLayout.cs

2
src/Avalonia.Base/Media/GlyphRun.cs

@ -918,7 +918,7 @@ namespace Avalonia.Media
_glyphRunImpl = platformRenderInterface.CreateGlyphRun(GlyphTypeface, FontRenderingEmSize, GlyphIndices, GlyphAdvances, GlyphOffsets); _glyphRunImpl = platformRenderInterface.CreateGlyphRun(GlyphTypeface, FontRenderingEmSize, GlyphIndices, GlyphAdvances, GlyphOffsets);
} }
void IDisposable.Dispose() public void Dispose()
{ {
_glyphRunImpl?.Dispose(); _glyphRunImpl?.Dispose();
} }

29
src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs

@ -1,17 +1,20 @@
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting namespace Avalonia.Media.TextFormatting
{ {
public sealed class ShapedBuffer : IList<GlyphInfo> public sealed class ShapedBuffer : IList<GlyphInfo>, IDisposable
{ {
private static readonly IComparer<GlyphInfo> s_clusterComparer = new CompareClusters(); private static readonly IComparer<GlyphInfo> s_clusterComparer = new CompareClusters();
private bool _rented;
public ShapedBuffer(CharacterBufferRange characterBufferRange, int bufferLength, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) : public ShapedBuffer(CharacterBufferRange characterBufferRange, int bufferLength, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) :
this(characterBufferRange, new GlyphInfo[bufferLength], glyphTypeface, fontRenderingEmSize, bidiLevel) this(characterBufferRange, ArrayPool<GlyphInfo>.Shared.Rent(bufferLength), glyphTypeface, fontRenderingEmSize, bidiLevel)
{ {
_rented = true;
Length = bufferLength;
} }
internal ShapedBuffer(CharacterBufferRange characterBufferRange, ArraySlice<GlyphInfo> glyphInfos, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) internal ShapedBuffer(CharacterBufferRange characterBufferRange, ArraySlice<GlyphInfo> glyphInfos, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
@ -21,11 +24,12 @@ namespace Avalonia.Media.TextFormatting
GlyphTypeface = glyphTypeface; GlyphTypeface = glyphTypeface;
FontRenderingEmSize = fontRenderingEmSize; FontRenderingEmSize = fontRenderingEmSize;
BidiLevel = bidiLevel; BidiLevel = bidiLevel;
Length = GlyphInfos.Length;
} }
internal ArraySlice<GlyphInfo> GlyphInfos { get; } internal ArraySlice<GlyphInfo> GlyphInfos { get; }
public int Length => GlyphInfos.Length; public int Length { get; }
public IGlyphTypeface GlyphTypeface { get; } public IGlyphTypeface GlyphTypeface { get; }
@ -260,6 +264,23 @@ namespace Avalonia.Media.TextFormatting
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
} }
public void Dispose()
{
GC.SuppressFinalize(this);
if (_rented)
{
GlyphInfos.ReturnRent();
}
}
~ShapedBuffer()
{
if (_rented)
{
GlyphInfos.ReturnRent();
}
}
} }
public readonly record struct GlyphInfo public readonly record struct GlyphInfo

8
src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs

@ -6,7 +6,7 @@ namespace Avalonia.Media.TextFormatting
/// <summary> /// <summary>
/// A text run that holds shaped characters. /// A text run that holds shaped characters.
/// </summary> /// </summary>
public sealed class ShapedTextRun : DrawableTextRun public sealed class ShapedTextRun : DrawableTextRun, IDisposable
{ {
private GlyphRun? _glyphRun; private GlyphRun? _glyphRun;
@ -199,5 +199,11 @@ namespace Avalonia.Media.TextFormatting
ShapedBuffer.GlyphClusters, ShapedBuffer.GlyphClusters,
BidiLevel); BidiLevel);
} }
public void Dispose()
{
_glyphRun?.Dispose();
ShapedBuffer.Dispose();
}
} }
} }

10
src/Avalonia.Base/Media/TextFormatting/TextLayout.cs

@ -8,7 +8,7 @@ namespace Avalonia.Media.TextFormatting
/// <summary> /// <summary>
/// Represents a multi line text layout. /// Represents a multi line text layout.
/// </summary> /// </summary>
public class TextLayout public class TextLayout : IDisposable
{ {
private readonly ITextSource _textSource; private readonly ITextSource _textSource;
private readonly TextParagraphProperties _paragraphProperties; private readonly TextParagraphProperties _paragraphProperties;
@ -561,5 +561,13 @@ namespace Avalonia.Media.TextFormatting
return _textTrimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, _paragraphProperties.DefaultTextRunProperties)); return _textTrimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, _paragraphProperties.DefaultTextRunProperties));
} }
public void Dispose()
{
foreach (var line in TextLines)
{
line.Dispose();
}
}
} }
} }

4
src/Avalonia.Base/Media/TextFormatting/TextLine.cs

@ -6,7 +6,7 @@ namespace Avalonia.Media.TextFormatting
/// <summary> /// <summary>
/// Represents a line of text that is used for text rendering. /// Represents a line of text that is used for text rendering.
/// </summary> /// </summary>
public abstract class TextLine public abstract class TextLine : IDisposable
{ {
/// <summary> /// <summary>
/// Gets the text runs that are contained within a line. /// Gets the text runs that are contained within a line.
@ -207,5 +207,7 @@ namespace Avalonia.Media.TextFormatting
/// <param name="textLength">number of characters of the specified range</param> /// <param name="textLength">number of characters of the specified range</param>
/// <returns>an array of bounding rectangles.</returns> /// <returns>an array of bounding rectangles.</returns>
public abstract IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceCharacterIndex, int textLength); public abstract IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceCharacterIndex, int textLength);
public abstract void Dispose();
} }
} }

9
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting namespace Avalonia.Media.TextFormatting
@ -934,6 +935,14 @@ namespace Avalonia.Media.TextFormatting
return GetTextBoundsRightToLeft(firstTextSourceIndex, textLength); return GetTextBoundsRightToLeft(firstTextSourceIndex, textLength);
} }
public override void Dispose()
{
foreach (var run in _textRuns.OfType<ShapedTextRun>())
{
run.Dispose();
}
}
public TextLineImpl FinalizeLine() public TextLineImpl FinalizeLine()
{ {
_textLineMetrics = CreateLineMetrics(); _textLineMetrics = CreateLineMetrics();

9
src/Avalonia.Base/Utilities/ArraySlice.cs

@ -3,6 +3,7 @@
// Ported from: https://github.com/SixLabors/Fonts/ // Ported from: https://github.com/SixLabors/Fonts/
using System; using System;
using System.Buffers;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -185,5 +186,13 @@ namespace Avalonia.Utilities
/// <inheritdoc/> /// <inheritdoc/>
int IReadOnlyCollection<T>.Count => Length; int IReadOnlyCollection<T>.Count => Length;
public void ReturnRent()
{
if (_data != null)
{
ArrayPool<T>.Shared.Return(_data);
}
}
} }
} }

8
tests/Avalonia.Benchmarks/Text/HugeTextLayout.cs

@ -70,8 +70,12 @@ In respect that the structure of the sufficient amount poses problems and challe
[Benchmark] [Benchmark]
public TextLayout[] BuildManySmallTexts() => _manySmallStrings.Select(MakeLayout).ToArray(); public TextLayout[] BuildManySmallTexts() => _manySmallStrings.Select(MakeLayout).ToArray();
private static TextLayout MakeLayout(string str) private static TextLayout MakeLayout(string str)
=> new TextLayout(str, Typeface.Default, 12d, Brushes.Black, maxWidth:120); {
var layout = new TextLayout(str, Typeface.Default, 12d, Brushes.Black, maxWidth: 120);
layout.Dispose();
return layout;
}
public void Dispose() public void Dispose()
{ {

Loading…
Cancel
Save