Thread Tools Display Modes
09/01/15, 04:06 PM   #1
ZOS_ChipHilseberg
ZOS Staff!
Premium Member
Yes this person is from ZeniMax!
Join Date: Oct 2014
Posts: 551
2.1 and SetHandler

We've been profiling some UI code since 2.1 went live and we've noticed some spikes with SetHandler on controls. Especially when the code is frequently making a new anonymous closure and passing that to SetHandler. In a lot of cases this can be avoided during play time by setting the handler on the control in the XML or on a template (virtual = true) in the XML so that cost is paid during the load. The handler closure should also only be made once instead of every time SetHandler is called, by storing the closure in a local or on a table.
  Reply With Quote
09/02/15, 03:29 AM   #2
Wandamey
Guest
Posts: n/a
probably a stupid question but i suppose someone has to ask it...

what is a closure?

an example would be nice too.

Last edited by Wandamey : 09/02/15 at 03:34 AM.
  Reply With Quote
09/02/15, 03:58 AM   #3
Harven
 
Harven's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 135
Hey,
Wandamey, quick google search and here it is: http://www.lua.org/pil/6.1.html
  Reply With Quote
09/02/15, 04:10 AM   #4
Wandamey
Guest
Posts: n/a
...

Is there a difference between setting the Handler in a xml or setting on load with a control creation in lua?

now that i see what a closure is (in the great lines) i'd still like an example... i mean related to what Chip said.
  Reply With Quote
09/02/15, 04:16 AM   #5
merlight
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 671
I'll try to provide a concrete example from some add-on, I'll probably use Fyrakin's MiniMap because it seems to have been hit hard by this.
  Reply With Quote
09/02/15, 04:18 AM   #6
Wandamey
Guest
Posts: n/a
Originally Posted by merlight View Post
I'll try to provide a concrete example from some add-on, I'll probably use Fyrakin's MiniMap because it seems to have been hit hard by this.



I hope you can make it 3 lines with pictures


the big picture i see here is : store your functions first. but how do i pass a variable to it without using an Anonymous func again to call it? -- maybe the table?

Last edited by Wandamey : 09/02/15 at 04:23 AM.
  Reply With Quote
09/02/15, 04:43 AM   #7
merlight
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 671
Ok found something that might illustrate the issue and possible solution. From MiniMap.lua function AddLocation:
Lua Code:
  1. local pinTooltipInfo = ZO_MapPin.TOOLTIP_CREATORS[MAP_PIN_TYPE_LOCATION]
  2. locationPin:SetHandler("OnMouseEnter", function(pin)
  3.     FyrMM.SetTargetScale(pin, 1.3)
  4.     if not FyrMM.SV.PinTooltips then return end
  5.     if pinTooltipInfo.tooltip then
  6.         InitializeTooltip(ZO_MapLocationTooltip, Fyr_MM, TOPLEFT, 0, 0)
  7.         pinTooltipInfo.creator(pin.m_Pin)
  8.         IsCurrentLocation(pin)
  9.     end
  10. end)

The anonymous function uses pinTooltipInfo from outer scope, thus each time this runs, a new closure holding that local variable must be created.

Possible solution: define the handler function outside AddLocation, and change it to obtain pinTooltipInfo in a different way -- either make it a member of an argument it gets, like this:

Lua Code:
  1. locationPin.pinTooltipInfo = ZO_MapPin.TOOLTIP_CREATORS[MAP_PIN_TYPE_LOCATION]
  2. locationPin:SetHandler("OnMouseEnter", locationPin_OnMouseEnter)

or in this case it can easily be obtained from ZO_MapPin in the handler itself:

Lua Code:
  1. local function locationPin_OnMouseEnter(pin)
  2.     FyrMM.SetTargetScale(pin, 1.3)
  3.     if not FyrMM.SV.PinTooltips then return end
  4.     local pinTooltipInfo = ZO_MapPin.TOOLTIP_CREATORS[pin.m_PinType]
  5.     if pinTooltipInfo.tooltip then
  6.         InitializeTooltip(ZO_MapLocationTooltip, Fyr_MM, TOPLEFT, 0, 0)
  7.         pinTooltipInfo.creator(pin.m_Pin)
  8.         IsCurrentLocation(pin)
  9.     end
  10. end)
  Reply With Quote
