Thread Tools Display Modes
09/04/15, 04:53 AM   #41
Wandamey
Guest
Posts: n/a
Originally Posted by Fyrakin View Post
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


Originally Posted by Lodur View Post
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.
  Reply With Quote
09/04/15, 05:55 AM   #42
merlight
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 671
Originally Posted by Wandamey View Post
... 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.
  Reply With Quote
09/04/15, 06:24 AM   #43
Wandamey
Guest
Posts: n/a
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

Last edited by Wandamey : 09/04/15 at 06:49 AM.
  Reply With Quote
09/04/15, 07:05 AM   #44
merlight
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 671
Originally Posted by Wandamey View Post
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.

Originally Posted by Wandamey View Post
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.
  Reply With Quote
09/04/15, 07:30 AM   #45
Wandamey
Guest
Posts: n/a
Originally Posted by merlight View Post
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.
  Reply With Quote
09/04/15, 08:39 AM   #46
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,966
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?

Last edited by Baertram : 09/04/15 at 08:43 AM.
  Reply With Quote
09/04/15, 08:55 AM   #47
Wandamey
Guest
Posts: n/a
Originally Posted by Baertram View Post
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 :
button:SetHandler("OnMouseEnter", MyFunction)
now lets the teachers look at the homework

Last edited by Wandamey : 09/04/15 at 09:06 AM.
  Reply With Quote
09/04/15, 09:06 AM   #48
merlight
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 671
Originally Posted by Baertram View Post
I changed the button variable inside the anonymous SetHandler function to use the "self".
Yes, that's good.

Originally Posted by Baertram View Post
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.

Last edited by merlight : 09/04/15 at 09:11 AM.
  Reply With Quote
09/04/15, 09:25 AM   #49
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,966
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 ). 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.

Last edited by Baertram : 09/04/15 at 09:28 AM.
  Reply With Quote
09/04/15, 09:55 AM   #50
Wandamey
Guest
Posts: n/a
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)
  Reply With Quote
09/04/15, 10:03 AM   #51
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,966
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.
  Reply With Quote
09/04/15, 10:17 AM   #52
Wandamey
Guest
Posts: n/a
Originally Posted by Baertram View Post
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

Last edited by Wandamey : 09/04/15 at 11:43 AM.
  Reply With Quote
09/11/15, 01:58 AM   #53
sirinsidiator
 
sirinsidiator's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 1,578
Originally Posted by ZOS_ChipHilseberg View Post
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.
  Reply With Quote
09/11/15, 02:05 AM   #54
Fyrakin
 
Fyrakin's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 129
Originally Posted by sirinsidiator View Post
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.
  Reply With Quote
09/11/15, 02:19 AM   #55
sirinsidiator
 
sirinsidiator's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 1,578
I could, but who knows what happens before that callback gets called. It would make the result pretty much useless IMO.
  Reply With Quote
09/11/15, 03:57 AM   #56
Fyrakin
 
Fyrakin's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 129
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.
  Reply With Quote
09/11/15, 04:31 AM   #57
merlight
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 671
Originally Posted by sirinsidiator View Post
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.
  Reply With Quote
09/11/15, 10:49 AM   #58
sirinsidiator
 
sirinsidiator's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 1,578
Originally Posted by merlight View Post
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. }
  Reply With Quote
09/14/15, 01:52 PM   #59
Fyrakin
 
Fyrakin's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 129
Profiling event would certainly help to narrow down the most problematic pieces.
  Reply With Quote
09/17/15, 01:20 PM   #60
Lodur
AddOn Author - Click to view addons
Join Date: Feb 2014
Posts: 108
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.
  Reply With Quote

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


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