質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

4回答

21450閲覧

WPF CanvasコントロールにPathをたくさん描画すると非常に重い

ElecDove

総合スコア254

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

0グッド

2クリップ

投稿2017/07/08 12:21

編集2017/07/08 15:22

お世話になっております.

WPFのCanvasにPathをAddしてCAD風に線分や円弧を表示しています.

量しだいではありますが,添付した画像くらいの量になるとだいぶ重く,
描画の更新に0.5sくらいかかってしまい,画面内での移動や拡大縮小に難があります
また,もっと多くの図形も表示させる必要があるのでもう少しパフォーマンスがあがらないかなと思い質問させていただきました.

Pathオブジェクトに関しては,一度Addしたらそれのdataプロパティを変更するようにし,毎回DeleteしてAddのようなことはさせていません.(Pathオブジェクトを使いまわしていると表現すればよいのでしょうか)
(画面の描画を繰り返してもGCがほとんど発生しませんし,メモリ消費量が右肩上がり,なんてこともありません)

現状DataTemplateのようなものはPathオブジェクトに対して使用しておらず(勉強中のためまだ自分でかけるほどの力がないのです・・・)

線分の描画を更新しているメソッドを参考のため載せておきます.
あらかじめ,Canvas.ChildにPathがAddされた状態で以下のメソッドを実行します.

C#

1private void drawLine(Point Start_, Point End_, Point Offset_, Color Color_, Path p_) { 2 p_.Stroke = new SolidColorBrush(Color_); 3 p_.StrokeThickness = 1; 4 ((LineGeometry)p_.Data).StartPoint = new Point((Start_.X - Offset_.X) * Magnification, (Start_.Y - Offset_.Y) * Magnification); 5 ((LineGeometry)p_.Data).EndPoint = new Point((End_.X - Offset_.X) * Magnification, (End_.Y - Offset_.Y) * Magnification); 6}

また,二つのCanvasコントロールを重ねて,後のCanvasには図形を表示し,手前のCanvasにはグリッド線や範囲選択の長方形を表示したりと,別々のCanvasに分けたにもかかわらず後ろのCanvasが重いときは手前も重たくなりました.(後は一切描画更新をしない状態で手前だけ更新したとしても)

PCスペックは①i3-2370M+8GB-DDR3,②i7-4770HQ+8GB-DDR3の2台で確認しましたが動作速度に差は感じられなかったのでPCスペックに問題はないと思っております.

WPFの限界でしょうか・・・
何かヒントをいただければと思います
よろしくお願いいたします.

アプリ実行例

追記
余白でドラッグをグリグリやるときと,大量のPathの上空でグリグリやるときではCPUの使用量が2~3倍程度違うようでした.
CPU使用率で中央付近で山が高くなっていますが,左側はCanvas右上でのドラッグ,山の右側はCanvas左下でドラッグです.
赤い枠が写っていますが,これがマウスについてくる範囲選択の枠です
イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

まず、前提としてWPFのPathは描画が非常に遅いです。
これはベクターデータとしてリッチなコンテンツを描画することができるWPFの強みでもあり、パフォーマンスの低下という弱みでもあります。
パフォーマンス向上の上でPathなどのXXXGeometry コントロールは使用しないほうがいいと思います。

私も以前パフォーマンスに悩まされていたときは、以下のような方法で対処しました。

  1. Width/Height プロパティを固定値にする
  2. IsHitTestVisible プロパティをFalse にする
  3. XXXGeometry 系コントロールは使用しない
  4. UserControl は使用しない。

特に3,4 の方法は効果が高かったように感じられました。

その上で、複雑な図形などを描画する場合は、カスタムコントロールで"OnRender"メソッドをオーバーライドして図形を書いていました。
例えば線分を描画する場合は以下のようになります。

cs

