10

私は、C#/WPF で相互接続された非常に大規模なグラフを表示および操作するためのツールを作成したチームの一員です。グラフの表示と操作は、DrawingVisuals のセットを取り込んでキャンバスに表示するカスタム コントロールを介して行われます。グラフ内のノードには、エディターで作成されたカスタム形状がある場合があります。現在のコントロールは非常にうまく機能し、私たちのプログラムとかなり結合されていますが、はるかに大きなグラフ (20,000 以上のノードと多数の接続) を考慮すると、パフォーマンスに関する正当な懸念があります。

少し調査を行った後、次の 2 つのアプローチがあるようです。

  • グラフィックスが WriteableBitmap または InteropBitmap に描画される GDI+ ルート。
  • SlimDX または DirectX バリアント (D3DImage でホスト)


これら 2 つの非常に異なるアプローチを考えると、どのルートを検討するのが最適でしょうか。

  • グラフ全体を表示している場合でも、グラフの操作は高速でなければなりません。
  • ビジュアルの更新は高速である必要があります (色またはサイズの変更)
  • ヒット テストは高速である必要があります (点と四角形)。
  • 開発はタイムリーに完了する必要があります。

どの方法を使用しますか、またその理由は何ですか?

編集:
同様の質問があったようですが、回答がありません。

4

3 に答える 3

10

地図作成アプリケーションに GDI を使用しています。GDI+ は、たとえば DirectX よりも低速ですが、速度を上げるために使用できるものやトリックがたくさんあることがわかりました。データ自体を描画する前にデータを準備するために多くの CPU が使用されるため、GDI だけがボトルネックになることはありません。

注意すべき点 (これらは一般的なもので、他のグラフィック エンジンにも適用できます):

  1. まず第一に:測定します。プロファイラーを使用して、コードの実際のボトルネックを確認します。
  2. GDI プリミティブを再利用します。これは非常に重要です。同じまたは類似した 100,000 個のグラフィックス オブジェクトを描画する必要がある場合は、同じ などを使用しますPenBrushこれらのプリミティブの作成にはコストがかかります。
  3. レンダリング データをキャッシュします。たとえば、必要がなければ gfx 要素の位置を再計算しないでください。
  4. パン/ズームするときは、GDI+ の品質を下げてシーンを描画します(コストのかかる GDI 操作は使用しません)。Graphics品質を下げるためのオブジェクト設定がいくつかあります。ユーザーがパンを停止した後、高品質でシーンを描画します。
  5. パフォーマンスを向上させる多くの小さなこと。私はこのアプリを 2 ~ 3 年間開発してきました (または、もう 4 年間ですか?) 今でも改善する方法を見つけています :)。これがプロファイリングが重要な理由です。コードが変更され、これがパフォーマンスに影響を与える可能性があるため、新しいコードをプロファイリングする必要があります。

もう 1 つ: SlimDX は使用していませんが、Direct2D を試してみました (私は を参照していMicrosoft.WindowsAPICodePack.DirectX.Direct2D1ます)。私の場合、パフォーマンスは GDI+ よりかなり高速でしたが、ビットマップのレンダリングに問題があり、適切な解決策を見つける時間がありませんでした。

于 2012-04-25T07:02:17.637 に答える
3

最近、いくつかの描画コードをDirectXに移植し、その結果に非常に満足しています。私たちは主にビットバッシングを使用してビットマップをレンダリングしていましたが、数分で測定できるレンダリング時間が約1〜2秒に短縮されました。

SlimDXを使用してC++でのビットバッシングからC#でのDirect3Dに移行したため、これを使用状況と直接比較することはできませんが、桁違いではない場合でも、パフォーマンス上の利点が見られると思います。再見て。

SlimDXでDirect2Dを使用する方法を検討することをお勧めします。Direct2Dは何らかの理由でDirectX11と互換性がないため、DirectX10.1を使用する必要があります。WPFで描画APIを使用したことがある場合は、Direct2DのAPIはWPF描画APIに基づいているため、私が知る限り、Direct2Dについては既にご存知でしょう。Direct2Dの主な問題は、ドキュメントが不足していることと、Vista以降でのみ機能するという事実です。

