Composition API

Composition Visuals make up the visual tree structure which all other features of the composition API use and build on. The API allows developers to define and create one or many visual objects each representing a single node in a visual tree.

To get more info, you can refer to Microsoft's documentation.

Uno Platform currently supports a small number of the APIs in the Windows.UI.Composition namespace.

The rest of this article details Uno-specific considerations regarding Composition API.

On Android, most composition features are functional only for Android 10 (API 29) and above.

Compositor Thread [Android]

On Android, the composition refers to the Draw and OnDraw methods. To get more info about custom drawing on Android, you can refer to the Android's documentation.

By default, those methods are invoked on the UI Thread.

With Uno, you can request to run those methods on a dedicated thread by setting in your Android application's constructor:

Uno.CompositionConfiguration.Configuration = Uno.CompositionConfiguration.Options.Enabled;

This thread will also be used for independent animations.

When overriding the [On]Draw methods, it is very important not to access any state that can be edited from the UI Thread, including any DependencyProperty. Instead, you should capture the state of your control into a RenderNode during the ArrangeOverride and render it on the provided Canvas.

There are a few known issues associated with the used of the compositor thread, make sure to read the section below.

Brush Anti-aliasing [Skia Backends]

On Skia Desktop targets (X11, Framebuffer, macOS, and Windows), anti-aliasing is disabled by default for brushes, You can request it to be anti-aliased by setting this in your application's constructor:

#if HAS_UNO
    Uno.CompositionConfiguration.Configuration |= Uno.CompositionConfiguration.Options.UseBrushAntialiasing;
#endif

Or alternatively, if you want to enable all available Composition capabilities:

#if HAS_UNO
    Uno.CompositionConfiguration.Configuration = Uno.CompositionConfiguration.Options.Enabled;
#endif

Implemented APIs [Skia Backends]

Windows.UI.Composition

Windows.UI.Composition.Interactions

Windows.Graphics.Effects

Windows.Graphics.Effects.Interop

Microsoft.Graphics.Canvas.Effects (Win2D/Composition Effects)

Note that while Uno Platform implements these effects and their Win2D wrappers, the Win2D wrappers are still internal and not exposed to users, but the effects can still be used by temporary implementing the IGraphicsEffectD2D1Interop interface manually until the Win2D wrappers become public, like for example the GaussianBlurEffect can be implemented like this:

#nullable enable

using System;
using Windows.Graphics.Effects;
using Windows.Graphics.Effects.Interop;

internal class GaussianBlurEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
{
    private string _name = "GaussianBlurEffect";
    private Guid _id = new Guid("1FEB6D69-2FE6-4AC9-8C58-1D7F93E7A6A5");

    public string Name
    {
        get => _name;
        set => _name = value;
    }

    public IGraphicsEffectSource? Source { get; set; }

    public float BlurAmount { get; set; } = 3.0f;

    public Guid GetEffectId() => _id;

    public void GetNamedPropertyMapping(string name, out uint index, out GraphicsEffectPropertyMapping mapping)
    {
        switch (name)
        {
            case nameof(BlurAmount):
                {
                    index = 0;
                    mapping = GraphicsEffectPropertyMapping.Direct;
                    break;
                }
            default:
                {
                    index = 0xFF;
                    mapping = (GraphicsEffectPropertyMapping)0xFF;
                    break;
                }
        }
    }

    public object? GetProperty(uint index)
    {
        switch (index)
        {
            case 0:
                return BlurAmount;
            default:
                return null;
        }
    }

    public uint GetPropertyCount() => 1;
    public IGraphicsEffectSource? GetSource(uint index) => Source;
    public uint GetSourceCount() => 1;
}

The GUID used in the example above is the Effect CLSID of the Direct2D Gaussian Blur Effect. For a list of built-in Direct2D effects and their corresponding CLSIDs, see Direct2D Built-in Effects.

Known issues

  • [Android] When using the compositor thread, the native ripple effect of Android (used in native buttons) does not work.

  • [Skia Backends] Some Composition effects don't render properly (or at all) on software rendering (CPU), to check if Uno is running on the software rendering (CPU) or the hardware rendering (GPU), you can call CompositionCapabilities.GetForCurrentView().AreEffectsFast().