Tasharen Entertainment Forum

Support => NGUI 3 Support => Topic started by: newlife on March 20, 2015, 08:40:50 PM

Title: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on March 20, 2015, 08:40:50 PM
Hello,
we have developed the interface of our game with NGUI 2.7.0. Now, in order to use the more advanced features of NGUI 3.x, we update it to 3.7.5.
After the update, I noticed a huge performance drop, in particular when dragging the vertical leaderboard (made with a draggable panel). It contains 50 entries, of which only 10 are visible.
The performance drop occurs especially when clicking on the draggable panel and releasing it.
In the attached images, you can see the profiler of the same draggable panel with NGUI 2.7.0 and NGUI 3.7.5 (in both cases we have used Unity 4.5.5).
With NGUI 3.7.5, even if the CG if a bit smaller, the time frame goes from 10ms to an impressive 37.7 ms.

Is there a way to fix this? I already tried to uncheck "static" the UIPanel but without success.
Thanks.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: ArenMook on March 22, 2015, 07:59:51 AM
What's causing your GC allocation? NGUI should be causing no allocation at all while dragging panels if panel's culling is off.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on March 22, 2015, 11:50:41 AM
Panel's cull check box is unchecked.
I have attached a deep profile. As you can see, there are 22440 (!) call to betterlist.add.
Anyway, the problem seems to lay on drawcall functions, cause they allocate the most of the time in the frame.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: ArenMook on March 23, 2015, 09:39:58 AM
Right, that's just draw call creation. My question is what's causing the draw calls to be changed? While dragging with culling turned off, the entire draw call does not change at all. It only changes at the time you start dragging. Do you have some element that's constantly changing, causing draw calls to be re-created?
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on March 24, 2015, 01:49:07 PM
Quote
Right, that's just draw call creation. My question is what's causing the draw calls to be changed? While dragging with culling turned off, the entire draw call does not change at all. It only changes at the time you start dragging. Do you have some element that's constantly changing, causing draw calls to be re-created?
No, the panel is loaded and then it doens't change. Anyway, i included the deep profile of the same panel with NGUI 2.7.0 to make a comparison.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on March 25, 2015, 11:53:57 AM
Amy ideas? On a medium / low level device (LGP700) all the draggable panels are now almost unusable.
With NGUI 2.7.0 everything was much smoother.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: ArenMook on March 25, 2015, 12:20:07 PM
The behaviour of scrolling hasn't changed since 2.7, so it's something else. As I said, figure out what's causing the draw calls to be rebuilt and you will have your answer.

Add a Debug.Log to UIPanel.UpdateWidgets(), around line 1600.
  1.                                 // First update the widget's transform
  2.                                 if (w.UpdateTransform(frame) || mResized)
  3.                                 {
  4.                                         // Only proceed to checking the widget's visibility if it actually moved
  5.                                         bool vis = forceVisible || (w.CalculateCumulativeAlpha(frame) > 0.001f);
  6.                                         w.UpdateVisibility(vis, forceVisible || ((clipped || w.hideIfOffScreen) ? IsVisible(w) : true));
  7.                                         Debug.Log("Transform changed: " + w.name, w);
  8.                                 }
  9.                                
  10.                                 // Update the widget's geometry if necessary
  11.                                 if (w.UpdateGeometry(frame))
  12.                                 {
  13.                                         changed = true;
  14.                                         Debug.Log("Geometry changed: " + w.name, w);
  15.  
  16.                                         if (!mRebuild)
  17.                                         {
  18.                                                 if (w.drawCall != null)
  19.                                                 {
  20.                                                         w.drawCall.isDirty = true;
  21.                                                 }
  22.                                                 else
  23.                                                 {
  24.                                                         // Find an existing draw call, if possible
  25.                                                         FindDrawCall(w);
  26.                                                 }
  27.                                         }
  28.                                 }
