HelixToolkit.Nex.Avalonia
1.2.0
dotnet add package HelixToolkit.Nex.Avalonia --version 1.2.0
NuGet\Install-Package HelixToolkit.Nex.Avalonia -Version 1.2.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="HelixToolkit.Nex.Avalonia" Version="1.2.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="HelixToolkit.Nex.Avalonia" Version="1.2.0" />
<PackageReference Include="HelixToolkit.Nex.Avalonia" />
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add HelixToolkit.Nex.Avalonia --version 1.2.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: HelixToolkit.Nex.Avalonia, 1.2.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package HelixToolkit.Nex.Avalonia@1.2.0
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=HelixToolkit.Nex.Avalonia&version=1.2.0
#tool nuget:?package=HelixToolkit.Nex.Avalonia&version=1.2.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
# HelixToolkit.Nex.Avalonia
HelixToolkit.Nex.Avalonia is a C# package that integrates the Vulkan-native HelixToolkit.Nex 3D engine with [Avalonia](https://avaloniaui.net/) applications. It hosts the engine's offscreen Vulkan output inside the Avalonia visual tree on both **Windows** and **Linux**, presenting it through Avalonia's composition GPU-interop layer rather than a WinUI-style `SwapChainPanel` or DXGI swap chain.
## Overview
The engine renders offscreen with Vulkan; each platform shares that output into the Avalonia compositor and presents it through a `CompositionDrawingSurface` driven by `ICompositionGpuInterop` (the same interop surface used by the AvaloniaUI `GpuInterop/D3DDemo` sample).
Two platform composition paths share a single control:
- **Windows** — the engine renders into a D3D11 shared NT-handle texture (created by `SharedTextureFactory`, imported into Vulkan via `VulkanExternalMemoryImporter`). The same NT handle is imported into the Avalonia compositor, and engine writes / composition reads are serialized with keyed-mutex synchronization.
- **Linux** — the engine renders into a Vulkan image whose device memory is exported as external memory (opaque-fd / dma-buf). The exported POSIX file descriptor is imported into the Avalonia Vulkan compositor, and an exported Vulkan semaphore serializes engine writes against composition reads. No OpenGL/EGL or software-composition fallback is used.
The control reuses the shared partial-class viewport logic (`HelixToolkit.Nex.Interop.PlatformShared`) under the `HxAvalonia` compilation symbol — exactly the way the WPF and WinUI hosts do — so input handling, the render loop, and the bindable-property definitions stay consistent across hosts. An Avalonia `StyledProperty` adapter maps the shared WPF/WinUI dependency-property vocabulary onto Avalonia's property system.
The `Engine` is externally owned and shared across viewports; the control creates and owns only its per-viewport `RenderContext` and interop resources, and never disposes the `Engine`.
Key concepts:
- **Composition GPU interop** — engine output is imported into the compositor via `ICompositionGpuInterop` and shown in a `CompositionDrawingSurface`.
- **Platform bridges** — a single `IEngineOutputBridge` abstraction with a Windows shared-texture implementation and a Linux external-memory implementation.
- **Buffered output** — `BufferedEngineOutputBridge` rotates over several independent single-buffer bridges to decouple engine render from compositor read, improving frame rate.
- **Shared partial class** — one `HelixViewport` behavior across WPF/WinUI/Avalonia, selected by the `HxAvalonia` symbol.
- **StyledProperty adapter** — thin shims (`HelixProperty`, `DependencyProperty`, ...) so the shared property definitions compile under Avalonia.
## Key Types
| Type | Description |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `HelixViewport` | The Avalonia control that hosts the HelixToolkit.Nex engine output. |
| `HelixProperty` | Registers bindable properties on Avalonia's `StyledProperty` system with a WPF/WinUI-style signature. |
| `DependencyProperty` | Non-generic handle wrapping an `AvaloniaProperty` plus its change callback (adapter shim). |
| `CompositionSurfacePresenter` | Avalonia `Control` that presents the shared image via `CompositionDrawingSurface` + `ICompositionGpuInterop`. |
| `IEngineOutputBridge` | Abstraction over the per-viewport shared output resource and its synchronization. |
| `BufferedEngineOutputBridge` | Rotates over several independent single-buffer bridges to improve frame rate by decoupling engine render from compositor read. |
| `WindowsSharedTextureBridge` | Windows path: D3D11 shared NT-handle texture imported into Vulkan, keyed-mutex sync. |
| `LinuxExternalMemoryBridge` | Linux path: exportable Vulkan image (opaque-fd/dma-buf) with an exported semaphore. |
| `SharedImageDescription` | Platform-neutral description of the shared image passed to `ICompositionGpuInterop`. |
| `ISurfaceUpdateSync` | Abstracts the compositor surface update (keyed mutex on Windows, semaphores on Linux). |
### Bindable properties on `HelixViewport`
| Property | Type | Default | Description |
| -------------------- | --------------------- | -------- | --------------------------------------------------- |
| `Engine` | `Engine?` | `null` | The externally-owned engine that renders the scene. |
| `ViewportClient` | `IViewportClient?` | `null` | Supplies per-frame camera and scene data. |
| `CameraController` | `ICameraController?` | `null` | Translates pointer input into camera movement. |
| `RotateMouseButton` | `ViewportMouseButton` | `Left` | Mouse button that rotates the camera. |
| `PanMouseButton` | `ViewportMouseButton` | `Middle` | Mouse button that pans the camera. |
| `PointerRingEnabled` | `bool` | `false` | Toggles the on-screen pointer ring overlay. |
## Usage Examples
### Hosting the viewport in XAML
Add the namespace and bind the engine, viewport client, camera controller, and pointer-ring flag:
```xml
<Window
xmlns="https://github.com/avaloniaui"
xmlns:hx="clr-namespace:HelixToolkit.Nex.Avalonia;assembly=HelixToolkit.Nex.Avalonia">
<Grid>
<hx:HelixViewport
Engine="{Binding Engine}"
ViewportClient="{Binding FlyClient}"
CameraController="{Binding FlyCameraController}"
PointerRingEnabled="{Binding IsPointerRingEnabled, Mode=OneWay}" />
<Border HorizontalAlignment="Left" VerticalAlignment="Top" Margin="16"
Background="#CC1E1E2A" CornerRadius="8" Padding="12">
<TextBlock Text="Avalonia Interop" Foreground="White" />
</Border>
</Grid>
</Window>
Building a platform-appropriate engine
The Vulkan context must be created with the external-memory feature that matches the host platform:
#if WINDOWS
using var d3d11 = new D3D11DeviceManager();
IContext context = VulkanBuilder.CreateHeadless(new VulkanContextConfig
{
EnableExternalMemoryWin32 = true, // Windows: D3D11 shared-texture interop
RequiredDeviceLuid = d3d11.AdapterLuid, // pick the same adapter as D3D11
});
#else
IContext context = VulkanBuilder.CreateHeadless(new VulkanContextConfig
{
EnableExternalMemoryFd = true, // Linux: opaque-fd / dma-buf export
});
#endif
var engine = EngineBuilder.Create(context)
.WithDefaultNodes(renderToSwapchain: false)
.RenderToCustomTarget(Format.RGBA_UN8) // matches both platform bridges
.Build();
Registering a bindable property (shared partial class)
The shared ViewportProperties.cs registers properties through HelixProperty, which the Avalonia adapter maps onto StyledProperty:
public static readonly DependencyProperty EngineDp = HelixProperty.Register<HelixViewport, Engine?>(
"Engine",
null,
static (d, e) => ((HelixViewport)d).SetEngine((Engine?)e.NewValue));
Architecture Notes
- Presentation —
CompositionSurfacePresenterobtains the compositor, creates aCompositionDrawingSurface+CompositionSurfaceVisual, wires it as the element's child visual, and imports the shared image throughICompositionGpuInterop. There is noSwapChainPanelor DXGI swap chain. If GPU interop is unavailable in the current render session, the presenter logs a warning and skips presentation without throwing. - Bridges — the control selects
WindowsSharedTextureBridgeorLinuxExternalMemoryBridgeviaOperatingSystem.IsWindows(). Both implementIEngineOutputBridge(render target, import description, engine sync info, surface sync, resize). - Buffered Output —
BufferedEngineOutputBridgerotates over several independent single-buffer bridges to decouple engine render from compositor read, allowing the frame rate to reach the display refresh rate. - Render loop — frames are driven from the compositor via
TopLevel.RequestAnimationFrame(with aDispatcherTimerfallback), guarded by a valid engine/render-context/viewport-client and a nonzero size. Each tick renders the offscreen frame into the bridge target and presents it. - Resize & teardown — on resize the control waits for engine idle, recreates the shared output at the new size, and updates the camera-controller viewport size. On unload/dispose it releases the
RenderContextand all interop resources while leaving theEngineintact.
Notes & Gotchas
- Pointer input requires a hit-test surface. The 3D output is shown through an attached composition child visual, which is not part of standard visual hit-testing.
HelixViewportfills its bounds with a transparent brush inRenderso pointer events reach the control and drive the camera. Interactive Avalonia controls placed over the viewport still receive their own input. - Orientation. The compositor samples the imported image with the opposite vertical origin to the engine's output, so the surface visual is mirrored on the Y axis to present the scene right-side up.
- Imported image is cached. Importing a shared GPU texture is expensive, so the presenter imports once and reuses it every frame, re-importing only on the first frame or after a resize. Per-frame work is just the keyed-mutex/semaphore surface update.
- Build the view model off the UI thread. Constructing the engine and scene can block (scene building uses a synchronous-over-asynchronous path); doing it on the UI thread can deadlock and prevents the window from appearing. Build it on a background thread and assign the
DataContexton the UI thread.
Platform Support
- Windows — targets
net8.0-windowsand referencesHelixToolkit.Nex.Interop.DirectXfor the D3D11 shared-texture path. Requires a Vulkan context created withEnableExternalMemoryWin32 = trueand aRequiredDeviceLuidmatching the D3D11 adapter. - Linux — targets
net8.0with no DirectX dependency. Requires a Vulkan context created withEnableExternalMemoryFd = true(enablesVK_KHR_external_memory_fd, the optionalVK_EXT_external_memory_dma_buf, andVK_KHR_external_semaphore_fd).
A runnable cross-platform sample is available under Samples/Interop/Avalonia (AvaloniaInterop), demonstrating the viewport with an engine, a SceneSamples scene, an OrbitCameraController, and an Avalonia UI overlay composited on top of the 3D output.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
net8.0
- Avalonia (>= 12.0.0)
- HelixToolkit.Nex.Engine (>= 1.2.0)
- HelixToolkit.Nex.Graphics.Vulkan (>= 1.2.0)
- HelixToolkit.Nex.Interop (>= 1.2.0)
- HelixToolkit.Nex.Interop.DirectX (>= 1.2.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.2.0 | 96 | 7/17/2026 |