ESOUI

ESOUI (https://www.esoui.com/forums/index.php)
-   General Authoring Discussion (https://www.esoui.com/forums/forumdisplay.php?f=174)
-   -   2.1 and SetHandler (https://www.esoui.com/forums/showthread.php?t=5106)

ZOS_ChipHilseberg 09/01/15 04:06 PM

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.

Wandamey 09/02/15 03:29 AM

probably a stupid question but i suppose someone has to ask it...

what is a closure?

an example would be nice too.

Harven 09/02/15 03:58 AM

Hey,
Wandamey, quick google search and here it is: http://www.lua.org/pil/6.1.html ;)

Wandamey 09/02/15 04:10 AM

...

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.

merlight 09/02/15 04:16 AM

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.

Wandamey 09/02/15 04:18 AM

Quote:

Originally Posted by merlight (Post 23157)
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.


:eek:

I hope you can make it 3 lines with pictures :D


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?

merlight 09/02/15 04:43 AM

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)

Wandamey 09/02/15 05:04 AM

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.

Baertram 09/02/15 05:30 AM

Thx for asking the question about closures (didn't know the wording neither) Wandamey, and the explanation merlight!

Reading chips comment:
Quote:

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.

ZOS_ChipHilseberg 09/02/15 07:57 AM

Quote:

Originally Posted by Baertram (Post 23162)
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.

Fyrakin 09/02/15 08:17 AM

Thanks for heads up, now I have something to dig through :).

Wandamey 09/02/15 08:33 AM

Quote:

Originally Posted by ZOS_ChipHilseberg (Post 23166)
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?

Baertram 09/02/15 09:00 AM

Quote:

Originally Posted by Wandamey (Post 23168)
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 :banana:

votan 09/02/15 09:27 AM

Quote:

Originally Posted by Wandamey (Post 23168)
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)

ZOS_ChipHilseberg 09/02/15 09:32 AM

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.

votan 09/02/15 09:42 AM

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

Wandamey 09/02/15 09:48 AM

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 :D


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 )

Baertram 09/02/15 10:00 AM

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?

Wandamey 09/02/15 10:14 AM

lol that cross edit... i let you do the speaking now :D

ZOS_ChipHilseberg 09/02/15 10:49 AM

Quote:

Originally Posted by votan (Post 23172)
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.

Wandamey 09/02/15 11:12 AM

i'm not sure of the protocole here... do I have to sacrifice a Virgin or something to have my questions looked at?
I'm starting to feel a little butthurt tbh.

yeah top post of a second page. ok. nvm. forget it.

QuadroTony 09/02/15 12:00 PM

/offtop


Quote:

Originally Posted by Wandamey (Post 23178)
do I have to sacrifice a Virgin or something

Quote:

Originally Posted by Wandamey

nothing that you can buy, i'm not your *****.


Wandamey 09/02/15 12:09 PM

Quote:

Originally Posted by votan (Post 23172)
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



i suppose

Code:


onload : CreateControl +
texture:SetHandler("OnMouseEnter", DoSomeWhat) -- we got that now i think


local function DoSomeWhat(addon.a,addon.b,addon.c...)
    use addon.a
    use addon.b
    if addon.c then 
      blabla
  else
      blob
  end
end

--This is run often
function SetupTexture(a, b, c)
    texture:SetDimensions(a, c)
    addon.a = a
    addon.b = f(b)
    addon.c = constant  -- whatever
end

would that be the expected solution?


i repeat my question about "removing" a Handler (seems we got 2 different answers with baertram, maybe it would be a good thing to clear it)

is niling a handler as bad as overwrite it?

merlight 09/02/15 12:12 PM

Quote:

Originally Posted by Wandamey (Post 23173)
Edit: btw, how to "remove" a Handler? Setting it on nil will delete the previous "closure" too?

That's exactly how you do it. If there's no other reference to it, it'll be garbage collected eventually.

Wandamey 09/02/15 12:20 PM

so basically, i could patch anything very easily if the same function is called all the programm long by doing this (or just tweak the function with conditions if this has to change):

Lua Code:
  1. if control:GetHandler("OnThisaction") == nil then  control:SetHandler("OnThisaction", definedfunction) end
?

merlight 09/02/15 12:32 PM