P.S. And make sure you're on the latest version of NGUI. You mentioned 3.7.5, which is quite old now.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 10, 2015, 07:39:05 PM
Hello,
sorry for the late but i had to fix other issues in our project. I added the suggested Debug.Log in UIPanel, and it seems that all components of the leaderboard is causing the draw calls to be rebuilt during the drag (in the console there are 560 logs). See attached image as reference. I really dont know if this is normal or not. Please help.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 12, 2015, 08:22:06 AM
Any ideas? We really need this to be sorted out.
Thanks.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: yuewah on April 12, 2015, 09:35:35 AM
Are you scaling, moving the individual widgets during dragging ?
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 12, 2015, 04:18:18 PM
not at all. Its a simple draggable panel. The only distinctive trait is that entries are created runtime (cause leaderboard data is downloaded runtime from a server).
Anyway,everything is the same as the NGUI 2.7.0 version, which didint suffer of this issue.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: ArenMook on April 12, 2015, 04:46:21 PM
I just did a simple test:
1. New scene, ALT+SHIFT+S, ALT+SHIFT+C, right-click, add scroll view.
2. Drag & drop the sprite into the scroll view, right-click, attach drag scroll view.
3. Duplicated the sprite a few times, changed the names to make them unique.
4. Run, drag them around. I see the "Geometry changed: XXX" being called when items enter/exit the scroll view -- exactly what I expect as "culling" is on by default.
5. Changed "culling" to off, tried again -- nothing changes during the drag operation at all now.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 13, 2015, 04:55:32 AM
Well this is weird.
I checked the panel and, as expected, cull is false. During dragging, huge amounts of geometry changed calls.
Set cull to true, much less geometry changed calls (only items that enter or exit the scroll view).
Definitely a 3.7.5 bug.
I'll try with latest NGUI version.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 13, 2015, 09:29:33 PM
i just updated to the latest version on Asset Store (3.8.2) and the behaviour is the same:

if cull is false, during dragging there is huge amount of geometry changed calls.
If cull is true, much less geometry changed calls (only items that enter or exit the scroll view).

I really dont know what happening.
With cull set to true things seems much better anyway.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 14, 2015, 01:20:21 PM
I just did a simple test:
1. New scene, ALT+SHIFT+S, ALT+SHIFT+C, right-click, add scroll view.
2. Drag & drop the sprite into the scroll view, right-click, attach drag scroll view.
3. Duplicated the sprite a few times, changed the names to make them unique.
4. Run, drag them around. I see the "Geometry changed: XXX" being called when items enter/exit the scroll view -- exactly what I expect as "culling" is on by default.
5. Changed "culling" to off, tried again -- nothing changes during the drag operation at all now.

I made the same test with the included scroll view panel example and the results are the same when culliing is on, but when culling is off I see 148 logs call in the console.
Can you please check this?
Using NGUI 3.8.2 and Unity 4.5.5.

Anyway, performance of draggable panels are still far away from NGUI 2.7.0.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: ArenMook on April 16, 2015, 06:50:20 AM
You are seeing log entries because the scroll bar has to change every single frame as its dimensions change as you drag the scroll view.

Note how in my clean test there was no scroll bar?

Scroll bar changing causes every other widget in that panel to change as well. This is why it's a good idea to move the scroll bar onto its own panel.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 16, 2015, 08:08:08 AM
I made the same test with the included scroll view panel example and the results are the same when culliing is on, but when culling is off I see 148 logs call in the console.
Can you please check this?
Using NGUI 3.8.2 and Unity 4.5.5.

Anyway, performance of draggable panels are still far away from NGUI 2.7.0.

ArenMook can you please have a look to this? performance degradation is evident even on mid/high end device (Acer iconia tab 8)
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: ArenMook on April 16, 2015, 08:29:30 AM
I just told you what the rebuild is caused by, so not sure what else I can look at?
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 16, 2015, 09:13:08 PM
Hello Arenmook,
sorry but i didnt read your previous message.
To my tets, the issue is not only related to scroll bars.
I have another draggable vertical panel used to let the player choose his country. This panel has a lot of entries (one for each country in the world). Each entry is made by a label and a sprite with a button. No scroll bars. After upgrading to NGUI 3, huge performance drop for this panel too. Unusable with cull off, usable but with serious flaws even on decent device (Iconia Tab 8 ) with cull on.
Please advice.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: ArenMook on April 18, 2015, 12:46:48 PM
I would advise you to have a look at endless scroll views example. You can create a scroll view with only a few entries that will be reused as they go out of range.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 18, 2015, 06:48:03 PM
I would advise you to have a look at endless scroll views example. You can create a scroll view with only a few entries that will be reused as they go out of range.

