This repository hosts the managed bindings that expose the Linebender graphics stack—vello
for rendering, wgpu for portable GPU access, winit
for cross-platform windowing, peniko/kurbo for brush and geometry data, and the text pipeline built on
parley, skrifa, swash, and fontique—to .NET applications. VelloSharp wraps these native crates together
with AccessKit so scene building, surface presentation, input, accessibility, and text
layout share a cohesive managed API while still exposing low-level control over wgpu devices, queues, and surfaces.
Each native package has runtime-specific variants (for example VelloSharp.Native.Vello.win-x64) that are published alongside the platform-agnostic meta-package. When you reference the meta-package, dotnet restore automatically selects the correct RID-specific asset.
The codebase is split into native FFI crates, managed bindings, integration helpers, and sample applications:
- Native FFI crates (under
ffi/):ffi/vello_ffi– wraps the renderer, shader, SVG, Velato/Lottie, text, and wgpu stacks behind a single C ABI, including surface/swapchain helpers for GPU presentation.ffi/peniko_ffi– bridges paint/brush data so gradients, images, and style metadata can be inspected or constructed from managed code.ffi/kurbo_ffi– exposes the geometry primitives used across the stack (affine transforms, Bézier paths, and bounding-box helpers) without pulling in the full Rust curve library.ffi/winit_ffi– forwards windowing, input, and swap-chain negotiation so native event loops can be driven from .NET when desired.ffi/accesskit_ffi– serialises/deserialises AccessKit tree updates and action requests so accessibility data can flow between managed code and platform adapters.ffi/gauges-core– foundational scene hooks for industrial gauges, exposing a shared initialization point for the managed gauge controls.ffi/scada-runtime– SCADA runtime bootstrap layer that will surface alarm/state orchestration to managed dashboards.ffi/editor-core– entry points for the unified visual editor, enabling design-time composition features over the shared runtime.
- Managed assemblies (under
bindings/):VelloSharp- idiomatic C# wrappers for all native exports: scenes, fonts, images, surface renderers, the wgpu device helpers, and theKurboPath/KurboAffineandPenikoBrushutilities.VelloSharp.Windows.Core- shared Windows GPU plumbing (device/queue leasing, diagnostics, swapchain helpers, D3D11 ↔ D3D9 interop) used by WinForms and WPF hosts.VelloSharp.Integration.Wpf- WPF controls (VelloView,VelloNativeSwapChainView) with composition-based rendering, keyed-mutex coordination, diagnostics, and backend switching.VelloSharp.Integration.WinForms- WinForms control hosting that now delegates GPU lifetime management toVelloSharp.Windows.Core.VelloSharp.WinForms.Core- Windows Forms drawing surface abstractions (Graphics,Pen,Brush,Region) powered by the shared renderer.VelloSharp.Skia- a Skia-inspired helper layer that mapsSKCanvas/SKPath-style APIs onto Vello primitives for easier porting of existing SkiaSharp code.VelloSharp.Integration- optional helpers for Avalonia, SkiaSharp interop, and render-path negotiation (ship viadotnet add package VelloSharp.Integration).VelloSharp.Avalonia.Winit- Avalonia host glue that drives the winit-based surface renderer through the managed bindings.VelloSharp.Avalonia.Vello- Avalonia platform abstractions that adapt Vello surfaces and inputs into application-friendly controls.
- Application assemblies (under
src/):VelloSharp.Composition- composition primitives, plotting surfaces, and shared utilities consumed by higher-level dashboards.VelloSharp.ChartData- streaming data buses and sample buffers for real-time charts.VelloSharp.ChartDiagnostics- telemetry collectors and instrumentation helpers for chart runtimes.VelloSharp.ChartRuntime- scheduler, tick sources, and execution helpers shared by chart hosts.VelloSharp.ChartRuntime.Windows- Windows-specific dispatcher, swapchain, and composition integrations for charts.VelloSharp.ChartEngine- animation loops, color utilities, and renderer orchestrators for charts.VelloSharp.Charting- chart composition API, styling, axes, and legend utilities.VelloSharp.Charting.Avalonia- Avalonia UI controls and adapters that host the chart engine.VelloSharp.Charting.WinForms- WinForms chart controls backed by the shared runtime.VelloSharp.Charting.Wpf- WPF chart host controls with accessibility and overlay support.VelloSharp.Gauges- industrial gauge primitives backed by the native gauges runtime.VelloSharp.Scada- SCADA runtime, alarms, and dashboard orchestration services.VelloSharp.TreeDataGrid- interop layer around the native tree data grid renderer.VelloSharp.Editor- managed hooks for the editor core and composition tooling.
The managed packages are designed to be composed as needed. Use the snippets below as a starting point for each package.
- Builds scenes, drives the renderer, and manages images, fonts, and brushes.
- Combine with the native packages to target GPU, CPU, or sparse pipelines.
using System.Numerics;
using VelloSharp;
using var scene = new Scene();
var path = new PathBuilder()
.MoveTo(0, 0)
.LineTo(128, 0)
.LineTo(64, 96)
.Close();
scene.FillPath(path, FillRule.NonZero, Matrix3x2.Identity, RgbaColor.FromBytes(0, 128, 255, 255));
using var renderer = new Renderer(128, 96);
var buffer = new byte[128 * 96 * 4];
var renderParams = new RenderParams(128, 96, RgbaColor.FromBytes(0, 0, 0, 255));
renderer.Render(scene, renderParams, buffer, 128 * 4);- Supplies shared primitives such as
PathBuilder,RenderParams, gradients, and stroke styles. - Acts as the foundation for every other managed package.
using System.Numerics;
using VelloSharp;
var brush = new LinearGradientBrush((0, 0), (0, 200),
GradientStop.At(0f, RgbaColor.FromBytes(255, 0, 0, 255)),
GradientStop.At(1f, RgbaColor.FromBytes(0, 0, 255, 255)));
var path = new PathBuilder()
.MoveTo(32, 32)
.QuadraticTo(128, 0, 224, 128)
.Close();- Exposes raw structs/enums for window handles, colours, brushes, and status codes.
- Useful when interoperating with native surfaces or marshalling custom handles.
using System;
using VelloSharp;
IntPtr hwnd = /* obtain your HWND (for example via Form.Handle) */;
IntPtr hinstance = /* retrieve the module handle associated with that window */;
var win32Handle = SurfaceHandle.FromWin32(hwnd, hinstance);
var descriptor = new SurfaceDescriptor
{
Width = 1920,
Height = 1080,
PresentMode = PresentMode.AutoVsync,
Handle = win32Handle,
};- Provides the P/Invoke entry points used by the GPU renderer (
vello_ffi) and wgpu helpers. - Includes
GpuNativeHelpers.ThrowOnErrorfor consistent error translation.
using System;
using VelloSharp;
var rendererHandle = NativeMethods.vello_renderer_create(800, 600);
if (rendererHandle == IntPtr.Zero)
{
throw new InvalidOperationException(NativeHelpers.GetLastErrorMessage() ?? "Renderer creation failed.");
}
NativeMethods.vello_renderer_destroy(rendererHandle);- Direct bridge to the sparse CPU renderer (
vello_sparse_ffi) without the managed wrapper. - Ideal for embedding the SIMD-aware renderer inside existing native pipelines.
using System;
using VelloSharp;
var context = SparseNativeMethods.vello_sparse_render_context_create(640, 480);
try
{
SparseNativeHelpers.ThrowOnError(
SparseNativeMethods.vello_sparse_render_context_set_aliasing_threshold(context, 4),
nameof(SparseNativeMethods.vello_sparse_render_context_set_aliasing_threshold));
}
finally
{
SparseNativeMethods.vello_sparse_render_context_destroy(context);
}- Wraps text shaping, OpenType features, and variation axes with friendly record structs.
- Complements both the GPU and sparse renderers.
using VelloSharp.Text;
var options = VelloTextShaperOptions.CreateDefault(fontSize: 18f, isRightToLeft: false) with
{
Features = new[] { new VelloOpenTypeFeature("liga", 1) },
VariationAxes = new[] { new VelloVariationAxisValue("wght", 600f) },
};- Provides a HarfBuzzSharp-compatible API surface backed by
VelloSharp.Text, keeping existing text pipelines intact. - Ideal for Avalonia or SkiaSharp integrations that expect
HarfBuzzSharpassemblies while shaping through Vello.
using HarfBuzzSharp;
using var blob = Blob.FromFile("Assets/Fonts/Roboto-Regular.ttf");
using var face = new Face(blob, 0);
using var font = new Font(face);
using var buffer = new Buffer();
buffer.AddUtf8("VelloSharp");
buffer.GuessSegmentProperties();
font.Shape(buffer);- Supplies Avalonia controls (such as
VelloView), render-path negotiation, and cross-platform hosting utilities shared by UI integrations. - Centralises renderer lifecycle management so Skia, Avalonia, and Windows hosts can swap pipelines with one package reference.
using Avalonia;
using VelloSharp.Integration.Avalonia;
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseVelloSkiaTextServices()
.StartWithClassicDesktopLifetime(args);- Adds higher-level GPU utilities, AccessKit helpers, and native library registration.
- Automatically registers native DLLs through
NativeLibraryLoader.
using VelloSharp;
var request = AccessKitActionRequest.FromJson("{\"type\":\"Focus\",\"action_request_id\":1}");
using var document = request.ToJsonDocument();
Console.WriteLine(document.RootElement.GetProperty("type").GetString());- Recreates SkiaSharp-style APIs (
SKSurface,SKCanvas,SKPaint, etc.) backed by Vello scenes. - Perfect when porting Skia drawing code onto the Vello renderer.
using SkiaSharp;
var surface = SKSurface.Create(new SKImageInfo(256, 256));
surface.Canvas.Clear(SKColors.White);
using var paint = new SKPaint { Color = SKColors.DarkOrange, IsAntialias = true };
surface.Canvas.DrawCircle(128, 128, 96, paint);
surface.Flush();- Registers the GPU-enabled backend via module initialisers—no extra code required.
- Once referenced, Skia surfaces render via Vello + wgpu automatically.
using SkiaSharp;
var gpuSurface = SKSurface.Create(new SKImageInfo(1024, 512));
gpuSurface.Canvas.DrawRect(SKRect.Create(0, 0, 1024, 512), new SKPaint { Color = SKColors.CornflowerBlue });
gpuSurface.Flush();- Provides the sparse/CPU backend for the Skia integration.
- Reference alongside
VelloSharp.Skia.Coreto guarantee deterministic software rendering.
using SkiaSharp;
var cpuSurface = SKSurface.Create(new SKImageInfo(400, 200));
cpuSurface.Canvas.DrawText("CPU sparse", 20, 120, new SKPaint { Color = SKColors.Black, TextSize = 48 });
cpuSurface.Flush();- Offers
SkiaRenderBridgeto stream Vello render buffers into Skia bitmaps or surfaces. - Ideal for hybrid dashboards that mix Skia UI with Vello scenes.
using SkiaSharp;
using System.Numerics;
using VelloSharp;
using VelloSharp.Integration.Skia;
using VelloSharp.Rendering;
using var scene = new Scene();
scene.FillPath(new PathBuilder().MoveTo(0, 0).LineTo(256, 0).LineTo(256, 256).Close(), FillRule.NonZero, Matrix3x2.Identity, RgbaColor.Crimson);
using var surface = SKSurface.Create(new SKImageInfo(256, 256));
using var renderer = new Renderer(256, 256);
var renderParams = new RenderParams(256, 256, RgbaColor.FromBytes(0, 0, 0, 255));
SkiaRenderBridge.Render(surface, renderer, scene, renderParams);
surface.Flush();- Hooks the Vello renderer into Avalonia via
AppBuilder.UseVello. - Exposes
VelloPlatformOptionsfor FPS caps, clear colours, and antialiasing.
using Avalonia;
using VelloSharp.Avalonia.Vello;
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseVello(new VelloPlatformOptions { FramesPerSecond = 120 })
.StartWithClassicDesktopLifetime(args);- Registers the winit windowing subsystem for Avalonia (
AppBuilder.UseWinit). - Combine with
VelloSharp.Avalonia.Velloto run entirely on winit + Vello.
using Avalonia;
using VelloSharp.Avalonia.Winit;
AppBuilder.Configure<App>()
.UseWinit()
.StartWithClassicDesktopLifetime(args);VelloCanvasControlexposes the rawScenevia theDrawevent so you can record Vello commands directly in Avalonia layouts.VelloAnimatedCanvasControladds a managed render loop withTotalTime/DeltaTimetracking for kinetic compositions and dashboards.VelloSvgControlkeeps vector artwork crisp by renderingVelloSvgdocuments from streams, strings, or Avalonia resources.
<controls:VelloCanvasControl Draw="OnCanvasDraw"
xmlns:controls="clr-namespace:VelloSharp.Avalonia.Controls;assembly=VelloSharp.Avalonia.Controls"/>private void OnCanvasDraw(object? sender, VelloDrawEventArgs e)
{
var scene = e.Scene;
var transform = e.GlobalTransform;
var bounds = e.Bounds;
var backdrop = new PathBuilder()
.MoveTo(bounds.X, bounds.Y)
.LineTo(bounds.Right, bounds.Y)
.LineTo(bounds.Right, bounds.Bottom)
.LineTo(bounds.X, bounds.Bottom)
.Close();
scene.FillPath(backdrop, FillRule.NonZero, transform, new SolidColorBrush(RgbaColor.FromBytes(16, 22, 35)));
}See samples/AvaloniaVelloControlsSample for a full walkthrough covering custom drawing, animation, and SVG hosting.
- Supplies
VelloGraphicsDevice, swapchain helpers, and diagnostics events for Windows hosts. - Shared foundation for both WinForms and WPF integrations.
using VelloSharp.Windows;
using var device = new VelloGraphicsDevice(1920, 1080);
using var session = device.BeginSession(1920, 1080);
Console.WriteLine($"Surface size: {session.Width}x{session.Height}");- Adds WinForms-oriented helpers such as
VelloBitmapthat wrapSystem.Drawing.Bitmap. - Simplifies producing
ImageBrushpayloads from WinForms painting code.
using System.Drawing;
using VelloSharp.WinForms;
var bitmap = new Bitmap(256, 256, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using var velloBitmap = VelloBitmap.FromBitmap(bitmap);
Console.WriteLine($"Vello image size: {velloBitmap.Width}x{velloBitmap.Height}");- Ships
VelloRenderControl, a drop-in WinForms control with render-loop management. - Attach to
PaintSurfaceto draw scenes on demand or continuously.
using System.Windows.Forms;
using VelloSharp.WinForms.Integration;
var control = new VelloRenderControl { Dock = DockStyle.Fill };
control.PaintSurface += (_, e) =>
{
// Build your scene with e.Session.Scene and render via e.Session.Renderer.
};
var form = new Form { Text = "VelloSharp WinForms" };
form.Controls.Add(control);
Application.Run(form);- Provides
VelloNativeSwapChainViewfor WPF swapchain hosting with GPU/CPU fallbacks. - Exposes events (
PaintSurface,RenderSurface) for custom rendering logic.
<Window x:Class="VelloDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vello="clr-namespace:VelloSharp.Wpf.Integration;assembly=VelloSharp.Integration.Wpf"
Title="VelloSharp WPF" Height="450" Width="800">
<vello:VelloNativeSwapChainView RenderMode="Continuous"
PreferredBackend="Gpu"
RenderSurface="OnRenderSurface" />
</Window>- Integrates Vello rendering into Uno Platform controls and diagnostics tooling.
- Handy when projecting Vello scenes into WinUI or XAML Islands.
using VelloSharp.Uno.Controls;
var panel = new VelloSwapChainPanel();
((IVelloDiagnosticsProvider)panel).DiagnosticsUpdated += (_, e) =>
{
Console.WriteLine($"GPU presentations: {e.Diagnostics.SwapChainPresentations}");
};- Shared
VelloSwapChainPresenter, diagnostics, dispatcher, and AccessKit host helpers reused by WinUI, UWP, Uno, and MAUI bindings. - Targets
net8.0-windows10.0.17763today and ships AppContainer-safe dispatcher abstractions consumed by the UWP control.
using VelloSharp.Windows.Shared.Presenters;
var presenter = new VelloSwapChainPresenter(host, surfaceSource);
presenter.OnLoaded();
presenter.RequestRender();VelloSwapChainControlis a WinUI 3SwapChainPanelthat renders Vello scenes with full GPU acceleration.- See
samples/WinUIVelloGalleryfor an animated example and diagnostics overlay.
<Grid xmlns:vello="using:VelloSharp.Windows.Controls">
<vello:VelloSwapChainControl x:Name="SwapChain"
PreferredBackend="Gpu"
RenderMode="Continuous" />
</Grid>SwapChain.PaintSurface += (_, e) =>
{
var scene = e.Session.Scene;
scene.Reset();
var rect = new PathBuilder()
.MoveTo(0, 0).LineTo(e.Session.Width, 0)
.LineTo(e.Session.Width, e.Session.Height).LineTo(0, e.Session.Height)
.Close();
scene.FillPath(rect, FillRule.NonZero, Matrix3x2.Identity, new RgbaColor(0.2f, 0.3f, 0.65f, 1f));
};VelloSwapChainPanelnow mirrors the WinUI control with D3D12/Vulkan backends, WARP fallback, diagnostics, and AccessKit automation peers for UIA/AppContainer scenarios.- Targets
net8.0-windows10.0.19041(WinAppSDK) today; theuap10.0.19041packaging target remains tracked indocs/winui-uwp-vello-full-gpu-integration-plan.md.
<Grid xmlns:vello="using:VelloSharp.Uwp.Controls">
<vello:VelloSwapChainPanel x:Name="SwapChain"
PreferredBackend="Gpu"
RenderMode="OnDemand" />
</Grid>- Shared composition primitives for dashboards, gauges, and SCADA shells.
- Supplies geometry metrics and layout helpers consumed across the runtime.
using VelloSharp.Composition;
var metrics = new LabelMetrics(width: 120, height: 24, baseline: 18);
Console.WriteLine($"Label metrics {metrics.Width}x{metrics.Height}");- Lock-free data buses for streaming telemetry into chart scenes.
- Works with
Span<T>sources for minimal allocations.
using VelloSharp.ChartData;
var bus = new ChartDataBus(capacity: 4);
bus.Write(new[] { 1.0f, 2.5f, 3.75f });
if (bus.TryRead(out var slice))
{
Console.WriteLine($"Slice items: {slice.ItemCount}");
slice.Dispose();
}- Records frame statistics and exposes them through .NET diagnostics APIs.
- Pipe metrics into
Meter,Activity, or custom sinks.
using System;
using VelloSharp.ChartDiagnostics;
using var collector = new FrameDiagnosticsCollector();
collector.Record(new FrameStats(TimeSpan.FromMilliseconds(4.2), TimeSpan.FromMilliseconds(3.1), TimeSpan.FromMilliseconds(1.0), 128, DateTimeOffset.UtcNow));- The real-time chart renderer built on Vello primitives.
- Provides animation profiles, color spaces, and composition hooks.
using VelloSharp.ChartEngine;
var profile = ChartAnimationProfile.Default with { ReducedMotionEnabled = true };
var color = ChartColor.FromRgb(34, 139, 230);- High-level chart composition API, axes, legends, and styling utilities.
- Builds on the engine to define complete dashboard layouts.
using VelloSharp.Charting.Layout;
var orientation = AxisOrientation.Left;
Console.WriteLine($"Axis orientation: {orientation}");- Avalonia controls (
ChartView) that host the chart engine. - Integrates with Avalonia's styling, input, and dispatcher model.
using VelloSharp.Charting.Avalonia;
var chartView = new ChartView();- Windows Forms control hosting the chart runtime.
- Designed for drop-in use in existing WinForms dashboards.
using System;
using VelloSharp.Charting.WinForms;
[STAThread]
using var control = new ChartView();- WPF
ChartViewthat wires the chart engine into a swapchain-backed control. - Supports accessibility, composition overlays, and backend switching.
using VelloSharp.Charting.Wpf;
var control = new ChartView();- Scheduling primitives, frame tick sources, and render-loop helpers.
- Shared by Avalonia, WinForms, and WPF chart hosts.
using System;
using VelloSharp.ChartRuntime;
var scheduler = new RenderScheduler(TimeSpan.FromMilliseconds(16), TimeProvider.System);- Windows-specific tick sources and dispatcher integrations for charts.
- Provides WinForms and WinUI composition helpers.
using System.Windows.Forms;
using VelloSharp.ChartRuntime.Windows.WinForms;
using var control = new Control();
using var tickSource = new WinFormsTickSource(control);- Managed bridge to the native gauges runtime.
- Ensure the native module is loaded before rendering gauges.
using VelloSharp.Gauges;
GaugeModule.EnsureInitialized();- Initializes the native editor core used by the visual editor shell.
using VelloSharp.Editor;
EditorRuntime.EnsureInitialized();- SCADA dashboards, alarm orchestration, and runtime shell helpers.
using VelloSharp.Scada;
ScadaRuntime.EnsureInitialized();- Native-backed tree data grid model for hierarchical telemetry.
using VelloSharp.TreeDataGrid;
using var model = new TreeDataModel();
model.AttachRoots(new[] { new TreeNodeDescriptor(1, TreeRowKind.Data, 24f, hasChildren: false) });- Samples:
samples/AvaloniaVelloWinitDemo- minimal Avalonia desktop host covering CPU and GPU render paths through the AvaloniaNative/Vello stack.samples/AvaloniaVelloX11Demo- Linux-focused host that locks Avalonia to the X11 platform for backend validation.samples/AvaloniaVelloWin32Demo- Windows host configured for the Win32 platform while exercising the Vello renderer.samples/AvaloniaVelloNativeDemo- macOS host forced onto AvaloniaNative to vet the Vello integration end-to-end.samples/AvaloniaVelloExamples- expanded scene catalogue with renderer option toggles and surface fallbacks.samples/AvaloniaVelloControlsSample- quick tour of the reusable canvas, animation, and SVG controls.samples/AvaloniaVelloSkiaSharpSample- SkiaSharp shim gallery spanning lease-driven surfaces, SKCodec workflows, runtime effect editing, and geometry boolean operations across CPU/GPU backends.samples/AvaloniaSkiaMotionMark- a side-by-side Skia/Vello motion-mark visualiser built on the integration layer.samples/AvaloniaSkiaSparseMotionMarkShim- CPU sparse MotionMark shim that routes Vello scenes through the Velato Skia bridge without touching the GPU backend.samples/MauiVelloGallery- .NET MAUI gallery hosting the newVelloView; GPU animation now runs on Windows and ships in preview on MacCatalyst/iOS/Android via the new Metal/Vulkan presenters. The MAUI handler exposesSuppressGraphicsViewCompositorso applications can disable the fallback Skia compositor while Vello owns the swapchain.samples/VelloSharp.WpfSample- WPF composition host showcasingVelloView, backend toggles, diagnostics binding, and a MotionMark fast-path page driven through the new GPU render-surface API.samples/WinFormsMotionMarkShim- Windows Forms MotionMark GPU shim built atop the shared Windows GPU context, demonstratingVelloRenderControl, theRenderSurfacefast path, backend switching, and DPI-aware swapchain handling.
Run these from the repository root to go from a clean clone to native artifacts and managed binaries.
pwsh -ExecutionPolicy Bypass -File scripts\bootstrap-windows.ps1
pwsh -File scripts\build-native-windows.ps1
dotnet build VelloSharp.sln -c Release
pwsh -File scripts\copy-runtimes.ps1./scripts/bootstrap-macos.sh
./scripts/build-native-macos.sh
dotnet build VelloSharp.sln -c Release
./scripts/copy-runtimes.sh./scripts/bootstrap-linux.sh
./scripts/build-native-linux.sh
dotnet build VelloSharp.sln -c Release
./scripts/copy-runtimes.shEach build-native-* script compiles every FFI crate and stages the libraries under artifacts/runtimes/<rid>/native/. The copy script fans the native payloads into the managed project bin/ folders so the samples can run immediately. On Windows it also mirrors the runtimes into net8.0-windows outputs so the WinForms shim and MotionMark sample work out of the box.
VelloSharp.Integration.Wpf provides a composition-first hosting experience that aligns with the shared Windows GPU stack introduced in VelloSharp.Windows.Core. Key capabilities include:
VelloView, a WPFDecoratorthat renders into aD3DImageusing shared textures, honoursRenderMode,PreferredBackend, andRenderLoopDriver, and exposes a bindableDiagnosticsproperty (frame-rate smoothing, swapchain/device reset counters, keyed mutex contention).VelloNativeSwapChainView, an opt-in HWND swapchain host for diagnostics, exclusive full-screen scenarios, or interop with other DirectX components.- Automatic leasing of the shared GPU context across multiple controls, render suspension based on visibility, window state, or application activation, and keyed-mutex fallbacks with detailed diagnostics sourced from
VelloSharp.Windows.Core. - Both
VelloViewand the WinFormsVelloRenderControlexpose aRenderSurfaceevent (viaVelloSurfaceRenderEventArgs) so advanced callers can feed pre-recorded scenes or custom render graphs straight into the underlyingRendererand target surface.
The WinForms integration now consumes the very same shared Windows GPU primitives, so both VelloRenderControl and VelloView participate in the unified leasing, diagnostics, and asset-copy workflows. Refer to samples/VelloSharp.WpfSample for an end-to-end MVVM-friendly example that binds diagnostics, toggles render backends, and demonstrates clean suspension/resume behaviour.
Bootstrap the native toolchains and Rust before building the FFI crates or running the packaging scripts:
scripts/bootstrap-linux.shinstalls the required build essentials, GPU/windowing headers, and rustup on Debian/Ubuntu, Fedora, Arch, and openSUSE systems (run with sudo or as root).scripts/bootstrap-macos.shensures the Xcode Command Line Tools, Homebrew packages (CMake, Ninja, pkg-config, LLVM, Python), and rustup are installed.scripts/bootstrap-windows.ps1must be executed from an elevated PowerShell session; it installs Visual Studio Build Tools, CMake, Ninja, Git, and rustup via winget/Chocolatey when available.
Each script is idempotent and skips packages that are already present.
The FFI crates (vello_ffi, peniko_ffi, kurbo_ffi, winit_ffi, accesskit_ffi) share the same high-level
dependencies from the vendored Vello workspace. The table summarises the crates you interact with most when
updating or auditing the bindings:
| Dependency | Role in the bindings |
|---|---|
vello |
Core renderer that powers all scene, surface, and GPU/CPU pipelines exposed through vello_ffi. |
wgpu |
Graphics abstraction used to request adapters, devices, and swapchains for the GPU-backed render paths. |
vello_svg & velato |
Asset loaders surfaced via the FFI for SVG scenes and Lottie/After Effects playback. |
peniko & kurbo |
Brush, gradient, and geometry primitives re-exported in managed code for path building. |
skrifa, swash, parley, fontique |
Font parsing, shaping, and layout stack that feeds glyph runs into Vello. |
accesskit & accesskit_winit |
Accessibility tree interchange, shared with the Avalonia integrations. |
winit & raw-window-handle |
Window/event loop abstractions used by winit_ffi and the Avalonia platform glue. |
wgpu-profiler, pollster, futures-intrusive |
Utilities that bridge async renderer calls and surface GPU timings through the bindings. |
VelloSharp ships first-class wrappers for the wgpu API so .NET applications can configure adapters, devices, and
surfaces directly before handing targets to the renderer. The binding layer mirrors the Rust API surface closely:
WgpuInstance,WgpuAdapter,WgpuDevice, andWgpuQueuemap one-to-one with the native objects and expose all safety checks viaIDisposablelifetimes.SurfaceHandle.FromWin32,.FromAppKit,.FromWayland, and.FromXliblet you construct swapchains from any window handle obtained viawinit_ffior Avalonia'sINativePlatformHandleSurface.WgpuSurfaceconfiguration accepts the full backend matrix (DX12, Metal, Vulkan, or GLES) so the renderer can adopt whatever the host platform supports without recompiling native code.WgpuTexture,WgpuCommandEncoder, andWgpuBufferbindings make it feasible to interleave custom compute or upload work with Vello's own render passes when you need advanced scenarios.
The higher-level helpers in VelloSharp.Integration build on these primitives: Avalonia controls negotiate
swapchains through the same APIs, while the profiler hooks exposed by wgpu-profiler surface GPU timings back to
managed code. If you prefer a software path, the bindings can skip wgpu entirely and fall back to CPU rendering
without changing the managed API surface.
- FFI crates –
vello_ffi,peniko_ffi,winit_ffi, andaccesskit_ffiexpose 100% of their exported functions to .NET.kurbo_ffiis feature-complete for the bindings in this repository, with only six geometry helpers intentionally left unbound (seedocs/ffi-api-coverage.md). Native builds are validated across Windows, macOS, Linux, Android, iOS, and WebAssembly RIDs, and they share the samecargofeature flags as upstream Vello. - Managed bindings –
VelloSharpsurfaces the full renderer, scene graph, surface contexts, glyph and image helpers, SVG/Velato decoders, and the wgpu device/surface management APIs. Disposable wrappers and span validators guard the native lifetimes, and theRendererOptions/RenderParamsmirrors keep behaviour in sync with the Rust implementation. - Integration libraries -
VelloSharp.Windows.Corecentralises Windows-specific GPU context leasing, diagnostics, and swapchain/texture interop that are now consumed by both WinForms and WPF.VelloSharp.Integration.Wpfintroduces the composition-basedVelloView, the opt-inVelloNativeSwapChainView, keyed-mutex management, and bindable diagnostics, whileVelloSharp.Integration.WinFormsreuses the same core forVelloRenderControland MotionMark shims. Cross-platform glue continues to live inVelloSharp.Integration(Avalonia helpers, Skia bridges), withVelloSharp.Avalonia.*layering in the winit event loop bridge and Avalonia platform abstractions. - Samples and tooling – the Avalonia demos ship with automated runtime asset copying, configurable frame
pacing, and software/GPU fallbacks.
STATUS.mdand the plans underdocs/track the remaining backlog for surface handles, validation, and additional platform glue. - Packaging –
dotnet packproduces the aggregateVelloSharpNuGet plus theVelloSharp.Native.<rid>runtime packages (including charting, gauges, SCADA runtime, and editor core). The managed package now declares dependencies on the RID-specific native packages so consuming projects restore the correct binaries automatically. Helper scripts inscripts/collect, copy, and repackage the native artifacts for CI and local workflows, and the CI pipeline now packs each platform (Linux, Apple, Android, WASM, Windows) in dedicated jobs before a combine step publishes the merged artifact feed consumed by the managed packaging stage.
Install the Rust toolchain (Rust 1.86 or newer) before building the managed projects. The VelloSharp
MSBuild project now drives cargo build for every required native crate (accesskit_ffi, vello_ffi,
kurbo_ffi, peniko_ffi, and winit_ffi) for the active .NET runtime identifier and configuration.
Running any of the following commands produces the native artifacts and copies them to the managed output
directory under runtimes/<rid>/native/ (and alongside the binaries for convenience):
dotnet build bindings/VelloSharp/VelloSharp.csproj
dotnet build samples/AvaloniaVelloWinitDemo/AvaloniaVelloWinitDemo.csproj
dotnet run --project samples/AvaloniaVelloWinitDemo/AvaloniaVelloWinitDemo.csprojBy default the current host target triple is used. To build for an alternate RID, pass -r <rid> when
invoking dotnet build or set RuntimeIdentifier in your consuming project; make sure the corresponding
Rust target is installed (rustup target add <triple>). The produced files are named:
| RID | Triple | Artifact |
|---|---|---|
win-x64 |
x86_64-pc-windows-msvc |
vello_ffi.dll |
win-arm64 |
aarch64-pc-windows-msvc |
vello_ffi.dll |
osx-x64 |
x86_64-apple-darwin |
libvello_ffi.dylib |
osx-arm64 |
aarch64-apple-darwin |
libvello_ffi.dylib |
linux-x64 |
x86_64-unknown-linux-gnu |
libvello_ffi.so |
linux-arm64 |
aarch64-unknown-linux-gnu |
libvello_ffi.so |
Note: The native crates enable the
stdfeature forkurbo/penikointernally so the FFI layer can build against clean upstream submodules. If you invokecargo buildmanually for a specific crate, pass the same feature flags (or build through theVelloSharpproject) to avoidkurbo requires the \std` feature` errors.
The VelloSharp project is now NuGet-ready. Packing requires the native artifacts for each
runtime you want to redistribute:
- Build the native libraries (e.g., via CI) and collect them under a directory layout such as
runtimes/<rid>/native/<library>. - Set the
VelloNativeAssetsDirectoryproperty to that directory when invokingdotnet pack(for example,dotnet pack bindings/VelloSharp/VelloSharp.csproj -c Release -p:VelloSkipNativeBuild=true -p:VelloNativeAssetsDirectory=$PWD/artifacts/runtimes). - Optionally verify all runtimes by keeping the default
VelloRequireAllNativeAssets=true, or relax the check with-p:VelloRequireAllNativeAssets=falsewhen experimenting locally.
The generated .nupkg and .snupkg files are emitted under artifacts/nuget/.
CI publishes a managed-only VelloSharp package by passing -p:VelloIncludeNativeAssets=false. Native binaries are
distributed separately via the per-FFI runtime packages (VelloSharp.Native.AccessKit.*, Kurbo, Peniko, Vello,
VelloSparse, Winit). Applications should add whichever RIDs they need, for example:
dotnet add package VelloSharp.Native.Vello.win-x64 --prerelease
dotnet add package VelloSharp.Native.Winit.win-x64 --prereleaseInside this repository the samples reference the Windows x64 packaging projects directly (e.g.
packaging/VelloSharp.Native.Vello/VelloSharp.Native.Vello.csproj) so the native assets flow into bin/<TFM>/runtimes/.
Adjust the referenced RIDs if you are developing on a different platform.
Per-FFI packaging projects live under packaging/VelloSharp.Native.<Ffi>/. Each RID-specific project wraps
the corresponding native library into a standalone NuGet package so downstream applications can pull in only the
assets they need. A typical workflow looks like this:
- Build the native crates for the desired RID(s) (
dotnet build -r osx-arm64 bindings/VelloSharp/VelloSharp.csproj). - Run
./scripts/copy-runtimes.shto sync the generated artifacts into both sample outputs and eachpackaging/VelloSharp.Native.<Ffi>/runtimes/<rid>/nativedirectory. - Pack the runtime you care about, e.g.
dotnet pack packaging/VelloSharp.Native.Vello/VelloSharp.Native.Vello.osx-arm64.csproj. - Reference the RID-specific packages from your application.
The packaging props also emit fallback copies (for example osx alongside osx-arm64) so that RID roll-forward
continues to work when .NET probes runtimes/<baseRid>/native. The sample projects reference the Windows x64 packages so
the native binaries land in bin/<TFM>/runtimes/ without additional MSBuild logic; switch the RID suffixes if you are
developing on another platform.
Reference the VelloSharp project from your solution or publish it as a NuGet package.
A minimal render loop looks like:
using System.Numerics;
using VelloSharp;
using var renderer = new Renderer(width: 1024, height: 768);
using var scene = new Scene();
var path = new PathBuilder();
path.MoveTo(100, 100).LineTo(700, 200).LineTo(420, 540).Close();
scene.FillPath(path, FillRule.NonZero, Matrix3x2.Identity, RgbaColor.FromBytes(0x47, 0x91, 0xF9));
var buffer = new byte[1024 * 768 * 4];
renderer.Render(
scene,
new RenderParams(1024, 768, RgbaColor.FromBytes(0x10, 0x10, 0x12))
{
Format = RenderFormat.Bgra8,
},
buffer,
strideBytes: 1024 * 4);buffer now contains BGRA pixels ready for presentation via SkiaSharp, Avalonia or any other API; omit the assignment to Format to receive RGBA output instead.
The native layer now ships with optional helpers for common scene sources and GPU surfaces. All of them
round-trip through the managed API so you can mix and match with the existing Scene primitives.
VelloSvg uses the bundled vello_svg parser to ingest an SVG asset and append the generated scene graph to any
existing Scene:
using var scene = new Scene();
using var svg = VelloSvg.LoadFromFile("Assets/logo.svg", scale: 1.5f);
svg.Render(scene);
// Optionally apply additional transforms or authoring on the scene afterwards.Use LoadFromUtf8 for in-memory buffers and query the intrinsic size via the Size property to fit your layout.
The velato submodule provides high-quality Lottie playback. The managed
wrappers expose composition metadata and let you render into an existing Scene or build a standalone one per
frame:
using var composition = VelatoComposition.LoadFromFile("Assets/intro_lottie.json");
using var renderer = new VelatoRenderer();
var info = composition.Info; // duration, frame rate, target size
using var scene = renderer.Render(composition, frame: 42);
// Blend multiple compositions into a shared scene
renderer.Append(scene, composition, frame: 43, alpha: 0.7);When paired with wgpu, Vello can target swapchain textures directly. The managed side wraps the core handles so
you can drive the pipeline from your own windowing layer:
using var instance = new WgpuInstance();
var surfaceDescriptor = new SurfaceDescriptor
{
Width = width,
Height = height,
PresentMode = PresentMode.AutoVsync,
Handle = SurfaceHandle.FromWin32(hwnd), // or FromAppKit / FromWayland / FromXlib
};
using var surface = WgpuSurface.Create(instance, surfaceDescriptor);
using var adapter = instance.RequestAdapter(new WgpuRequestAdapterOptions
{
PowerPreference = WgpuPowerPreference.HighPerformance,
CompatibleSurface = surface,
});
using var device = adapter.RequestDevice(new WgpuDeviceDescriptor
{
Limits = WgpuLimitsPreset.Default,
});
using var renderer = new WgpuRenderer(device);
var surfaceTexture = surface.AcquireNextTexture();
using (var view = surfaceTexture.CreateView())
{
renderer.Render(scene, view, new RenderParams(width, height, baseColor)
{
Format = RenderFormat.Bgra8,
});
}
surfaceTexture.Present();
surfaceTexture.Dispose();All handles are disposable and throw once released, making it easy to integrate with using scopes. See the
Avalonia helpers below for a higher-level example.
Scene.FillPath and Scene.StrokePath accept the Brush hierarchy, enabling linear/radial gradients and image brushes in addition to solid colors. Example:
var brush = new LinearGradientBrush(
start: new Vector2(0, 0),
end: new Vector2(256, 0),
stops: new[]
{
new GradientStop(0f, RgbaColor.FromBytes(255, 0, 128)),
new GradientStop(1f, RgbaColor.FromBytes(0, 128, 255)),
});
scene.FillPath(path, FillRule.NonZero, Matrix3x2.Identity, brush);Layer management is accessible through Scene.PushLayer, Scene.PushLuminanceMaskLayer, and Scene.PopLayer, giving full control over blend modes and clip groups.
Tip: When interoperating with native paint data, wrap
PenikoBrushhandles withBrush.FromPenikoBrushto reuse gradients or solid fills produced viapeniko_ffi.
KurboPath, KurboAffine, and the rest of the managed geometry types are thin wrappers over kurbo_ffi. They
let you construct Bézier paths, apply affine transforms, and query bounds without pulling the full Rust library
into your application:
using var path = new KurboPath();
path.MoveTo(0, 0);
path.LineTo(128, 64);
path.CubicTo(256, 64, 256, 256, 128, 256);
path.Close();
var bounds = path.GetBounds();
path.ApplyAffine(KurboAffine.FromMatrix3x2(Matrix3x2.CreateRotation(MathF.PI / 4)));
var elements = path.GetElements();These helpers surface a managed-friendly representation of the geometry used throughout Vello without introducing additional allocations in the hot path.
Use Image.FromPixels and Scene.DrawImage to render textures directly. Glyph runs can be issued via Scene.DrawGlyphRun, which takes a Font, a glyph span, and GlyphRunOptions for fill or stroke rendering.
Renderer exposes an optional RendererOptions argument to select CPU-only rendering or limit the available anti-aliasing pipelines at creation time.
VelloSharp.Windows.Core, VelloSharp.WinForms.Core, and VelloSharp.Integration.WinForms bring the Vello renderer to Windows Forms.
VelloSharp.Windows.Corecentralises HWND-compatiblewgpudevice management, swapchain configuration, staging buffers, and diagnostics for Windows targets.VelloSharp.WinForms.Coremirrors familiarSystem.Drawingdrawing types (Graphics,Pen,Brush,Region,Bitmap,Font,StringFormat) on top of Vello scenes recorded throughVelloGraphicsandVelloGraphicsSession.VelloSharp.Integration.WinFormsships theVelloRenderControl, sharedWindowsGpuContextswapchain management, DPI-aware sizing, diagnostics, and automatic CPU fallbacks when the device is lost.
using VelloSharp.Windows;
using VelloSharp.WinForms;
using VelloSharp.WinForms.Integration;
var renderControl = new VelloRenderControl
{
Dock = DockStyle.Fill,
PreferredBackend = VelloRenderBackend.Gpu,
DeviceOptions = new VelloGraphicsDeviceOptions
{
Format = RenderFormat.Bgra8,
ColorSpace = WindowsColorSpace.Srgb,
},
};
renderControl.PaintSurface += (sender, e) =>
{
var scene = e.Session.Scene;
var path = new PathBuilder();
path.MoveTo(32, 32).LineTo(320, 96).LineTo(160, 240).Close();
scene.FillPath(path, FillRule.NonZero, Matrix3x2.Identity, RgbaColor.FromBytes(0x47, 0x91, 0xF9));
};
Controls.Add(renderControl);Set PreferredBackend to VelloRenderBackend.Cpu for software rendering, and reuse DeviceOptions across controls to tune swapchain format, color space, MSAA, and diagnostics. A single WindowsGpuContext instance is shared and reference-counted so multiple controls can share the same wgpu device safely.
samples/WinFormsMotionMarkShim demonstrates continuous animation, backend switching, and DPI-aware resizing on top of these APIs. dotnet add package VelloSharp.Integration.WinForms pulls in both WinForms assemblies (target net8.0-windows with <UseWindowsForms>true</UseWindowsForms>) and transitively restores the required native runtimes.
Avalonia support is split across four managed packages:
VelloSharp.Integration– reusable controls, render-path helpers, and utility services shared by Avalonia and SkiaSharp hosts.VelloSharp.Avalonia.Winit– a winit-based windowing backend that plugs into Avalonia'sIWindowingPlatform, dispatcher, clipboard, and screen services.VelloSharp.Avalonia.Vello– a Vello-powered rendering backend that implements Avalonia's platform render interfaces on top ofwgpu.VelloSharp.Avalonia.Controls– high-level Avalonia controls (canvas, animation surface, SVG presenter) built on the Vello renderer.
Opt in to the stack by extending your AppBuilder:
AppBuilder.Configure<App>()
.UseWinit()
.UseVello()
.WithInterFont();UseWinit registers a single-threaded winit event loop, raw handle surfaces, and clipboard/screen implementations for Windows and macOS today, with Wayland/X11 plumbing staged next. Unsupported capabilities such as tray icons, popups, or embedded top levels currently throw NotSupportedException so consumers can branch cleanly.
UseVello wires Avalonia's composition pipeline to the Vello renderer. It negotiates swapchains via wgpu, surfaces the profiler hooks, and falls back to the software VelloView path whenever swapchain creation is denied. The renderer shares the same accesskit_ffi bridge as the windowing layer, keeping accessibility metadata flowing into screen readers.
VelloSharp.Integration includes a reusable VelloView control that owns the renderer, scene, and
backing WriteableBitmap. Subscribe to RenderFrame or override OnRenderFrame to populate the
scene — the control manages size changes, render-loop invalidation, and stride/format negotiation for you:
using VelloSharp.Integration.Avalonia;
public sealed class DemoView : VelloView
{
public DemoView()
{
RenderParameters = RenderParameters with
{
BaseColor = RgbaColor.FromBytes(18, 18, 20),
Antialiasing = AntialiasingMode.Msaa8,
};
RenderFrame += context =>
{
var scene = context.Scene;
scene.Reset();
// build your Vello scene here
};
}
}Set IsLoopEnabled to false if you prefer to drive the control manually via RequestRender().
The Avalonia integration now drives the shared wgpu wrappers. VelloSurfaceView tries to obtain a native
platform handle (HWND, NSWindow, and, in a future update, Wayland/X11). When the handle is available it creates
a WgpuInstance, WgpuSurface, and WgpuRenderer, rendering directly into swapchain textures and presenting
them via wgpu. If the platform cannot provide a compatible handle, or surface configuration fails, the control
transparently falls back to the software VelloView path. You can continue to update the scene through
RenderFrame without special casing either mode.
Applications that need deeper control can replicate the same sequence manually: construct a SurfaceDescriptor
from a window handle with SurfaceHandle.FromWin32, .FromAppKit, .FromWayland, or .FromXlib, configure the
surface with your preferred PresentMode, and call WgpuRenderer.Render with the acquired texture view. The
control exposes RendererOptions, RenderParameters, and IsLoopEnabled so you can tune anti-aliasing, swapchain
formats, or frame pacing at runtime.
| Capability | Winit + Vello stack | Built-in Avalonia stack |
|---|---|---|
| Platform coverage | Windows and macOS shipping today, with X11/Wayland support staged; exposes RawWindowHandle values for swapchains. |
Win32, AppKit, X11, and Wayland backends ship with the framework today. |
| Windowing features | Single dispatcher thread with one top-level window; tray icons, popups, and embedding report NotSupportedException. |
Mature support for multiple windows, popups, tray icons, and embedding scenarios. |
| Rendering backend | Vello on top of wgpu (DX12, Metal, Vulkan, or GLES) with automatic fallbacks to the software path. |
Skia renderer with GPU backends (OpenGL, Vulkan, Metal, ANGLE/DirectX) and CPU fallback managed by Avalonia's compositor. |
| Swapchain control | Applications choose surface formats and PresentMode via WgpuSurface descriptors. |
Swapchain setup is internal to Avalonia; apps rely on compositor defaults and the Skia backend configuration. |
| GPU extensibility | Full wgpu device/queue access lets you mix custom compute or capture passes with Vello rendering. |
GPU access limited to compositor hooks; extending beyond Skia requires custom native backends. |
| Accessibility | AccessKit updates flow through winit_ffi so assistive tech stays in sync. |
Platform accessibility stacks (UIA/AX/AT-SPI) driven by Avalonia's native backends. |
Run the Avalonia Vello desktop samples to exercise the platform-specific hosting stacks end-to-end:
dotnet run --project samples/AvaloniaVelloWinitDemo/AvaloniaVelloWinitDemo.csproj
dotnet run --project samples/AvaloniaVelloX11Demo/AvaloniaVelloX11Demo.csproj
dotnet run --project samples/AvaloniaVelloWin32Demo/AvaloniaVelloWin32Demo.csproj
dotnet run --project samples/AvaloniaVelloNativeDemo/AvaloniaVelloNativeDemo.csprojThe Avalonia examples catalogue continues to showcase the controls on the stock platforms:
dotnet run --project samples/AvaloniaVelloExamples/AvaloniaVelloExamples.csproj
dotnet run --project samples/AvaloniaVelloControlsSample/AvaloniaVelloControlsSample.csprojBoth samples include project references to the VelloSharp.Native.*.win-x64 packaging projects. Run
scripts/pack-native-nugets (or copy prebuilt assets into the packaging/.../runtimes directories) before restoring so the
native DLLs land under bin/<TFM>/runtimes/. Install the Rust toolchain only if you plan to rebuild those artifacts.
VelloSharp.Skia provides a compatibility layer that mirrors the public API of SkiaSharp types while delegating
all rendering to the Vello engine. The shim keeps porting friction low by re-implementing familiar entry points
such as SKCanvas, SKPaint, SKPath, SKImage, and SKTypeface on top of the managed bindings. Highlights:
- Existing SkiaSharp rendering code can be recompiled by switching
using SkiaSharp;tousing VelloSharp.Skia;. - The shim exposes
SKSurface.Create(VelloSurface surface)extensions so Vello swapchains or the AvaloniaVelloSurfaceViewrender directly into SkiaSharp abstractions. - Text and font services integrate with the same
parley/skrifa/swashstack used by the native renderer. CallAppBuilder.UseVelloSkiaTextServices()when bootstrapping Avalonia to replace Skia text backends with the shimmed implementations. - Recording APIs (
SKPictureRecorder,SKPicture) emit Vello scenes, letting you replay existing Skia display lists through the Vello renderer.
📚 A per-type comparison between the SkiaSharp APIs, their shimmed counterparts, and the underlying VelloSharp building blocks lives in
docs/skiasharp-shim-api-coverage.md. Update that matrix alongside any new shim work.
SKCanvasnow mirrors core SkiaSharp commands:SaveLayer(...),RestoreToCount(...),QuickReject(...), clip variants (ClipRect,ClipRoundRect,ClipPath), and shape drawing helpers such asDrawLine,DrawRect,DrawRoundRect,DrawOval, andDrawPaint/DrawColor.- Blending metadata flows through
SKPaint.BlendMode, matching SkiaSharp’s enumeration while mapping to Vello layer blends under the hood. - Geometry helpers (
SKRoundRect,SKRect.Empty,SKPath.GetBounds()) provide the data needed for interoperability with Skia-centric code bases.
ℹ️
SKClipOperation.Differenceis not yet implemented. The shim treats all clip calls asIntersectuntil Vello exposes a compatible difference/composition path.
| Category | Shim status | Notes |
|---|---|---|
Canvas & recording (SKSurface, SKCanvas, SKPictureRecorder, SKPicture) |
âś… Core drawing, save/restore, picture replay on Vello scenes. | Remaining work: richer blend modes, scene serialization. |
Paint & shaders (SKPaint, SKShader, gradients, blend modes) |
Advanced Skia blend/composite modes collapse to SrcOver today. |
|
Raster resources (SKImage, SKBitmap, SKCodec, SKImageInfo) |
JPEG/WebP decode and colour-space handling tracked in shim backlog. | |
Geometry (SKPath, SKRoundRect, SKRect, SKMatrix) |
âś… Path building and transforms forwarded to Vello. | Region combination helpers still pending. |
Text (SKTypeface, SKFont, SKFontManager, SKTextBlob) |
Font hinting/edging toggles stored but not yet honoured by renderer. | |
Backend selection (SkiaBackendService, CPU/GPU adapters) |
âś… Module initializers register Vello GPU + sparse CPU pipelines. | Ensure the correct package is referenced for your target backend. |
Minimal example creating a shim surface and drawing into it:
using VelloSharp;
using VelloSharp.Skia;
var info = new SKImageInfo(512, 512, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
using var surface = SKSurface.Create(info);
var canvas = surface.Canvas;
canvas.Clear(new SKColor(0x12, 0x12, 0x14));
using var paint = new SKPaint
{
Color = new SKColor(0x47, 0x91, 0xF9),
IsAntialias = true,
};
canvas.DrawCircle(256, 256, 200, paint);
using var renderer = new Renderer((uint)info.Width, (uint)info.Height);
var renderParams = new RenderParams(
(uint)info.Width,
(uint)info.Height,
RgbaColor.FromBytes(0x12, 0x12, 0x14))
{
Format = RenderFormat.Bgra8,
};
var stride = info.RowBytes;
var pixels = new byte[stride * info.Height];
renderer.Render(surface.Scene, renderParams, pixels, stride);pixels now contains BGRA output that you can upload to textures or pass to existing SkiaSharp consumers. For
zero-copy presentation, pair the shim with VelloSharp.Integration.Skia.SkiaRenderBridge.Render(surface, renderer, surface.Scene, renderParams) so Vello writes straight into SKSurface/SKBitmap instances.
VelloSharp.Integration.Skia.SkiaRenderBridge renders straight into SKBitmap or SKSurface
instances and picks the correct render format based on the underlying color type and stride:
using SkiaSharp;
using VelloSharp.Integration.Skia;
void RenderToSurface(SKSurface surface, Renderer renderer, Scene scene, RenderParams renderParams)
{
SkiaRenderBridge.Render(surface, renderer, scene, renderParams);
surface.Canvas.Flush();
}
void RenderToBitmap(SKBitmap bitmap, Renderer renderer, Scene scene, RenderParams renderParams)
=> SkiaRenderBridge.Render(bitmap, renderer, scene, renderParams);The helper inspects the target stride and format, and falls back to an intermediate bitmap when Skia does not expose CPU pixels for GPU-backed surfaces.
For advanced scenarios you can work directly with raw buffers using
VelloSharp.Integration.Rendering.VelloRenderPath:
Span<byte> span = GetBuffer();
var descriptor = new RenderTargetDescriptor((uint)width, (uint)height, RenderFormat.Bgra8, stride);
VelloRenderPath.Render(renderer, scene, span, renderParams, descriptor);The descriptor validates stride and size, while Render adjusts RenderParams to the negotiated format
before invoking the GPU or CPU pipeline.
The repository includes helper scripts that wire up the CI flow and simplify local builds. All scripts emit
artifacts under artifacts/ and are safe to combine with dotnet build/cargo invocations.
scripts/build-native-macos.sh [target] [profile] [sdk] [rid]– builds all FFI crates for macOS/iOS targets (Vello, Peniko, Kurbo, AccessKit, Winit). Pass an Apple SDK name (for examplemacosxoriphoneos) to compile against a specific SDK, and optionally override the runtime identifier. Defaults tox86_64-apple-darwininreleasemode.scripts/build-native-linux.sh [target] [profile] [rid]– cross-compiles the native crates for GNU/Linux platforms, defaulting tox86_64-unknown-linux-gnu. Supplyaarch64-unknown-linux-gnuto produce the ARM64 variant.scripts/build-native-windows.ps1 [target] [profile] [rid]– a PowerShell helper for the Windows MSVC builds. Run from PowerShell or pwsh. Automatically maps the target triple towin-x64/win-arm64unless a RID is provided.scripts/build-native-android.sh [target] [profile] [rid]– targets Android via the NDK and builds the entire FFI set. RequiresANDROID_NDK_HOMEand adds the toolchain binaries toPATHbefore callingcargo.scripts/build-native-wasm.sh [target] [profile] [rid]– compiles the WebAssembly and static library variants for the FFI crates targetingwasm32-unknown-unknown.
All build scripts copy the produced library into artifacts/runtimes/<rid>/native/, making the payload immediately
available to packaging steps.
scripts/collect-native-artifacts.sh [source-dir] [dest-dir]– normalises arbitrary build outputs into theruntimes/<rid>/native/layout by scanning fornativefolders and copying their contents into the destination. Used by CI to gather per-RID outputs before packing.scripts/copy-runtimes.sh [artifacts-dir] [targets…]/scripts/copy-runtimes.ps1 [artifactsDir] [targets…]– copies the assembled runtime folder into project outputs and sample applications. The script defaults to propagating assets intoDebug/Releasenet8.0builds for the library, integrations, and samples, but you can override the target projects, configurations, or frameworks viaCOPY_CONFIGURATIONS/COPY_TARGET_FRAMEWORKS.scripts/pack-native-nugets.sh [runtimes-dir] [output-dir]/scripts/pack-native-nugets.ps1 [runtimesDir] [outputDir]– iterates the collected runtimes and packs the correspondingVelloSharp.Native.<rid>NuGet packages. Each package simply embeds thenativefolder for its RID.scripts/pack-managed-nugets.sh [output-dir] [native-feed]/scripts/pack-managed-nugets.ps1 [nugetOutput] [nativeFeed]– builds the managed projects inRelease, registers a temporary NuGet source pointing at the native packages, and packs the aggregateVelloSharpNuGet withVelloUseNativePackageDependencies=true. Run this afterpack-native-nugets.sh/pack-native-nugets.ps1to produce a coherent set of packages underartifacts/nuget/.scripts/remove-runtimes.sh [targets…]/scripts/remove-runtimes.ps1 [targets…]– deletes copied runtime folders from the default build outputs (or the ones supplied throughREMOVE_RUNTIMES_CONFIGURATIONS/REMOVE_RUNTIMES_TARGET_FRAMEWORKS), keeping local trees tidy between packaging runs.
scripts/run-integration-tests.sh [options]/scripts/run-integration-tests.ps1 [-Configuration <cfg> [-Framework <tfm>] [-Platform <linux|macos|windows>] [--ManagedOnly] [--NativeOnly]]– runs the managed and native integration projects for the requested platform (defaults to the host OS). Specify--platform/-Platformto override the detection,--configuration/-Configurationto pick Debug or Release,--framework/-Frameworkto constrain the target TFM, and--managed-onlyor--native-onlyto focus on a single set of projects.
ffi/vello_ffi: Rust source for the native shared library.ffi/*_ffi: Companion crates exposing AccessKit, Kurbo, Peniko, and Winit bindings consumed by the managed layer.VelloSharp: C# wrapper library withScene,Renderer, and path-building helpers.VelloSharp.Integration: optional Avalonia and Skia helpers with render-path negotiation utilities.samples/AvaloniaVelloWinitDemo: Avalonia desktop sample that exercises the bindings through the AvaloniaNative/Vello path.samples/AvaloniaVelloExamples: showcases the expanded scene catalogue on Avalonia with GPU fallback logic.samples/AvaloniaVelloControlsSample: demonstrates the reusable VelloSharp Avalonia controls (canvas, animation, SVG).samples/AvaloniaVelloSkiaSharpSample: end-to-end SkiaSharp shim gallery covering runtime effects, SKCodec IO, boolean geometry, and CPU/GPU backend switching atop the Vello lease pipeline.extern/vello: upstream renderer sources (core crate, sparse strips, shaders, and examples).extern/kurbo: geometry primitives consumed bykurbo_ffiand Vello.extern/peniko: brush/image utilities re-exported throughextern/peniko_shim.extern/peniko_shim: compatibility shim that preserves the legacypenikocrate API surface.extern/velato: submodule that powers the Lottie/After Effects pipeline.extern/vello_svg: submodule responsible for SVG parsing.extern/wgpu: vendored subset of wgpu used by the FFI for portable GPU access.extern/winit: upstream windowing stack used by the native event-loop bridge.
The entire repository—including the managed bindings, native FFI crates, integrations, and samples—is distributed
under the MIT License. NuGet packages produced via dotnet pack ship with the same MIT license text (LICENSE) so
the published artifacts match the source tree.
To honour upstream obligations, the packages also embed the MIT/Apache-2.0 notices from the Linebender components the
FFI layer depends on (vello, kurbo, peniko, wgpu, etc.). Vendored submodules retain their original licenses—
refer to each directory for the exact terms.