Author Topic: Fill() / Full rebuild on UIPanel fills heap  (Read 7228 times)

breakmachine

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 3
  • Posts: 71
    • View Profile
Fill() / Full rebuild on UIPanel fills heap
« on: October 11, 2013, 02:24:19 AM »
I'm still struggling with heap allocation in my game using NGUI.

The main problem is that NGUI does a full rebuild of all drawcalls on a number of occasions. For instance when setting the depth of a panel by code, when enabling a panel and sometimes when enabling/disabling widgets.

Only workaround I can think of is setting up separate UIs to minimize the effect of a full rebuild but I'm not really keen on it.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Fill() / Full rebuild on UIPanel fills heap
« Reply #1 on: October 11, 2013, 06:51:26 AM »
Yes, a full rebuild is necessary when a new panel comes into play. Why is this an issue? Surely you don't enable / disable panels every frame?

breakmachine

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 3
  • Posts: 71
    • View Profile
Re: Fill() / Full rebuild on UIPanel fills heap
« Reply #2 on: October 11, 2013, 02:56:52 PM »
No of course not every frame. But since I have healthbars, points when killed and radar representation for each enemy this results in quite a few "ever moving" sprites. I don't want to redraw these sprites every frame so I make them into panels (because you can move panels without redrawing mesh). So whenever an enemy takes damage I want to enable the panel with that specific healthbar, when an enemy dies I want to enable a panel with a point label and whenever an enemy spawns or dies I want to enable or disable a panel with the radar representation of that enemy.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Fill() / Full rebuild on UIPanel fills heap
« Reply #3 on: October 12, 2013, 09:43:39 AM »
I see. Well, I'll do an optimization pass on all of this in the near future. For now I suggest just not worrying about it. :)

sqspbx

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 1
    • View Profile
Re: Fill() / Full rebuild on UIPanel fills heap
« Reply #4 on: November 06, 2013, 06:44:02 AM »
I'm currently working on a scrollable panel with an infinite scroll in it and I've got a performance problem. When i pass some data to elements of my scrollable content, some of them (actually, UISprites) become enabled/disabled. And this somehow causes a full rebuild of the root UIPanel of my widget.
As a result I've got a huge performance drop when dragging my panel. Does the optimization, mentioned above, involve this problem? Or are there some other tricks out there to fix this issue?

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Fill() / Full rebuild on UIPanel fills heap
« Reply #5 on: November 06, 2013, 07:09:10 AM »
Why do they become disabled/enabled? The key here is to not do it often, as disabling / enabling widgets causes the batching logic to re-evaluate all the widgets in the panel due to a potentially altered depth hierarchy.

breakmachine

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 3
  • Posts: 71
    • View Profile
Re: Fill() / Full rebuild on UIPanel fills heap
« Reply #6 on: November 06, 2013, 12:13:57 PM »
I have attached a screenshot of the profiler to inspire you to fix this issue :)

JeremyBurgess

  • Guest
Re: Fill() / Full rebuild on UIPanel fills heap
« Reply #7 on: November 14, 2013, 07:59:22 AM »
Hi guys,

We're seeing similar problems to this primarily due to two things:
 1. An animation which makes a UI element flicker by turning it on and off. (We can fix this by making it effect colour, but it's unfortunate to need to).
 2. Materials being dynamically set by UITextures being populated from the web (such as user's facebook pictures).

The root of the problem as I see it is really that the performance hit/GC alloced gets worse in proportion to the size of your UI, and not in proportion to how one organises stuff. Basically, the way things are now, a full rebuild will always happen every now and then, and if your UI is sufficiently big this will always be slow and expensive. I'm not sure what your plans are for optimising this, but I would like to see you remove the need to refresh the whole UI draw call hierarchy when a single thing disables or enables.

My suggestion (and what I'm probably going to have a stab at doing if I can find the time) is to do one of 2 things:
 1. Bin the widgets by panel and rebuild each panel when the dirtying changes occur. This is non-ideal due to the fact that it invalidates a lot of assumptions you've made in NGUI 3 allowing you to aggregate draw calls across panels, however given that we can now control panel depth this appeals to me intuitively.
 2. Bin the widgets by panel depth, and dirty the bins when the dirtying events occur. This should preserve behaviour of NGUI completely, while allowing a user to completely isolate frequently changed widgets from the rest of the draw call hierarchy.

I'm not yet sure how feasible this is, but I'll post here if I manage to do it.

Cheers,
Jeremy

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Fill() / Full rebuild on UIPanel fills heap
« Reply #8 on: November 14, 2013, 02:03:23 PM »
This is a top priority item on my plate right now. I won't be always re-sorting the list in the future which will improve performance a fair bit in this case.

JeremyBurgess

  • Guest
Re: Fill() / Full rebuild on UIPanel fills heap
« Reply #9 on: November 14, 2013, 02:14:52 PM »
That's great to hear! Not needing to re-sort will be very helpful. Are you planning to break things back down so that enabling a single widget anywhere doesn't incur a full rebuild of the draw call structure? I've started looking at doing it on my end, but there are interplays between the common widget list and the implicit list of widgets per panel which make it a larger task than I had anticipated.

I guess at a fundamental level, if I were to remove the common UI Widget list completely it would actually make it quite a bit easier - I'll post here with how I go, but I unfortunately have a lot of other priorities which mean I can't give this the time I'd like. I think it should be fairly straightforward if I give each panel a pointer back to its widgets (as well as each widget knowing its panel), and then update the depth bins based on these much smaller lists.

Incidentally, I've settled on option 2 - although I think it's much more complex the perspective of managing the widget draw bins, it means the draw calls are much more straightforward to handle - their render queues can be generated in a fairly straightforward manner (something along the lines of panelDepth * 1000 + panelDrawCallIndex), rather than needing to have some magic hackery to generate a render queue number based on the number of panels which currently exist (which could change and require a complete rebuild similar to what already happens).

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Fill() / Full rebuild on UIPanel fills heap
« Reply #10 on: November 14, 2013, 03:52:26 PM »
I'm thinking of keeping the sprite list, just adding some checks that wouldn't ask for a rebuild if the sprite got inserted into a specific draw call and didn't need to break up other draw calls. We'll see after I do it.