You just need to avoid creating a thousand closures. Once you do that, you will notice that your code doesn't need to call SetHandler a thousand times, because all the calls would be identical. Aim for removing unnecessary closures. Set the handler once after constructing the control.

If you need something more complex, like switching an OnUpdate handler on when resizing and off when it's done, you naturally do have to call SetHandler multiple times, but chances are you don't need to create multiple closures.

Wandamey 09/02/15 12:50 PM

Quote:

Originally Posted by merlight (Post 23183)
You just need to avoid creating a thousand closures. Once you do that, you will notice that your code doesn't need to call SetHandler a thousand times, because all the calls would be identical. Aim for removing unnecessary closures. Set the handler once after constructing the control.

If you need something more complex, like switching an OnUpdate handler on when resizing and off when it's done, you naturally do have to call SetHandler multiple times, but chances are you don't need to create multiple closures.

thing is i get it overall, but when i look at my plugin for someone else's addon, there are a lot of things I can't control. But i'll figure out something. Just hoping it won't be worse after from another point of view than this particular problem.

Fyrakin 09/02/15 02:49 PM

OK, I just changed texture handlers in My add-on using pre-declared functions and setting handlers only when new controls are created. I expected to see much better results, though performance improvement is visible but not as I hoped :(.

Randactyl 09/02/15 04:24 PM

My question is, what changed in the IC update that made this so much more of a problem than it was previously? Of course we should not be creating unnecessary closures, but it seems like the game is handling the issue with less grace than it should (or at least less than it was previously).

Wandamey 09/02/15 05:26 PM

Quote:

Originally Posted by Fyrakin (Post 23187)
OK, I just changed texture handlers in My add-on using pre-declared functions and setting handlers only when new controls are created. I expected to see much better results, though performance improvement is visible but not as I hoped :(.

wait, i'm almost done with mine... it'll make the tv shows pretending that it takes only one blondine to shut down the internet look ridiculous.

(yeah I feel guilty for last week-end now)

Fyrakin 09/03/15 02:15 AM

Quote:

Originally Posted by Randactyl (Post 23192)
My question is, what changed in the IC update that made this so much more of a problem than it was previously? Of course we should not be creating unnecessary closures, but it seems like the game is handling the issue with less grace than it should (or at least less than it was previously).

Introduction of OnTextureLodead? I suspectį it also led to memory leak of sorts.

Baertram 09/03/15 03:54 AM

haha, I like "OnTextureLodead". This explains in short why the peaks happen :D

Fyrakin 09/03/15 04:47 AM

Quote:

Originally Posted by Baertram (Post 23213)
haha, I like "OnTextureLodead". This explains in short why the peaks happen :D

:D I was starting to think no one will see that.

@AlphaLemming 09/03/15 05:46 AM

Is there any example for designing a UI with XML and using it in LUA with my Addon ?
I never was happy to create this all in LUA.

Fyrakin 09/03/15 06:12 AM

Quote:

Originally Posted by @AlphaLemming (Post 23227)
Is there any example for designing a UI with XML and using it in LUA with my Addon ?
I never was happy to create this all in LUA.

I have some controls like buttons with handlers configured in XML, you can look at MiniMap.xml

@AlphaLemming 09/03/15 07:44 AM

Thanks, that will help. Is there a possibilty to use array names for XML elements?

ZOS_ChipHilseberg 09/03/15 07:46 AM

Quote:

Originally Posted by Fyrakin (Post 23211)
Introduction of OnTextureLodead? I suspectį it also led to memory leak of sorts.

Internally we haven't experienced any noticable change in performance of the stock UI (which runs as an addon the same as the custom addons). So any information on what in particular is triggering the performance problems you are seeing is helpful. Why do you think OnTextureLoaded is involved?

Fyrakin 09/03/15 09:50 AM

Quote:

Originally Posted by ZOS_ChipHilseberg (Post 23232)
Internally we haven't experienced any noticable change in performance of the stock UI (which runs as an addon the same as the custom addons). So any information on what in particular is triggering the performance problems you are seeing is helpful. Why do you think OnTextureLoaded is involved?

Because after update, while on PTS I was experiencing severe performance degrade compared to what was on-live. Its just my theory, suspicion, superstition, etc. Well, to be honest we have very limited profiling means/tools. Just a game client and strong will to do something. We have no luxury to monitor which process utilizes most resources so we have to do it on try and error basis. So, based on /script d(collectgarbage("count")) output I observed a very fast lua memory usage increase I was comparing it to how it ran on live and pts, these figures haven't changed.

So, as you suggested I changed sethandler setting during add-on init for most of the texture add-on would use (handlers mostly for tooltips). I even moved anonymous functions to predeclared ones. I didn't see any change in resource consumption and I haven't noticed any signifficant performance improvement. SO, what else you would suggest us to do? I as many others would like to have minimap functionality, but at current state it is to say the least is not usable.

Follow up.
I managed to address the performance issues. Even though I no longer use my own tables to store and manage custom pin data I still observed fast collectgarbage("count") build up.

Fyrakin 09/03/15 10:31 PM

Quote:

Originally Posted by Wandamey (Post 23158)
:eek:

I hope you can make it 3 lines with pictures :D


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?

What about self.your variable?

Lodur 09/04/15 01:02 AM

Quote:

Originally Posted by Wandamey (Post 23153)
probably a stupid question but i suppose someone has to ask it...

what is a closure?

an example would be nice too.

A closure is a combination of context and code...

Wandamey 09/04/15 04:53 AM

Quote:

Originally Posted by Fyrakin (Post 23249)
What about self.your variable?

thanks, i figured it out with votan example, and everything is uploaded.
I was afraid i wouldn't use properly the "self", but i would have used an addon variable to temporary store it if it hadn't worked.
surprisingly, it went so flawlessly that I had to recheck 3 times that i didn't edit in the pts folder instead of the live one :D


Quote:

Originally Posted by Lodur (Post 23250)
A closure is a combination of context and code...

that's what i understood from harven's link, but I still dont understand why it doesn't simply replace the previous one when the context is the same. Not that it prevents me from doing what must be done, but it is still cloudy unclear.

merlight 09/04/15 05:55 AM

Quote:

Originally Posted by Wandamey (Post 23254)
... I still dont understand why it doesn't simply replace the previous one when the context is the same.

Think about this for a while. The context is not the same on each run, that's the whole point of creating a closure. Outer local variables are different, completely separate from the previous run's local variables.

Wandamey 09/04/15 06:24 AM

it's a bit hard to tell i I don't know how it is stored and processed in the first place.

i mean, the handler has an identifier : control/"OnWhatever", i thought what you store in it would replace the old one, and eventually replace the previous results with the new ones when ran again, why would the memory keep the old runs ad vitam aeteram?


edit.. well ok, different variables... it doesn't explain why rewriting a stored procedure like in the example you gave me or like votan asked is that bad. (i see it can be better by not running unecessary calls, but it aint the end of the world either, i mean, ZOS_DanB even recommanded to do an update all session long when DerBombo asked for an EVENT that triggers at the end of the session...)


re edit : to understand why i'm a bit puzzled, maybe i should add that until now I thought that the interest of having local vars was that (i believed) they were trashed after the function was run

merlight 09/04/15 07:05 AM

Quote:

Originally Posted by Wandamey (Post 23257)
i mean, the handler has an identifier : control/"OnWhatever", i thought what you store in it would replace the old one

Yes, of course. The problem is probably not SetHandler per se. It's the fact that creating a closure requires a dynamic allocation (I don't even know whether just one, or one for each variable; depends on how much Lua optimizes that). Remember that everything you allocate must eventually be collected after you discard it. Too many closures might be thrashing the garbage collector.

Quote:

Originally Posted by Wandamey (Post 23257)
re edit : to understand why i'm a bit puzzled, maybe i should add that until now I thought that the interest of having local vars was that (i believed) they were trashed after the function was run

Local variables may only exist in registers (i.e. cost virtually nothing) as long as they're not used in any closure. Once you create a closure, they HAVE to be moved to dynamically allocated memory, because the closure typically outlives the block it has been created in.

Wandamey 09/04/15 07:30 AM

Quote:

Originally Posted by merlight (Post 23261)
Local variables may only exist in registers (i.e. cost virtually nothing) as long as they're not used in any closure. Once you create a closure, they HAVE to be moved to dynamically allocated memory, because the closure typically outlives the block it has been created in.

whow thanks.



as soon as my head get rif of this picture, it should be way more clear.

Baertram 09/04/15 08:39 AM

To see if I understood everything correct about the performance stuff I'd like to show you an example and ask, how I need to change it (including ideas).

Current code:
Lua Code:
  1. button:SetHandler("OnMouseEnter", function(self)
  2. --tooltipText is a local defined variable, outside of the closure, but inside the same function which sets the handler                 
  3. tooltipText = outputFilterState(false, settingsVars.settings.splitFilters, p_FilterPanelId, buttonId, mappingVars.settingsFilterStateToText[tostring(getSettingsIsFilterOn(buttonId, p_FilterPanelId))])
  4.                     if tooltipText ~= "" then
  5.                         local showToolTip = true
  6.                         --Don't show a tooltip if the context menu for gear sets is shown at the filter button
  7.                         if contextMenu.GearSetFilter[locVars.gFilterWhere] ~= nil then
  8.                             showToolTip = contextMenu.GearSetFilter[locVars.gFilterWhere]:IsHidden()
  9.                         end
  10.                         --Don't show a tooltip if the context menu for research, deconstruction & improvement is shown at the filter button
  11.                         if showToolTip and contextMenu.ResDecImpFilter[locVars.gFilterWhere] ~= nil then
  12.                             showToolTip = contextMenu.ResDecImpFilter[locVars.gFilterWhere]:IsHidden()
  13.                         end
  14.                         if showToolTip and contextMenu.SellGuildIntFilter[locVars.gFilterWhere] ~= nil then
  15.                             showToolTip = contextMenu.SellGuildIntFilter[locVars.gFilterWhere]:IsHidden()
  16.                         end
  17.                         if showToolTip then
  18.                             ZO_Tooltips_ShowTextTooltip(button, BOTTOM, tooltipText)
  19.                         end
  20.                     end
  21.                 end)

Optimized code:
Lua Code:
  1. button.tooltipText = tooltipText
  2. button:SetHandler("OnMouseEnter", function(self)
  3.     button.tooltipText = outputFilterState(false, settingsVars.settings.splitFilters, p_FilterPanelId, buttonId, mappingVars.settingsFilterStateToText[tostring(getSettingsIsFilterOn(buttonId, p_FilterPanelId))])
  4.     if self.tooltipText ~= "" then
  5.         local showToolTip = true
  6.         --Don't show a tooltip if the context menu for gear sets is shown at the filter button
  7.         if contextMenu.GearSetFilter[locVars.gFilterWhere] ~= nil then
  8.             showToolTip = contextMenu.GearSetFilter[locVars.gFilterWhere]:IsHidden()
  9.         end
  10.         --Don't show a tooltip if the context menu for research, deconstruction & improvement is shown at the filter button
  11.         if showToolTip and contextMenu.ResDecImpFilter[locVars.gFilterWhere] ~= nil then
  12.             showToolTip = contextMenu.ResDecImpFilter[locVars.gFilterWhere]:IsHidden()
  13.         end
  14.                     if showToolTip and contextMenu.SellGuildIntFilter[locVars.gFilterWhere] ~= nil then
  15.                         showToolTip = contextMenu.SellGuildIntFilter[locVars.gFilterWhere]:IsHidden()
  16.                     end
  17.         if showToolTip then
  18.             ZO_Tooltips_ShowTextTooltip(self, BOTTOM, self.tooltipText)
  19.         end
  20.     end
  21. end)

I changed the button variable inside the anonymous SetHandler function to use the "self".
And I changed the local tooltipText variable usage inside the anonymous SetHandler function to use the self.tooltipText variable now, which was passed to the button control outside the closure.

But what about all the other variables used inside the anonymous SetHandler function, like "buttonId" (which is a parameter of the calling function, which is setting the handler), "settingsVars.settings.splitFilters" (which is an addon wide known local variable set as the settings in LAM 2.0 panel get changed), "p_FilterPanelId" (which is a parameter of the calling function too)?
Do I need to pass them to the button control too and use the self.variableName inside the anonymouse function then?

Wandamey 09/04/15 08:55 AM

Quote:

Originally Posted by Baertram (Post 23270)
snip


i think you should define : in the first example
button.tooltiptext = tooltiptext

-- Edit : and define it here, not in the function btw with
Lua Code:
  1. button.tooltipText = outputFilterState(false, settingsVars.settings.splitFilters, p_FilterPanelId, buttonId, mappingVars.settingsFilterStateToText[tostring(getSettingsIsFilterOn(buttonId, p_FilterPanelId))])


elsewhere you define this
Code:

local function MyFunction(self)

 local  tooltiptext = self.tooltiptext  -- just to keep the same variable and not mix everything up if your function is long like my arm - self here is button, cause it's called from the button

  blabla all your code from the anonymous function blabla

end

then for the Handler :
Quote:

button:SetHandler("OnMouseEnter", MyFunction)
now lets the teachers look at the homework :o

merlight 09/04/15 09:06 AM

Quote:

Originally Posted by Baertram (Post 23270)
I changed the button variable inside the anonymous SetHandler function to use the "self".

Yes, that's good.

Quote:

Originally Posted by Baertram (Post 23270)
And I changed the local tooltipText variable usage inside the anonymous SetHandler function to use the self.tooltipText variable now, which was passed to the button control outside the closure.

I don't see why it can't be local inside the anonymous function, you're assigning it from outputFilterState() right there. Edit: I guess you want to call outputFilterState inside the handler, not before it's set, because settings might change in-between.

outputFilterState, buttonId, settingsVars... everything that's local above the anonymous function definition goes into the closure's context.

I think you don't need to worry about that if you're only doing the whole thing once per user action. I'm saying this because that's what I do. For example here:
https://github.com/merlight/eso-merT...window.lua#L52. This function is called once after window creation. There's one closure for savePos and one for resizeStart, and they're used for handlers on lines 58-60. But each time resizeStart is called, it creates a new closure for OnUpdate handler. Does it bother me? I could put self or panel inside control and define that handler outside. But resizeStart is only called once per click on control border, that's not enough to force me to make it a little bit more convoluted.

Baertram 09/04/15 09:25 AM

Thanks for the hints. I'll try to change it to local functions instead of the anonymous then too.

Ok maybe it is easier to understand where the current variables are used, and how they are used, if you take a look at the current version of FCOItemSaver and look inside the function "AddOrChangeFilterButton(...)" in source code line 6300.

The SetHandler call I described above is in line 6336.

The tooltipText could be inside the closure too, yes. I don't know why I defined it outside in the past as it will be overwritten by the outputFilterState(...) function again. I guess I had some reason and need to change and test to find it again (or drop it :D). Thanks.
But you're right: I'm only setting the handler, and updating the tooltip text, if the user decided to use the tooltip for that button.
So if he changes the settings and moves the mouse over the existing button the handler needs to be updated to hide the tooltip instead. I guess I need to NIL the handler then too before changing it.

This example here, function AddOrChangeFilterButton(...), is called everytime you open the crafting station, mail panel, player trade panel, guild bank, bank to update the inventory filter buttons. So it is not used constantly but after a user action, right.

Wandamey 09/04/15 09:55 AM

shouldn't buttonId be a member of self at some point?

(haven't looked at the whole code yet and i don't know when or how you declare it, but thats why i suggested to define your tooltip text outside btw)

Baertram 09/04/15 10:03 AM

Defining it outside won't work as the settings could be changed after the handler was set.
And buttonId is a parameter of the calling function AddOrChangeFilterButton. So I guess, and that is what I asked :-), yes: it should be connected to the button control too.

