Yet another LateUpdate performance thread. Using NGUI 3.0.6f7 we were doing some performance testing and it appears that there is significant overhead involved here. We have about 1000 widgets in our scene. None of the widgets move so the panels are marked as static. We were doing some profiling and we ran into a question that I'm curious if you could look at.
Our first test:
UIPanel
--UITexture
--UIPanel
----UISprite
----UISprite
----UISprite
----(An additional 11 sprites here)
--UIPanel
----UISprite
----UISprite
----UISprite
----(An additional 11 sprites here)
There were 94 panels of these 11 sprite sets to equate to about 1000 UIWidgets. The idea was that we could use the panels to determine if they were visible or not on the camera and disable and enable it. However, just having them all visible produced some very disturbing profiling results. Our late update call was taking 13ms or 87% of a naked scene's CPU and was already causing frame rate problems in the editor. We looked at the deep profile of it and it was spending 98% of the time inside of the LateUpdate -> UpdateWidgets() call.
Then we did the following layout (basically all the widgets were parented to one panel)
UIPanel
--UITexture
--UISprite
--UISprite
--etc.
We expected this to be faster (obviously fewer panels and draw calls), but we found that the profiler said it was < 1ms (7%)! Meaning it was upwards of 13x faster than sticking them on sub panels so I went to investigate what might be happening.
Inside of a UIPanel.LateUpdate() there is a loop that is confusing me, that I'm hoping you might be able to shed some implementation light on.
UIPanel.LateUpdate() {
// Only the very first panel should be doing the update logic
if (list[0] != this) return;
// Update all panels
for (int i = 0; i < list.size; ++i)
{
UIPanel panel = list[i];
panel.mUpdateTime = RealTime.time;
panel.UpdateTransformMatrix();
panel.UpdateLayers();
panel.UpdateWidgets();
}
...
}
Then inside of the UIPanel.UpdateWidgets() this is happening
UIPanel.UpdateWidgets() {
....
for (int i = 0, imax = UIWidget.list.size; i < imax; ++i)
{
UIWidget w = UIWidget.list[i];
// If the widget is visible, update it
if (w.panel == this && w.enabled)
.....
}
....
}
So the problem I have is that if you have a lot of panels there is significant overhead iterating through every widget that many times (even if not much is done because it checks the equality on the w.panel vs this).
My recommendation would be to iterate through all of the widgets only once and check their parent panel for information relating to their visibility. As there are likely significantly more widgets than panels. Something like this
UIPanel.LateUpdate() {
if (list[0] != this) return;
// Update all panels
for (int i = 0; i < list.size; ++i)
{
UIPanel panel = list[i];
panel.mUpdateTime = RealTime.time;
panel.UpdateTransformMatrix();
panel.UpdateLayers();
// REMOVE THIS panel.UpdateWidgets();
}
// Update Widgets - Update all widgets only once, instead of iterating through the list once per panel
for (int i = 0, imax = UIWidget.list.size; i < imax; ++i)
{
UIWidget w = UIWidget.list[i];
// If the widget is visible, update it
if (w.panel.enabled)
.....
}
...
}
I hope that makes sense. If I'm wrong about this implementation I'd appreciate any information you can provide on why it was implemented as described above.
Thanks for your time!