View Full Version : How Variable scope affects runtime - experimental results!


MrPete
6th January 2015, 06:30
Hi all,

I've been around computing since... well, let's just say I know how punch cards, paper tape, toggle switches, cassette tape storage etc... work :)

But I'm new to AVIsynth and frameserving. So, I've had questions. With the help of several of you, particularly StainlessS (both "live" and his plugins too), I've learned much.

This thread shares a few highlights (I think) I've learned, about variable scope and usage, based on the script given below. The script is nothing special... just something that gave me tools to play.

I would love it if anyone finds serious errors in what I've done... I do not want to be misled by my own investigation!

My environment: 2.6, all the "usual" goodies auto-loaded (RT*, GrunT, etc)

A Few Lessons Learned
(probably listed in various places but 'twas not obvious to me!)

VARIABLES
Scope: either global or *exactly* local, ie no scope inheritance at all. To be visible in an inner (function or script) scope, variables must be global or be passed in.

WARNING: Passing a global variable as if it were local will NOT make you happy. See below about Runtime Sequence for why. (Thus, do NOT list any global variables in the args list of ScriptClip!) Never Treat A Global As Local!

Value/Reference: All passing is by VALUE. Thus, to modify outside the current scope requires either a returned value or modifying a global variable.

Detecting Var Attributes:
1) The RT_Stats package has RT_VarExists("name"). This is NOT built into the language.
2) The Defined() function works only on vars that already exist. It is mostly useful for checking if passed-in parameters have a value or not. (The UnDefined() function returns an "Undefined" value that can be stored in a var. Nothing to do with if a var exists.)
3) At present there's no way to detect if a var is local vs global, and no diagnostic method for dumping the global/local stack.

RUNTIME SEQUENCE

RunTime Code Order: See link for details (http://avisynth.nl/index.php/The_script_execution_model/The_filter_graph) but basically:
* During the first (parse) phase, runtime scripts (eg ScriptClip) are not executed AT ALL.
* During the second (frame server) phase, parse-phase scripts (ie outside of ScriptClip and friends) are not executed AT ALL.

So, independent of what your code says,
a) Parse Phase Scripts run
b) Runtime scripts run -- initialized and sequenced based on auto-analysis of the Parse phase scripts.

Impact of NON-runtime Code: Whatever happens LAST at each line of code in the parse phase is how each variable is remembered for setting up frame serving. In other words, the final value of each local variable is preserved for initializing function calls, etc. NONE of the local values are lost just because a function finished executing. When a runtime routine runs, the remembered value of the appropriate variable(s) is used.

DEBUG NOTE: Make Sure your runtime code is used in some way to produce the clip that's returned in the end! If it is not "seen" to be part of that, it will not be made part of the chain... and WILL NOT execute (at all) at runtime. (This is why I did an overlay of my two ScriptClip results...)

IMPORTANT:
- The INITIALIZED value of a (non-global) variable at run-time will be the FINAL value seen for that variable, at that line of code, at parse-time!

SO for example in my script below, the GlobalStatVar goes through the following interesting sequence:

Parse Phase
- Set to 999 at top level
- Is 999 when the first ScriptClip() is parsed. Meaningless as this is a global var. [Note that ScriptClip is NOT run now]
- Is 999 when NOTICED AS A PASS-BY-VALUE LOCAL to be used with the second ScriptClip() [This one is NOT run now either]
- Set to 777 at the end of the function