DirectX 10 / WPF相互運用機能を試したことはありませんが、可能だと思います(http://stackoverflow.com/questions/1252780/d3dimage-using-dx10)

編集:単純なポリゴンを描画するコードから比較してみようと思いました。最初のWPFバージョン:

        StreamGeometry geometry = new StreamGeometry();

        using (StreamGeometryContext ctx = geometry.Open())
        {
            foreach (Polygon polygon in mask.Polygons)
            {
                bool first = true;

                foreach (Vector2 p in polygon.Points)
                {
                    Point point = new Point(p.X, p.Y);

                    if (first)
                    {
                        ctx.BeginFigure(point, true, true);
                        first = false;
                    }
                    else
                    {
                        ctx.LineTo(point, false, false);
                    }
                }
            }
        }

現在、Direct2Dバージョン:

        Texture2D maskTexture = helper.CreateRenderTexture(width, height);

        RenderTargetProperties props = new RenderTargetProperties
        {
            HorizontalDpi = 96,
            PixelFormat = new PixelFormat(SlimDX.DXGI.Format.Unknown, AlphaMode.Premultiplied),
            Type = RenderTargetType.Default,
            Usage = RenderTargetUsage.None,
            VerticalDpi = 96,
        };

        using (SlimDX.Direct2D.Factory factory = new SlimDX.Direct2D.Factory())
        using (SlimDX.DXGI.Surface surface = maskTexture.AsSurface())
        using (RenderTarget target = RenderTarget.FromDXGI(factory, surface, props))
        using (SlimDX.Direct2D.Brush brush = new SolidColorBrush(target, new SlimDX.Color4(System.Drawing.Color.Red)))
        using (PathGeometry geometry = new PathGeometry(factory))
        using (SimplifiedGeometrySink sink = geometry.Open())
        {
            foreach (Polygon polygon in mask.Polygons)
            {
                PointF[] points = new PointF[polygon.Points.Count()];
                int i = 0;

                foreach (Vector2 p in polygon.Points)
                {
                    points[i++] = new PointF(p.X * width, p.Y * height);
                }

                sink.BeginFigure(points[0], FigureBegin.Filled);
                sink.AddLines(points);
                sink.EndFigure(FigureEnd.Closed);
            }

            sink.Close();
            target.BeginDraw();
            target.FillGeometry(geometry, brush);
            target.EndDraw();
        }

ご覧のとおり、Direct2Dバージョンは少し手間がかかります(そして私が書いたいくつかのヘルパー関数に依存しています)が、実際にはかなり似ています。

于 2012-04-25T19:16:50.117 に答える
1

それぞれのアプローチの長所と短所をリストアップしてみましょう。これにより、どちらを使用するかについてのアイデアが得られるでしょう。

GDIの長所

  • ベクトル形状を簡単に描画
  • 余分なライブラリを含める必要はありません

GDI の短所

  • DXより遅い
  • 「派手な」描画(グラデーションなど)を制限する必要があるか、処理が遅くなる可能性があります
  • ダイアグラムをインタラクティブにする必要がある場合 - 最適なオプションではない可能性があります

スリムDXの長所

  • GDIよりも高速でありながら、派手な描画を行うことができます
  • 描画がインタラクティブな場合 - このアプローチははるかに優れています
  • プリミティブを描画するので、各ズーム レベルで品質を制御できます。

SlimDXの短所

  • 単純な図形を描くのは簡単ではありません - 独自の抽象化を作成するか、図形を描くのに役立つライブラリを使用する必要があります
  • 特に以前に使用したことがない場合、GDI を使用するのは簡単ではありません

そして、おそらくここに入れるのを忘れていましたが、おそらくこれらは手始めに十分でしょうか?

-A.

于 2012-04-23T20:32:16.393 に答える