AviSynth / AviSynthPlus

AviSynth with improvements
http://avs-plus.net
981 stars 76 forks source link

Plugins cross-compiled with mingw don't load (C++ API) #281

Open Frechdachs opened 2 years ago

Frechdachs commented 2 years ago

Loading plugins that are cross-compiled with mingw for Windows doesn't work. A manual LoadPlugin call executes without error, but the filters provided by the plugin won't be available

Steps to reproduce:

Consider the following simple plugin that includes a single filter that only passes through frames:

#include <avisynth/avisynth.h>

class PassThrough : public GenericVideoFilter
{
public:
    PassThrough(PClip _child);
    ~PassThrough();
    int __stdcall SetCacheHints(int cachehints, int frame_range) override;
    PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *env) override;
};

PassThrough::PassThrough(PClip _child) : GenericVideoFilter(_child)
{
}

PassThrough::~PassThrough()
{
}

int __stdcall PassThrough::SetCacheHints(int cachehints, int frame_range)
{
    return cachehints == CACHE_GET_MTMODE ? MT_NICE_FILTER : 0;
}

PVideoFrame __stdcall PassThrough::GetFrame(int n, IScriptEnvironment *env)
{
    PVideoFrame src = child->GetFrame(n, env);
    return src;
}

AVSValue __cdecl passthrough_create(AVSValue args, void *user_data, IScriptEnvironment *env)
{
    PClip clip = args[0].AsClip();
    return new PassThrough(clip);
}

const AVS_Linkage *AVS_linkage;

extern "C" __declspec(dllexport) const char * __stdcall AvisynthPluginInit3(IScriptEnvironment *env, const AVS_Linkage * const vectors)
{
    AVS_linkage = vectors;

    env->AddFunction("PassThrough", "c", passthrough_create, nullptr);

    return "Passthrough plugin";
}

Compile it with

x86_64-w64-mingw32-g++ -std=c++11 -shared -fPIC passthrough.cpp -o passthrough.dll

(I only tested 64-bit.) Then trying to load the plugin with LoadPlugin("passthrough.dll") will have no effect. Not even an error message.

On the other hand, compiling it for Linux with the following command works just fine. On Linux, the plugin gets loaded and the filter is usable.

g++ -std=c++11 -shared -fPIC passthrough.cpp -o libpassthrough.so

Cross-compilation was tested on Arch Linux with their official mingw packages. I have attached the cross-compiled .dll together with their dependencies (it depends on libgcc_s_seh-1.dll, libstdc++6.dll and libwinpthread-1.dll). passthrough.zip

I hope this helps to figure out what is going on.

BTW, the C API cross-compiles just fine.

DJATOM commented 2 years ago

Afaik that's a known problem. Use msvc or mingw-compiled libavisynth.dll (msvc-built plugins will not work).

qyot27 commented 2 years ago

Yes, this is a long-standing known issue, and AFAIK an unavoidable one. MSVC's and GCC's C++ ABIs are fundamentally not portable due to name mangling, exception handling, and the like (and the given excuse is that while you can probably get close enough to compatibility to fudge it on some level, AviSynth's C++ API was never written with that in mind, and changing it now would break existing C++ plugins...I don't know how true that is, but that's the claim). Unfortunately, because the C++ API came first (and GCC compatibility didn't exist until it was introduced into Plus in 2016), the vast majority of plugins out there use the C++ API and not the C API.

The C API was itself at least partially intended to overcome this (the other big reason was that it was actually intended to help ease the porting effort to Linux...which didn't actually happen for another like, 17 years after the C interface was created).

AviSynth+ can be built using MinGW-w64, though, and that build can run C++ plugins that were built with MinGW-w64. Additionally, because Windows has to be all kinds of irritating, the difference in calling convention between the Win32 and Win64 APIs means that while a 64-bit build of FFmpeg can use either an MSVC-built or GCC-built AviSynth+, a 32-bit build of FFmpeg can't, and requires a separate build just to support 32-bit GCC builds of AviSynth+ (the times I've put out test builds of FFmpeg or mpv, those builds are the ones with the -avsgcc suffix).

If you do have a build of AviSynth+GCC, it does recognize plugins_gcc and plugins64_gcc as the autoload paths for plugins, and ignores the regular plugins/plugins64 directories. MSVC builds are the opposite, and ignore the _gcc directories.

In terms of the theoretically possible, it's been floated before that it might be possible to write a filter that wraps C++ plugins and loads them through the C interface instead, but as of yet no one's tried or if they have, they either haven't succeeded or haven't made it public.

On practically every other operating system, GCC (or Clang using its default GCC compatibility) is so dominant that there's almost no chance of running into this problem. Other C++ compilers do exist, but like Clang either they defer to matching GCC's ABI, or they just don't have anywhere close to a notable install base to make any incompatibility a problem.

Frechdachs commented 2 years ago

That's unfortunate. But thank you for this extensive and informative reply.