Unity 6.7 Alpha 6 UI Toolkit Update (Shader Graph Filters)

Hi Team,

Great work on another UI Toolkit update!

Could you please share the documentation link for UI Toolkit: Shader Graph support for UIToolkit Filters? If formal documentation isn’t available yet, a short overview of this feature would be greatly appreciated.

Thanks!

I don’t think any documentation has landed for this yet, but basically you should be able to select UI Toolkit Filters as a Shader Graph target. Before this, you had to write a text shader file which had to include UnityUIEFilter.cginc, like in this Swirl custom filter. But now you will be able to use Shader Graph to author these types of shaders.

2 Likes

Any info about what UI Components are?

1 Like

Components are a new experimental feature for VisualElements, to enable it, go into Project Settings → UI Toolkit → “Enable UI Components” Please give us feedback if you try it!

Using components, you can attach custom data, or logic to any VisualElement, either by code, or via UXML. Documentation with samples will come later during the beta cycle. Here is a not-so-quick primer:

You define a small struct, annotate it with attributes, and the glue code gets generated:

[VisualElementComponent]
public partial struct AnalyticsClickComponent
{
    [UxmlAttribute] public string eventName;
    public int clickCount;

    [RegisterCallback(typeof(ClickEvent))]
    static void OnClick(ref AnalyticsClickComponentself, VisualElement owner, ClickEvent _)
    {
        self.clickCount++;                    // writes into the stored struct, not a copy
        Analytics.Send(self.eventName, owner.name, self.clickCount);
    }
}

You can attach it via c#:

saveButton.AddComponent(new AnalyticsClickComponent{ eventName = "save_clicked" });
int clicks = saveButton.GetComponent<AnalyticsClickComponent>().clickCount;
saveButton.RemoveComponent<AnalyticsClickComponent>();

Or in UXML, as a child of the element you want to track:

<ui:Button text="Save">
    <game:AnalyticsClickComponent event-name="save_clicked" />
</ui:Button>

This way you can extend functionality without having to explicitly subclass each VisualElement type. And unlike Manipulator, these are fully available through uxml, uss, databindings, etc.

As I mentioned, we lean on source generation to do most of the glue code for you, like [UxmlElement] and [UxmlAttribute] replaced the old UxmlFactory/traits system.

Here is a quick list of the available attributes:
On your struct declaration:

  • [VisualElementComponent]: to declare your struct as a VisualElement component
  • [RequiresElementOfType(typeof(TextElement))]: limits the component to one kind of element, and in return your handlers get that type as owner instead of plain VisualElement.
  • [RequiresComponentOfType(typeof(Other))] : says your component needs another component on the same element. When yours is added, the ones it needs are added first. (There are subtleties that will be described in the full documentation)

On each field:

  • [UxmlAttribute]: the field can be set in UXML and in the inspector.
  • [CreateProperty]: makes the field work with data binding.
  • [StyleProperty("--my-prop")]: a custom USS property, read from the owner VisualElement’s styles.

On methods:

  • [RegisterCallback(typeof(SomeEvent))]: a static method that handles an event. It is hooked up when you add the component and unhooked when you remove it.
  • [GenerateVisualContent]: lets the component draw, through the owner element.
  • [OnComponentChanged]: called once a frame after your fields change, so you can refresh/invalidate whatever is needed.
  • [UxmlCreateInstanceMethod]: a static factory that returns a component with real starting values, instead of every field being zero. Some unity targets run on C# 9, so you can’t use field initializers on a struct;
  • [ReleaseComponentResources]: a static method that to free whatever your component allocated (native containers, etc)
  • [OnComponentAdded]/[OnComponentRemoved]: [OnComponentRemoved] is not a substitute for [ReleaseComponentResources]. It only runs when someone explicitly removes the component, so it can be skipped.

Some other things to know/limitations:

  • There can be only one component of each type per element
  • structs only. No inheritance. Use composition
  • If every field in a component is unmanaged, data is allocated in fast native memory. A single managed field in a component (like a string, an array or a reference to an object) forces us to allocate it using a managed allocation.
  • GetComponent<> gives you a ref to the real stored data. Use it inside a method, don’t hold onto it longer.
  • The UI in the UIBuilder is still very minimal and you can’t author data bindings directly, you need to go though uxml.

Remember, this entire thing is still experimental. We want to get your feedback on the feature!

11 Likes

Hi

This is amazing update :slight_smile:

One thing to add is [RequireNativeAllocation] with this attribute you must to error or exception instead of silently allocate component in ManagedLand :slight_smile:

Interesting that it’s done with structs. How are they stored internally without boxing?

Native code magic

3 Likes

Huh, very interesting. Something to study and potentially use myself.

Glad we’re getting components though. Honestly UI Toolkit should’ve been component based from the start.