Android
C#
Unity

UnityのGL.IssuePluginEventを使ってandroidのJavaメソッドを呼ぶ方法

More than 1 year has passed since last update.

昨日投稿した「Unityから画像のピクセル情報をネイティブに渡す方法」の続きです。

昨日のプロジェクトではOnPostRenderでJavaのメソッドを呼んで、Javaネイティブ側でglReadPixelsを実行させていたのですが、UnityのMultithreaded Renderingに対応していませんでした。
そこで、GL.IssuePluginEventを使ってMultithreaded Renderingに対応させようということなのですが、結果から言えば半分できて半分諦めました。

更新したサンプルプロジェクトはこちら
https://bitbucket.org/HoshiyamaTakaaki/pixelreadstest

GL.IssuePluginEventはレンダリングスレッドから指定のC++メソッドを呼んでくれる命令です。
なのでそのままではJavaメソッドは呼べません。
そこでいったんC++のメソッドを経由してJNI経由でJavaのstaticメソッドを呼び出します。

CameraOnPost.cs
#if !UNITY_EDITOR
#if UNITY_ANDROID
    [DllImport("glutil")]
    private static extern IntPtr getRenderEventFunc();
#endif
#endif

    private void OnPostRender()
    {
#if UNITY_ANDROID && !UNITY_EDITOR
        GL.IssuePluginEvent(getRenderEventFunc(), m_TargetTexture.GetNativeTexturePtr().ToInt32());
        //m_NativePlugin.CallStatic("sendRgbaFrame", m_TargetTexture.GetNativeTexturePtr().ToInt32());
        //GL.InvalidateState();
#endif
    }
glutil.cpp
extern "C" {
    using UnityRenderEvent = void(*)(int);

    UnityRenderEvent getRenderEventFunc();
}

// call from Unity(IssuePluginEvent)
void onRenderEvent(int eventId)
{
    LIBENC_LOGD("onRenderEvent:%d", eventId);
    JNIEnv *jenv = getEnv();
    if (gjClass_UnityConnect == NULL)
    {
        gjClass_UnityConnect = (jclass)jenv->NewGlobalRef(findClass(jenv, "jp/ne/pickle/libpixelreads/UnityConnect"));
        gjMethodId_sendRgbaFrame = jenv->GetStaticMethodID(gjClass_UnityConnect, "sendRgbaFrame", "(I)V");
    }
    if (gjClass_UnityConnect != NULL)
    {
        jenv->CallStaticVoidMethod(gjClass_UnityConnect, gjMethodId_sendRgbaFrame, eventId);
        jthrowable mException = jenv->ExceptionOccurred();
        if (mException)
        {
            jenv->ExceptionDescribe();
            jenv->ExceptionClear();
        }
    }
    else
    {
        LIBENC_LOGE("jClass not got");
    }
}

// call from Unity(IssuePluginEvent)
UnityRenderEvent getRenderEventFunc()
{
    return onRenderEvent;
}

このような形で、C#からC++のonRenderEventがレンダリングスレッドで呼ばれます。
そしてメソッド内でJavaの目的のメソッドを探してアクセスを行います。
ちなみにJNI_OnLoadが呼ばれるスレッドとは別スレッドとなるので、JavaVM以外のJNIEnvなどの情報は取得し直す必要があるようです。

これで一応Javaメソッドの呼び出しは行えるようになりましたが、ここからJava側でOpenGLESの操作を行う場合に、色々とRuntimeエラーが発生しました。
たぶんサンプルコードではJava側のOpenGLESの初期化(UnityConnect.initialize)などが別スレッドで行われているからだと思います。
GL_INVALID_OPERATIONが出まくります。

というわけで、スレッドを意識して処理をきちんと整理すれば動くようになるんじゃないかと思いますが、正直もうこれ以上沼にはまる時間はないので、Multithreaded Renderingは諦めようと思います。

Androidは本当に沼です。