Windows 10 April 2018 Update (1803) に関するあれこれ
体験談でしかなく裏取りは相変わらずしていないのであしからず
入れたPC
今のところLaptop以外は問題なさそう
Laptopは
Surface Laptopが起動しなくなった
— ぷにお (@vl_o_lv) 2018年5月8日
修復オプションも全滅
Windows のインストールとして認識された合計数: 0
からのWindows 10 Sから入れ直し→1803でUEFI突入であきらめてサポートとチャット
サポートさんに
を教えてもらい1709に戻して使用中
という久々にトラブルシューティングゲームして楽しかった(楽しくなかった
他のPCはインテルSSDが高いからほかのにしてたおかげて助かったのか?
最近のアップデートは優秀でこんな感じの異常はPreview時に全部見つかっていると思い込んでいたのが敗因ですね(すっかりハードの異常だと思って交換する気満々でした)
IEの動作が変わった?
これはVisual Studio 2017側かもしれません
って感じにしておいた場合、今まではIEがそれぞれ立ち上がっていたんですが、今はIE一つが立ち上がって一つのタブに両方表示しようとします(結果的に遅い方のページが表示される)
なんか昔こんな感じの動作への切り替えがはやったような気がしますがすでに過去の事なので(新しく立ち上げるのか既存に突っ込むのかっていう、あの・・・何とかって技術)
まぁWebサービス (IIS Expressで立ち上がるやつ)は両方立ち上がっているので問題ないですし、そもそも複数のWebプロジェクトを同時にデバッグとか私以外にいないんじゃないかという感じなので気にしない
まとめ
みんな早くアップデートするといいよ (とくにSurface勢)
WPFで速い描画方法が知りたい!
WPFで描画処理書いていてもっと速く描きたい!(ていうか自分の書いた処理遅い!)ということで実験を兼ねてプロジェクト作ってあわよくばすごい人に速い方法教えてもらおう!
プロジェクトはこちら
簡単な説明
class CustomDrawControl : FrameworkElement { protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); switch (DrawType) { case DrawType.NotFreeze: case DrawType.Freeze: case DrawType.Grouping: case DrawType.BackingStore: } _counter++; Task.Run(async () => { await Task.Delay(1); await Dispatcher.BeginInvoke((Action)this.InvalidateVisual); }); } }
こんな感じでOnRender内でいろんな方法で描画してみてFPSを確認してみます
(FPSの算出方法間違ってるとかそもそもOnRender使っちゃダメとかそういう突っ込みあったら教えてください)
描画はフレーム毎に色が変わるペン(パレットは8色)で点(短い線)をたくさん描きます
計測
最大FPS
何も描かないで上の処理のようにOnRender
でTask.Delay(1) -> InvalidateVisual()
ってやるだけで大体64fpsでした
もっと速くなると思っていたのでこの再描画方法がそもそも間違っているんじゃないか疑惑が・・・
FreezableをFreezeした場合としなかった場合
遅い → Freeze()シロ っていう流れはググるとよく出てくるので実際に比較してみました
Freezeしない
最大で 20 fps くらい
Freezableな奴を片っ端からFreezeする
最大 64 fps
Freezeシロ
点の数は3000個で実験したのですがここまで差が出るとは
ちゃんとFreezeしようね
3パターンで計測
本番
ですがネット調べたけど私には3パターンしか見つけられませんでした
ここからは点の数を10000個で実験です
2018/4/12
デバッグビルドで計測していたり、一つは完全に間違った処理だったりなので以下はスクショ入れ替えてます
1点ずつ描画
private void FreezeRender(DrawingContext drawingContext) { foreach (var p in _particles) { drawingContext.DrawLine(penList[p.Pallete], new Point(p.X1, p.Y1), new Point(p.X2, p.Y2)); } }
こんな感じで描きます(余計なの消しているので全部見たければGitHubからどうぞ)
結果は50 fpsちょいでした
パレット毎にまとめて描画
パスはGeometryにまとめて描画した方が速いみたいな書き込みをみたので、パレット毎に一気に描画してみます
private void GroupingRender(DrawingContext drawingContext) { foreach (var g in _particles.GroupBy(p => p.Pallete)) { var geometry = new StreamGeometry(); using (var context = geometry.Open()) { foreach (var p in g) { context.BeginFigure(new Point(p.X1, p.Y1), false, false); context.LineTo(new Point(p.X2, p.Y2), true, false); } } drawingContext.DrawGeometry(null, penList[g.Key], geometry); } }
余計なの消していますが(以下略
60 fps以上出ているのでまだ余裕がありそうです(UIの操作が普通にできるので)
ダブルバッファリング?
DrawingGroup作ってそっちに描いてOnRender時はそれを描け的なことを見かけたので実験
昔懐かしのダブルバッファリングなのだろうか?
readonly DrawingGroup _backingStore = new DrawingGroup(); private void BackingStoreRender(DrawingContext drawingContext) { var context = _backingStore.Open(); foreach (var p in _particles) { context.DrawLine(penList[p.Pallete], new Point(p.X1, p.Y1), new Point(p.X2, p.Y2)); } context.Close(); drawingContext.DrawDrawing(_backingStore); }
10 fpsくらい
backingStore使って~っていうのは多分こういう使い方じゃないんだよね(OnRenderで事前に描画しておいたのを表示とかそういう使い方かな?)
まとめて描画できるならまとめよう
とりあえずまとめて描けるならまとめた方が速そう
2018/4/16 追記
FPSは
CompositionTarget.Rendering
で測るみたいなのでそっちに移しましたDrawType.WriteableBitmapを追加しました
スクショとかないけど一番速かったです
雑な測定結果はGitHubの方にまとめてあるのでそちらを参照ということで・・・
まとめ
WPFの便利なもろもろを使いつつ速い描画を求めるならWriteableBitmapになるんですかね?
これより速そうなのだとDirectXくらいしか思いつかないんですが、それだと自由度が無くなりすぎだと思うんですよね(透過できないのが痛い)
WPFのDrawingContextでアニメーション
DrawingContextの謎メソッド(自分の中で)の実験
WPFでちょっと変わった表現をしたい時とか結局自分で描きますよね(WPFに限らずですが)
例えば
public class DrawTest : FrameworkElement { protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); var center = new Point(this.ActualWidth / 2, this.ActualHeight / 2); drawingContext.DrawEllipse(Brushes.Orange, null, center, 20, 20); } }
こんなクラス作ってMainWindowに張り付けると
こんな感じに中央にオレンジの円を描画します
上で使ったDrawingContext.DrawEllipseが円を描くメソッドなわけですがこいつには
public abstract void DrawEllipse(Brush brush, Pen pen, Point center, double radiusX, double radiusY); public abstract void DrawEllipse(Brush brush, Pen pen, Point center, AnimationClock centerAnimations, double radiusX, AnimationClock radiusXAnimations, double radiusY, AnimationClock radiusYAnimations);
こんな感じにオーバーロードな奴がもう一つあります
AnimationClockを引数に持つんですが、AnimationClockってことはアニメーションしてくれんの?でもアニメーションってOnRenderで一コマずつ描くんじゃないの??どういうこと???といつも疑問に思って試していませんでした
ためしてみよう
簡単に試せるんだから試してみました
protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); var center = new Point(this.ActualWidth / 2, this.ActualHeight / 2); var pointAnimation = new PointAnimation(new Point(this.ActualWidth, this.ActualHeight / 2), new Duration(TimeSpan.FromSeconds(3))); pointAnimation.AutoReverse = true; var doubleAnimation = new DoubleAnimation(20, 5, new Duration(TimeSpan.FromSeconds(5))); doubleAnimation.AutoReverse = true; drawingContext.DrawEllipse(Brushes.Orange, null, center, pointAnimation.CreateClock(), 20, doubleAnimation.CreateClock(), 20, doubleAnimation.CreateClock()); }
OnRenderを書き換え
centerAnimationsにはPointAnimationを、radiusAnimationsにはDoubleAnimationをつっこみます
これで実行すると
うごいているよぉぉぉ~~~
OnRenderはちゃんと(?)1回しか呼ばれてません
予想通りだったんだけど予想通りでびっくりしました
また一つWPFに詳しくなったぞ