Revisiting InterOp with Unity, UWP and DirectX

  • June 28, 2017
  • Views

    1,260

In the last few years — particularly with the release of new 3D-focused devices in Virtual Reality (VR), New User Input, and Mixed Reality — we’ve seen Unity grow from an easy platform for game developers into one of the main development tools for graphical 2D/3D, simulation, visualization and cross-platform software solutions.

Unity has its own runtime system and, while it makes it easier to create heavy graphics and UI-focused applications from games to simulations without having to build common components from scratch, the runtime can, in some cases, become limiting. In our hackfest with Aveva, we encountered a requirement that made us consider a hybrid approach in which we would use the workstation and server GPU while still use Unity Runtime as the main platform for handling device input, 3D model processing, camera, physics, and UI. To use this approach, we needed to build plugins and utilize the WebRTC library.

Fortunately, Unity supports low-level plugins that allow access to native APIs to get closer to the hardware. On the Microsoft Windows platform, native code plugins can be written for Standalone (typically PC Desktop) x86/x64 and Universal Windows Platform (UWP) x86/x64 to access DirectX for graphics and device APIs for hardware features. Unity provides documentation for low-level plugins for DirectX,  but limited details are available about the full interaction from the plugin back to Unity. This effort brings us back to C/C++ and InterOp when .NET and managed code were introduced, and the need to utilize the wealth of existing native libraries.

In our reference example (UnityWithWebRTC), the requirements are pushed further with the need to use a UWP package coming from Nuget.org. WebRTC is an open source project that provides browsers, mobile applications, and apps real-time communication (RTC) capabilities using simple APIs. The UWP Sample App PeerCC was used as the reference for creating the bridging library between Unity and the WebRTC package.

There are multiple ways to handle the integration. The example uses this setup:

Low-Level Plugin

The image frame is handled in the DirectX plugin, which takes an H.264 encoded byte array for decoding and conversion to the matching texture color format and updating the texture object. Processing the image frame in the Unity script in a CoRoutine would not work, as the operations would be limited to the script update cycle. The plugin access requires DllImport to call the native code from the managed Unity script (ControlScript.cs).

The DllImport attribute provides a link back to the available method/functions in the native library to be called from the managed C# code in the Unity Runtime. Now that the code in the native library can be executed, the next challenge is passing parameters. Passing by Value is not an issue as the name implies: the value is merely passed to the method. It becomes tricky, however, when Pass by Reference is needed, such as in a use case where a byte array needs to be passed from managed code to unmanaged/native methods.

GCHandle.Alloc is used with GCHandleType.Pinned since we are passing by reference a byte array to a native plugin to prevent garbage collection and the object from being moved during the plugin execution.

Managing Updates

The Unity Runtime and DirectX Plugin share a Texture Object. The texture provides the image data to a Quad GameObject in the Unity Scene while the Texture data is updated in the plugin.

The plugin is signaled from the Unity Runtime when it can execute an update on the shared graphics context objects via a CoRoutine that waits for EndOfFrame and then invokes GL.IssuePluginEvent(GetRenderEventFunc(), 1).

On the plugin DLL, the GetRenderEventFunc() triggers when the plugin can invoke the graphics context UpdateSubresource(d3dtex, 0, NULL, dataPtr, rowPitch, 0) to update the Texture object. Since the texture is defined by Unity, the update here carries forward to the texture and object in the Unity Scene:

UWP Libraries

Compared to low-level plugins, working with Managed UWP Libraries is simpler — the main step involves putting the .DLL or .WINMD into the Project Hierarchy and defining its properties.

The UWP WebRTC libraries can also be included in the plugins and invoked from Unity C# Scripts which is really managed code running on .NET Runtime when deployed on Windows. WebRtc.DLL and WebRtc.WINMD are the only files needed and the “Don’t Process” (no patching) checkbox is set.

With UWP Plugins, the libraries can be referenced in the Unity Scripts the same way as referenced libraries or dependencies. The code sections typically need to be wrapped by compiler directives as they are not recognized by the Unity Editor.

Summary

As Unity applications become more complex, the need arises to extend Unity to provide additional capabilities. In our work with Aveva, we used Unity’s plugin architecture to utilize the workstation and server GPU, while still leveraging the Unity runtime for input and display.

The Unity with WebRTC Project is posted on GitHub to serve as a reference for the setup and interaction of components. Feel free to send us questions, comments, and feedback.

 

Related Articles

Leave a reply

Your email address will not be published. Required fields are marked *