Wandamey 09/04/15 10:17 AM

Quote:

Originally Posted by Baertram (Post 23277)
Defining it outside won't work as the settings could be changed after the handler was set.
And buttonId is a parameter of the calling function AddOrChangeFilterButton. So I guess, and that is what I asked :-), yes: it should be connected to the button control too.

hhmmm, when you enter the mouse, it'll use self.tooltiptext aka button.tooltiptext which calls the function, so it should be updated.

i should look at your code first, just don't listen to me. am just writing out loud.

ok, i looked at it... i saw keanu reeves and a colored blob at first but still 2 solutions maybe?

either just redefine
button.buttonId = buttonId
(same with the panelId i suppose)

or button.tooltipText = function(blabla) <-- put this outside of your check : if (checkIfButtonexists == nil) , so it's updated when it needs to be.
And, the most important here : wait for another advice than mine :p

sirinsidiator 09/11/15 01:58 AM

Quote:

Originally Posted by ZOS_ChipHilseberg (Post 23145)
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.

Any chances you could add some functions for profiling to the API?
It would be enough if we had a function that gave us an updated time inside a function in order to allow us to write our own tools.
As far as I have seen all time functions only update between execution, so if I called GetGameTimeMilliseconds() twice with some heavy processing in between I would get the same value from both calls.

