Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: reduce SKPaint and SKPath allocations #18476

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
63 changes: 51 additions & 12 deletions src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,27 +94,37 @@ private SKPaint TryCreateAndClearStrokePaint(SKColorFilter? colorFilter)
private SKPaint TryCreateAndClearFillPaint(SKColorFilter? colorFilter)
=> TryCreateAndClearPaint(ref _fillPaint, false, colorFilter, CompositionConfiguration.UseBrushAntialiasing);

// TODO: profile the impact of this optimization and consider removing it
// It's hacky and can break if SkiaSharp exposes new properties that
// are then used and modified in CompositionBrush.UpdatePaint().
private static SKPaint TryCreateAndClearPaint(ref SKPaint? paint, bool isStroke, SKColorFilter? colorFilter, bool isHighQuality = false)
{
if (paint == null)
{
// Initialization
paint = new SKPaint();
paint.IsStroke = isStroke;
paint.IsAntialias = true;
paint.IsAutohinted = true;

if (isHighQuality)
{
paint.FilterQuality = SKFilterQuality.High;
}
}
else
{
// defaults
paint.IsAntialias = false;
paint.BlendMode = SKBlendMode.SrcOver;
paint.FakeBoldText = false;
paint.HintingLevel = SKPaintHinting.Normal;
paint.IsDither = false;
paint.IsEmbeddedBitmapText = false;
paint.IsLinearText = false;
paint.LcdRenderText = false;
paint.StrokeCap = SKStrokeCap.Butt;
paint.StrokeJoin = SKStrokeJoin.Miter;
paint.StrokeMiter = 4;
paint.StrokeWidth = 0;
paint.SubpixelText = false;
paint.TextAlign = SKTextAlign.Left;
paint.TextEncoding = SKTextEncoding.Utf8;
paint.TextScaleX = 1;
paint.TextSize = 12;

// Cleanup
// - Brushes can change, we cant leave color and shader garbage
// from last rendering around for the next pass.
paint.Color = SKColors.White; // Transparent color wouldn't draw anything
if (paint.Shader is { } shader)
{
shader.Dispose();
Expand All @@ -126,9 +136,38 @@ private static SKPaint TryCreateAndClearPaint(ref SKPaint? paint, bool isStroke,
pathEffect.Dispose();
paint.PathEffect = null;
}

if (paint.ImageFilter is { } imageFilter)
{
imageFilter.Dispose();
paint.ImageFilter = null;
}

if (paint.MaskFilter is { } maskFilter)
{
maskFilter.Dispose();
paint.MaskFilter = null;
}

if (paint.Typeface is { } typeface)
{
typeface.Dispose();
paint.Typeface = null;
}
}

paint.IsStroke = isStroke;

// uno-specific defaults
paint.Color = SKColors.White; // Transparent color wouldn't draw anything
paint.IsAutohinted = true;
// paint.IsAntialias = true; // IMPORTANT: don't set this to true by default. It breaks canvas clipping on Linux for some reason.
ramezgerges marked this conversation as resolved.
Show resolved Hide resolved

paint.ColorFilter = colorFilter;
if (isHighQuality)
{
paint.FilterQuality = SKFilterQuality.High;
}

return paint;
}
Expand Down
34 changes: 31 additions & 3 deletions src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Shapes/Given_Path.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
using System;
using Windows.Foundation;
using System.Threading.Tasks;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Shapes;
using MUXControlsTestApp.Utilities;
using SamplesApp.UITests;
using Uno.UI.RuntimeTests.Helpers;
using Size = Windows.Foundation.Size;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Shapes
{
[TestClass]
[RunsOnUIThread]
public class Given_Path
{
[TestMethod]
[RunsOnUIThread]
[UnoWorkItem("https://github.com/unoplatform/uno/issues/6846")]
public void Should_not_throw_if_Path_Data_is_set_to_null()
{
Expand All @@ -23,7 +28,6 @@ public void Should_not_throw_if_Path_Data_is_set_to_null()
}

[TestMethod]
[RunsOnUIThread]
public void Should_Not_Include_Control_Points_Bounds()
{
#if WINAPPSDK
Expand All @@ -41,5 +45,29 @@ public void Should_Not_Include_Control_Points_Bounds()
Assert.IsTrue(Math.Abs(50 - SUT.DesiredSize.Height) <= 1, $"Actual size: {SUT.DesiredSize}");
#endif
}

// This tests a workaround for a Linux-specific skia bug.
// This doesn't actually repro for some reason, so it's here for documentation purposes.
ramezgerges marked this conversation as resolved.
Show resolved Hide resolved
[TestMethod]
[UnoWorkItem("https://github.com/unoplatform/uno/issues/18473")]
public async Task When_Path_Clipped_Inside_ScrollViewer()
{
var root = (FrameworkElement)XamlReader.Load(
"""
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Border Height="100" HorizontalAlignment="Stretch" Background="Blue" />
<ScrollViewer Height="30">
<Path Fill="Red" Data="m 0,0 c -0.639,0 -1.276,0.243 -1.765,0.729 -0.977,0.974 -0.98,2.557 -0.006,3.535 L 16.925,23.032 -1.768,41.725 c -0.976,0.976 -0.976,2.559 0,3.535 0.977,0.976 2.559,0.976 3.536,0 L 22.225,24.803 c 0.975,-0.975 0.976,-2.555 0.004,-3.532 L 1.771,0.736 C 1.283,0.245 0.642,0 0,0" />
</ScrollViewer>
</StackPanel>
""");
await UITestHelper.Load(root);

root.FindVisualChildByType<ScrollViewer>().ScrollToVerticalOffset(9999);
await UITestHelper.WaitForIdle();

var screenshot = await UITestHelper.ScreenShot(root);
ImageAssert.DoesNotHaveColorInRectangle(screenshot, new System.Drawing.Rectangle(0, 0, screenshot.Width, screenshot.Height * 60 / 100), Microsoft.UI.Colors.Red);
}
}
}
Loading