Performance Optimization

[currently using 2021.3.10]

While porting one of our more intensive UGUI controls to UIToolkit, we’ve run into some significant performance issues (up to this point we were optimistic that the UIT version would perform better). I’m not sure what the best way to optimize this is.

We have a control which handles pointer events:

selectedRegion.RegisterCallback<PointerDownEvent>((evt) =>
{
    PointerCaptureHelper.CapturePointer(selectedRegion, evt.pointerId);
});
selectedRegion.RegisterCallback<PointerUpEvent>((evt) =>
{
    PointerCaptureHelper.ReleasePointer(selectedRegion, evt.pointerId);
});
selectedRegion.RegisterCallback<PointerMoveEvent>((evt) =>
{
    if (PointerCaptureHelper.HasPointerCapture(selectedRegion, evt.pointerId))
    {
        var x = evt.localPosition.x / selectedRegion.resolvedStyle.width;
        CurrentTime = Mathf.Lerp(rangeSlider.MinValue, rangeSlider.MaxValue, x);
    }
});

The impact on layout from the PointerMoveEvent above is that a single 2px-wide VisualElement gets repositioned:

float marker_x = /* compute new position */;
marker.style.left = new StyleLength(new Length(marker_x, LengthUnit.Pixel));

This element is marked with

marker.usageHints = UsageHints.DynamicTransform;
marker.pickingMode = PickingMode.Ignore;

And there are ~30 other similar elements which are configured the same way, but are not moving on this frame (other input events may cause them to move). The parent element is marked with UsageHints.GroupTransform.

With all this, our frame time is dominated by layout updates, and the final render takes ~90ms (with deep profiling enabled, but even in a packaged build without the profiler attached it’s still pretty slow). Are there any other tips for improving performance in more complex interactive controls?

In UGUI we worked around this problem by not displaying these dynamic elements as actual elements, but instead just had one RawImage into which they were drawn at the end of each frame via RawImage.SetPixel(), so they didn’t participate in layout updates at all. I was hoping not to need to take a similar approach with the UIT implementation, though, so we could take advantage of USS to style them better and to allow them to be interactable elements using regular pointer events.

Another optimization we had to make in the UGUI version was to avoid processing many events directly, because we would often get multiple updates in a single frame. Instead, all values were just accumulated and then applied at the end of the frame in LateUpdate. I’m not sure if that’s also something we need to do here, but if so I’m also not sure if there’s any kind of UIT event which is fired exactly once per frame.

The render update from the same frame as above:

Wooops, my bad, sorry. The expensive re-layout turned out to be due to another element being created dynamically via CloneTree. I have a plan for improving this but haven’t gotten to it, yet, hah. I guess that the layout update is more expensive now than previously because the overall complexity of the UI tree is worse with the new control.

GroupTransform shouldn’t improve performance when moving elements through layout/position properties.

I’d recommend moving them with the transform property and use the DynamicTransform hint on each of them.

This thread has some background info: How to efficiently move a large amount of elements?

How many children with DynamicTransform do you have under the parent with GroupTransform (about 30 if I understand correctly)? Also, can you confirm that both the parent and the children move? Another question: do you use absolute positioning for the children?

Btw, DepthFirstOnVisualsChanged (which is somewhat equivalent to the RenderChain.UpdateVisuals marker when you’re not using Deep Profile) is not the time it takes to render, but the time it takes to regenerate the meshes after the changes. If you want to monitor the time it takes for the rendering itself, look at the UIR.DrawChain marker.

Apart from the position of the elements, do you change the size, or the content (e.g. text)?