mono / SkiaSharp

SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.
MIT License
4.52k stars 540 forks source link

General Performance Improvements #689

Open mattleibow opened 6 years ago

mattleibow commented 6 years ago

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

ojb500 commented 6 years ago

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.

mattleibow commented 5 years ago

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

softlion commented 5 years ago

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 points, use "in ReadOnlySpan points" (prefix with in). This prevents the runtime from reallocating structs. It's also in the new C# optimizations list.

MichaelRumpler commented 5 years ago

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.