WPF アプリで OpenGL の描画結果をはめ込めたらいいなと思ってトライしてみることにしました。
WPF は DirectX9 で動いているため、またアプリ全体で1つのウィンドウハンドルとなっているため、従来の C# フォームアプリのように特定の部分だけ描画を任せるというようなことが難しくなっています。
一般的な方法
WPF アプリでは HWNDHost というクラスを用いることで、従来型の方法でコントロールを使用することができます。
これを用いて OpenGL の描画コントロールを作り、そこに描くといったことが可能になります。
多くの解説ではこちらの方式で語られていると思われます。
DirectX9の場合
DirectX9 の描画結果の場合には D3DImage というクラスが WPF では提供されています。
これを用いることで描画結果を貼り付けることが可能になっています。
可能にはなっていますが、該当の HWND を渡して描画系の初期化、だけでは済まない点はちょっと面倒な感じであります。
D3D11Image
比較的最近ではありますが D3DImage を継承した新しいクラスの実装があります。
D3D11Image というクラスで、 Microsoft のサンプルで WPFDXInterop というものに含まれています。
これは DirectX11 (D3D11) の描画結果を貼り付けるためのシロモノです。
ただこのサンプル、現時点においてはサンプルの実行後 DirectX のリソースが一部リークするので、
ちょっとだけ修正が必要でした。Debug Layer を使わないと報告されないので、追試験する方はご注意を。
とはいっても問題があるのはサンプル側なので D3D11Image コントロール側ではありませんでした。
OpenGLの描画について
この WPFDXInterop のコードは GitHub あるので、サンプルコードを書き換えて、OpenGL の描画結果を貼り付けるように変更してみました。
ポイントは DirectX11 ではウィンドウハンドルなしで初期化できるのに対して、OpenGL ではウィンドウハンドルが必要になります。
OpenGL らしいものが思い浮かばなかったので定番の teapot を描画してみました。
WPF の各コントロールでパラメータを設定して、DLL 側に通知しています。
C++ で実装した DLL の中で OpenGL の描画コードが実行されています。
まとめ
一応 OpenGL の描画結果を HWNDHost,WinFormsHost を使う以外の方法で貼り付けてみました。
D3DImage は共有リソースを使った貼り付け方法なので、素の状態と比べるとどうしてもパフォーマンスは劣ります。
HWNDHost を使っての方式の方が色々と情報も多く、変なところにはまらないで済む可能性も多そうではあります。
なぜ今回 HWNDHostやWinFormsHost を使わずにチャレンジしたかというと、 Airspace の問題を考えずに済むかなと思ったからです。
元のサンプルコードが DLL で描かれていたためにそれに習いましたが、C++/CLI (CX) あたりで分離せずに書いてしまう方法もありかなと思います。
付録・参考情報など
- Mitigating Airspace Issues In WPF Applications
- Direct3D9 および WPF の相互運用性のパフォーマンスに関する考慮事項
- WPF と Direct3D9 の相互運用性
- 技術領域の概要
空域 (Airspace) の問題が、どのように日本語訳されているかで検索を引っかけるのが難しい気がしました。
どのようにタイトル付けされているか確認してみたら、”技術領域の概要”と (現時点では)なっていました。昔は空域だった気がするんですが気のせいかも。
コメント