LoginSignup
3
3

More than 5 years have passed since last update.

Unity上でSharpDXする

Last updated at Posted at 2017-04-11

UnityはLow-level Native Plugin InterfaceによりD3D11にアクセスできるが、SharpDXでも同様のことをすることができた。言うなればHigh-level Native Plugin Interface?

重大な弱点

これEditorで実行するとmonoのリソース回収時(Editor終了時または、2回目以降のPlay時)にデッドロックで固まるのが発覚したのですが、回避方法が見つかりました。

    [DllImport("mono")]
    static extern IntPtr mono_thread_current();

    [DllImport("mono")]
    static extern IntPtr mono_thread_detach(IntPtr p);

    void OnRender(int eventID)
    {
        try
        {
            // 処理
        }
        finally
        {
            mono_thread_detach(mono_thread_current());
        }
    }

OnRenderの最後でmono_thread_detachをコールする。

後で詳しく書く。

環境

  • Windows10 64bit(CreatorsUpdate適用済み)
  • Unity5.6.0f3
  • VisualStudio2017

コード

dllの配置。
folder.png

その1。適当に.Net3.5プロジェクトを作成してSharpDXの.Net3.5対応バージョンを探る

  • VisualStudio2015で新規プロジェクトをクラシックデスクトップのどれか、.Netバージョン3.5で作成する。
  • nugetでSharpDXの動作するバージョンを探す。
  • SharpDX-2.6.3ならいけた。

これもUnityのランタイムが.Net4互換にアップデートするまでの措置だがいつになることやら。

その2。Unityのプロジェクトを作成して上記のプロジェクトからSharpDXのdllを投入する

  • SharpDX.dll
  • SharpDX.DXGI.dll
  • SharpDX.Direct3D11.dll

standalone.png

こういう感じのテストコード。

using UnityEngine;

public class SharpDx : MonoBehaviour
{
    void Start()
    {
        var tex = new Texture2D(640, 480);

        // テクスチャのポインタをSharpDXに渡してID3D11Texture2Dとして扱う
        using (var t = new SharpDX.Direct3D11.Texture2D(tex.GetNativeTexturePtr()))
        {
            // 実験
            var desc=t.Description;
            Debug.Log(desc.Width);
        }
    }
}

ちゃんとDescription(D3D11_TEXTURE2D_DESC)が取れた。

その2.5。しかしデバッガがアタッチできない

warning MSB3268: プライマリ参照 "SharpDX" は、現在ターゲットされているフレームワークで解決できなかったフレームワーク アセンブリ "System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" に間接的に依存するため、解決できませんでした。".NETFramework,Version=v3.5,Profile=Unity Subset v3.5"。この問題を解決するには、参照 "SharpDX" を削除するか、"System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" を含むフレームワーク バージョンにアプリケーションを再ターゲットしてください。

追加で

  • C:\Windows\Microsoft.NET\Framework64\v2.0.50727\System.Drawing.dll
  • C:\Windows\Microsoft.NET\Framework64\v2.0.50727\System.Windows.Forms.dll

をpluginsに投入する。

その3。ならばUWPだ

  • 最新版のSharpDX-3.1.1
  • SharpDX.dll
  • SharpDX.DXGI.dll
  • SharpDX.Direct3D11.dll

Placeholderをセットするべし

uwp.png

Windows10をターゲットにしてプロジェクトをエクスポート。

wsa.png

Debug, X86, ローカルマシーンを指定してビルド実行すると以下のエラーが出た。

error CS0012: 型 'ComObject' は、参照されていないアセンブリに定義されています。アセンブリ 'SharpDX, Version=3.1.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1' に参照を追加する必要があります。
error CS1674: 'Texture2D': using ステートメントで使用される型は、暗黙的に 'System.IDisposable' への変換が可能でなければなりません。

うぅむ。
Assembly-CSharpにSharpDXをnugetすることで動いた。
コンパイルエラーにならないのでわかりにくい。

uwp-sharpdx.png

APP_EXPORT_DIR/GeneratedProjects/UWP/Assembly-CSharp/project.json
{
  "dependencies": {
    "Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0",
    "SharpDX": "3.1.1",
    "SharpDX.Direct3D11": "3.1.1",
    "SharpDX.DXGI": "3.1.1"
  },
  "frameworks": {
    "uap10.0": {}
  },
  "runtimes": {
    "win10-arm": {},
    "win10-arm-aot": {},
    "win10-x86": {},
    "win10-x86-aot": {},
    "win10-x64": {},
    "win10-x64-aot": {}
  }
}

その4。公式のLowLevelNativePluginのデモプロジェクトを移植する

なるほど、わりと動きそう。
もう少し大きい例を試してみようということでLowLevelNativePluginの内容をSharpDXに移植してみよう。
これ。

できた

ss.png

だいたい動いている

ID3D11Deviceを渡す

以下の関数はC#で作ったとしてもUnityから呼んでもらうことができないので

void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces);
void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload();

別の方法でDeviceを渡す。

ID3D11Texture2D::GetDevice

を使う。
SetTextureFromUnity関数でDeviceもいただく。

    static IntPtr g_TextureHandle;
    static int g_TextureWidth = 0;
    static int g_TextureHeight = 0;
    public static void SetTextureFromUnity(System.IntPtr texture, int w, int h)
    {
        g_TextureHandle = texture;
        g_TextureWidth = w;
        g_TextureHeight = h;

        using (var dxTexture = new SharpDX.Direct3D11.Texture2D(texture))
        {
            var device=dxTexture.Device;
        }
    }

ID3D11Deviceを返す

UnityPluginUnload()

が無いので

OnApplicationQuit

で参照を返せるようにIDisposableなクラスを作ることにした。

ToDo

  • 深度バッファが有効にならない
  • エディターの終了時に固まる(一度でもGL.IssuePluginEventを呼び出すとそうなる様子。回避するためにUnityPluginLoadとUnityPluginUnload受付用のpluginを作る必要があるかもしれない)
  • わりとクラッシュする(GCでポインターが移動したときのような気がするが・・・)
  • SharpDX関連がDisposeすべきか否かよくわからない(初期化時に作って使い回したほうがいいかも。きれいに終了できないのだけども)

まとめ

UnityとSharpDXを組み合わせることでc++無用でNativePluginのように直接D3D11にアクセスできた。
Android、iOS等のクロスプラットフォームが不要で、そこまでCPU側のパフォーマンスにシビアではない用途で使い道があると思う。
画素や頂点を計算して更新するようなタイプはさすがにC++で書いた方がいいだろうけど、別のAPIやネットワークとのグルーコードなどはスレッドに乗せて少々遅かろうがスクリプトスレッドの邪魔をしなければよろしいという観点とC++めんどくせぇ(堕落)、ましてUWP向けのC++やC++/CX・・・という気持ちです。

具体的には、

  • MediaFoundation(SharpDXに実装がある)からのテクスチャ更新
  • Direct2D(SharpDXに実装がある)からのテクスチャ更新

  • レンダリング結果をCPUに戻して使う(動画なら30FPSでOKなど)

等の用途をC#で完結させることを見込んでいる。
そして・・・

hololens.png

Hololensでも動いたよ。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3