Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion. Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules. |
27th October 2013, 10:55 | #1 | Link |
Registered User
Join Date: Mar 2007
Posts: 408
|
Runtime variables scope and lifetime
EDIT 13th November 2013: put discussion result here. The code example is a framework for a versatile and robust RTE usage.
Code:
#~ Example needs Gavino's GRunT, GScript and StainlessS' RT_Stats plugins function RTE(clip c, string ExampleVarFromNonRTE_Parameter) { ####################################### # Unique Identifier Definition ####################################### Try { global RTE_InstanceNumber = RTE_InstanceNumber+1 } catch(err) { global RTE_InstanceNumber = 1 } # Runtime Environment Preparation & Call GScript(RT_StrReplace(""" #" for use without GScript, use Eval( instead ####################################### # 1) Unique Global Variables Initialization ####################################### global RTE_last_frame%%% = -1 # ... more variables as needed ####################################### # 2) Unique Runtime Function Definition ####################################### function RTE_%%%(clip c, string JustMyExampleString) { # ... insert complete runtime code here # NOTE: Avoid GScript("...") and Eval("...") inside this function, i.e. all functions which expect a script string Try { c Subtitle(string(current_frame - RTE_last_frame%%%)) Subtitle(JustMyExampleString, align=4) global RTE_last_frame%%% = current_frame return last } catch (err) { assert(false, "RTE_ScriptClip %%%: "+err) } } ####################################### # 3) Unique Runtime Call ####################################### ARGS = "ExampleVarFromNonRTE_Parameter" c #ScriptClip must be a one-liner: GScriptClip("RTE_%%%(last, "+ARGS+")", local=true, args=ARGS) """, "%%%", string(RTE_InstanceNumber))) #" return last } ExampleVarFromNonRTE = "hi there" BlankClip() StackHorizontal(last.RTE(ExampleVarFromNonRTE), last.RTE("it works")) (probably this is a reopening of earlier discussions) Many, especially powerful scripts like 'Srestore()' use the runtime environment to vary processing on frame level. The variables they use inside the runtime environment are typically 'shared', in that they share one scope. The variables are 'static' in that their lifetime is same as script lifetime. During rendering stage, the value from the previously processed frame is still there when the function (graph node) is called again. This approach has the advantage, that the function can build the processing on the previous processing, which is crucial for many algorithms. It has the severe disadvantage however, that multiple instances of the function mash up their variables (even worse: no matter if the same runtime function is used in several nodes of the graph, or even a different function uses a variable with the same name). It makes it impossible to use the function for more than one node in the video graph without interference, as shown in the example. In a proper implementation and environment, both frames should show a '1' when the clip is rendered forward frame by frame, or the distance from the previous frame otherwise: Code:
#~ Example needs Gavino's GRunt plugin! function RTE(clip c) { c.GScriptClip(""" # " Try { last_frame = last_frame } catch(err) { last_frame = -100 } Subtitle(string(current_frame - last_frame)) last_frame = current_frame last """, local=false) #" } BlankClip() StackHorizontal(last.RTE(), last.RTE()) This makes the scopes of each RTE instance separate, which is highly desirable in many cases, allowing more than one instance (node) to work properly, and reliefs from problems like 'tunneling x' between different functions. *But* as second effect, the lifetimes are then restricted to one execution of the function. For the next rendered frame, all previous information is lost. Thus, all algorithms that tie in with the previous frame can not be implemented this way - Except they use global variables, but then the other problem reappears. For all plugins, it is not a question that it is possible to use them twice ore more in a script, and if they use static variables - no problem. But for scripted functions, it seems impossible. Any suggestions? Last edited by martin53; 13th November 2013 at 08:30. Reason: better (I think) wording |
27th October 2013, 11:49 | #2 | Link |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,437
|
One crude way is for the function to use unique variable names for each instance. For example, using a global counter to keep track of calls and appending the 'instance number' to each variable name. A bit messy since you need to build up the run-time script dynamically inside the function using string concatenation.
|
27th October 2013, 12:38 | #3 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 11,042
|
As per Gavino method above but using string replacement rather than very messy concatenation, would be my choice.
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? |
27th October 2013, 14:57 | #5 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 11,042
|
M53, how would a suite of eg
RT_ArrayAlloc, RT_ArrayGetInt, RT_ArrayGetFloat, RT_ArraySetInt, RT_ArraySetFloat, RT_ArrayAddInt, RT_ArrayAddFloat, RT_ArrayGetSize, style funcs grab you, using a file to store 4 byte int or 4 byte floats which MUST be user controlled as far as contents is concerned, ie an int would produce rubbish if accessed as float ? It would provide fast random access to read array elements. EDIT: Change of mind, would be of fixed type on RT_ArrayAlloc, int, float or string, with fixed maximum length strings if string (default 256). Bit OT, hope you dont mind, guess I should have posted in RT thread.
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? Last edited by StainlessS; 27th October 2013 at 15:16. |
27th October 2013, 15:37 | #6 | Link | |
Registered User
Join Date: Mar 2007
Posts: 408
|
Quote:
Honestly, I thought it was near impossible. But in the run to demonstrate how messy an example would look like, I figured out something not *that* bad. The impact on RTE script readability is small, and it might even be seen as obvious marks for variables that persist between calls. EDIT 13th Nov 2013: Extracted+assembled the runtime commands into a function definition. This is important because the string inside ScriptClip("") is re-evaluated for every frame and will make the AviSynth string heap overflow. AviSynth does not clean up the heap until script end. Moved example to 1st post I can't say I love it. But I think this will replace my previous ScriptClip() template. And of course I hope it may be useful for other RTE writers, too. The marker %%% can be replaced with any personal favourite. EDIT: included init part for global variables according to Gavino's proposal some posts below. Note: The Try ... catch ... function inside the GScriptClip() is typically not needed for a RTE function which processes the output clip. But it is highly useful for RTE-made 'helper' clips, which themselves are used by another RTE function, because a potential error message in such a clip will not be visible in the output clip Last edited by martin53; 13th November 2013 at 08:31. Reason: included var initialization |
|
27th October 2013, 15:56 | #7 | Link |
Registered User
Join Date: Mar 2007
Posts: 408
|
EDIT: This post is outdated because the initialization is now included in the full example above.
And don't forget: It is dangerous/buggy to define global vars for RTE functions at global script level like it was usual all the time: ... global RTE_myVar = 0 ... since no such variable without appended index will exist any more, and the really existing ones remain undefined. To stay indeppendent from number of instances, maybe better style to define inside RTE, after check that they're undefined: Code:
GScriptClip(""" ... try { __ = RTE_myVar%%%%%% } catch(err) { global RTE_myVar%%%%%% = 0 } ... first use of RTE_myVar%%%%%% in RTE ... """, local=true) Last edited by martin53; 27th October 2013 at 20:01. Reason: reference to init in post above |
27th October 2013, 16:20 | #9 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 11,042
|
M53, while i'm doing Array thing, I'll do an RT_IncrGlobal(String GlobalName,int "default"=1) style func.
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? Last edited by StainlessS; 27th October 2013 at 16:25. |
27th October 2013, 16:55 | #10 | Link |
Registered User
Join Date: Mar 2007
Posts: 408
|
Just applied the described concept on the AWB() script.
- It's great! For the first time, it is possible to compare settings side by side! That also would enrich Srestore() and others. |
27th October 2013, 18:03 | #11 | Link | |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,437
|
Quote:
Code:
Eval("global RTE_myVar"+string(RTE_InstanceNumber)+"=0") If staying with the try/catch approach, note the assignment to __ is unnecessary: Code:
try { RTE_myVar%%%%%% } catch(err) { global RTE_myVar%%%%%% = 0 } |
|
27th October 2013, 19:34 | #12 | Link | |
Registered User
Join Date: Mar 2007
Posts: 408
|
Quote:
The first suggestion is good. I think I'd use the RT_StrReplace again instead of the concatenation, so a whole bunch of variables can be processed in one script block and it still looks readable. I think I thoroughly tried the elimination of the assignment, but I failed. I remember you gave me that same hint already, and tried to use it - will try again, maybe I just almost used the right syntax. EDIT: checked both positive. Will edit the above post. Last edited by martin53; 27th October 2013 at 19:42. Reason: Checked proposals |
|
28th June 2014, 11:10 | #13 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 11,042
|
That is a very long Safari you are on M53, when are you coming back ?
I've just been playing with this to good effect (great multi-instance scene change detector coming). Below my preferred usage of your example framework. I've used '@@@' rather than '%%%' as '%' can be used in eg String() func and some RT_Stats formatted output funcs. I've also re-ordered some things and implemented Debug output so you can verify conformity with intent. EDIT: Also, I think eg ImageSource() uses '%%' as an Escape for '%' where you want a single percent char in a filename, and I think it is also possible that eg '%%%' could be legally used in ImageSource, to mean literal single percent, followed by inserted frame number. Here tis: Code:
Function RTE(clip c, string "ExampleVarFromNonRTE_Parameter") { FuncS=""" ####################################### # 1) Unique Runtime Function Definition ####################################### Function RTE_@@@(clip c, string JustMyExampleString) { # No Optional Arguments in unique RTE func # ... insert complete runtime code here # NOTE: Avoid GScript("...") and Eval("...") inside this function, i.e. all functions which expect a script string # Can use Gscript if/else etc in here if called via GScript(InstS) rather than Eval(InstS) Try { c Subtitle(string(current_frame - RTE_last_frame@@@)) Subtitle(JustMyExampleString, align=4) global RTE_last_frame@@@ = current_frame return last } catch (err) { assert(false, "RTE_ScriptClip @@@: "+err) } } ####################################### # 2) Unique Global Variables Initialization ####################################### global RTE_last_frame@@@ = -1 # ... more variables as needed ####################################### # 3) Unique Runtime Call ####################################### ARGS = "ExampleVarFromNonRTE_Parameter" #ScriptClip must be a one-liner: Last.GScriptClip("RTE_@@@(last, "+ARGS+")", local=true, args=ARGS) # Grunt needed for Args """ # Optional args must be handled in main function ExampleVarFromNonRTE_Parameter=Default(ExampleVarFromNonRTE_Parameter,"No Example String given") ####################################### # 4) Unique Identifier Definition ####################################### RT_IncrGlobal("RTE_InstanceNumber") # Instead of Try/Catch InstS = RT_StrReplace(FuncS,"@@@",String(RTE_InstanceNumber)) # Unique Instance function string RT_TxtWriteFile(InstS,"DEBUG_RTE_"+String(RTE_InstanceNumber)+".TXT") c # Runtime Environment Call GScript(InstS) # for use without GScript, use Eval( instead return last } ExampleVarFromNonRTE = "hi there" #ExampleVarFromNonRTE = RT_Undefined BlankClip() StackHorizontal(last.RTE(ExampleVarFromNonRTE), last.RTE("it works")) Code:
####################################### # 1) Unique Runtime Function Definition ####################################### Function RTE_2(clip c, string JustMyExampleString) { # No Optional Arguments in unique RTE func # ... insert complete runtime code here # NOTE: Avoid GScript("...") and Eval("...") inside this function, i.e. all functions which expect a script string # Can use Gscript if/else etc in here if called via GScript(InstS) rather than Eval(InstS) Try { c Subtitle(string(current_frame - RTE_last_frame2)) Subtitle(JustMyExampleString, align=4) global RTE_last_frame2 = current_frame return last } catch (err) { assert(false, "RTE_ScriptClip 2: "+err) } } ####################################### # 2) Unique Global Variables Initialization ####################################### global RTE_last_frame2 = -1 # ... more variables as needed ####################################### # 3) Unique Runtime Call ####################################### ARGS = "ExampleVarFromNonRTE_Parameter" #ScriptClip must be a one-liner: Last.GScriptClip("RTE_2(last, "+ARGS+")", local=true, args=ARGS) # Grunt needed for Args
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? Last edited by StainlessS; 1st July 2014 at 18:04. |
29th June 2014, 13:51 | #14 | Link | |
Registered User
Join Date: Aug 2008
Location: Isle of Man
Posts: 588
|
Quote:
In martin53's presumed absence, feel free to pipe up with an explanation, StainlessS or Gavino . |
|
29th June 2014, 15:09 | #15 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 11,042
|
I'm not sure exactly what M53 was referring to there, I have had a bit of a look for another thread that
resulted in this thread, but could not find it. But, Whenever you do something like Subtitle("AveLuma="+String(AverageLuma)) inside ScriptClip, it results in a new string (the concatenated string, and at every frame, perhaps even 3 strings, the string for "AveLuma=", the String(AverageLuma) and the concatenated one) that occupies heap memory until Avisynth closes down (I was personally quite shocked when I discovered that there was no memory garbage collection, not even strings created in script local functions [I think]). I'm not alogether sure but think that, if you call scriptclip with ScriptClip("""Subtitle("AveLuma="+String(AverageLuma))""") then the ScriptClip string arg is instantiated only once, but the SubTitle string arg at every frame. EDIT: Same with Eval(), strings created at every frame if used inside of Scriptclip, but if you eg use Grunt()'s 'args' arg, then you can pass in a named argument string variable from outside, and could do an Eval on this arg string variable that was instantiated only once at main script level. EDIT: Just had a think about it, ScriptClip does NOT instantiate string mem for its arg at every frame, it is a filter that is linked into the filter graph, and so the string would only be created when calling the Scriptclip constructor, before frame serving starts. Inside of ScriptClip, ScriptClip parses the string given to its constructor and at every frame, and every function and filter is called as if for the first time, all quoted strings inside of Scriptclip are instantiated and on each frame. If a GScript function is instantiated, eg GSCript(""" Function fn() { Return "Hello"} """) , I dont know if it is in some way tokenized, and the "Hello" string allocated memory just the once, or every time the function is called. Perhaps even ScriptClip tokenizes its string arg and so string literals may only be allocated once, perhaps the chosen one will enlighten us (although he has probably enlightened me on same subject several gazillon times already). Oh, just found this in developer forum, perhaps of some interest. http://forum.doom9.org/showthread.php?p=1587841
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? Last edited by StainlessS; 29th June 2014 at 16:22. |
30th June 2014, 00:19 | #16 | Link |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,437
|
As StainlessS says, the string argument to ScriptClip() is instantiated once only, when the outer script is parsed.
However, at run-time, that string is itself parsed afresh by ScriptClip() for every frame, each time using up space in Avisynth's string table for variable names, etc. Most of the time, this is not significant, but if ScriptClip is called with a very long run-time script or called recursively to a great depth, coupled with lots of source frames, it could eventually eat up a lot of memory. To avoid this, it is recommended to use functions to cut down the size of a long run-time script. See this post. |
30th June 2014, 09:40 | #17 | Link | |
Registered User
Join Date: Aug 2008
Location: Isle of Man
Posts: 588
|
That's very helpful, thanks guys.
More on topic, what about nested variables and arguments of functions pre-defined at the top-level script scope but called at runtime? According to the documentation Quote:
When is memory used by variables declared in "top-level" runtime code released? And, for the sake of completeness, that of variables and arguments of functions declared in runtime code (not that I can think of any good reason for that)? EDIT: I suspect some or most of this is probably addressed by StainlessS' post above, in which case I humbly ask your indulgence for my limited understanding! Last edited by fvisagie; 30th June 2014 at 09:48. |
|
30th June 2014, 10:25 | #18 | Link | |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,437
|
Quote:
However, string values themselves just contain pointers to the actual characters which (for most strings, and certainly those created by script operations) are stored in the string heap. As StainlessS pointed out above, the contents of this heap are not freed until the script is unloaded, even though the string values pointing to them may have been deleted. |
|
30th June 2014, 11:55 | #19 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 11,042
|
Even if desired by Avisynth developers, I dont think strings (the actual string characters) could be freed, as it is
in docs that plugin devs can return a string to Avisynth using env->SaveString to make a copy of a plugin's string (mem allocation is quite separate between plugins and Avisynth, string memory is copied from plugin memory heap to Avisynth memory heap, giving Avisynth total ownership and duty to later free mem), and if they (plug devs) keep a pointer to that memory block they can re-use it so long as they do not overwrite the bounds of that memory block which is owned by Avisynth. One thing I have found though, (and I dont think it was mentioned in docs anywhere), if setting a local or global variable from within a plugin, it is unnecessary to env->SaveString() the name of the variable, if it already exists, Avisynth will not re-create another variable (with identical name) in the string table, the old one will be re-used. (kinda makes sense really). The only way that some script local strings could be freed would be if future versions of avisynth kept separate track of strings created locally by the parser, and were in no way returned by - or created by a plugin. [Although perhaps a new env-SaveString2() or whatever could be added which plugin writers could not later access contents of.] EDIT: I'm not actually aware of any plugin that later uses string memory now belonging to Avisynth, although I would not be surprised if AVSLib did so.
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? Last edited by StainlessS; 30th June 2014 at 12:10. |
Thread Tools | Search this Thread |
Display Modes | |
|
|