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.
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.
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.