Aren, thank you for your suggestion, but i REALLY encourage you to examine in depth this issue, its causing a huge performance drop in ANY draggable panel, and this has nothing to do with scroll bar. Tested on the very same device (LGP700), wit the very same interface, the version made with NGUI 2.7.0 is usable while the one made with NGUI 3.8 is mostly unusable (drags occurs after several seconds on the leaderboard panel; its completely unusable on the flags panel).

If you want to test for yourself, this is the link of our app (made with NGU 2.7.0) on google play:

https://play.google.com/store/apps/details?id=com.realdriftportnew.sipon

If you will, I can include you in the beta test page of our new version which is using NGUI 3.8.2, so you can check performance differences.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: Nicki on April 19, 2015, 08:27:52 AM
newlife, can you make a repro case, so I can try it? Just pm me a link, then I'll take a look.

Preferably with as close a setup to what you have problems with as you can.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 19, 2015, 09:33:01 PM
newlife, can you make a repro case, so I can try it? Just pm me a link, then I'll take a look.

Preferably with as close a setup to what you have problems with as you can.

I made some further tests with the profiler and it seems that there is a bug with cull option in NGUI 3.x.
I have two version of the same project, one with NGUI 2.7 and the other with NGUI 3.8.
Profiling the project with NGUI 2.7.0 while dragging the draggable panel gave these results:

cull on: 16ms  average frame time
cull off: 4ms   average frame time

Profiling the project with NGUI 3.8.2 while dragging the draggable panel gave these results:

cull on: 15ms  average frame time
cull off: peaks of 290ms , 15ms  average frame time


So the behaviour seems similar with cull on, while with cull off there must be something wrong.
Please let me know what you think.


Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 20, 2015, 07:09:26 AM
This issue is reproducible with any draggable panel, the more the entries in the panel the more the issue is evident.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: ArenMook on April 20, 2015, 08:08:18 PM
newlife please create a repro case for me to look at that clearly illustrates the problem. I tried it several times on my end now and cannot see any issues. Culling works as expected, culling off also works as expected. Geometry is not rebuilt while dragging. If I pause the drag operation for a bit, then culling kicks in -- but nothing changes while dragging.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 21, 2015, 07:13:39 AM
Create two new projects, one with NGUI 2.7 and the other with NGUI 3.7.8. Open the included example (scroll view camera). Remove anything not related to the draggable panel (scroll bar etc.)
We are going to concentrate on cull off cause with cull on results seems similar.
Put a good amount of items in the view (I put 200 items) in order to emphasize the issue.
Open the profiler and set as deep. Simulate a real world user inputs (make some drags with a second of wait between one and the other).

In the attached images you can see the results I got. With NGUI 2.7 there is a big peak at the start of the drag but is narrow and the UI is responsive. With NGUI 3.7.8 there is the same big peak but is much more long and UI is not responsive at all (seconds of waits for a drag).

Please tell me what you think.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 22, 2015, 07:12:58 AM
Any news regarding this? We are going to release an update of our game today or tomorrow, i would like to know if its possible to fix this.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: ArenMook on April 22, 2015, 02:26:49 PM
Sorry but I can't compare NGUI 2 to NGUI 3. They are very different UI systems, and it would be like comparing a bus to a bike. Both have wheels and can carry people, but how they go about accomplishing that is very different.

If you want me to investigate a specific issue in NGUI 3, as I mentioned please create a repro case for me to look at. You can submit it to support at tasharen.com (email).

