hecomi / uDesktopDuplication

Desktop Duplication API implementation for Unity (only for Windows 8/10)
http://tips.hecomi.com/entry/2016/12/04/125641
MIT License
557 stars 97 forks source link

[Question] uDDをアタッチしたGameObjectのメインテクスチャーをRawImageに設定したとき #32

Closed gtk2k closed 5 years ago

gtk2k commented 5 years ago

uDDの映像を別のGameObjectにコピー表示を行おうと思い別のGameObjectにRawImageをアタッチし、

anotherGameObject.GetComponent<RawImage>().Texture = uddGameObject.GetComponent<Renderer>().material.mainTexture;

としたら、とりあえずRawImageにも表示されるようになったのですが、マルチモニター環境においてモニターを切り替え(monitorId切替)たときRawImageの映像が固まってしまいます。ただ、プライマリーモニターに戻すと映像もリアルタイムに反映される(元に戻る)ようになります。 モニターを切り替えてもリアルタイムに表示されるようにするための何か回避策があるようでしたら教えていただけませんか。よろしくお願いします。

hecomi commented 5 years ago

内部的にはパフォーマンスのためにモニタ毎に更新するか否かのダーティフラグを立てています。

そのため、この shouldBeUpdated フラグが立たないと更新が止まってしまいます。

また、テクスチャそのものは Monitor クラスが保持していて、ネイティブプラグインではこれを非同期で更新しており、Texture クラスはそこから Texture2D を持ってくる形になっています。

これらをまとめますと、コピーする際は次のような手順で行うのが良いと思います。

uDesktopDuplication.Monitor uddMonitor;

void Copy()
{
    uddMonitor = uddGameObject.GetComponent<uDesktopDuplication.Texture>().monitor;
    anotherGameObject.GetComponent<RawImage>().Texture = uddMonitor.texture;
}

void Update()
{
    uddMonitor.shouldBeUpdated = true;
}
hecomi commented 5 years ago

と、思いましたが monitorId 切り替えで更新されなくなるのはバグな気がしますので調査いたします。。

hecomi commented 5 years ago

現状、uDesktopDuplication.Texture ですと mainTexture にのみしか書き込まれるパスが存在しないので、汎用性がないのが問題ですね。。

この仕組みは別途検討いたします(RealSense SDK のような方式が参考になりそうと現在考えています(http://tips.hecomi.com/entry/2019/06/04/235243))

取り敢えずになってしまいますが、anotherGameObject に上記のように uDesktopDuplication.Monitor を保持し、Update など更新をかけたいタイミングで shouldBeUpdated フラグを立てるコンポーネントを貼り付けて対処していただけると、現状でも更新されると思います。。

gtk2k commented 5 years ago

ありがとうございます。 shouldBeUpdatedフラグをUpdate()でtrueにすることでリアルタイムに更新されるようになったのですが、uDD側は切り替わるのですがRawImage側が切り替わらずにプライマリーモニターのままでリアルタイムに更新されるようになってしまいました。

hecomi commented 5 years ago

返信送れてしまい申し訳ありません。 別経路でご解決されている旨をうかがいましたが、一応テクスチャの更新の流れについて補足をしておきます。

内部的には、shouldBeUpdate フラグを立てると Manager で以下のように Monitor.Render() が呼ばれます。

https://github.com/hecomi/uDesktopDuplication/blob/20589c23b952dcd26c1dd949022c97f08366dd38/Assets/uDesktopDuplication/Scripts/Manager.cs#L218-L230

この処理は次のように Low-Level Native Plugin Interface の IssuePluginEvent() を呼び出してネイティブで非同期に更新処理が走ります。

https://github.com/hecomi/uDesktopDuplication/blob/20589c23b952dcd26c1dd949022c97f08366dd38/Assets/uDesktopDuplication/Scripts/Monitor.cs#L294-L300

このテクスチャは以下のように作成されています。

https://github.com/hecomi/uDesktopDuplication/blob/20589c23b952dcd26c1dd949022c97f08366dd38/Assets/uDesktopDuplication/Scripts/Manager.cs#L218-L230

https://github.com/hecomi/uDesktopDuplication/blob/20589c23b952dcd26c1dd949022c97f08366dd38/Assets/uDesktopDuplication/Scripts/Monitor.cs#L328-L335

なので、常にこの Monitor.texture を参照し続ける形にし、かつ shouldBeUpdated フラグを更新していればテクスチャは更新されるのが想定の動きとなります。

ただし、画面の大きさを変えたりロストした場合などは Manager.Reinitialize() が実行され、この Monitor クラス毎、texture も破棄されます。

https://github.com/hecomi/uDesktopDuplication/blob/20589c23b952dcd26c1dd949022c97f08366dd38/Assets/uDesktopDuplication/Scripts/Manager.cs#L152-L160

Texture クラスではこれをハンドルするために、モニタの ID を保存しておき、再初期化のイベントハンドラに引っ掛けてモニタの再取得処理を行っています:

https://github.com/hecomi/uDesktopDuplication/blob/20589c23b952dcd26c1dd949022c97f08366dd38/Assets/uDesktopDuplication/Scripts/Texture.cs#L256

テクスチャの更新の全容は上記のようになります。

gtk2k commented 5 years ago

返事が遅れてしまい申し訳ございません。 詳細な解説ありがとうございます。 把握し、期待する動作をさせることができました。