</Compile>
<Compile Include="SystemStringExtensions.cs" />
<Compile Include="Utilities.cs" />
+ <Compile Include="カウンタ\FPS.cs" />
<Compile Include="カウンタ\QPCTimer.cs" />
+ <Compile Include="カウンタ\単純増加後不変カウンタ.cs" />
+ <Compile Include="カウンタ\単純増加後反復カウンタ.cs" />
+ <Compile Include="カウンタ\定間隔進行.cs" />
<Compile Include="フォルダ.cs" />
<Compile Include="メディア\テクスチャ.cs" />
<Compile Include="メディア\デバイスリソース.cs" />
--- /dev/null
+using System;
+using System.Collections.Generic;
+
+namespace FDK.カウンタ
+{
+ /// <summary>
+ /// FPS(1秒間の進行処理回数)と VPS(1秒間の描画処理回数)を計測する。
+ /// </summary>
+ /// <remarks>
+ /// 計測するだけで、表示はしない。
+ /// FPSをカウントする() を呼び出さないと、VPS も更新されないので注意。
+ /// </remarks>
+ public class FPS
+ {
+ public int 現在のFPS
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ return this.bs_現在のFPS;
+ }
+ }
+ }
+ public int 現在のVPS
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ return this.bs_現在のVPS;
+ }
+ }
+ }
+
+ public FPS()
+ {
+ this.初めてのFPS更新 = true;
+ this.bs_現在のFPS = 0;
+ this.bs_現在のVPS = 0;
+ }
+
+ // 進行処理時に呼び出す想定。
+ public void FPSをカウントする()
+ {
+ lock( this.排他利用 )
+ {
+ if( this.初めてのFPS更新 )
+ {
+ this.初めてのFPS更新 = false;
+ this.fps用カウンタ = 0;
+ this.vps用カウンタ = 0;
+ this.定間隔進行 = new 定間隔進行();
+ this.定間隔進行.経過時間の計測を開始する();
+ }
+ else
+ {
+ // fps 更新。
+ this.fps用カウンタ++;
+
+ // 1秒ごとに FPS, VPS プロパティの値を更新。
+ this.定間隔進行.経過時間の分だけ進行する( 1000, () => {
+
+ this.bs_現在のFPS = this.fps用カウンタ;
+ this.bs_現在のVPS = this.vps用カウンタ;
+ this.fps用カウンタ = 0;
+ this.vps用カウンタ = 0;
+
+ } );
+ }
+ }
+ }
+
+ // 描画時に呼び出す想定。
+ public void VPSをカウントする()
+ {
+ lock( this.排他利用 )
+ {
+ this.vps用カウンタ++;
+ }
+ }
+
+ private int fps用カウンタ = 0;
+ private int vps用カウンタ = 0;
+ private FDK.カウンタ.定間隔進行 定間隔進行 = null;
+ private bool 初めてのFPS更新 = true;
+
+ // Write のほうが多いだろうから、ReaderWriterLockSlim より高速な lock を使う。
+ private readonly object 排他利用 = new object();
+
+ #region " バックストア "
+ //-----------------
+ private int bs_現在のFPS = 0;
+ private int bs_現在のVPS = 0;
+ //-----------------
+ #endregion
+ }
+}
--- /dev/null
+using System;
+using System.Diagnostics;
+
+namespace FDK.カウンタ
+{
+ /// <summary>
+ /// ある int 型整数を、開始値から終了値まで、一定間隔で単純増加させるカウンタ。
+ /// 終了値に達したら、それ以降は終了値を維持する(不変)。
+ /// </summary>
+ public class 単純増加後不変カウンタ
+ {
+ /// <summary>
+ /// カウンタを進行し、現在の値を取得する。
+ /// </summary>
+ public int 現在値
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ this.進行する();
+ return this.bs_現在値;
+ }
+ }
+ }
+ /// <summary>
+ /// カウンタを進行し、現在の値を割合(0.0:開始値 ~ 1.0:終了値)に変換して取得する。
+ /// </summary>
+ public float 現在値の割合
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ Debug.Assert( 0 != ( this.bs_終了値 - this.bs_開始値 ) );
+ this.進行する();
+ return (float) ( this.bs_現在値 - this.bs_開始値 ) / (float) ( this.bs_終了値 - this.bs_開始値 );
+ }
+ }
+ }
+ public int 開始値
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ return this.bs_開始値;
+ }
+ }
+ }
+ public int 終了値
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ return this.bs_終了値;
+ }
+ }
+ }
+ /// <summary>
+ /// カウンタを進行し、その結果、カウンタの進行がまだ動作中なら true を返す。(終了値に達しているかどうかは無関係。)
+ /// </summary>
+ public bool 動作中である
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ this.進行する(); // 終了してるかどうか判定する前に、溜まってる進行を全部消化する。
+ return this.bs_動作中;
+ }
+ }
+ }
+ /// <summary>
+ /// カウンタの進行が一時停止されているなら true を返す。(終了値に達しているかどうかは無関係。)
+ /// </summary>
+ public bool 停止中である
+ => !this.動作中である;
+ /// <summary>
+ /// カウンタを進行し、その結果、現在値が終了値に達していたら true を返す。
+ /// </summary>
+ public bool 終了値に達した
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ this.進行する();
+ return ( this.bs_現在値 >= this.bs_終了値 );
+ }
+ }
+ }
+ /// <summary>
+ /// カウンタを進行し、その結果、まだ現在値が終了値に達していないら true を返す。
+ /// </summary>
+ public bool 終了値に達していない
+ => !this.終了値に達した;
+
+ /// <summary>
+ /// コンストラクタ(1) 初期化のみ行い、カウンタは開始しない。
+ /// </summary>
+ public 単純増加後不変カウンタ()
+ {
+ this.間隔ms = QPCTimer.未使用;
+ this.定間隔進行 = null;
+
+ this.bs_開始値 = 0;
+ this.bs_終了値 = 0;
+ this.bs_現在値 = 0;
+ this.bs_動作中 = false;
+ }
+ /// <summary>
+ /// コンストラクタ(2) 初期化と同時にカウンタを開始する。
+ /// </summary>
+ public 単純増加後不変カウンタ( int 最初の値, int 最後の値, long 値をひとつ増加させるのにかける時間ms = 1000 ) : this()
+ {
+ this.開始する( 最初の値, 最後の値, 値をひとつ増加させるのにかける時間ms );
+ }
+
+ public void 開始する( int 最初の値, int 最後の値, long 値をひとつ増加させるのにかける時間ms = 1000 )
+ {
+ lock( this.排他利用 )
+ {
+ this.間隔ms = 値をひとつ増加させるのにかける時間ms;
+ this.定間隔進行 = new 定間隔進行(); // 同時に開始する。
+ this.bs_開始値 = 最初の値;
+ this.bs_終了値 = System.Math.Max( 最初の値, 最後の値 ); // 逆転禁止。
+ this.bs_現在値 = 最初の値;
+ this.bs_動作中 = true;
+ }
+ }
+ public void 一時停止する()
+ {
+ lock( this.排他利用 )
+ {
+ this.定間隔進行.経過時間の計測を一時停止する();
+ this.bs_動作中 = false;
+ }
+ }
+ public void 再開する()
+ {
+ lock( this.排他利用 )
+ {
+ this.定間隔進行.経過時間の計測を再開する();
+ this.bs_動作中 = true;
+ }
+ }
+
+ private long 間隔ms = QPCTimer.未使用;
+ private 定間隔進行 定間隔進行 = null;
+ private readonly object 排他利用 = new object();
+
+ /// <summary>
+ /// 前回のこのメソッドの呼び出しからの経過時間をもとに、必要なだけ現在値を増加させる。
+ /// カウント値が終了値に達している場合は、それ以上増加しない(終了値を維持する)。
+ /// </summary>
+ private void 進行する()
+ {
+ if( QPCTimer.未使用 == this.間隔ms )
+ return; // 開始されていないなら無視。
+
+ lock( this.排他利用 )
+ {
+ this.定間隔進行?.経過時間の分だけ進行する( this.間隔ms, () => {
+
+ if( this.bs_動作中 )
+ {
+ if( this.bs_現在値 < this.bs_終了値 ) // 終了値以降、現在値は不変。
+ this.bs_現在値++;
+ }
+
+ } );
+ }
+ }
+
+ #region " バックストア。"
+ //---------------------------------
+ private int bs_開始値 = 0;
+ private int bs_終了値 = 0;
+ private int bs_現在値 = 0;
+ private bool bs_動作中 = false;
+ //---------------------------------
+ #endregion
+ }
+}
--- /dev/null
+using System;
+using System.Diagnostics;
+
+namespace FDK.カウンタ
+{
+ /// <summary>
+ /// ある int 型整数を、開始値から終了値まで、一定間隔で単純増加させるカウンタ。
+ /// 終了値に達したら開始値に戻る。以降、それを繰り返す。
+ /// </summary>
+ public class 単純増加後反復カウンタ
+ {
+ /// <summary>
+ /// カウンタを進行し、現在の値を取得する。
+ /// </summary>
+ public int 現在値
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ this.進行する();
+ return this.bs_現在値;
+ }
+ }
+ }
+ /// <summary>
+ /// カウンタを進行し、現在の値を割合(0.0:開始値 ~ 1.0:終了値)に変換して取得する。
+ /// </summary>
+ public float 現在値の割合
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ Debug.Assert( 0 != ( this.bs_終了値 - this.bs_開始値 ) );
+ this.進行する();
+ return (float) ( this.bs_現在値 - this.bs_開始値 ) / (float) ( this.bs_終了値 - this.bs_開始値 );
+ }
+ }
+ }
+ public int 開始値
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ return this.bs_開始値;
+ }
+ }
+ }
+ public int 終了値
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ return this.bs_終了値;
+ }
+ }
+ }
+ /// <summary>
+ /// カウンタを進行し、その結果、カウンタの進行がまだ動作中なら true を返す。(終了値に達しているかどうかは別問題。)
+ /// </summary>
+ public bool 動作中である
+ {
+ get
+ {
+ lock( this.排他利用 )
+ {
+ this.進行する(); // 終了してるかどうか判定する前に、溜まってる進行を全部消化する。
+ return this.bs_動作中;
+ }
+ }
+ }
+ /// <summary>
+ /// カウンタの進行が一時停止されているなら true を返す。(終了値に達しているかどうかは別問題。)
+ /// </summary>
+ public bool 停止中である
+ => !this.動作中である;
+
+ /// <summary>
+ /// コンストラクタ(1) 初期化のみ行い、カウンタは開始しない。
+ /// </summary>
+ public 単純増加後反復カウンタ()
+ {
+ this.間隔ms = QPCTimer.未使用;
+ this.定間隔進行 = null;
+
+ this.bs_開始値 = 0;
+ this.bs_終了値 = 0;
+ this.bs_現在値 = 0;
+ this.bs_動作中 = false;
+ }
+ /// <summary>
+ /// コンストラクタ(2) 初期化と同時にカウンタを開始する。
+ /// </summary>
+ public 単純増加後反復カウンタ( int 最初の値, int 最後の値, long 値をひとつ増加させるのにかける時間ms = 1000 ) : this()
+ {
+ this.開始する( 最初の値, 最後の値, 値をひとつ増加させるのにかける時間ms );
+ }
+
+ public void 開始する( int 最初の値, int 最後の値, long 値をひとつ増加させるのにかける時間ms = 1000 )
+ {
+ lock( this.排他利用 )
+ {
+ this.間隔ms = 値をひとつ増加させるのにかける時間ms;
+ this.定間隔進行 = new 定間隔進行(); // 同時に開始する。
+ this.bs_開始値 = 最初の値;
+ this.bs_終了値 = System.Math.Max( 最初の値, 最後の値 ); // 逆転禁止。
+ this.bs_現在値 = 最初の値;
+ this.bs_動作中 = true;
+ }
+ }
+ public void 一時停止する()
+ {
+ lock( this.排他利用 )
+ {
+ this.定間隔進行.経過時間の計測を一時停止する();
+ this.bs_動作中 = false;
+ }
+ }
+ public void 再開する()
+ {
+ lock( this.排他利用 )
+ {
+ this.定間隔進行.経過時間の計測を再開する();
+ this.bs_動作中 = true;
+ }
+ }
+
+ private long 間隔ms = QPCTimer.未使用;
+ private 定間隔進行 定間隔進行 = null;
+ private readonly object 排他利用 = new object();
+
+ /// <summary>
+ /// 前回のこのメソッドの呼び出しからの経過時間をもとに、必要なだけ現在値を増加させる。
+ /// カウント値が終了値に達している場合は、開始値に繰り戻す。
+ /// </summary>
+ private void 進行する()
+ {
+ Debug.Assert( QPCTimer.未使用 != this.間隔ms );
+
+ lock( this.排他利用 )
+ {
+ this.定間隔進行?.経過時間の分だけ進行する( this.間隔ms, () => {
+
+ if( this.bs_動作中 )
+ {
+ this.bs_現在値++;
+
+ // 終了値を超えていれば開始値に戻る。
+ if( this.bs_現在値 > this.bs_終了値 )
+ this.bs_現在値 = this.bs_開始値;
+ }
+
+ } );
+ }
+ }
+
+ #region " バックストア。"
+ //---------------------------------
+ private int bs_開始値 = 0;
+ private int bs_終了値 = 0;
+ private int bs_現在値 = 0;
+ private bool bs_動作中 = false;
+ //---------------------------------
+ #endregion
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+
+namespace FDK.カウンタ
+{
+ /// <summary>
+ /// 一定間隔ごとの進行処理を実現するクラス。
+ /// </summary>
+ /// <remarks>
+ /// <para> 例えば、
+ /// <code>
+ /// var cf = new C定間隔進行();
+ /// cf.経過時間の分だけ進行する( 400, 定間隔処理 );
+ /// </code>
+ /// と記述した場合、400ms ごとに 定間隔処理() が実行されるようになる。
+ /// </para>
+ /// <para>
+ /// ただし、この動作は「経過時間の分だけ進行する()」メソッドを呼び出した時に、定間隔処理() を「必要な回数だけ反復実行」する仕様である。
+ /// 例えば、先述の例において、メソッドの呼び出し時点で、前回の同メソッド(またはコンストラクタ)の呼び出しから 900ms が経過していたとすると、
+ /// 定間隔処理() は 900÷400 = 2回実行され、残りの 100ms が経過時間として次回に繰り越される。
+ /// (一回の処理に時間がかかった場合にも定間隔処理が並列実行されるわけではない。)
+ /// </para>
+ /// <para>
+ /// なお、定間隔処理にラムダ式を使用する場合は、キャプチャ変数のライフサイクル(実行される時点でまだ存在しているか否か)に留意し、弱い参照 の利用も検討すること。
+ /// </para>
+ /// </remarks>
+ public class 定間隔進行
+ {
+ /// <summary>
+ /// コンストラクタ。初期化と同時に、間隔の計測も開始する。
+ /// </summary>
+ public 定間隔進行()
+ {
+ this.経過時間の計測を開始する();
+ }
+ public void 経過時間の計測を開始する()
+ {
+ lock( this.排他利用 )
+ {
+ this.Timer.リセットする();
+ }
+ }
+ public void 経過時間の計測を一時停止する()
+ {
+ lock( this.排他利用 )
+ {
+ this.Timer.一時停止する();
+ }
+ }
+ public void 経過時間の計測を再開する()
+ {
+ lock( this.排他利用 )
+ {
+ this.Timer.再開する();
+ }
+ }
+ public void 経過時間の分だけ進行する( long 間隔ms, Action 定間隔処理 )
+ {
+ lock( this.排他利用 )
+ {
+ // 現在時刻を取得。
+ this.Timer.現在のカウントをキャプチャする();
+ long 現在時刻ms = this.Timer.現在のキャプチャカウント100ns単位 / 10000;
+
+ // 初めての進行の場合、前回時刻を初期化する。
+ if( QPCTimer.未使用 == this.前回の進行時刻ms )
+ {
+ this.前回の進行時刻ms = 現在時刻ms;
+ }
+
+ // (ないと思うが)タイマが一回りしてしまった時のための保護。正常動作を保証するものではない。
+ if( 現在時刻ms < this.前回の進行時刻ms )
+ {
+ this.前回の進行時刻ms = 現在時刻ms;
+ }
+
+ // 経過時間における回数だけ、処理を実行する。
+ while( ( 現在時刻ms - this.前回の進行時刻ms ) >= 間隔ms )
+ {
+ 定間隔処理();
+ this.前回の進行時刻ms += 間隔ms;
+ }
+ }
+ }
+
+ private long 前回の進行時刻ms = QPCTimer.未使用;
+ private readonly QPCTimer Timer = new QPCTimer();
+ private readonly object 排他利用 = new object();
+ }
+}