Skip to content

Commit f8efae7

Browse files
committed
perf(skia): Reuse surfaces/bitmaps and reduce native calls
- Cache surfaces and bitmap between invocation - Avoid repetitive calls ending up calling native code (transition is costly), e.g. - successive calls to properties like `surface.Canvas` and `_bitmap.Width` - multiple calls to `_bitmap.GetPixels(out _)` - Dispose existing `SKBitmap` and `SKSurface` when needed (e.g. window resizing) - Removed nullable on `float? _dpi;` is already assigned to a value (by .ctor) and `UpdateDpi` never set it to `null` Measuring the impact was tricky since there are huge differences between frame redraw times. I ended up logging an average of a 1000 frames. Numbers are in ticks (100 ns). **before** ``` trce: Uno.UI.Runtime.Skia.SoftwareRenderSurface[0] Average render time: 116098 trce: Uno.UI.Runtime.Skia.SoftwareRenderSurface[0] Average render time: 116879 trce: Uno.UI.Runtime.Skia.SoftwareRenderSurface[0] Average render time: 114771 trce: Uno.UI.Runtime.Skia.SoftwareRenderSurface[0] Average render time: 114271 ``` **after** ``` trce: Uno.UI.Runtime.Skia.SoftwareRenderSurface[0] Average render time: 112252 trce: Uno.UI.Runtime.Skia.SoftwareRenderSurface[0] Average render time: 114341 trce: Uno.UI.Runtime.Skia.SoftwareRenderSurface[0] Average render time: 115398 trce: Uno.UI.Runtime.Skia.SoftwareRenderSurface[0] Average render time: 113393 ```
1 parent 5fe24f2 commit f8efae7

File tree

1 file changed

+29
-35
lines changed

1 file changed

+29
-35
lines changed

src/Uno.UI.Runtime.Skia.Gtk/SoftwareRenderSurface.cs

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.IO;
55
using System.Runtime.InteropServices;
6+
using Cairo;
67
using SkiaSharp;
78
using Uno.Extensions;
89
using Uno.UI.Xaml.Core;
@@ -11,7 +12,6 @@
1112
using Uno.Foundation.Logging;
1213
using Windows.UI.Xaml.Controls;
1314
using System.Diagnostics;
14-
using System.Runtime.InteropServices;
1515
using Uno.UI.Runtime.Skia.Helpers.Windows;
1616
using Uno.UI.Runtime.Skia.Helpers.Dpi;
1717
using Windows.Graphics.Display;
@@ -22,10 +22,13 @@ internal class SoftwareRenderSurface : Gtk.DrawingArea, IRenderSurface
2222
{
2323
private readonly DisplayInformation _displayInformation;
2424
private FocusManager? _focusManager;
25+
private SKSurface? _surface;
2526
private SKBitmap? _bitmap;
27+
private int _bheight, _bwidth;
28+
private ImageSurface? _gtkSurface;
2629
private int renderCount;
2730

28-
private float? _dpi = 1;
31+
private float _dpi = 1;
2932

3033
private readonly SKColorType _colorType;
3134

@@ -67,54 +70,45 @@ private void InvalidateOverlays()
6770
private void Invalidate()
6871
=> QueueDrawArea(0, 0, 10000, 10000);
6972

70-
protected override bool OnDrawn(Cairo.Context cr)
73+
protected override bool OnDrawn(Context cr)
7174
{
7275
Stopwatch? sw = null;
7376

74-
int width, height;
75-
7677
if (this.Log().IsEnabled(LogLevel.Trace))
7778
{
7879
sw = Stopwatch.StartNew();
7980
this.Log().Trace($"Render {renderCount++}");
8081
}
8182

82-
var dpi = UpdateDpi();
83-
84-
width = (int)AllocatedWidth;
85-
height = (int)AllocatedHeight;
86-
87-
var scaledWidth = (int)(width * dpi);
88-
var scaledHeight = (int)(height * dpi);
83+
var scaledWidth = (int)(AllocatedWidth * _dpi);
84+
var scaledHeight = (int)(AllocatedHeight * _dpi);
8985

90-
var info = new SKImageInfo(scaledWidth, scaledHeight, _colorType, SKAlphaType.Premul);
91-
92-
// reset the bitmap if the size has changed
93-
if (_bitmap == null || info.Width != _bitmap.Width || info.Height != _bitmap.Height)
86+
// reset the surfaces (skia/cairo) and bitmap if the size has changed
87+
if (_surface == null || scaledWidth != _bwidth || scaledHeight != _bheight)
9488
{
89+
_gtkSurface?.Dispose ();
90+
_surface?.Dispose();
91+
_bitmap?.Dispose();
92+
93+
var info = new SKImageInfo(scaledWidth, scaledHeight, _colorType, SKAlphaType.Premul);
9594
_bitmap = new SKBitmap(info);
95+
_bwidth = _bitmap.Width;
96+
_bheight = _bitmap.Height;
97+
var pixels = _bitmap.GetPixels(out _);
98+
_surface = SKSurface.Create(info, pixels);
99+
_gtkSurface = new ImageSurface(pixels, Format.Argb32, _bwidth, _bheight, _bwidth * 4);
96100
}
97101

98-
using (var surface = SKSurface.Create(info, _bitmap.GetPixels(out _)))
99-
{
100-
surface.Canvas.Clear(SKColors.White);
101-
102-
surface.Canvas.Scale(dpi);
102+
var canvas = _surface.Canvas;
103+
canvas.Clear(SKColors.White);
104+
canvas.Scale(_dpi);
103105

104-
WUX.Window.Current.Compositor.Render(surface);
106+
WUX.Window.Current.Compositor.Render(_surface);
105107

106-
using (var gtkSurface = new Cairo.ImageSurface(
107-
_bitmap.GetPixels(out _),
108-
Cairo.Format.Argb32,
109-
_bitmap.Width, _bitmap.Height,
110-
_bitmap.Width * 4))
111-
{
112-
gtkSurface.MarkDirty();
113-
cr.Scale(1 / dpi, 1 / dpi);
114-
cr.SetSourceSurface(gtkSurface, 0, 0);
115-
cr.Paint();
116-
}
117-
}
108+
_gtkSurface!.MarkDirty();
109+
cr.Scale(1 / _dpi, 1 / _dpi);
110+
cr.SetSourceSurface(_gtkSurface, 0, 0);
111+
cr.Paint();
118112

119113
if (this.Log().IsEnabled(LogLevel.Trace))
120114
{
@@ -133,6 +127,6 @@ public void TakeScreenshot(string filePath)
133127
_bitmap?.Encode(wstream, SKEncodedImageFormat.Png, 100);
134128
}
135129

136-
private float UpdateDpi() => _dpi ??= (float)_displayInformation.RawPixelsPerViewPixel;
130+
private float UpdateDpi() => _dpi = (float)_displayInformation.RawPixelsPerViewPixel;
137131
}
138132
}

0 commit comments

Comments
 (0)