DirectX 11 Texture Sharing in Unity3D [Tutorial]

Posted by admin | 28. August 2019 | Allgemein, Blog, Downloads, Tutorial

Sometimes, you want to share a Texture that you have created in one application to another application on the same machine. When performance is important, the DirectX Texture Sharing feature should be interesting for you. It let’s you share a DirectX texture between multiple processes – for example, if you want to make a Texture that you have created in Unity available to another program or, vice versa, when you want to use an externally created texture inside Unity. The Texture Sharing feature lets you access the texture with zero latency, instantaneously – nice!

In this tutorial, I will just very briefly explain how to get your external created DirectX texture into Unity using the SharpDX library. However, I will only describe the part on the Unity side; the DirectX-part (the texture creation etc.) will not be covered in this.

Importing SharpDX into your Unity project

SharpDX is a library that acts as a C# wrapper for DirectX. Since Unity does not allow easily to call DirectX native functions, we need to import this library into Unity. This is easiest if you download the SharpDX DLLs via Visual Studio’s NuGet functionality: Just open the Visual Studio Solution for your Unity project and download the SharpDX NuGet packages you need (in my case, this was the SharpDX package and the SharpDX Direct3D11 package). You can download the packages via NuGet: https://www.nuget.org/packages/SharpDX

Now, inside your project find the DLLs from the NuGet packages (e.g. in Packages\SharpDX.Direct3D11.4.2.0\lib\net45) and copy them into the Plugins folder of your Unity Assets. After a restart, you should be able to now import SharpDX and call the respective functions.

But first, I had to receive the pointer to the shared texture and initialize it inside Unity:

    private IntPtr mlabTextureHandle = IntPtr.Zero;

    private void GetImageFromDX11TextureSharing(byte[] data)
    {
        if (texSharingInitiated)
            return;

        var intptrstr = System.Text.Encoding.UTF8.GetString(data);
        long intptr;

        if (long.TryParse(intptrstr, out intptr))
        {
            mlabTextureHandle = new IntPtr(intptr);
            shouldUpdateMlabTextureRef = true;
            FlipCameraTexture(true);
        }
        else
            throw new FormatException("Shared texture pointer has not the right format");
    }

Accessing the shared texture inside Unity

The tricky part is now that Unity tries to hide the DirectX device to you, but you need it for the proper communication with your graphics card. Therefore, I had to find a kind of „hack“ to get the device Unity is using (see code). Once you have the device, you can call your DirectX functions as usual. In this case, you have to call OpenSharedResource and create a new ShaderResourceView on the shared texture. Then you just have to call Unity’s CreateExternalTexture function, passing the native pointer to your ShaderResourceView of your shared texture. That’s it!
Please mind that the Texture you are creating inside Unity of course has to match the Type, width and height of the shared texture you want to access.

    private void UpdateMlabTextureRef()
    {
        if (texSharingInitiated)
            return;
        if (mlabTextureHandle == IntPtr.Zero || mlabTextureHandle == null)
            throw new ArgumentNullException("DX11SharedTextureHandle");

        // Hack into Unity's D3D device
        var castedTex = new SharpDX.Direct3D11.Texture2D(texture.GetNativeTexturePtr()); 
        var deviceChild = castedTex.QueryInterface<SharpDX.Direct3D11.DeviceChild>();
        var d3d11device = deviceChild.Device;

        // Get Shader Resource View to shared texture
        var d3d11tex = d3d11device.OpenSharedResource<SharpDX.Direct3D11.Texture2D>(mlabTextureHandle);
        var d3d11srv = new SharpDX.Direct3D11.ShaderResourceView(d3d11device, d3d11tex);

        // Create texture in Unity context
        var unityTex = Texture2D.CreateExternalTexture((int)renderWidth * 2,
                    (int)renderHeight,
                    TextureFormat.RGBA32,
                    false,
                    true,
                    d3d11srv.NativePointer);

        leftEyeImage.texture = unityTex;
        rightEyeImage.texture = unityTex;

        ConsoleDispatcher.Print("Successfully established DirectX texture sharing with MeVisLab");

        texSharingInitiated = true;
    }

This was tested using Windows 10 64Bit, SharpDX 4.2.0, Visual Studio 2017 and Unity 2018.3.5.

Tags
, ,

Add a comment

*Please complete all fields correctly

Related Blogs

Posted by admin | 06 Oktober 2019
Working with Compute Shader in Unreal is complicated and annoying: One finds rarely information on the web on how to implement, use or include them in Unreal - which is…
Posted by admin | 15 Juli 2019
It is well-known that building WebRTC from source can be a quite painful process because the WebRTC library has many dependecies and a very complex build pipeline. In a recent…
Posted by admin | 16 Juli 2018
Download the Plugin for the Unreal Engine here: https://github.com/ValentinKraft/UE4_SortingComputeShader The compute shader that handles the sorting: //Since we can't #include private Engine shaders such as Common.ush we have to copy…