I’m building SimdSkia, an open-source 2D rendering engine for .NET, with a C# CPU rasterizer, SIMD acceleration, SkiaSharp-style APIs, and an Avalonia rendering backend.

The motivation is straightforward. I wanted to explore how much of a desktop graphics stack could move into managed C#, while reducing the native Skia payload and preserving familiar drawing APIs.

The project targets .NET 10 and uses the MIT license. Source code and examples

What it provides

The current implementation includes paths, clipping, gradients, image sampling, blending, static TrueType fonts, basic English and Chinese text layout, and image codecs through Sdcb.SimdCodecs.

For Avalonia desktop applications, backend selection currently works as follows.

Platform Default rendering
Windows Direct2D, with CPU fallback
Linux under X11/XWayland Hardware Vulkan, with CPU fallback
macOS CPU rendering through Avalonia’s native window host
Android CPU/SIMD rendering

The CPU drawing implementation runs independently of Google Skia. GPU backends call system graphics APIs, window integration uses platform hosts, and image compression uses .NET’s compression facilities. Architecture and dependencies

Drawing your first image

Install the standalone drawing package.

dotnet add package SimdSkia --version 0.1.4 

The compatibility layer exposes the SkiaSharp namespace.

using SkiaSharp; using var bitmap = new SKBitmap(320, 180); using var canvas = new SKCanvas(bitmap); canvas.Clear(SKColors.White); using var paint = new SKPaint { Color = new SKColor(62, 90, 220), IsAntialias = true }; canvas.DrawRoundRect( SKRect.Create(24, 24, 272, 132), 24, 24, paint); using var png = bitmap.Encode(SKEncodedImageFormat.Png, 100) ?? throw new InvalidOperationException("PNG encoding failed."); using var output = File.Create("hello-simdskia.png"); png.SaveTo(output); 

Using it with Avalonia

Install the desktop integration package in your startup project.

dotnet add package SimdSkia.Avalonia.Desktop --version 0.1.4 

Follow the migration guide to adjust package references, then configure the application builder.

using SimdSkia.Avalonia; AppBuilder.Configure<App>() .UseSimdSkiaDesktop() .StartWithClassicDesktopLifetime(args); 

Desktop integration has been tested against Avalonia versions 12.1.0 through 12.1.5. Applications with third-party controls should check their dependencies on Skia-specific features. Full migration guide

What happened to NativeAOT deployment size?

For the same Windows x64 example and bundled font, the measured deployment sizes were as follows.

Measurement Original renderer SimdSkia
Executable 18.70 MiB 24.47 MiB
Native DLLs 12.82 MiB 0.00 MiB
Required runtime files, including font 41.16 MiB 30.66 MiB
Compressed ZIP 16.35 MiB 13.03 MiB

The SimdSkia executable grew, while total deployment size fell by approximately 25.5% in this example. These measurements apply to this specific application and publishing configuration. Size measurements and methodology

Performance is workload-dependent

The November 10 source revision was tested on Windows x64 with an Intel i7-8700K, GTX 1050 Ti, and .NET 10.0.14. The comparison baseline used SkiaSharp 3.119.4.

Several CPU workloads still need substantial optimization.

CPU workload SkiaSharp SimdSkia SimdSkia / original
Mesh 0.330 ms 1.409 ms 4.26×
Patch 0.784 ms 2.029 ms 2.59×
Atlas 0.040 ms 0.243 ms 6.13×
Runtime effect 0.280 ms 0.736 ms 2.63×
Perlin noise 13.362 ms 4.585 ms 0.34×

All five workloads passed their existing original-renderer image-comparison thresholds. The runtime-effect optimization reduced SimdSkia’s own previous time from 5.435 ms to 0.736 ms.

CPU rendering comparison

The report also covers actual NativeAOT windows, CPU time, allocations, working set, private memory, and Vulkan/Direct2D completion times including synchronization and readback. Full-page CPU and GPU comparisons still fail the current strict pixel-error thresholds, so their timing results remain diagnostic. The measured source changes extend beyond the published 0.1.4 package. Full benchmark report

Memory improvements have specific conditions

In one fixed-font NativeAOT controls example, private memory fell from 53.5 MiB to 37.9 MiB after eliminating duplicate font-loading buffers. The original renderer used 32.4 MiB.

A separate system-font test showed 49.8 MiB for SimdSkia versus 41.4 MiB for the original default renderer. The benefit depends on the application’s font-loading path.

NativeAOT private memory comparison

Compatibility and remaining work

The public-signature audit matched 3,230 of 3,235 entries against SkiaSharp 3.119.4. Migration requires recompiling code against the compatibility layer and validating application behavior.

Remaining work includes native Skia interoperation gaps, text and curve rendering differences, some JPEG comparisons, and slower mesh, atlas, and runtime-effect workloads. Complete pages also require further visual validation. Compatibility report

I’d especially welcome small reproductions from developers using Avalonia, NativeAOT, or SkiaSharp. Real application workloads will help identify which compatibility and performance improvements matter most.

GitHub repository, examples, and issue tracker