Fyrakin 09/11/15 02:05 AM

Quote:

Originally Posted by sirinsidiator (Post 23435)
Any chances you could add some functions for profiling to the API?
It would be enough if we had a function that gave us an updated time inside a function in order to allow us to write our own tools.
As far as I have seen all time functions only update between execution, so if I called GetGameTimeMilliseconds() twice with some heavy processing in between I would get the same value from both calls.

For this purpose you can easily write a callback, but I agree we could use some profiling means, IE memory usage by add-on, cpu costs by add-on etc.

sirinsidiator 09/11/15 02:19 AM

I could, but who knows what happens before that callback gets called. It would make the result pretty much useless IMO.

Fyrakin 09/11/15 03:57 AM

I would make a callback logger and call it whenever I want to log something. Process with many forks can do the callback with a parameter where it can send some essential info. If something and somewhere happens logger might have a clue if it get a sensible info through parameter.

merlight 09/11/15 04:31 AM

Quote:

Originally Posted by sirinsidiator (Post 23435)
Any chances you could add some functions for profiling to the API?
It would be enough if we had a function that gave us an updated time inside a function in order to allow us to write our own tools.
As far as I have seen all time functions only update between execution, so if I called GetGameTimeMilliseconds() twice with some heavy processing in between I would get the same value from both calls.

