2011年3月20日日曜日

SlimDXでDirect2DのWPF連携

Direct2DとWPFでググって出てくるサイトはここですね。


ここを読むとWPFでDirect2Dを使う方法がわかるようです。

ふむふむ、なるほど。そういうことだったのか!



……まったく分からん

最初はリンク先の図すら分からなかったのですが、調べるうちにだんだんと分かるようになってきました。そのことと、簡単に連携させる方法をこの記事で書こうと思います。
簡単に連携させる方法はWindowsFormsHostクラスを使う方法で、調べる前から知っていたのですが、あまりスマートじゃない気がしていました。それで色々調べていたのですが、他の方法が難しすぎるので、もうこの方法でいきたいと思います。実際に検証してみればわかりますが、速度も遅いということはありません。



まずはWPFの復習からやりましょうか。
復習と言ってもこのブログでは書いたことが無いし、書いていたホームページはもう閉鎖してしまいましたから、実際には初になるでしょう。

WPF(ウィンドウズ・プレゼンテーション・ファウンデーション)の厳密な説明はウィキペディアを参考にしてください。WPFXAMLというものと一緒に出てきたため、またマイクロソフトがよくわからないことを始めた(やれやれ)と思われがちです。実はXAMLなんて全く必要ありません。これまでのC#言語でそのまま扱うことができます。
WPFはこれまでのフォーム(ウィンドウ)アプリケーションの後継と 考えることができます。フォームと同じようにボタンやテキストボックスといった部品を配置できます。従来のフォームアプリケーションの基本的な部品に加え て、円や四角といった図形もフォーム部品と同じように扱うことができるようになっています。さらには3D図形や動画までペタペタと配置することができます。そんなこのとができるのは、裏でDirectXを使っているからです。

……と、こんなことを昔書いていました。ここに書いているようにWPFはDirectXを使っています。Direct3D9です。

そして、Direct2Dが使っているのはDirect3D10.1です。このDirect3D9とDirect3D10.1を連携させるのが難しい。難しすぎるのです。

Direct3Dのバージョンは9から、9Ex、10、10.1、11となっており、9から10.1は結構離れています。ちなみに9Exと10の連携は簡単らしいです。そして、10.1以降はDXGI1.1というインターフェースを備えているので10.1と11の連携は簡単です。
しかし、9と10.1の連携は難しいのです。

私はWPFがいつかDirect3D9から別のバージョンに移ってくれると思っているので、わざわざ9と10.1の連携をやる気はありません。
いいですか? やる気が無いだけで、決してやろうとしてできなかったわけじゃないですからね!


さて、では、簡単に連携させる方法を書きましょう。動かすのは[SlimDXでDirect2D]と同じで背景を真っ黒にするだけのものです。
ちなみにこの方法はDirect2D以外でも利用できます。

最初にWPFのウィンドウを表示させるプログラムを載せます。その後でそのプログラムにDirect2Dの部分を追加します。


using System;
using System.Windows;

namespace WPFSample
{
    public class OriginalWindow : Window
    {
        public OriginalWindow()
        {
            this.Title = "WPF Sample";
        }
    }

    class Progarm
    {
        [STAThread]
        static void Main()
        {
            var app = new Application();
            app.Run(new OriginalWindow());
        }
    }
}

WPFのプログラムは基本的にWindowクラスを継承したクラスを自作し、それを実行するというものです。この場合自作したのはOriginalWindowクラスです。
Main関数の中で作ったウィンドウを実行しています。

では、Direct2Dを導入してみましょう。

using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Forms.Integration;
using SlimDX;
using SlimDX.Direct2D;

namespace Direct2DSample
{
    public class SlimDXControl : System.Windows.Forms.Control
    {
        protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs pevent)
        {
        }
    }

    public class OriginalWindow : Window
    {
        public OriginalWindow()
        {
            this.Title = "SlimDX - Direct2D Sample";
            this.SizeToContent = SizeToContent.WidthAndHeight;

            var host = new WindowsFormsHost(){
                Width  = 400,
                Height = 300,
            };
            this.Content = host;

            var control = new SlimDXControl(){
                Width  = 400,
                Height = 300,
            };
            host.Child = control;

            /* ここからDirect2D */
            var factory = new Factory();

            var target = new WindowRenderTarget(factory,new WindowRenderTargetProperties(){
                Handle    = control.Handle,
                PixelSize = control.ClientSize
            });

            CompositionTarget.Rendering += (sender,e)=>{
                target.BeginDraw();
                target.Clear(new Color4(0,0,0,0));

                target.EndDraw();
            };
        }
    }

    class Progarm
    {
        [STAThread]
        static void Main()
        {
            var app = new Application();
            app.Run(new OriginalWindow());

            foreach (var item in ObjectTable.Objects)
              item.Dispose();
        }
    }
}

それじゃあ説明ですね。

    public class SlimDXControl : System.Windows.Forms.Control
    {
        protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs pevent)
        {
        }
    }
