[Unity]TextureをSwiftのNativePluginから送る

# Unity に Texture を Swift の Native Plugin から送る

* UnityのTextureのポインタをSwiftだけで作ったNative Pluginに渡して処理するの逆の話です

https://qiita.com/fuziki/items/8ab522516a19e2323811

* ネイティブで作成した Texture を Unity へ送って表示させたい場合があります
* 例えば、macOS のスクリーンキャプチャの映像を Unity で表示させる事ができます
* macOS や iOS では、MTLTexture から Texture2D.CreateExternalTexture を使って Texture2D を取得可能です

# 今回のサンプル
* リポジトリはこちらです

https://github.com/fuziki/mcDesktopCapture

* [Swift だけで、Unity の iOS の Native Plugin を作る](https://qiita.com/fuziki/items/955c2b35514bcfc37969) の内容をベースにしています
* macOS で、スクリーンキャプチャした結果を Cube のテクスチャに貼っています

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">unityでmacのデスクトップキャプチャできた！ <a href="https://t.co/x2AkZ6z6zF">pic.twitter.com/x2AkZ6z6zF</a></p>&mdash; ふじき (@fzkqi) <a href="https://twitter.com/fzkqi/status/1402810152642760704?ref_src=twsrc%5Etfw">June 10, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# Texture2D.CreateExternalTexture
* [Texture2D.CreateExternalTexture](https://docs.unity3d.com/ja/current/ScriptReference/Texture2D.CreateExternalTexture.html) を使うことで、ネイティブのテクスチャオブジェクトから Unity テクスチャを作成できます
* iOS や macOS の場合は、ネイティブのテクスチャオブジェクトは MTLTexture です
* そこで、MTLTexture のポインタ、テクスチャの高さと幅を用意します


# Unityの実装
## Native Plugin の定義

* width と height のポインタを渡して、ネイティブに代入させます
* 戻り値として、MTLTexture のポインタを IntPtr として受け取ります

```c#
[DllImport("mcDesktopCapture")]
private static extern IntPtr mcDesktopCapture_getCurrentFrame2(ref int width, ref int height);
```

## Texture を取得する

* 取得したポインタから、Texture2D.CreateExternalTexture を使って Texture を得て設定しています

```c#
int w = -1;
int h = -1;
IntPtr texturePtr = mcDesktopCapture_getCurrentFrame2(ref w, ref h);
Texture2D texture = Texture2D.CreateExternalTexture(w, h, TextureFormat.ARGB32, false, false, texturePtr);
Renderer m_Renderer = GetComponent<Renderer>();
m_Renderer.material.SetTexture("_MainTex", texture);
```

# Swift の実装

* `@_cdecl` を付けて、C# で作成した関数を実装します
* ref を UnsafeMutablePointer として、IntPtr を UnsafeMutableRawPointer として読み替えます
* width と height にテクスチャの大きさを設定します
* retain してテクスチャのポインタを返します

```swift
@_cdecl("mcDesktopCapture_getCurrentFrame2")
public func mcDesktopCapture_getCurrentFrame2(_ width: UnsafeMutablePointer<Int64>,
                                              _ height: UnsafeMutablePointer<Int64>) -> UnsafeMutableRawPointer {
    let texture: MTLTexture = getTexture()
    width.initialize(to: Int64(texture.width))
    height.initialize(to: Int64(texture.height))
    return Unmanaged.passRetained(texture).toOpaque()
}
```

* 破棄が必要な場合は、takeRetainedValue を使います

```swift
@_cdecl("mcDesktopCapture_clearFrame")
public func mcDesktopCapture_clearFrame(_ texturePtr: UnsafeMutableRawPointer) {
    _ = Unmanaged<MTLTexture>.fromOpaque(texturePtr).takeRetainedValue()
}
```

# ライブラリの出力と設定

* こちらの記事の `Framework でビルドする` の章以降と同じ手順で実行ビルド可能です

https://qiita.com/fuziki/items/955c2b35514bcfc37969#framework-%E3%81%A7%E3%83%93%E3%83%AB%E3%83%89%E3%81%99%E3%82%8B

* 作成した ライブラリ を Unity のプロジェクトにコピーして、Texture を取得できていることを確認します

# おわりに
* Texture2D.CreateExternalTexture を使うことで、Swift だけで作ったライブラリから、Unity へ Texture を送る仕組みを実装できました
* CreateExternalTexture を関連する関数に、UpdateExternalTexture があり、Texture の作成だけでなく、更新も可能です
* Swift から Unity に Texture を送る仕組みは、応用の幅が広いので、活用して行きたいです