1 public class CustomControl1 : Control 2 { 3 static CustomControl1() 4 { 5 DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1))); 6 } 7 8 protected override void OnRender(DrawingContext drawingContext) 9 { 10 base.OnRender(drawingContext); 11 12 // ここに描画処理を実装する。 13 var pen = new Pen(BorderBrush, 1); 14 drawingContext.DrawLine(pen, _previousPoint, _currentPoint); 15 } 16 }

図形の拡大、縮小については以下のページが参考になるのではないでしょうか
http://grabacr.net/archives/1723

何か解決のヒントになれば幸いです。

投稿2017/07/09 08:36

IYEMON018

総合スコア202

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ElecDove

2017/07/09 09:13

回答ありがとうございます. OnRenderというメソッドがあるのですね 調べて使ってみたいと思います
guest

0

ベストアンサー

以前質問者さんが WPFで2D(将来的には3Dも)を扱いたい で質問されたとき、Canvasはパフォーマンスが良くないので事前に検証されることをお勧めしましたが、試されましたか?

Canvasにエレメントは配置していく通常のアプローチでは厳しい気がします。
IsHitTestVisible の無効化や、Freeze可能なものすべてFreezeしても、描画対象の絶対数が大きいと厳しいかと思います。
もし私だったら、

・WriteEbleBitmap等を使って画像として表示する。
WriteableBitmapExを使うとラクです。

・DrawingVisual で描画する

といったことを試すと思います。
どちらも画像として表示するものですので、例えばお絵かきした部品のマウスオーバーでTooltipを表示する、といったことはそのままではできませんが。。

投稿2017/07/10 00:23

ebiryo

総合スコア797

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ElecDove

2017/07/10 07:14

回答ありがとうございます. > 事前に検証 の結果が現在のこの状態だと思っていたのですが認識が間違っていましたでしょうか 単にPathを追加していくだけだと現状のようにだいぶ重いです.(縮小時には図形を抽象化して表示する,くらいの工夫をすれば実用的な速度ではありますが) > もし私だったら、 > ・WriteEbleBitmap等を使って画像として表示する。 > ⇒WriteableBitmapExを使うとラクです。 > ・DrawingVisual で描画する どうせマウスオーバーでTooltipを表示みたいな作業はそのつど画面(ビットマップ)を書き直すくらいの覚悟ではいたのでかまわないといえばかまわないですが・・・. いずれにせよちょっとメソッドを追加するとか設定を変えるとか,そんな感じに簡単にパフォーマンスに劇的な変化を与える方法はないようですね Bitmapで描画もぜひ試してみたいのですが,それ以前に実装しなければならない機能が山のように残っているため(描画とは関係ない内部処理),まずはそちらを実装してからになってしまいそうです なかなかすぐに結果をお返事できるような状況でなく申し訳ありません
ebiryo

2017/07/10 08:00

検証は、取りあえずはCanvasが一つだけあるWindowのサンプルプロジェクトを作成し、そこの大量のPathを配置してどのくらいだとNGになるのか調べる、くらいでも目安になると思います(提示されたプログラムがそうだ、ということであれば悪しからず。。) Canvasのパフォーマンスは皆さん困っている箇所です(WPF Canvas Performanceで検索すると色々でてくるかと思います) ⇒つまり、プロパティをちょっと変えれば改善するようなことではないです。 あまり参考にならない回答で申し訳ないですが、良いプログラムができるといいですね。頑張ってください。
ElecDove

2017/07/10 08:08 編集

>検証は、取りあえずはCanvasが一つだけあるWindowのサンプルプロジェクトを作成し、そこの大量のPathを配置してどのくらいだとNGになるのか調べる、くらいでも目安になると思います( このような方法でよかったのですね(汗 いろいろ実装してから「やっぱり遅いなぁ」となっていたので無駄な努力とまではいきませんが中々無駄でした(笑 確かにWPFのレンダリング回りのパフォーマンスの話は検索するとたくさんヒットして驚きます Gridと比べてCanvasは早いのだろうと(座標が絶対座標になるので)勝手に思い込んでおりましたが... Ebiryo様にはほかにも多数の質問で回答いただき大変助けられ感謝しております ありがとうございました
guest

0

http://proprogrammer.hatenadiary.jp/entry/2014/12/25/014448
この記事に書いてあることは試してみました?

投稿2017/07/08 17:26

kiichi54321

総合スコア1984

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ElecDove

2017/07/09 11:50 編集

回答ありがとうございます 試しているところですが,エラーに悩まされています・・・(別の質問として投稿させていただきました ↓ 解決しました が,どうもやりたいこととは微妙に違うみたいなので保留中です
guest

0

あてずっぽうのコメントであることをお断りせねばなりませんが、例えばPhotoShopのような画像ソフトで多数のレイヤーを重ねた状態で一番上にある画像を変更したり移動したりということをよく行うと思います。その際に下位のレイヤーが多少多くても(といっても数段ぐらいしかやったことないですが)一番上のレイヤーの編集に支障をきたすようなもたつきはありません。

PhotoShopがどのように最適化しているか知る由もありませんが、一つ考えられることは、動かない殆どの部分の画像のレンダリングは一旦オフスクリーンイメージバッファに対して行ってしまい、編集状態では常に1枚の背景画像と一番手前の編集レイヤーの2レイヤー」とすると再描画の際のレンダリングのオーバーヘッドは軽減されるのではないかと思いました。

本件でいえばCanvas上へパスを用いて多数のオブジェクトを配置するとオブジェクトを変更する度に(影響のある部分のクリッピングを行ったとしても)かなり多数のパスの再レンダリングが必要でありそのために遅くなっているのではないかと思えました。

実験として、背景に画像を置いた上でご質問にある画像の一部分の部品程度のパスのみ編集する実験をしてみてはいかがでしょうか?それで軽くなるようなら、「編集対象以外の図形をオフスクリーンバッファーへレンダリングしておく」といった工夫で高速化できる可能性があると思います。

投稿2017/07/08 14:40

KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ElecDove

2017/07/08 15:12

回答ありがとうございます まず,手前にあるCanvasの背景を不透過にし,大量のPathがまったく見えない状況にしてみました. 結果は失敗で,軽くなることはありませんでした. 次に,二つのCanvasの間にImageコントロールを設置しWindowsのサンプルピクチャ(jpg)を表示,手前のCanvasの背景は不透過という状況にしてみました こちらも結果は失敗でした. そして気がついたことがあります. Canvas上をドラッグすると,ドラッグ範囲を示す長方形を描画しているのですが,Pathが大量にある部分の上空では急激に重たくなり,余白部分では非常にスムーズに動きます また,オフスクリーンバッファへのレンダリングはゲームプログラム(DxLib等)では何度か聞いたことがあるのですが,WPFでも利用できるものなのでしょうか
KSwordOfHaste

2017/07/08 15:30 編集

Canvasの背景を不透明にしても重いということは、そのパスが最前面に見えるかどうかとは無関係にレンダリング自体は行われていると推測します。動かない部分の図形はCanvasやパスそのものには描画せずに背景画像のみへ描画すると効果がみえてくる気がします。 > オフスクリーンバッファ WPFそのものを殆どさわったことがないので直接の知識はないですが、調べる価値はあると思います。WPFで画像が扱えなかったり、重なったレイヤーが実現不可能といった制約はないように思えます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問