ここでコントロールを継承したクラスを作っています。ここで言うコントロールとは、Windowsフォームアプリケーションで言うところのコントロールで、ウィンドウに配置する部品の基礎になるものです。そのままだとウィンドウのサイズを変えたときなどに背景が描画されてしまうので、OnPaintBackgroundメソッドをオーバーライドしています。


            this.SizeToContent = SizeToContent.WidthAndHeight;
この文を書いておくことで、コンテンツのサイズに合わせてウィンドウのサイズを調整してくれます。コンテンツのサイズを400×300にしたらウィンドウもそうなります(もちろんタイトルバーは除く)。

            var host = new WindowsFormsHost(){
                Width  = 400,
                Height = 300,
            };
            this.Content = host;
これがかの有名なWindowsFormsHostです。これを使えばWPFで、これまでのフォームアプリの遺産を活用することができます。注意が必要なのは、このクラスを使用するときは、"WindowsFormsIntegration.dll"への参照を加えないといけません。その中にWindowsFormsHostが入っているので。
サイズを400×300とし、ウィンドウのコンテンツにしています。

            var control = new SlimDXControl(){
                Width  = 400,
                Height = 300,
            };
            host.Child = control;
ホストするためのコントロールを作っています。サイズは400×300です。このサイズをWindowsFormsHostと合わせておかないと楽しいことになります。

            CompositionTarget.Rendering += (sender,e)=>{
CompositionTarget.Renderingイベントは描画の度に呼ばれるイベントです。そのため、ここに描画処理を書きます。
ちなみにFPSは……よく分かりません。30くらいだと思います。
ディスパッチャタイマーを使う方法もありまして、それだとFPSが60くらいになります。しかし、私にはWPFが一回描画をする間に、2回Direct2Dの描画を行っているような気がしてなりません。もしそうであれば非効率です。
数日かけて色々と検証したのですが、分かりませんでした。だいたいの予測などはあるのですが、正しいかどうか分からないので書きません。


何と言うか、ほら、

WPFでDirect2Dを使うことに意味があるのか

という結論です。
いっそのことWPFで全部描いちゃうほうがいいと思います。たぶんWPFのほうが簡単です。

ぐだぐだでしたがこれにて!

15 件のコメント:

  1. どうもです。
    毎度です。

    >私と周りの人間関係のようです。
     オラと同じ・・・いやはや(^^;

     自分のホームページをお伝えしてませんでした。
     ここです。
     http://nora66.com/nora3/
     全然無関係な畑遊びのページですけど、見に来て下さい。
     後日、落ち着いてからゆっくり読ませて頂きます。

    返信削除
  2. >Mooさん

    どうもです!
    いやいや、今回は特別出来が悪いので読まなくていいですよ~。

    おお、HPを持っていらっしゃったんですか。確かに畑遊びですね。うん、畑遊びですね。
    将来落ち着いてきたら自分もやってみたいものです←コツコツができないお前には絶対無理だ

    返信削除
  3.  どもども。
     毎度です。
     やっと少し落ち着いてきました。
     まだガソリンと灯油が手に入らないんですが、その他は大体普段の生活に戻りました。

    >Matrix構造体がしょぼい
     ええ~!
     これ、困っちゃいます。
     グラフを描いた後で平行移動やら拡大縮小するのに不便だとアカンっす。

     WPFですか?
     全然知らないんです(^^;
     でもこっちの方が便利なら、こっちを使っていきたいと思います。
     質問できる程度に調べてから、またお邪魔に来ます。

    >書いていたホームページはもう閉鎖してしまいました
     残念。
     あちこち見に行ってみます。

    返信削除
  4. >Mooさん

    WPFはたぶんDirect2Dよりも難しいですよ。Visual Studio 2010が無いと辛いかもです。実際に使ってみてどちらを使うか判断されると良いと思います!

    返信削除
  5. 了解しました。
    Visual Studio 2010はなんとかなると思うので、ちょっと手出ししてみます・・・ちょっとだけ。
    撃沈したらまた他のに逃げます(^^;

    こちらを読み始めたところでした。
    http://www.atmarkit.co.jp/fdotnet/chushin/introwpf_index/index.html

    返信削除
  6. >Mooさん

    おお~、頑張ってください。
    @ITは良い記事ばっかりですよね。@ITに並ぶくらい良い記事を書くのが夢であります。

    返信削除
  7.  自分は衣緒さんの記事が好きですよ。
     例えば・・・
    >WPFはこれまでのフォーム(ウィンドウ)アプリケーションの後継と 考えることができます。
     こう言う風に「普段着の言葉」で聞いてるからこそ、@ITを読んでも頭に入ります。

     また”Direct2Dの問題点」も、自分で気が付くまでにはかなり調べないとならなかったはず・・気が付いてから「むっきっき~」となるのはキツイです。

     また・・・
    >いっそのことWPFで全部描いちゃうほうがいいと思います。
     こう言われて始めて「WPFも調べなくちゃ」と気が付かされました。

     とても感謝しています。

    返信削除
  8.  どうも「完全初心者」にはこちらが優しいようです。
     http://codezine.jp/article/detail/910

     VisualStudio2010の無料版で試してますが、なんだか割といけそうです・・・今のところは。

    返信削除
  9. >Mooさん

    そちらの記事は本当にしっかり書かれていると思います。違和感無く読めました。上手い人は上手いですね。会員じゃないので途中までしか読めませんでしたが…。

    XAML自体の仕様はなかなか大きいので、すべてを把握するのはやや大変かもしれません……

    こう書かれているようにWPFはそれほどでも無いのに、XAMLが非常に難しいです。それもそのはず。XAMLはVS2010のデザイナ等を使って編集することが前提となっているからです。だからVS2010を勧めました。

    そうですね。いきなり「とりあえず使ってみて」なんて言われてもポカーンですね。入門サイトを読む前にそもそもそれが何なのかということ知らないと、読んでも頭に入りませんよね。私はそこに注意して書いているつもりです。
    ちょっとした嫌味になりますが、『猫でもわかる』シリーズはすごく良い本なのですが、最初読んだときの感想は「猫って人間よりも頭がいいんだね!」でした。
    その後、色々な知識を得てから読んでみると、本当に分かりやすく書いてあって、知っている人と知らない人のギャップは激しいものだと感じました。

    返信削除
  10. >読んでも頭に入りませんよね
     そうそう。
     そうなんです。
     「何をする為の物なんだ?」
     「大体からして俺の役に立つの?」が最初だし。
     自分の役に立つかどうかが最初に分からないと、無駄に読み進めないとならないわけで。

    >「猫って人間よりも頭がいいんだね!」
     あっはっは(^^;

    >だからVS2010を勧めました。
     ありがとうございます。
     VS2010でやると、デザイナでボタンを配置したりイベントをセットする度にXamlのコードが勝手に書き換わるのが目に見えます。
     「あ、そう言う関係なのね」
     と、かなり分かりやすい感じです・・・今のところは。
     ただ、これで高速にグラフが描けて、平行移動や拡大縮小が楽になるかどうかが未だ分かりません。
     自分の目的はWPFを覚える事じゃなくて「高速にグラフ描画する」なんで。
     もう少し触ってみます。

    返信削除
  11. >Mooさん

    >自分の役に立つかどうかが最初に分からないと、無駄に読み進めないとならないわけで。

    ですね! 私のような面倒くさがりは一文字頭に入れるのも苦痛ですから。

    グラフは図形じゃなくって線ですからね。なかなか難しそうです

    返信削除
  12.  WPFは早くも諦めることにしました。
     理由はここに書いてあるまんま。
     http://www.cs-dotnet.com/2010/09/winform-to-wpf.html

     特に遅いのは致命的です。
     早く動かしたくてDirectXを考えてるのに、遅いんじゃ意味無し。
     それとXPで動かないってのもアカンすね。

     XNAを再度調査してみます。
     XNAでPanelにラインを描けると良いんですけど、可能かどうか調べてみようと思います。

     SlimDXで2Dがバリバリ使えればそれが一番なんですけどね~

    返信削除
  13. >Mooさん

    WPFは確かXPで動くはずです。Direct2DはXPで動きません。←これについて記事にしてませんでした…。

    XPで動かしたいならばDirect3D 9になると思います。2Dなのに3Dを勉強しないといけないのでかなり面倒ですが。
    Visual Studio 2008ならばXNAがいいかもですね。他にもManaged DirectXもありますし、SlimDXのDirect3D9もあります。結局はどれもDirect3D 9を使うので同じですよ。

    グラフであれば、もしかしたらライブラリに頼らずにビットマップのピクセルを直接書き換えた方が速いという可能性もあります。
    また、探せばグラフを描くことに最適化されたコントロールがあるかもしれません。
    作る物次第になると思いますが、描画の遅いImageコントロールなんかでも、アルゴリズムを変えたら十分な速度が出せるかもしれません。

    選択肢は様々ですね^^
    選択肢が色々あると私は何日も悩んでしまうのですが、いつも最終的な結論は「全部やろう」です(笑)

    返信削除
  14. >結局はどれもDirect3D 9を使うので同じですよ。
     あ、そう言う事なんですか。
     了解しました。

    >アルゴリズムを変えたら十分な速度が出せるかもしれません。
     そうなんですよね。
     でも折角だからハードウエアアクセレレータをいじってみたい(笑)
     それにやっぱり「より早く動く」ってのが魅力的です。ごちゃごちゃとプログラムが混んできても、描画が早ければ余裕を持てるんで。

    >いつも最終的な結論は「全部やろう」です
     はい~
     同じくです。
     一応全部やらないと、どうも納得できないですよね。
     で、XNAを少し調べたら、その次は「C#でOpenGL」とかを探検してみようかと思ってました(^^;

    返信削除
  15. >Mooさん

    OpenGLですか~。OpenGLなら他のOSからも使えるので便利ですね。

    返信削除