Engine Initialization Guide
This guide captures the initialization flow for the native-backed chart engine and the responsibilities owned by the managed host. The APIs stabilised in this iteration are designed for low-latency, double-buffered rendering with predictable GPU resource reuse.
Prerequisites
- Copy the
vello_chart_engine
native library into the target application'sruntimes/<rid>/native
directory. The build scripts (scripts/build-native-*.{ps1,sh}
) produce the binaries and place them underartifacts/runtimes/
. - Reference the following projects from your host application:
VelloSharp
(core bindings)VelloSharp.ChartEngine
VelloSharp.ChartDiagnostics
VelloSharp.ChartRuntime
- Ensure
AllowUnsafeBlocks
is enabled for assemblies that call the FFI surface (already set forVelloSharp.ChartEngine
).
Initialization Flow
-
Instantiate the engine
var engine = new ChartEngine.ChartEngine(new ChartEngineOptions
{
VisibleDuration = TimeSpan.FromMinutes(2),
VerticalPaddingRatio = 0.08,
FrameBudget = TimeSpan.FromMilliseconds(8),
StrokeWidth = 1.5,
ShowAxes = true, // set to false to render data-only views
Palette = new[]
{
ChartColor.FromRgb(0x3A, 0xB8, 0xFF),
ChartColor.FromRgb(0xF4, 0x5E, 0x8C),
ChartColor.FromRgb(0x81, 0xFF, 0xF9),
ChartColor.FromRgb(0xFF, 0xD1, 0x4F),
},
});The constructor forwards options to the Rust engine and allocates the double-buffered scene pool.
StrokeWidth
controls the base line thickness for all series,Palette
overrides the default color ramp, andShowAxes
toggles the time/value axes and grid rendering. -
Register render scheduling
TheRenderScheduler
remains in managed code to integrate with UI frameworks:engine.ScheduleRender(tick =>
{
// Typically call RequestRender on the hosting view
}); -
Publish streaming samples
CallPumpData
with time-series spans. Samples are forwarded to the native data pipeline using a stack-allocated fast path for small bursts.engine.PumpData(samples); // samples is ReadOnlySpan<ChartSamplePoint>
-
Render into the provided scene
When the host view requests a frame, callRender
with the scene supplied byVelloView
.engine.Render(context.Scene, context.Width, context.Height);
The native engine writes into its double-buffered
Scene
objects and appends the active buffer into the managed scene handle. -
Consume diagnostics
FrameDiagnosticsCollector
is updated with each frame. You can queryengine.LastFrameStats
or subscribe to the recorded histograms for telemetry.
Runtime Styling Overrides
- Update the palette without tearing down the engine:
// Passing Span<T> lets you reuse buffers or slice existing theme data
engine.UpdatePalette(stackalloc[]
{
ChartColor.FromRgb(0x2F, 0x95, 0xFF),
ChartColor.FromRgb(0xF9, 0x65, 0x7D),
ChartColor.FromRgb(0x4E, 0xC9, 0x74),
});
// Revert to the built-in palette
engine.UpdatePalette(ReadOnlySpan<ChartColor>.Empty); - Apply per-series overrides for labels, thickness, and colors without recreating the engine:
Overrides are tri-state: call
Span<ChartSeriesOverride> overrides = stackalloc[]
{
new ChartSeriesOverride(seriesId: 1)
.WithLabel("Bid")
.WithStrokeWidth(2.0)
.WithColor(ChartColor.FromRgb(0x3A, 0xB8, 0xFF)),
new ChartSeriesOverride(seriesId: 2)
.WithLabel("Ask")
.ClearStrokeWidth()
.WithColor(ChartColor.FromRgb(0xF4, 0x5E, 0x8C)),
};
engine.ApplySeriesOverrides(overrides);Clear*
helpers to remove an override and fall back to the shared defaults.
Configuring Series Definitions
- Declare the rendering behaviour for each series up front. Definitions drive legend markers, overlay styling, and how the native engine builds GPU buffers.
engine.ConfigureSeries(new ChartSeriesDefinition[]
{
new LineSeriesDefinition(0)
{
StrokeWidth = 2.0,
FillOpacity = 0.18,
},
new ScatterSeriesDefinition(1)
{
MarkerSize = 6.0,
},
new BarSeriesDefinition(2)
{
Baseline = 0,
BarWidthSeconds = 6.0,
},
}); - Series that are not explicitly configured fall back to the line rendering defaults.
GPU Surface Hosting
ChartView
now composesVelloSurfaceView
, so GPU swapchains are attached automatically when supported by the platform window handle.- Surface resizing and presentation are handled by the shared host; the control transparently falls back to the bitmap path when hardware acceleration is unavailable.
- Surface tuning remains accessible via the regular properties:
ChartHost.RendererOptions = ChartHost.RendererOptions with { BaseDpi = new Vector2(96f, 96f) };
ChartHost.RenderParameters = ChartHost.RenderParameters with { BaseColor = RgbaColor.FromBytes(0x10, 0x15, 0x1F) };
Axes and Grid
- The engine now computes a shared viewport for all series and renders labelled time/value axes with adaptive tick spacing.
- Grid lines inherit those ticks, giving trading dashboards quick alignment cues without extra overlay code.
- Axes consume dynamic padding around the plot. When you set
ShowAxes = false
, the chart expands to the full control bounds for sparkline layouts.
Diagnostics and Tracing
- The Rust engine emits
tracing
events which are bridged to.NET
viaChartEngineEventSource
. Consumers can listen with ETW/EventSource listeners orEventListener
. - The trace pipeline is best-effort; if the callback registration fails the engine continues without raising exceptions.
Renderer Pooling
The Avalonia host (ChartView
) relies on VelloSurfaceView
for GPU presentation. The Rust engine reuses internal Scene
buffers, and the managed layer keeps the VelloSurfaceView
renderer pool hot, ensuring command buffers and textures are recycled every frame.
Disposal Semantics
Always dispose the engine from the UI thread that owns the scheduler:
engine.Dispose();
Disposal tears down the scheduler, diagnostics collectors, and the native handle in one pass.
Next Steps
- Extend the EventSource listener to forward metrics into application-level telemetry.
- Combine runtime palette updates with per-series overrides to drive live theming or interaction-driven callouts.
- Incorporate structured tracing IDs once additional spans are stabilised on the Rust side.