09/02/15, 05:04 AM   #8
Wandamey
Guest
Posts: n/a
what if i have something iterated like this


for k,v in pairs(table) do
control[k]:SetHandler("OnWhatever", function(self) myfunc(self, k, v.data) end)
end

like most of user tooltips could do for example.
  Reply With Quote
09/02/15, 05:30 AM   #9
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,912
Thx for asking the question about closures (didn't know the wording neither) Wandamey, and the explanation merlight!

Reading chips comment:
In a lot of cases this can be avoided during play time by setting the handler on the control in the XML or on a template (virtual = true) in the XML so that cost is paid during the load. The handler closure should also only be made once instead of every time SetHandler is called, by storing the closure in a local or on a table
I'd like to repeat Wandamey's question:

Is there a difference between setting the handler via XML, or creating the controls in the lua script and assigning the handlers there?
In what cases should we use XML definitions then.
And how are we supposed to define it in an XML file?

Maybe we can get an example of an anonymous closure that is creating spikes, and the same thing fixed in lua script, and in addition fixed in XML instead? This would be great.
  Reply With Quote
09/02/15, 07:57 AM   #10
ZOS_ChipHilseberg
ZOS Staff!
Premium Member
Yes this person is from ZeniMax!
Join Date: Oct 2014
Posts: 551
Originally Posted by Baertram View Post
Is there a difference between setting the handler via XML, or creating the controls in the lua script and assigning the handlers there?
In what cases should we use XML definitions then.
And how are we supposed to define it in an XML file?
Defining a handler in XML would set it on the control at the control's creation, which in the case of a non-virtual control would be during the load screen, which would hide any cost. This is pretty much the same as doing it in Lua during the add-on loading process. The main thing to avoid would be making a new closure and calling SetHandler with it frequently. Unless you're closing over local variables with the closure you should be able to only call SetHandler once on a control (for each handler type). If you are closing over local variables then you may want to consider not doing that, and storing the values on the control instead.
  Reply With Quote
09/02/15, 08:17 AM   #11
Fyrakin
 
Fyrakin's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 129
Thanks for heads up, now I have something to dig through .
  Reply With Quote
09/02/15, 08:33 AM   #12
Wandamey
Guest
Posts: n/a
Originally Posted by ZOS_ChipHilseberg View Post
Defining a handler in XML would set it on the control at the control's creation, which in the case of a non-virtual control would be during the load screen, which would hide any cost. This is pretty much the same as doing it in Lua during the add-on loading process. The main thing to avoid would be making a new closure and calling SetHandler with it frequently. Unless you're closing over local variables with the closure you should be able to only call SetHandler once on a control (for each handler type). If you are closing over local variables then you may want to consider not doing that, and storing the values on the control instead.

whaow... how to scare the noobs.
sry but i still need a translation.

are you saying "stop using anonymous functions for your handlers" or there is more to it?
  Reply With Quote
09/02/15, 09:00 AM   #13
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,912
Originally Posted by Wandamey View Post
whaow... how to scare the noobs.
sry but i still need a translation.

are you saying "stop using anonymous functions for your handlers" or there is more to it?
Same here An example with

a) don't use anymore
b) use this instead

would be a great help
  Reply With Quote
09/02/15, 09:27 AM   #14
votan
 
votan's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2014
Posts: 577
Originally Posted by Wandamey View Post
whaow... how to scare the noobs.
sry but i still need a translation.

are you saying "stop using anonymous functions for your handlers" or there is more to it?
Avoid using variables declared out-side anonymous, nested functions, if you can pass them via member of control.
Lua Code:
  1. local var = "outside"
  2. ctl:SetHandler("WhatEver", function() d("from " .. var)
  3. -- var must be "magically" known by storing a closure.
  4. -- (Which is how large??? And stored how long???)
  5. end)
