Building plug-ins for desktop platforms
Low-level native plug-in Profiler API

Low-level native plug-in interface

In addition to the basic script interface, Native Code Plug-ins in Unity can receive callbacks when certain events happen. This is mostly used to implement low-level rendering in your plug-in and enable it to work with Unity’s multithreaded rendering.

定义 Unity 所公开的接口的头文件随 Editor 一起提供。

接口注册表

A plug-in should export UnityPluginLoad and UnityPluginUnload functions to handle main Unity events. See IUnityInterface.h for the correct signatures. IUnityInterfaces is provided to the plug-in to access further Unity APIs.

# include "IUnityInterface.h"
# include "IUnityGraphics.h"
// Unity 插件加载事件
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
    UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
    IUnityGraphics* graphics = unityInterfaces->Get<IUnityGraphics>();
}

访问图形设备

A plug-in can access generic graphics device functionality by getting the IUnityGraphics interface. In earlier versions of Unity a UnitySetGraphicsDevice function had to be exported in order to receive notification about events on the graphics device. Starting with Unity 5.2 the new IUnityGraphics interface (found in IUnityGraphics.h) provides a way to register a callback.

# 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;
        }
    };
}

Plug-in Callbacks on the Rendering Thread

如果平台和可用 CPU 数允许,则在 Unity 中的渲染可以是多线程的。当使用多线程渲染时,执行这些渲染 API 命令的线程完全独立于运行 MonoBehaviour 脚本的线程。因此,您的插件并非总是可以立即开始进行渲染,因为它可能会干扰渲染线程此时正在进行的工作。

为了从插件中进行任何渲染,应从脚本中调用 GL.IssuePluginEvent。这将导致从渲染线程调用提供的本机函数。例如,如果从摄像机的 OnPostRender 函数调用 GL.IssuePluginEvent,则在该摄像机已完成渲染后,您将立即获得插件回调。

IUnityGraphics.h 中提供了 UnityRenderingEvent 回调的签名。 原生插件代码示例:

// 用于处理特定渲染事件的插件函数
static void UNITY_INTERFACE_API OnRenderEvent(int eventID)
{
    //TODO:用户渲染代码
}
    
// 自由定义的函数,用于将回调传递给特定于插件的脚本
extern "C" UnityRenderingEvent UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
    GetRenderEventFunc()
{
    return OnRenderEvent;
}

Managed plug-in code example:

# if UNITY_IPHONE &amp;&amp; !UNITY_EDITOR
[DllImport ("__Internal")]
# else
[DllImport("RenderingPlugin")]
# endif
private static extern IntPtr GetRenderEventFunc();
    
// 对要在渲染线程上调用的特定回调进行排队
GL.IssuePluginEvent(GetRenderEventFunc(), 1);

现在还可通过 CommandBuffer.IssuePluginEvent 将此类回调添加到 CommandBuffer。

Plug-in using the OpenGL graphics API

有两种 OpenGL 对象:跨 OpenGL 上下文共享的对象(纹理、缓冲、渲染缓冲、采样器、查询、着色器和程序对象)和每个 OpenGL 的上下文对象(顶点数组、帧缓冲、程序管线、变换反馈和同步对象)。

Unity 使用多个 OpenGL 上下文。在初始化和关闭 Editor 和播放器时,我们依赖于上下文,但我们使用专用上下文进行渲染。因此,在 kUnityGfxDeviceEventInitializekUnityGfxDeviceEventShutdown 事件期间无法创建每个上下文的对象。

For example, a native plug-in can’t create a vertex array object during a kUnityGfxDeviceEventInitialize event and use it in a UnityRenderingEvent callback, because the active context is not the one used during the vertex array object creation.

示例

An example of a low-level rendering plug-in is on bitbucket: bitbucket.org/Unity-Technologies/graphicsdemos (NativeRenderingPlugin folder). It demonstrates two things:

  • 在完成所有常规渲染后,从 C++ 代码渲染旋转三角形。
  • 从 C++ 代码填充程序化纹理(使用 Texture.GetNativeTexturePtr 来访问该纹理)。

该项目适用于:

  • Windows (Visual Studio 2015) with Direct3D 9, Direct3D 11, Direct3D 12, OpenGL and Vulkan.
  • 装有 Metal 和 OpenGL 的 Mac OS X (Xcode)。
  • 装有 Direct3D 11 和 Direct3D 12 的通用 Windows 平台。
  • WebGL
  • Android (ndk-build) with OpenGL ES 2.0, 3.x and Vulkan

• 2017–05–16 Page amended with no editorial review

Building plug-ins for desktop platforms
Low-level native plug-in Profiler API