Also note that the Professional version of NGUI was optimized recently (side-effect of me looking into this post a few days earlier), so it will perform even better than stock 3.8.2.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 22, 2015, 08:20:46 PM
ArenMook,
the issue im talking here is a performance drop of the draggable panel after updating from 2.7.0 to 3.x.
So the issue is low performance with draggable panel with a substantial amount of items in it (extremely evident on low power Android device).
The repro case is, as wrote in the previous message:

Open the included example (scroll view camera). Remove anything not related to the draggable panel (scroll bar etc.)
Set cull off.
Put a good amount of items in the view (I put 200 items) in order to emphasize the issue.
Open the profiler and set as deep. Simulate a real world user inputs (make some drags with a second of wait between one and the other).
The issue is that the UI is not responsive at all (seconds of waits for a drag).
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: ArenMook on April 24, 2015, 07:40:09 AM
Deep profile should not be used for anything other than detecting garbage collection allocations. Deep profile performance is ALWAYS wrong. Check Unity's documentation for it. I've explained it dozens of times on this forum already, and the simple explanation is this:

Deep profiling adds a certain amount of overhead to every function call. So say you have one function that takes 1 ms to execute, and another function that takes 0.001 ms. Under normal conditions, first function's execution will take the same time as 1000 of second function's executions. However since deep profile adds overhead, say 0.1 ms... first function's execution becomes 1.1 ms, and second function becomes 0.101 ms. Now 1000 calls to the second function gives you execution time of 101 ms, suddenly making you believe that there is a problem where in fact there isn't one.

Pro version of NGUI's test of your example gives nothing to go by, as I mentioned. The delay between drags does nothing there. When you begin your drag operation, there is a short delay where the big draw buffer gets created from all the widgets (which is expected), and after that -- nothing.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 28, 2015, 07:03:20 AM
I can finally reply after the release of the update of our app.
ArenMook, I think that you misunderstand the aim of the thread i opened here. You are trying everything to "demonstrate" that im wrong and that you product is cool.
There is no reason to do that, im using NGUI cause I think that its the best GUI for Unity.
The aim of this thread is to understand with you what is causing the performance drop, nothing else. It seems that you think Im writing here to degrade you product.
Do please reconsider that. A said, Im here to work with you in a win-win way, not to argue with you.
Let me know what you think.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: Nicki on April 29, 2015, 01:27:59 PM
Well, there's a lot of things that changed with 3.0+. The structure of panels changed, which would potentially give you misleading information comparing 2.7 with 3+ with deep profiling. It may be, that these underlying changes are at root of the performance issue you've noticed. It's just really hard to pinpoint, since there's oceans of changes that's happened since then. That said, improving the performance is obviously something everyone wants.

I'll try to do the test you just did right now and measure some performance on the newest and see if there's anything glaring, if nothing else to confirm or counter your findings.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on April 30, 2015, 03:56:18 PM
Thank you for your reply Nicky. Keep in mind that i suggested to use deep profile just to have more information, the result is perfectly the same with normal profile in the editor and of course on a android device (and on any target i think, cause the performance drop is present in the editor).
Im curious to know the results of your tests.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: Nicki on May 03, 2015, 05:30:52 PM
I have an in depth analysis of this written up on my other computer, but the forum has been down all day, so it'll have to wait until I'm home from work tomorrow.

The long and short of it, you can gain a lot of performance by switching the widgetsAreStatic on in the panel while dragging, and I did some optimizations to the existing code that should make it faster when dragging panels around.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: Nicki on May 04, 2015, 12:31:32 AM
So, I've taken a closer look at this and I think mostly the performance changes you are observing is a consequence of how the system of NGUI has changed from 2.7 to 3+.

