SkiaSharp icon indicating copy to clipboard operation
SkiaSharp copied to clipboard

General Performance Improvements

Open mattleibow opened this issue 7 years ago • 4 comments

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

mattleibow avatar Nov 16 '18 20:11 mattleibow

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.

ojb500 avatar Nov 17 '18 14:11 ojb500

This is something that we will be looking at in the future.

mattleibow avatar Dec 04 '18 17:12 mattleibow

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.

softlion avatar Mar 27 '19 10:03 softlion

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.

MichaelRumpler avatar May 27 '19 10:05 MichaelRumpler