SkiaSharp
SkiaSharp copied to clipboard
General Performance Improvements
Description
With .NET 4.8 and .NET Core 2.1 and the like, there have been new types introduced to reduce allocations and increase performance. We should really start to have a look at this and see where/how we can make use of new things.
VS bug #731696
I've been looking at passing Spans to Skia in my project, where I use a lot of pooled SKPoint buffers.
I've got a class that looks like this
internal static class SkiaApiEx
{
// ...snip...
[DllImport(SKIA, CallingConvention = CallingConvention.Cdecl)]
public extern static void sk_path_add_poly(sk_path_t cpath, IntPtr pPoints, int count, [MarshalAs(UnmanagedType.I1)] bool close);
public static void AddPoly(this SKPath path, ReadOnlySpan<SKPoint> points, bool close)
{
unsafe
{
fixed (SKPoint* p = points)
{
sk_path_add_poly(path.Handle, (IntPtr)p, points.Length, close);
}
}
}
}
Not sure if it is the right pattern idiomatically, but works wonderfully for me in my project.
This is something that we will be looking at in the future.
I suppose someone should look at that faster, as it's easy enough. I've just profiled my app and SKPoint allocations are 20% of all allocations. This would reduce allocations by 50%. Also instead of using ReadOnlySpan<SKPoint> points, use "in ReadOnlySpan<SKPoint> points" (prefix with in). This prevents the runtime from reallocating structs. It's also in the new C# optimizations list.
I need to access the pixels of a SKBitmap. To do this I wrote these extension methods:
public static unsafe Span<byte> GetPixelSpan(this SKBitmap bitmap)
{
var ptr = SkiaApi.sk_bitmap_get_pixels(bitmap.Handle, out var length);
return new Span<byte>(ptr.ToPointer(), length.ToInt32());
}
public static unsafe Span<uint> GetPixelSpanUInt(this SKBitmap bitmap)
{
var ptr = SkiaApi.sk_bitmap_get_pixels(bitmap.Handle, out var length);
return new Span<uint>(ptr.ToPointer(), length.ToInt32());
}
// this allows something like GetPixelSpan<Foobar>()
public static unsafe Span<T> GetPixelSpan<T>(this SKBitmap bitmap)
{
var ptr = SkiaApi.sk_bitmap_get_pixels(bitmap.Handle, out var length);
return new Span<T>(ptr.ToPointer(), length.ToInt32());
}
With these methods I get a Span of the pixels and can work with it in a safe way.
SKBitmap.Bytes copies the whole bitmap to a new byte[], so this is very slow and needs more memory.
SKBitmap.GetPixels returns an IntPtr and needs unsafe code to work with it.
GetPixelSpan has none of those drawbacks.
Of course you don't need all three of those methods. The last is enough, but it enables somebody to write GetPixelSpan<Foobar>() which does not make sense. Your choice which methods you'll use.