View Full Version : Avisynth+
pinterf
18th May 2016, 19:59
Just wanted to ask, if there is any specific in the nnedi3_rpow2 function, because it seems that applying SetFilterMode on it is simply ignored and works like a NICE filter. I saw parallel calls to the rpow2 function in the debug logs and the corruption pattern is something like that nnedi3 core is working on the same internal buffers. 4x size images, U and V copied to the Y plane, etc.
Have to dig into it.
Remark: it looks like a regular avisynth script but written in C, learned again something new (Invoke).
Chikuzen
18th May 2016, 20:53
The MPEG2Source issue was fixed so maybe there's hope for nnedi3_row2.
The way to fix nnedi3_rpow2 issue is just remove tritical's PlanarFrame hack and internal buffers to make nnedi3 as MT_NICE_FILTER.
jpsdr
19th May 2016, 08:48
The way to fix nnedi3_rpow2 issue is just remove tritical's PlanarFrame hack and internal buffers to make nnedi3 as MT_NICE_FILTER.
But this is used only inside nnedi3, as nnedi3_rpow2 is "just" calling nnedi3. If it was this (and i agree for the possibility), shouldn't nnedi3 in that case also have corruption ? But it seems it's not the case.
Nevertheless, it could indeed be interesting and cleaner if possible to get rid of this hack, but it means i have to dig deaper on the code, and for now, i have no idea (and not much time) of how doing it.
Any advice, clue, very global ideas like (function "xxx" and "yyy" are a good start to look) is welcome.
Basicaly, the 1rst step would be, how doing properly what it's doing (creating using/buffer).
Chikuzen
19th May 2016, 10:36
But this is used only inside nnedi3, as nnedi3_rpow2 is "just" calling nnedi3. If it was this (and i agree for the possibility), shouldn't nnedi3 in that case also have corruption ? But it seems it's not the case.
env->Invoke does not automatically insert a cache after the filter you invoke. (http://avisynth.nl/index.php/Filter_SDK/Env_Invoke)
It seems that avisynth can't handle automaticaly a filter which invoked from other filter.
If it's possible, why plugin author have to call "InternalCache" manually ?
I don't think Avisynth+ can choose MT mode appropriately on invoked filter though it cannot insert internal cache automatically.
Any advice, clue, very global ideas like (function "xxx" and "yyy" are a good start to look) is welcome.
Basicaly, the 1rst step would be, how doing properly what it's doing (creating using/buffer).
TCannyMod (https://github.com/chikuzen/TCannyMod/blob/master/avisynth/src/tcannymod.cpp) ?
tcanny uses PlanarFrame hack and floating point read/write buffer allocated on constructor.
I removed them and change allocate buffers per each GetFrame().
Groucho2004
19th May 2016, 10:48
Wouldn't it be better to use a script function to achieve rpow2 with nnedi3/(f)turn calls and sort out chroma/luma alignment issues in that function instead of hard-coding it into the plugin?
jpsdr
19th May 2016, 12:01
If i understand properly, basicaly, it means that any filter which needs buffer and allocate them once for all in constructor and so free them in destructor (which is normaly the best and correct way to do) will be screwed with MT mode, because for the same instance/variable filter, several getframe can be runned in the same time (which doesn't occur in not MT mode), and so both will access the same buffer and corrupt each other.
Issue is not because of PlanarFrame hack, it's deeper, meaning that for being MT compatible, if you need buffers, you have to allocate and free them in GetFrame... Honestly, i don't like the idea, but don't know if actualy there is a better solution.
We are hitting here something deeper. It's design restriction. If you want your filter being MT compatible, you have design restrictions. But again, it's specific to this MT mode.
A better solution, but again hitting MT specific design, would be that constructor allow, once for all, several buffers, the number of buffer being decided by the number of multi-thread, and each GetFrame get some "Thread number" information, and so the correct buffer is used by the getframe. Again, the MT scheduler must assure that there is never two getframe of the same filter instance with the same number.
Anyone of course can discuss, agree, disagree (but in that case explain me why and where i'm wrong) with this.
Chikuzen
19th May 2016, 13:39
@jpsdr
If your filter repeats alloc/free same sized memory, Windows calculates the memory needed by all threads, and reserves them.
Freed buffer doesn't return to the system and waits for the next request, and reused just as it is.
Thus, allocate/free buffers per GetFrame is cheap costs now a day.
And this is very akin to the scheduler you want actually.
I think that implementing buffer scheduler is worse idea than re-implementing memcpy.
Myrsloik
19th May 2016, 14:46
Actually this is a bit of a problem in practice. In vs2015 and probably all somewhat modern versions it's only fast for small allocations. Like up to 1080p frames. Once you start allocating a whole 4k frame of space it simply calls virtualalloc. That's quite slow. Even stupid buffer reuse behind a global mutex easily beats that.
Not going to comment on any specific ideas but some kind of buffer reuse can (unfortunately) still help. Or just shove in tcmalloc if you don't use vs2015 and it'll basically do it for you.
Chikuzen
19th May 2016, 15:17
@mylsroyk
hmmm, interesting.
In that case first avisynth itself should use tcmalloc like VapourSynth.
jpsdr
19th May 2016, 15:38
I juste take a look at how to use mutex, i think i'll try to do something with some kind of buffer scheduler, but don't expect it too soon.
Edit :
I'll need to know how to do the following in the plugin :
- Get the avisynth version, to not use MT functions if you're not in a MT version (basicaly, if you're in avs+ or not, and which release).
- If you're in avs+, is there a way to detect if MT has been enable or not ? And if MT has been enabled, is there a way to know how many thread will be running ?
Is there a link on some information/tutorial or anything which explain how to do the listed before ?
:thanks:
Edit 2 :
Or... I'll just add (at the end, of course), an "MT" parameter, set to "1" by default, where you specify the maximum number of possible threads.
pinterf
19th May 2016, 16:16
I'm lost.
I have put a mutex in the nnedi3 class, and use it inside nnedi3 GetFrame, and there is no curruption. There are still parallel calls to nnedi3::GetFrame with the same frame number, but they are for different class instances. Different class, different mutexes, they are working fine.
But this is just a workaround and not the basic problem.
Except that how nnedi3_rpow2 works internally, it should work as-is.
But after inserting the nnedi3 mutex, something is still weird.
Avs 2.6 mt runs at 28.7 fps
Avisynth+
no prefetch: 7.6 fps (13%, thread count=20)
prefetch(1): 7.7 fps (13%, thread count=20) (same)
prefetch(2): 8.2 fps (15%, thread count=21)
prefetch(4): 8.7 fps (15%, thread count=23)
prefetch(8): 8.7 fps (15%, thread count=27)
TurboPascal7
19th May 2016, 16:52
jpsdr
I'm just gonna leave this (https://github.com/AviSynth/AviSynthPlus/blob/MT/avs_core/core/avisynth.cpp#L980) here. And here's an example (https://github.com/tp7/SangNom2/blob/e1f56542dac1e2ee69ff3f68e80339d23938ca01/SangNom2/sangnom.cpp#L833).
It should be pretty obvious that avisynth+ would not require every single plugin author ever to implement his own memory pool.
And I really should stop checking this thread.
jpsdr
19th May 2016, 19:08
AVSValue __cdecl Create_SangNom2(AVSValue args, void*, IScriptEnvironment* env) {
if (!env->FunctionExists("SetFilterMtMode")) {
env->ThrowError("SangNom2: this plugin only works with multithreaded versions of Avisynth+!");
}
Euh... Not for me ! ;)
But thanks for the example, it will allow me to see how to get informations and check things.
If the memory pool handle things "right", meaning allocating only if something asked doesn't exist yet, meaning it should stop allocating very quickly and only giving allready allocating pointers, yes, indeed, this doesn't bother me. But, if it's allocating/freeing on each getframe, i don't like it.
But didn't take a look at the pool code, so for now, doubt benefit...
real.finder
19th May 2016, 21:55
@jpsdr
I don't know if you could do it or not, but if you link nnedi3 with AVSTP it will easy to control MT in the script outside nnedi3
jackoneill
20th May 2016, 07:38
Reminder that NNEDI3 for Avisynth has internal multithreading: https://github.com/jpsdr/NNEDI3/blob/master/nnedi3/nnedi3.cpp#L577
jpsdr
20th May 2016, 08:43
This is begining nnedi3 specific, so i'll switch to the nnedi3 thread to continue discuss this topic.
MysteryX
23rd May 2016, 05:00
I'll add it, better yet, maybe you can make your plugin self-register the appropriate MT mode. Take a look here: http://forum.doom9.org/showthread.php?t=168856&page=30#post1667529
I tried adding it but it doesn't seem to be having any effect.
This should cause the memory usage to go WAY down, and also to run much slower, which doesn't happen.
class ExecuteShader : public GenericVideoFilter {
public:
// Supported MT mode for AviSynth+
int __stdcall SetCacheHints(int cachehints, int frame_range) override {
return cachehints == CACHE_GET_MTMODE ? MT_NICE_FILTER : 0;
}
...
}
MysteryX
28th May 2016, 05:17
AviSynthShader now specifies its supported MT modes to AviSynth+ and is not necessary in the MT definition file anymore.
KNLMeans, however, isn't in the file either, so this line must be added.
SetFilterMTMode("KNLMeansCL", MT_SERIALIZED)
jpsdr
28th May 2016, 08:46
Is there a place/link which explain the differences and impact between all MT status like, MT_SERIALIZED, MT_NICE_FILTER to eventualy add in my filter the report of what they support.
I need the list of all the avaible status, and what they exactly mean for the filter reporting a such status. Where there is such information ?
A few hints are in AviSynth Wiki: AviSynth+ (http://avisynth.nl/index.php/AviSynth%2B); a few more are in the Avisynth+ MT modes definitions (https://gist.github.com/tp7/8899021) (you may have to log in to github to have access here).
As a thumb rule: To be an MT_NICE_FILTER, the code must be programmed in a "threading aware" ~ "re-entrant" style (preferably only function parameters and local variables, no global variables where they don't need to be global) and should not fork own threads. If a filter produces at least probably wrong output in an MT environment, it may have to be set to MT_SERIALIZED even though that will produce a bottleneck, reducing its execution to one thread and possibly even requesting its input in an ordered manner. Filters creating own threads may work as MT_MULTI_INSTANCE filter only in case their number of internal threads is limited, possibly to only 1.
MysteryX
28th May 2016, 15:13
From what I understand,
MT_NICE_FILTER means there is one class instance and GetFrame gets called several times at once from various threads on the same class. When Pinterf went through my code, he wrote "Say no to globalism!" No global variables unless they are read-only. All working buffers must be created within GetFrame.
MT_MULTI_INSTANCE means that one instance of the class gets created for each thread and each instance is single-threaded. One thread may process frames 1, 4, 7 and 9 while other threads process other frames.
I'm not yet sure what impact MT_SERIALIZED has on the code.
The way to define it is in the Init function
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors) {
AVS_linkage = vectors;
env->AddFunction("ConvertToShader", "c[Precision]i[lsb]b", Create_ConvertToShader, 0);
env->AddFunction("ConvertFromShader", "c[Precision]i[Format]s[lsb]b", Create_ConvertFromShader, 0);
env->AddFunction("Shader", "c[Path]s[EntryPoint]s[ShaderModel]s[Param0]s[Param1]s[Param2]s[Param3]s[Param4]s[Param5]s[Param6]s[Param7]s[Param8]s[Clip1]i[Clip2]i[Clip3]i[Clip4]i[Clip5]i[Clip6]i[Clip7]i[Clip8]i[Clip9]i[Output]i[Width]i[Height]i", Create_Shader, 0);
env->AddFunction("ExecuteShader", "c[Clip1]c[Clip2]c[Clip3]c[Clip4]c[Clip5]c[Clip6]c[Clip7]c[Clip8]c[Clip9]c[Clip1Precision]i[Clip2Precision]i[Clip3Precision]i[Clip4Precision]i[Clip5Precision]i[Clip6Precision]i[Clip7Precision]i[Clip8Precision]i[Clip9Precision]i[Precision]i[OutputPrecision]i", Create_ExecuteShader, 0);
void* is_plus = env->FunctionExists("SetFilterMTMode") ? "true" : nullptr;
if (is_plus != nullptr) {
auto env2 = static_cast<IScriptEnvironment2*>(env);
env2->SetFilterMTMode("ConvertToShader", MT_NICE_FILTER, true);
env2->SetFilterMTMode("ConvertFromShader", MT_NICE_FILTER, true);
env2->SetFilterMTMode("Shader", MT_NICE_FILTER, true);
env2->SetFilterMTMode("ExecuteShader", MT_MULTI_INSTANCE, true);
}
return "Shader plugin";
}
Chikuzen
28th May 2016, 15:59
void* is_plus = env->FunctionExists("SetFilterMTMode") ? "true" : nullptr;
if (is_plus != nullptr) {
auto env2 = static_cast<IScriptEnvironment2*>(env);
env2->SetFilterMTMode("ConvertToShader", MT_NICE_FILTER, true);
env2->SetFilterMTMode("ConvertFromShader", MT_NICE_FILTER, true);
env2->SetFilterMTMode("Shader", MT_NICE_FILTER, true);
env2->SetFilterMTMode("ExecuteShader", MT_MULTI_INSTANCE, true);
}
Why do you convert bool to void* there is no need to do it ?
if (env->FunctionExists("SetFilterMTMode")) {
auto env2 = static_cast<IScriptEnvironment2*>(env);
env2->SetFilterMTMode("ConvertToShader", MT_NICE_FILTER, true);
env2->SetFilterMTMode("ConvertFromShader", MT_NICE_FILTER, true);
env2->SetFilterMTMode("Shader", MT_NICE_FILTER, true);
env2->SetFilterMTMode("ExecuteShader", MT_MULTI_INSTANCE, true);
}
I think this is smart and better.
MysteryX
28th May 2016, 17:18
I just copied the code from someone else. Monkey see monkey do :)
I agree it was written in a weird way!
Reel.Deel
28th May 2016, 20:32
Is there a place/link which explain the differences and impact between all MT status like, MT_SERIALIZED, MT_NICE_FILTER to eventualy add in my filter the report of what they support.
I need the list of all the avaible status, and what they exactly mean for the filter reporting a such status. Where there is such information ?
I don't know if you've seen this already but read this post by ultim: http://forum.doom9.org/showthread.php?p=1651071#post1651071
Groucho2004
28th May 2016, 21:31
I don't know if you've seen this already but read this post by ultim: http://forum.doom9.org/showthread.php?t=168856&page=11#post1651071
I have seen this here and also on avisynth.nl - when you link to Doom9 forum posts, your links always contain a page reference which of course only works when users have not changed their posts/page forum preference.
Every time I click on one of your links I get transported to some random place since I don't use the default.
I suggest you use the "Link" reference, top right of every post.
Reel.Deel
28th May 2016, 21:45
I have seen this here and also on avisynth.nl - when you link to Doom9 forum posts, your links always contain a page reference which of course only works when users have not changed their posts/page forum preference.
Every time I click on one of your links I get transported to some random place since I don't use the default.
I suggest you use the "Link" reference, top right of every post.
Thanks for pointing that out, I'll use the suggested link from now.
The links have always worked for me so I never noticed. I'll try to fix the wiki links as I come across them.
Groucho2004
28th May 2016, 21:49
Thanks for pointing that out, I'll use the suggested link from now.
The links have always worked for me so I never noticed. I'll try to fix the wiki links as I come across them.
I think it's only when you link to specific posts, not when you link a whole thread.
So for everyone else: reply #204 (http://forum.doom9.org/showthread.php?p=1651071#post1651071)
Reel.Deel
28th May 2016, 22:16
I think it's only when you link to specific posts, not when you link a whole thread.
Correct. A while back archive.org had trouble archiving the top right link so I used the alternative link because it worked. I used that style on the wiki just so if it ever gets crawled by archived.org. Just checked and it seems the top-right link also works now.
Sorry to anyone who was directed to a random place. That was not my intent. :o
So for everyone else: reply #204 (http://forum.doom9.org/showthread.php?p=1651071#post1651071)
I fixed the link in #1628.
jpsdr
29th May 2016, 09:42
Ok, i think i understand the differences between the 3.
MT_NICE_FILTER : Not possible if you have to use global internal buffer. Personnaly, i'll never malloc/free on each get frame, and don't want to use MT specific features making the filter working only on MT versions. But on something so simple as brigthness/contrast (for exemple), it will work.
MT_MULTI_INSTANCE : You can use global internal buffer, no several getframe from the same instance, but input frames are "random", so works only if, basicaly, the filter needs only the actual frame. I think it's this case for the actual nnedi3.
MT_SERIALIZED : The filter needs the frames to be entered in the correct order, and probably also have buffer. A simple example would be something like an RII filter (like y[n]=0.5*x[n]+0.25*x[n-1]+0.25*y[n-1]). A such filter is not compatible with the others two modes.
Thanks, things are clear now. I think...
I believe even a filter which requires a temporal range of frames can be MT-friendly if the AviSynth(+) core knows about it working on a temporal window and buffering such a range...
jackoneill
29th May 2016, 19:43
Personnaly, i'll never malloc/free on each get frame,
It's really not that bad. The VapourSynth plugin with work buffers allocated in the getframe function is about as fast as the Avisynth plugin. See http://forum.doom9.org/showthread.php?p=1600999#post1600999 and some posts below.
fAy01
30th May 2016, 03:35
The current installer of avs-plus detects avisynth 2.5/2.6. Is there a possibility to have both 32bit and 64bit working without having to overwrite or uninstall the other?
For 32 bit applications, only one DLL can be registered system-wide as AviSynth frameserver, either the "vanilla" AviSynth DLL or the AviSynth+ DLL.
But it is possible to place one of the DLLs next to the application into the same directory, and if the application loads "the next available avisynth.dll", it will probably look first in its own directory before looking into the Windows system directory. This way it may be possible to exchange the AviSynth version used by a specific application independently of the system-wide AviSynth installation, by installing the application in two directories (preferably outside an UAC protected branch, means: not in "Program files") and copying either DLL to either directory.
For 64 bit applications, there is no reliable 64-bit "vanilla" AviSynth build, anyway.
Groucho2004
30th May 2016, 08:54
The current installer of avs-plus detects avisynth 2.5/2.6. Is there a possibility to have both 32bit and 64bit working without having to overwrite or uninstall the other?
Any 32 bit and 64 bit Avisynth versions can live happily together. The 32 bit avisynth.dll/devil.dll are placed in SysWOW64, the 64 bit versions in system32. A client application will (should) automatically load the correct DLL.
However, only Avisynth+ has the option of specifying separate plugin directories which is needed for auto-loading filters.
To answer your question - yes, you can install just 64 bit AVS+ alongside your existing 32 bit Avisynth.
Groucho2004
30th May 2016, 09:09
For 32 bit applications, only one DLL can be registered system-wide as AviSynth frameserver, either the "vanilla" AviSynth DLL or the AviSynth+ DLL.
Avisynth.dll does not need registration. A client application can load any avisynth.dll.
For 64 bit applications, there is no reliable 64-bit "vanilla" AviSynth build, anyway.I find AVS+ r1576 and the latest build from pinterf very usable.
I find AVS+ r1576 and the latest build from pinterf very usable.
But that's AviSynth+ (plus), not vanilla/legacy/original AviSynth... how should I call it to make it distinct from the "plus" fork?
A 64-bit AviSynth (without "plus") does exist; but it was mainly known as unstable (I guess many of the programming tricks the 32-bit version survived would crash the 64-bit version, and only few plugins were ported before this project was abandoned).
Groucho2004
30th May 2016, 09:37
But that's AviSynth+ (plus), not vanilla/legacy/original AviSynth... how should I call it to make it distinct from the "plus" fork?
A 64-bit AviSynth (without "plus") does exist; but it was mainly known as unstable (I guess many of the programming tricks the 32-bit version survived would crash the 64-bit version, and only few plugins were ported before this project was abandoned).
I know what you meant and I agree with all of the above. :D
I just pointed out that there are a couple of very usable 64 bit builds because your statement "there is no reliable 64-bit "vanilla" AviSynth build, anyway" may be misleading for folks not familiar with vanilla flavour in this context.
MysteryX
30th May 2016, 11:10
I posted some time ago about AviSynth+ x64 taking HUGE amounts of memory. The fault is mine. AviSynthShader takes more than twice the memory in x64, trying to figure out why. If I remove that, all the other filters take slightly more memory but perform faster.
From what I tested so far, AviSynthShader may be the only x64 filter that isn't working as expected.
fAy01
30th May 2016, 21:01
Any 32 bit and 64 bit Avisynth versions can live happily together. The 32 bit avisynth.dll/devil.dll are placed in SysWOW64, the 64 bit versions in system32. A client application will (should) automatically load the correct DLL.
However, only Avisynth+ has the option of specifying separate plugin directories which is needed for auto-loading filters.
To answer your question - yes, you can install just 64 bit AVS+ alongside your existing 32 bit Avisynth.
https://i.imgur.com/limOBsh.png
avs-plus suggests otherwise :)
Groucho2004
30th May 2016, 21:13
https://i.imgur.com/limOBsh.png
avs-plus suggests otherwise :)
Odd. I use this tool (http://forum.doom9.org/showthread.php?t=172124) to install/switch Avisynth versions and I assure you that Avisynth 2.6 and Avisynth+ 64 bit each work perfectly fine:
http://s33.postimg.org/kv0sde2en/Image1.png
qyot27
30th May 2016, 21:26
@fAy01:
You do realize that AviSynth+ installs both 32 and 64 bit versions, right? There's no reason whatsoever to use classic AviSynth for 32-bit and only Plus for 64-bit, instead of Plus for both. You can't put both the 32-bit-only classic AviSynth and a 32-bit AviSynth+ in sysWOW64, you have to choose one or the other. It is exactly the same reason you can't put AviSynth 2.5 and AviSynth 2.6 in sysWOW64 at the same time, not that there's some kind of 'incompatibility' involved.
Although if you need to use FFmpeg, you have to use AviSynth+ r1779, r1825, or pinterf's test builds, not r1576. This is because FFmpeg no longer supports the pre-2.6 AviSynth API.
Groucho2004
30th May 2016, 21:42
@fAy01:
I just tried the installer and got the same warning dialog. However, I clicked "Next" and selected only the 64 bit version. The original 32 bit version as well as the 64 bit version are working fine.
I agree however with qyot27 - just uninstall the old version and install AVS+ 32 and 64 bit.
MysteryX
31st May 2016, 07:07
As several mentioned, you can only have one 32-bit version and one 64-bit version of AviSynth.dll, but they can be different, as one is in Windows\System32 and the other is in Windows\Syswow64. You can manually place the AviSynth DLL version you like into each of these folders.
In addition to that, you can use a separate version per application by placing another AviSynth.dll version into the same folder as the EXE and that version will be used instead.
Knowing this is especially useful for testing and comparing various versions.
pinterf
31st May 2016, 09:32
I posted some time ago about AviSynth+ x64 taking HUGE amounts of memory. The fault is mine. AviSynthShader takes more than twice the memory in x64, trying to figure out why. If I remove that, all the other filters take slightly more memory but perform faster.
From what I tested so far, AviSynthShader may be the only x64 filter that isn't working as expected.
Pointers are double in size in x64. Although there are arrays and structs with pointers in your code but it does not explain the difference. Perhaps DirectX?
MysteryX
31st May 2016, 09:41
Pointers are double in size in x64. Although there are arrays and structs with pointers in your code but it does not explain the difference. Perhaps DirectX?
Yes it's most likely related to a difference in the way DX9 manages its texture memory buffers in x64. The issue is being discussed here. (http://www.gamedev.net/topic/678905-dx9-doubling-memory-usage-in-x64/)
tormento
31st May 2016, 09:42
What about creating a installer with latest versions from pinterf? ;)
fAy01
31st May 2016, 10:07
What about creating a installer with latest versions from pinterf? ;)
he's busy porting other plugins to x64.
pinterf
31st May 2016, 11:25
he's busy porting other plugins to x64.
And I have no experience in installers.
pinterf
31st May 2016, 11:44
Is it intentional that ColorbarsHD is 1288x720 and not 1280x720?
The avisynth doc also mentions 1288.
vBulletin® v3.8.11, Copyright ©2000-2025, vBulletin Solutions Inc.