[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.