GetFrameTimeMilliseconds() returns the same value during a single UI frame, yes.

GetGameTimeMilliseconds() returns actual time since the game was started, so it basically works for measuring time spent within a function, but the results are not very consistent. They depend on thread scheduling (how much CPU time the Lua VM is given), how it struggles with memory allocations etc. If you want to evaluate whether func1 is more efficient than func2, you'll have to call them hundreds of thousands of times to get some usable averages.

sirinsidiator 09/11/15 10:49 AM

Quote:

Originally Posted by merlight (Post 23444)
GetFrameTimeMilliseconds() returns the same value during a single UI frame, yes.

GetGameTimeMilliseconds() returns actual time since the game was started, so it basically works for measuring time spent within a function, but the results are not very consistent. They depend on thread scheduling (how much CPU time the Lua VM is given), how it struggles with memory allocations etc. If you want to evaluate whether func1 is more efficient than func2, you'll have to call them hundreds of thousands of times to get some usable averages.

I might have missed GetGameTimeMilliseconds. The other time functions that I tested returned the same value every time.
What I did was something like this:
Lua Code:
  1. local GetTime = WhateverTimeFunction
  2. function SomeFunction()
  3.   local start = GetTime()
  4.   -- do something that makes the game freeze for a second
  5.   df("This took %d", GetTime() - start)
  6. end