Analysis
I tested in the Editor 5.0.1p2 on the "Example 14 - Endless Scroll" by making about 200 entries in the middle green scroll list and while dragging or scrolling the UIPanel.LateUpdate balloons up to ~65% CPU usage as you also saw in your example, newlife. I'm testing without culling enabled on either UIPanel or UIWrapContent.
(http://i.imgur.com/JW2OPD4.png)

Using the deep profiler, we can dig a little deeper to see what could be causing this. Note, as always, that the numbers will be skewed as the deep profiler adds overhead to each method, which will also trigger a bit more garbage collection etc. That said, the picture looks very similar to the previous one.

(http://i.imgur.com/tzpm33a.png)

6.5 ms to UIPanel.UpdateWidgets
4.8 ms to UIPanel.FillDrawCall

Looking deeper into UpdateWidgets we see this:

(http://i.imgur.com/zWFh43y.png)

ApplyTransform seems to take the largest chunk of the work, UpdateGeometry itself second and UpdateGeometry and UpdateTransform about even. (Caveat: You see the List.Add and List.Clear instead of BetterList because I experimented with switching back to the regular list to see if there was a difference - there's not really.)

Let's take a look at the code for these methods:

  1. void UpdateWidgets()
  2. {
  3.         bool changed = false;
  4.         bool forceVisible = false;
  5.         bool clipped = hasCumulativeClipping;
  6.  
  7.         if (!cullWhileDragging)
  8.         {
  9.                 for (int i = 0; i < UIScrollView.list.size; ++i)
  10.                 {
  11.                         UIScrollView sv = UIScrollView.list[i];
  12.                         if (sv.panel == this && sv.isDragging) forceVisible = true;
  13.                 }
  14.         }
  15.  
  16.         if (mForced != forceVisible)
  17.         {
  18.                 mForced = forceVisible;
  19.                 mResized = true;
  20.         }
  21.  
  22.         // Update all widgets
  23.         for (int i = 0, imax = widgets.Count; i < imax; ++i)
  24.         {
  25.                 UIWidget w = widgets[i];
  26.  
  27.                 // If the widget is visible, update it
  28.                 if (w.panel == this && w.enabled)
  29.                 {
  30.                         int frame = Time.frameCount;
  31.  
  32.                         // First update the widget's transform
  33.                         if (w.UpdateTransform(frame) || mResized)
  34.                         {
  35.                                 // Only proceed to checking the widget's visibility if it actually moved
  36.                                 bool vis = forceVisible || (w.CalculateCumulativeAlpha(frame) > 0.001f);
  37.                                 w.UpdateVisibility(vis, forceVisible || ((clipped || w.hideIfOffScreen) ? IsVisible(w) : true));
  38.                         }
  39.                        
  40.                         // Update the widget's geometry if necessary
  41.                         if (w.UpdateGeometry(frame))
  42.                         {
  43.                                 changed = true;
  44.                                 //Debug.Log("Geometry changed: " + w.name + " " + frame, w);
  45.  
  46.                                 if (!mRebuild)
  47.                                 {
  48.                                         // Find an existing draw call, if possible
  49.                                         if (w.drawCall != null) w.drawCall.isDirty = true;
  50.                                         else FindDrawCall(w);
  51.                                 }
  52.                         }
  53.                 }
  54.         }
  55.  
  56.         // Inform the changed event listeners
  57.         if (changed && onGeometryUpdated != null) onGeometryUpdated();
  58.         mResized = false;
  59. }
  60.        

Foreach widget drawn it calls UpdateTransform and UpdateGeometry once, consistent with the deep profile. There seems to be no immediate "bad things"â„¢ going on, unless there are side effects to some of the properties. There are  minor things we can do to improve it, like moving the int frame = Time.frameCount outside the loop (assuming overhead from Time.frameCount) and checking which of the (w.panel == this) or (w.enabled) is heavier, since if the first one fails the second is not run. We can also simplify the ternery conditional ( ?: ) to save a conditional.
  1. w.UpdateVisibility(vis, forceVisible || ((!clipped && !w.hideIfOffScreen) || IsVisible(w)));
  2.  

Not much else to do in that method, from my perspective. So we move on to UIWidget.UpdateTransform (following the method structure).

  1. public bool UpdateTransform (int frame)
  2. {
  3.         Transform trans = cachedTransform;
  4.         mPlayMode = Application.isPlaying;
  5.  
  6. #if UNITY_EDITOR
  7.         if (mMoved || !mPlayMode)
  8. #else
  9.         if (mMoved)
  10. #endif
  11.         {
  12.                 mMoved = true;
  13.                 mMatrixFrame = -1;
  14.                 trans.hasChanged = false;
  15.                 Vector2 offset = pivotOffset;
  16.  
  17.                 float x0 = -offset.x * mWidth;
  18.                 float y0 = -offset.y * mHeight;
  19.                 float x1 = x0 + mWidth;
  20.                 float y1 = y0 + mHeight;
  21.  
  22.                 mOldV0 = panel.worldToLocal.MultiplyPoint3x4(trans.TransformPoint(x0, y0, 0f));
  23.                 mOldV1 = panel.worldToLocal.MultiplyPoint3x4(trans.TransformPoint(x1, y1, 0f));
  24.         }
  25.         else if (!panel.widgetsAreStatic && trans.hasChanged)
  26.         {
  27.                 mMoved = true;
  28.                 mMatrixFrame = -1;
  29.                 trans.hasChanged = false;
  30.                 Vector2 offset = pivotOffset;
  31.  
  32.                 float x0 = -offset.x * mWidth;
  33.                 float y0 = -offset.y * mHeight;
  34.                 float x1 = x0 + mWidth;
  35.                 float y1 = y0 + mHeight;
  36.  
  37.                 Vector3 v0 = panel.worldToLocal.MultiplyPoint3x4(trans.TransformPoint(x0, y0, 0f));
  38.                 Vector3 v1 = panel.worldToLocal.MultiplyPoint3x4(trans.TransformPoint(x1, y1, 0f));
  39.  
  40.                 if (Vector3.SqrMagnitude(mOldV0 - v0) > 0.000001f ||
  41.                         Vector3.SqrMagnitude(mOldV1 - v1) > 0.000001f)
  42.                 {
  43.                         mMoved = true;
  44.                         mOldV0 = v0;
  45.                         mOldV1 = v1;
  46.                 }
  47.         }
  48.  
  49.         // Notify the listeners
  50.         if (mMoved && onChange != null) onChange();
  51.         return mMoved || mChanged;
  52. }
  53.        

We can see in the picture above (although it's slightly cut off) that we call Vector3.SqrMagnitude which means, we're in the second condition in this instance - which we would expect, as none of the individual widgets have moved and only the parent panel is scrolling. Now, I think it's a bit weird that it even gets in here, but the trans.hasChanged must take the parent into account, which is a little annoying as it causes overhead for us here.

If you KNOW your widgets will not change while scrolling, you can set the panel to be static (widgetsAreStatic), which will escape all the calculations above.

I'm wondering if the first mMoved = true should actually be mMoved = false, as there is a second check for movement with the sqrmagnitude. That would potentially save whatever is hooked up to the OnChange and the stuff back in UIPanel. I'll test this out later. Also, we don't use the frame parameter at all - I imagine this is a leftover.

Moving on to UIWidget.UpdateGeometry

  1. public bool UpdateGeometry (int frame)
  2. {
  3.         // Has the alpha changed?
  4.         float finalAlpha = CalculateFinalAlpha(frame);
  5.         if (mIsVisibleByAlpha && mLastAlpha != finalAlpha) mChanged = true;
  6.         mLastAlpha = finalAlpha;
  7.  
  8.         if (mChanged)
  9.         {
  10.                 mChanged = false;
  11.  
  12.                 if (mIsVisibleByAlpha && finalAlpha > 0.001f && shader != null)
  13.                 {
  14.                         bool hadVertices = geometry.hasVertices;
  15.  
  16.                         if (fillGeometry)
  17.                         {
  18.                                 geometry.Clear();
  19.                                 OnFill(geometry.verts, geometry.uvs, geometry.cols);
  20.                         }
  21.  
  22.                         if (geometry.hasVertices)
  23.                         {
  24.                                 // Want to see what's being filled? Uncomment this line.
  25.                                 //Debug.Log("Fill " + name + " (" + Time.frameCount + ")");
  26.  
  27.                                 if (mMatrixFrame != frame)
  28.                                 {
  29.                                         mLocalToPanel = panel.worldToLocal * cachedTransform.localToWorldMatrix;
  30.                                         mMatrixFrame = frame;
  31.                                 }
  32.                                 geometry.ApplyTransform(mLocalToPanel);
  33.                                 mMoved = false;
  34.                                 return true;
  35.                         }
  36.                         return hadVertices;
  37.                 }
  38.                 else if (geometry.hasVertices)
  39.                 {
  40.                         if (fillGeometry) geometry.Clear();
  41.                         mMoved = false;
  42.                         return true;
  43.                 }
  44.         }
  45.         else if (mMoved && geometry.hasVertices)
  46.         {
  47.                 if (mMatrixFrame != frame)
  48.                 {
  49.                         mLocalToPanel = panel.worldToLocal * cachedTransform.localToWorldMatrix;
  50.                         mMatrixFrame = frame;
  51.                 }
  52.                 geometry.ApplyTransform(mLocalToPanel);
  53.                 mMoved = false;
  54.                 return true;
  55.         }
  56.         mMoved = false;
  57.         return false;
  58. }
  59.  

First, we saw in the deep profiler that the two culprits are the method itself and the ApplyTransform, which means we can largely ignore any other method calls inside the method.
The Matrix4x4 multiply operator leads us to the condition:

  1. else if (mMoved && geometry.hasVertices)

Remember that the mMatrixFrame was set to -1 in the UpdateTransform and mMoved was set to true, this means that this one will always run assuming the widget has vertices  (Sprites, labels). There doesn't seem to be any way to improve this code directly, apart from avoiding some of the calls ealier by not having mMoved set if something hasn't moved.

Looking inside the UIGeometry.ApplyTransform

  1. public void ApplyTransform (Matrix4x4 widgetToPanel)
  2. {
  3.         if (verts.size > 0)
  4.         {
  5.                 mRtpVerts.Clear();
  6.                 for (int i = 0, imax = verts.size; i < imax; ++i) mRtpVerts.Add(widgetToPanel.MultiplyPoint3x4(verts[i]));
  7.  
  8.                 // Calculate the widget's normal and tangent
  9.                 mRtpNormal = widgetToPanel.MultiplyVector(Vector3.back).normalized;
  10.                 Vector3 tangent = widgetToPanel.MultiplyVector(Vector3.right).normalized;
  11.                 mRtpTan = new Vector4(tangent.x, tangent.y, tangent.z, -1f);
  12.         }
  13.         else mRtpVerts.Clear();
  14. }
  15.  
The relative-to-panel vertices, normals and tangents are calculated and saved - doing this every frame means Clearing a list and refiling it with these. Only thing I can see as a potential optimization is to avoid generating normals and tangents unless they are needed (set in the UIPanel drawing the widget). Other than that, the best optimization is to avoid re-applying the transform at all.


Applying improvements
Using times on UIPanel.LateUpdate from a regular profiler (not deep)
Stock: 1.75 - 2.0ms
move time.frameCount: no significant difference
UpdateWidgets optimizations:
simplify conditional: 1.80+ ms slightly slower (boo), removed again.
UpdateTransform optimizations:
mMoved default to false : 0.45 - 1.40ms - A significant improvement. This may have sideeffects, however.
UpdateGeometry optimizations:
pass generateNormals to ApplyTransform: no significant difference.
UIPanel Inspector optimizations:
Turning on Static: 0.20 - 0.60ms A very significant improvement.


Before drawing any conclusions, these numbers assume the computer I am using, the particulars of what's running in the background etc. This means you cannot compare the raw numbers with anything else than other tests from the same setup. I've also only tested with some 200 elements in the list, and not tested the scaling of it (say to 2000). That said, the internal percentages do speak for themselves given these caveats.

1) There is an optimization to be had in changing the UIWidget.UpdateTransform to not set mMoved every frame unless the transform itself has moved or changed - this needs some more tests, to make sure it doesn't break functionality.
2) If you know your widgets does not change while scrolling you can switch the widgetsAreStatic to true on the UIPanel to gain significant performace increase.

This has been a fun little trip debugging performance; I'll test the potential optimizations out some more in other scenarios and make sure they don't break anything, then pullrequest it to the main trunk.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: newlife on May 16, 2015, 06:45:15 PM
Nicki,
I read your post carefully.
You made an extensive testing, but your conclusion (turning on Static on panel) is something almost obvious.
You also forgot to make a compare with 2.7 performances, which are the target performance to aim.
Even with static on, performance differences are HUGE.
Today I tested my app (both old and new version) on a Samsung Galaxy S4 mini, which is a good performance device.
Performance differences on big draggable panel are extremely evident (old version very smooth, new version a lot of breaks in the scroll).

I think that the reason why there aren't a lot of people complaining about this is cause few people use big draggable panel and / or test they app on powerful devices.
Anyway I think that this is a major issue in NGUI 3.x, cause it implies that there must be something wrong in the code.

I really hope to hear ArenMook thought about this, hopefully not a conservative reply.

Thanks,
Michele Di Lena
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: r.pedra on May 18, 2015, 03:34:10 AM
Yeah, we have this problem with a ScrollPanel with 180 widgets underneath. You can see a big mountain in the profiler when scrolling. It's worse on the device (LG G3, which is powerful).
The only solution to this problem is to give the user an option to disable objects in the scrollview(this is a map with planes moving in realtime). But you can not do this for every scrollview. (Change to static didn't do anything for us)
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: Nicki on May 22, 2015, 02:44:27 PM
Hey Michele & r.pedra,

Thanks for reading. I sorta felt the "use static" would be painfully obvious, but I mainly wanted to show the numbers associated to make it clearer just how much it helps.

I purposely didn't make a comparison with 2.7, because there have been so many underlying changes that it's not a fair comparison - performance in certain areas, like huge scroll lists, have been sacrificed in order to have explicit ordering the way it is now and other feature improvements.

There are ways to combat this, by not just putting all objects in but instead using containers which you fill with information when they're supposed to draw - then you're generally limited to <10 containers at any one time vastly increasing your performance (and making unlimited scroll lists possible).

NGUI does not do this internally at this point, and it's up to the individual developer to implement this. Without changing the scrollviews fundamentally, it's not something that's easily done on NGUI's end.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: ArenMook on May 22, 2015, 05:34:14 PM
I can look into this in a bit, but right now I am extremely busy with Windward. It just launched on Steam and has been a very hectic experience.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: r.pedra on May 27, 2015, 11:29:17 AM
Yes I saw that on Steam, front page , that's awesome ! You're like super rich now ahah :P
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: r.pedra on May 27, 2015, 11:37:14 AM
Hey Michele & r.pedra,

Thanks for reading. I sorta felt the "use static" would be painfully obvious, but I mainly wanted to show the numbers associated to make it clearer just how much it helps.

I purposely didn't make a comparison with 2.7, because there have been so many underlying changes that it's not a fair comparison - performance in certain areas, like huge scroll lists, have been sacrificed in order to have explicit ordering the way it is now and other feature improvements.

There are ways to combat this, by not just putting all objects in but instead using containers which you fill with information when they're supposed to draw - then you're generally limited to <10 containers at any one time vastly increasing your performance (and making unlimited scroll lists possible).

NGUI does not do this internally at this point, and it's up to the individual developer to implement this. Without changing the scrollviews fundamentally, it's not something that's easily done on NGUI's end.

Hi don't think our use case is compatible with your solution.

Basically You have a map:

----------
|          |
|          |
|          |
----------

But the user only see a part of this map and can scroll on it.
On this map, planes are moving along path rendered with Vectrosity(that literally have no impact on the scrollview perf).
What you suggest is to move the planes into multiple Panel is that right? It will increase drawcall no? Is the drawcall increase going to make it worse?

PS: I link an image of what we're doing, this way, you will understand it better
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: yuewah on May 30, 2015, 10:00:21 AM
@r.pedra, is the plane contains other UI components ? e.g. UILabel, UIButton ?? If not, you should use other method if you want to optimise it, e.g. Particle System.
Title: Re: Huge performance drop after updatating from 2.7.0 to 3.7.5
Post by: r.pedra on June 01, 2015, 03:32:51 AM
Not a bad idea. I'll work on it