=>
Lua Code:
  1. local var = "outside"
  2. ctl.save_var = var
  3. ctl:SetHandler("WhatEver", function(self) d("from " .. self.save_var)
  4. -- self is ctl, because handler get passed the control they are assigned to.
  5. -- But now all used variables are declared inside:
  6. -- Function is "stand-alone". => no closure.
  7. -- And does not need to be nested anymore.
  8.  end)
  Reply With Quote
09/02/15, 09:32 AM   #15
ZOS_ChipHilseberg
ZOS Staff!
Premium Member
Yes this person is from ZeniMax!
Join Date: Oct 2014
Posts: 551
1. Best:
Lua Code:
  1. <Texture name="Sample">
  2.     <OnMouseEnter>
  3.         --Code
  4.     </OnMouseEnter>
  5. </Texture>

2. Also Good:
Lua Code:
  1. --This is run once when the addon loads
  2. function InitializeAddon(texture)
  3.     texture:SetHandler("OnMouseEnter", function()
  4.         --Code
  5.     end)
  6. end

3. Bad:
Lua Code:
  1. --This is run often
  2. function SetupTexture(texture, width, height)
  3.     texture:SetHandler("OnMouseEnter", function()
  4.         --Code
  5.     end)
  6.     texture:SetDimensions(width, height)
  7. end

Instead of 3, which keeps setting the same handler code over and over again use 1 or 2 to only do it once.
  Reply With Quote
09/02/15, 09:42 AM   #16
votan
 
votan's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2014
Posts: 577
Question:
What if like this:
Bad?
Lua Code:
  1. local function DoSomeWhat()
  2.         --Code
  3. end
  4.  
  5. --This is run often
  6. function SetupTexture(texture, width, height)
  7.     texture:SetHandler("OnMouseEnter", DoSomeWhat)
  8.     texture:SetDimensions(width, height)
  9. end
  Reply With Quote
09/02/15, 09:48 AM   #17
Wandamey
Guest
Posts: n/a
aahh that's less scary.

if for example i set up my control with the anchors and all this like this, it's ok even if created in the course of the game? : (will create only once)

Code:
if not (control) then 
   control = wm:CreateControl(blabla)
   etc...
   control:SetHandler("blabla", function)
end
and if i got it, it's better to use 2 different textures each one with its own handler, then hide/show the one you need instead of swaping dds/Handler?

and votan, thanks for the workaround... i learned something like that not long ago, still need to work on it


Edit: btw, how to "remove" a Handler? Setting it on nil will delete the previous "closure" too? i suppose not if rewriting it just creates more ( i may not get exactly what a closure is but i hope you get the idea of the question )

Last edited by Wandamey : 09/02/15 at 10:09 AM.
  Reply With Quote
09/02/15, 10:00 AM   #18
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,912
Thanks for the examples, they really helped.

I hope using the local function for the handler, like votan asked, is ok too.
And I also hope the ~= nil check in Wandamey's example is alright, as the handler should only be assigned once then.

Using 2 different texture controls (and showing texture 1 as you are hiding texture 2), instead of only 1 texture control where you just change the dds file, only makes sense if the handler changes too. But even then I'd think removing the old handler and setting a new one is better then creating a second texture control in total?

Last edited by Baertram : 09/02/15 at 10:09 AM.
  Reply With Quote
09/02/15, 10:14 AM   #19
Wandamey
Guest
Posts: n/a
lol that cross edit... i let you do the speaking now
  Reply With Quote
09/02/15, 10:49 AM   #20
ZOS_ChipHilseberg
ZOS Staff!
Premium Member
Yes this person is from ZeniMax!
Join Date: Oct 2014
Posts: 551
Originally Posted by votan View Post
Question:
What if like this:
Bad?
Lua Code:
  1. local function DoSomeWhat()
  2.         --Code
  3. end
  4.  
  5. --This is run often
  6. function SetupTexture(texture, width, height)
  7.     texture:SetHandler("OnMouseEnter", DoSomeWhat)
  8.     texture:SetDimensions(width, height)
  9. end
This is slightly better than the bad example. It doesn't remake the closure every time. But it's still a waste to keep setting that same exact handler code on the control repeatedly. So it would be best to set some sort of flag to only do it once.
  Reply With Quote

ESOUI » Developer Discussions » General Authoring Discussion » 2.1 and SetHandler

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off