RunTime
- Still is 777
- For each frame, the two ScriptClips are called, in sequence. The other diagnostic code is NOT run as it is not part of the filter chain (aka filter graph) used to produce the final result.
FRAME 0
- ScriptClip #1 - Global value is 777 (and gets incremented)
- ScriptClip #2 - val is passed as an arg and is LOCAL. It is preinitialized to the parse-phase remembered value: 999
- ScriptClip #2 does increment the global value to 1000
FRAME 1
- ScriptClip #1 - Global value is 1000 (and gets incremented)
- ScriptClip #2 - val is passed as an arg and is LOCAL. It is preinitialized to the parse-phase remembered value: 999
- ScriptClip #2 does increment the global value to 1000
FRAME 2
- ScriptClip #1 - Global value is 1000 (and gets incremented)
- ScriptClip #2 - val is passed as an arg and is LOCAL. It is preinitialized to the parse-phase remembered value: 999
- ScriptClip #2 does increment the global value to 1000
...ad nauseumeum

Impact of Runtime Cache:
- Each frame is only processed (for sure) the first time it is seen. After that, there are no guarantees!
- There seems to be a flexible frame-result cache such that if I return to the same frame, it may or may not need to be (re)rendered.




My code:
FLGS=25 # Add Together for required:- 1=CPP DLL, 2=C v2.5 DLL, 4=C v2.0 DLL, 8=AVSI Import, 16=AVS Import
DEBUG=True
VERBOSE=False
n= AutoLoadPlugs("C:\Apps\Video\AviSynth 2.5\Plugins\","",flgs=FLGS,debug=DEBUG,verbose=VERBOSE)

TopLevVar = "TopLev Var"
global GlobalVar = "Global Var"
global GlobalStatVar = 999
myclip=Colorbars.ConvertToYV12 # AverageLuma Planar ONLY

isTopTop = (RT_VarExist("TopLevVar") ? "Exists" : "Doesn't Exist")
isTopGlb = (RT_VarExist("GlobalVar") ? "Exists" : "Doesn't Exist")
isTopStat= (RT_VarExist("GlobalStatVar") ? "Exists" : "Doesn't Exist")
RT_DebugF("TOP Init "+"Level\nTopLevVar %s\nGLobalVar %s\nGlobalStatVar %s\ngStat val=%d",isTopTop,isTopGlb,isTopStat,GlobalStatVar)

result = CheckFunc(myclip,isTopTop,isTopGlb,GlobalStatVar)

isTopTop = (RT_VarExist("TopLevVar") ? "Exists" : "Doesn't Exist")
isTopGlb = (RT_VarExist("GlobalVar") ? "Exists" : "Doesn't Exist")
isTopStat= (RT_VarExist("GlobalStatVar") ? "Exists" : "Doesn't Exist")
RT_DebugF("TOP After "+"Level\nTopLevVar %s\nGLobalVar %s\nGlobalStatVar %s\ngStat val=%d\n\n",isTopTop,isTopGlb,isTopStat,GlobalStatVar)

return result

function CheckFunc(clip c,string def1,string def2, int int1)
{
LocalVar = "Local"
isLocLoc = (RT_VarExist("LocalVar") ? "Exists" : "Doesn't Exist")
isLocTop = (RT_VarExist("TopLevVar") ? "Exists" : "Doesn't Exist")
isLocGlb = (RT_VarExist("GlobalVar") ? "Exists" : "Doesn't Exist")
isLocStat= (RT_VarExist("GlobalStatVar") ? "Exists" : "Doesn't Exist")
RT_DebugF("LOCAL init "+"Level\nLocalVar %s\nTopLevVar %s\nGLobalVar %s\nGlobalStatVar %s\ngStat val=%d",isLocLoc,isLocTop,isLocGlb,isLocStat,GlobalStatVar)
#global GlobalStatVar = 888
extraVar = "extra"

f0 = c.ScriptClip("""
xyz=AverageLuma()
ScriptVar = "Script"
last.RT_SubTitle("Frame: "+string(current_frame)+"\nGlobalVar ="+string(GlobalStatVar))
isSScr = (RT_VarExist("ScriptVar") ? "Exists" : "Doesn't Exist")
isSLoc = (RT_VarExist("LocalVar") ? "Exists" : "Doesn't Exist")
isSTop = (RT_VarExist("TopLevVar") ? "Exists" : "Doesn't Exist")
isSGlb = (RT_VarExist("GlobalVar") ? "Exists" : "Doesn't Exist")
isSStat= (RT_VarExist("GlobalStatVar") ? "Exists" : "Doesn't Exist")
RT_DebugF("SCRIPT "+"Level\nFrame %d\nScriptVar %s\nLocalVar %s\nTopLevVar %s\nGLobalVar %s\nGlobalStatVar %s\ngStat val=%d",current_frame,isSScr,isSLoc,isSTop,isSGlb,isSStat,GlobalStatVar)
global GlobalStatVar = GlobalStatVar+1
return last

""", args="extraVar,LocalVar")

f1 = c.ScriptClip("""
xyz=AverageLuma()
Script2Var = "Script 2 "
last.RT_SubTitle("\n\n\nFrame2: "+string(current_frame)+"\nGlobalVar ="+string(GlobalStatVar))
isSScr = (RT_VarExist("ScriptVar") ? "Exists" : "Doesn't Exist")
isSLoc = (RT_VarExist("LocalVar") ? "Exists" : "Doesn't Exist")
isSTop = (RT_VarExist("TopLevVar") ? "Exists" : "Doesn't Exist")
isSGlb = (RT_VarExist("GlobalVar") ? "Exists" : "Doesn't Exist")
isSStat= (RT_VarExist("GlobalStatVar") ? "Exists" : "Doesn't Exist")
RT_DebugF("SCRIPT2 "+"Level\nFrame %d\nScriptVar %s\nLocalVar %s\nTopLevVar %s\nGLobalVar %s\nGlobalStatVar %s\ngStat val=%d",current_frame,isSScr,isSLoc,isSTop,isSGlb,isSStat,GlobalStatVar)
global GlobalStatVar = GlobalStatVar+1
return last

""", args="extraVar,LocalVar,GlobalStatVar")
global GlobalStatVar = 777
isLocLoc = (RT_VarExist("LocalVar") ? "Exists" : "Doesn't Exist")
isLocTop = (RT_VarExist("TopLevVar") ? "Exists" : "Doesn't Exist")
isLocGlb = (RT_VarExist("GlobalVar") ? "Exists" : "Doesn't Exist")
isLocStat= (RT_VarExist("GlobalStatVar") ? "Exists" : "Doesn't Exist")
RT_DebugF("LOCAL done "+"Level\nLocalVar %s\nTopLevVar %s\nGLobalVar %s\nGlobalStatVar %s\ngStat val=%d\n",isLocLoc,isLocTop,isLocGlb,isLocStat,GlobalStatVar)

return(Overlay(f0,f1,opacity=0.5))
}

StainlessS
6th January 2015, 13:44
AutoLoadPlugs("C:\Apps\Video\AviSynth 2.5\Plugins\"

If that is your standard Plugin directory, then the CPP and AVSI files are being autoloaded anyway, the only effect of the
AutoLoadPlugs script will be to load AVS files (you could avoid altogether by simply renaming AVS to avsi).

TheFluff
6th January 2015, 14:09
Avisynth script is baby's first graph description language from the 90's and it's terrible in all the ways you'd think it's terrible, plus in some ways that are downright arcane. Despite this, for some reason some people on this forum really really want to use it for all kinds of completely inappropriate hackery that would be much easier to do in a non-toy language. Grunt is kind of an effort to make it less terrible but it's still Avisynth script and considered harmful.

If you really want to continue down this path of madness you pretty much need to read up on how the Avisynth internals actually work. It's really not that complex because as I said, it's baby's first language. However, most of the time if it's annoying but possible to do in Avisynth script it's probably trivial if you write a C++ plugin instead (and in that case it's also likely to execute a few orders of magnitude faster). These days there are also Avisynth alternatives (http://forum.doom9.org/forumdisplay.php?f=82) that just have bindings for a real language instead of rolling their own, and then you can get away with scripting stuff just being slow rather than slow and awful.