基本的なスクリプトのインターフェースに加えて、プラグイン は Unity 上で発生した特定のイベントをコールバックで受け取ることができます。これはよく、プラグインで低レベルのレンダリングを実装し、Unity のマルチスレッドレンダリングを操作するために使用されます。
Unity はインターフェースを定義するヘッダとエディターを一緒に提供しています。
プラグインは、主要な Unity イベントを扱うために UnityPluginLoad
と UnityPluginUnload
をエクスポートします。正しいシグネチャは IUnityInterface.h
を参照してください。その他の Unity API にアクセスするには、プラグインに IUnityInterfaces
を示します。
# include "IUnityInterface.h"
# include "IUnityGraphics.h"
// Unity プラグインロードイベント
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
IUnityGraphics* graphics = unityInterfaces->Get<IUnityGraphics>();
}
プラグインは、 IUnityGraphics
インターフェースを取得することでジェネリックのデバイス機能にアクセスできます。古いバージョンの Unity では、 グラフィックスデバイス上のイベントに関する通知を受け取るには UnitySetGraphicsDevice
関数をエクスポートする必要がありました。 Unity 5.2 からは、新しい IUnityGraphics インターフェース(IUnityGraphics.h
にあります)でコールバックを登録することが可能となっています。
# include "IUnityInterface.h"
# include "IUnityGraphics.h"
static IUnityInterfaces* s_UnityInterfaces = NULL;
static IUnityGraphics* s_Graphics = NULL;
static UnityGfxRenderer s_RendererType = kUnityGfxRendererNull;
// Unity プラグインロードイベント
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
s_UnityInterfaces = unityInterfaces;
s_Graphics = unityInterfaces->Get<IUnityGraphics>();
s_Graphics->RegisterDeviceEventCallback(OnGraphicsDeviceEvent);
// OnGraphicsDeviceEvent(initialize) をプラグインのロードに手動で実行して
// グラフィックスデバイスがすでに初期化されている場合でもイベントを逃さないようにします
OnGraphicsDeviceEvent(kUnityGfxDeviceEventInitialize);
}
// Unity プラグインアンロードイベント
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
UnityPluginUnload()
{
s_Graphics->UnregisterDeviceEventCallback(OnGraphicsDeviceEvent);
}
static void UNITY_INTERFACE_API
OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType)
{
switch (eventType)
{
case kUnityGfxDeviceEventInitialize:
{
s_RendererType = s_Graphics->GetRenderer();
//TODO: ユーザー初期化コード
break;
}
case kUnityGfxDeviceEventShutdown:
{
s_RendererType = kUnityGfxRendererNull;
//TODO: ユーザーシャットダウンコード
break;
}
case kUnityGfxDeviceEventBeforeReset:
{
//TODO: ユーザー Direct3D 9 コード
break;
}
case kUnityGfxDeviceEventAfterReset:
{
//TODO: ユーザー Direct3D 9 コード
break;
}
};
}
Unity のレンダリングは、プラットフォームと CPU の数が一定の条件を満たした場合は、マルチスレッドで行われます。マルチスレッドのレンダリングが使用されるとき、レンダリング API の命令は、MonoBehaviour スクリプトを実行するスレッドとは完全に別の 1 つのスレッド上で行われます。結果として、プラグインがいくつかのレンダリングを即座に開始することが常に可能というわけではありません。なぜなら、その時レンダースレッドが行っている処理に干渉する場合があるからです。
プラグインから どのような レンダリングを行う場合でも、スクリプトから GL.IssuePluginEvent を参照しメインスレッドから呼び出されるようにする必要があります。例えば、カメラの OnPostRender 関数から GL.IssuePluginEvent を呼び出す場合、プラグインはカメラのレンダリング終了後すぐにコールバックを取得します。
UnityRenderingEvent
コールバックのシグネチャは IUnityGraphics.h
で示されます。
ネイティブプラグイン コードサンプル
// 特定のレンダリングイベントを処理するプラグイン関数
static void UNITY_INTERFACE_API OnRenderEvent(int eventID)
{
//TODO: ユーザーレンダリングコード
}
// プラグイン特有のスクリプトにコールバックを渡すための自由に定義した関数
extern "C" UnityRenderingEvent UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
GetRenderEventFunc()
{
return OnRenderEvent;
}
マネージドプラグイン コード例
# if UNITY_IPHONE && !UNITY_EDITOR
[DllImport ("__Internal")]
# else
[DllImport("RenderingPlugin")]
# endif
private static extern IntPtr GetRenderEventFunc();
// 指定のコールバックがレンダースレッドで呼び出されるようにキューで待機
GL.IssuePluginEvent(GetRenderEventFunc(), 1);
このコールバックは CommandBuffer.IssuePluginEvent によって CommandBuffers に追加することもできます。
2 種類の OpenGL オブジェクトがあります。OpenGL コンテキストをまたいで共有されるオブジェクト (テクスチャ、バッファ、レンダーバッファ、サンプラー、クエリ、シェーダー、プログラムオブジェクト) と OpenGL コンテキストごとのオブジェクト (頂点配列、フレームバッファ、プログラムパイプライン、トランスフォームフィードバック、同期オブジェクト) です。
Unity は複数の OpenGL コンテキストを使用します。エディターとプレイヤーの初期化と終了のときは マスター コンテキストに依存しますが、レンダリングには専門のコンテキストを使用します。したがって、kUnityGfxDeviceEventInitialize
と kUnityGfxDeviceEventShutdown
イベントの間、コンテキストごとのオブジェクトを作成することはできません。
例えば、ネイティブのプラグインは kUnityGfxDeviceEventInitialize
イベントの間に頂点配列オブジェクトを作成し、それをUnityRenderingEvent
コールバックで使用することはできません。アクティブなコンテキストは、頂点配列オブジェクトの作成のときに使用されたものではないからです。
低レベルレンダリングプラグインの例は bitbucket にあります。2 つの例を紹介しています。 ここでダウンロードできます。 (NativeRenderingPlugin フォルダー)
プロジェクトは以下の仕様で動作します。