From f6535c6b26845f7e327ab494e5ff50b53fb108d8 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Wed, 11 Jan 2023 16:01:37 +0300 Subject: [PATCH 1/3] [Text] Dispose Text Layout in TextBlock and TextPresenter --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 3 +++ src/Avalonia.Controls/TextBlock.cs | 11 ++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 480a8ee7a7..f599511392 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -588,6 +588,7 @@ namespace Avalonia.Controls.Presenters protected virtual void InvalidateTextLayout() { + _textLayout?.Dispose(); _textLayout = null; InvalidateMeasure(); @@ -597,6 +598,7 @@ namespace Avalonia.Controls.Presenters { _constraint = availableSize; + _textLayout?.Dispose(); _textLayout = null; InvalidateArrange(); @@ -622,6 +624,7 @@ namespace Avalonia.Controls.Presenters _constraint = new Size(Math.Ceiling(finalSize.Width), double.PositiveInfinity); + _textLayout?.Dispose(); _textLayout = null; return finalSize; diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 7a0da1ecc5..31ddf1f985 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -173,13 +173,7 @@ namespace Avalonia.Controls /// /// Gets the used to render the text. /// - public TextLayout TextLayout - { - get - { - return _textLayout ??= CreateTextLayout(_text); - } - } + public TextLayout TextLayout => _textLayout ??= CreateTextLayout(_text); /// /// Gets or sets the padding to place around the . @@ -648,6 +642,7 @@ namespace Avalonia.Controls /// protected void InvalidateTextLayout() { + _textLayout?.Dispose(); _textLayout = null; InvalidateVisual(); @@ -663,6 +658,7 @@ namespace Avalonia.Controls _constraint = availableSize.Deflate(padding); + _textLayout?.Dispose(); _textLayout = null; var inlines = Inlines; @@ -726,6 +722,7 @@ namespace Avalonia.Controls _constraint = new Size(Math.Ceiling(finalSize.Deflate(padding).Width), double.PositiveInfinity); + _textLayout?.Dispose(); _textLayout = null; if (HasComplexContent) From 56e94e424c9b120f8fac3aa450bbe43ceae22d81 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Wed, 11 Jan 2023 16:35:23 +0300 Subject: [PATCH 2/3] [Text] Add textblocks virtualization benchmark, rename field --- .../Media/TextFormatting/ShapedBuffer.cs | 8 +++---- .../Text/HugeTextLayout.cs | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs index 5cf153d199..40500670ad 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs @@ -8,12 +8,12 @@ namespace Avalonia.Media.TextFormatting public sealed class ShapedBuffer : IList, IDisposable { private static readonly IComparer s_clusterComparer = new CompareClusters(); - private bool _rented; + private bool _bufferRented; public ShapedBuffer(CharacterBufferRange characterBufferRange, int bufferLength, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) : this(characterBufferRange, ArrayPool.Shared.Rent(bufferLength), glyphTypeface, fontRenderingEmSize, bidiLevel) { - _rented = true; + _bufferRented = true; Length = bufferLength; } @@ -268,7 +268,7 @@ namespace Avalonia.Media.TextFormatting public void Dispose() { GC.SuppressFinalize(this); - if (_rented) + if (_bufferRented) { GlyphInfos.ReturnRent(); } @@ -276,7 +276,7 @@ namespace Avalonia.Media.TextFormatting ~ShapedBuffer() { - if (_rented) + if (_bufferRented) { GlyphInfos.ReturnRent(); } diff --git a/tests/Avalonia.Benchmarks/Text/HugeTextLayout.cs b/tests/Avalonia.Benchmarks/Text/HugeTextLayout.cs index 20fa268a85..bf0253e9ab 100644 --- a/tests/Avalonia.Benchmarks/Text/HugeTextLayout.cs +++ b/tests/Avalonia.Benchmarks/Text/HugeTextLayout.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Avalonia.Controls; using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.UnitTests; @@ -70,6 +71,26 @@ In respect that the structure of the sufficient amount poses problems and challe [Benchmark] public TextLayout[] BuildManySmallTexts() => _manySmallStrings.Select(MakeLayout).ToArray(); + [Benchmark] + public void VirtualizeTextBlocks() + { + var blocks = new TextBlock[10]; + for (var i = 0; i < blocks.Length; i++) + { + blocks[i] = new TextBlock + { + Width = 120, + Height = 32, + }; + } + + for (int i = 0, j = 0; i < _manySmallStrings.Length; i++, j = j < blocks.Length - 1 ? j + 1 : 0) + { + blocks[j].Text = _manySmallStrings[i]; + blocks[j].Measure(new Size(200, 200)); + } + } + private static TextLayout MakeLayout(string str) { var layout = new TextLayout(str, Typeface.Default, 12d, Brushes.Black, maxWidth: 120); From 91fd89017e3f0b43886422715b091c42cacf034b Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Wed, 11 Jan 2023 17:34:48 +0300 Subject: [PATCH 3/3] [Text] do not use linq on dispose --- src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index af954eb5e9..6034e9f679 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -981,9 +981,12 @@ namespace Avalonia.Media.TextFormatting public override void Dispose() { - foreach (var run in _textRuns.OfType()) + for (int i = 0; i < _textRuns.Count; i++) { - run.Dispose(); + if (_textRuns[i] is ShapedTextRun shapedTextRun) + { + shapedTextRun.Dispose(); + } } }