That's pretty tedious to add to every function though.

Best case we would get a value in UserSettings.txt which enables a profiling event that returns data for each frame.
EVENT_FRAME_EXECUTION_TIMES ( integer frameTime, integer cpuPercent, integer memoryAllocation, object callstack, object addonData )
Callstack would contain all calls that happened during the frame in tree form:
Lua Code:
  1. callstack = {
  2.   [1] = {
  3.     functionName = "MyFunction",
  4.     callTime = 123456789, -- nanoseconds?
  5.     file = "MyAddon.lua",
  6.     line = "123"
  7.     callstack = {
  8.       [1] = { ... }, -- same as before
  9.       [2] = { ... }
  10.     },
  11.     [2] = { ... },
  12. }
AddonData shows the cpuPercent and memoryAllocation for individual addons.
Lua Code:
  1. addonData = {
  2.   [1] = {
  3.     addonName = "MyAddon",
  4.     cpuPercent = 12, -- %
  5.     memoryAllocation = 123456 -- Bytes
  6.   }
  7. }

Fyrakin 09/14/15 01:52 PM

Profiling event would certainly help to narrow down the most problematic pieces.

Lodur 09/17/15 01:20 PM

I just found in MailLooter I was calling SetHandler 4 times in my ZO_ScrollList row setup function. This can be called a lot if your scrolling through the list...

Since ZO_ScrollList uses a row template - the only way to fix this is in XML.

Just a heads up for anyone else doing what I did with a ZO_ScrollList.


All times are GMT -6. The time now is 12:02 PM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2014 - 2022 MMOUI