C#(.NET10)です。自作アプリのFPSが安定しない原因を調査しています(突然カクっと画面がカタつく
ことがちょいちょいある)。いろいろ削っていき、とりあえずシンプルなコードで測定してみました。
画面クリア ⇒ 一行表示 ⇒ ScreenFlip というだけのループで試してみました。
using DxLibDLL;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
class Program
{
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
public static extern int timeBeginPeriod(int uPeriod);
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
public static extern int timeEndPeriod(int uPeriod);
private static readonly string MutexName = "MyDxLib_App_2026";
private static Mutex _mutex;
static void Main()
{
_mutex = new Mutex(true, MutexName, out bool createdNew);
if (!createdNew)
{
_mutex.Dispose();
return;
}
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // .NET10用
timeBeginPeriod(1); // 1ms精度
DX.ChangeWindowMode(DX.TRUE);
DX.SetGraphMode(1920, 1080, 32);
DX.SetAlwaysRunFlag(DX.TRUE); // 常時稼働
DX.SetEnableXAudioFlag(DX.TRUE);
DX.SetWindowSizeExtendRate(1.0);
DX.SetMultiThreadFlag(DX.TRUE);
DX.SetZBufferBitDepth(24);
DX.SetCreateDrawValidGraphZBufferBitDepth(24);
DX.SetFullSceneAntiAliasingMode(4, 2);
DX.SetDrawValidMultiSample(4, 2);
DX.SetCreateSoundIgnoreLoopAreaInfo(DX.TRUE);
DX.SetBackgroundColor(16, 16, 52);
DX.SetFontCacheCharNum(3072);
DX.SetWaitVSyncFlag(DX.FALSE); // VSync待たない
if (DX.DxLib_Init() == -1) return;
DX.SetDrawMode(DX.DX_DRAWMODE_BILINEAR);
DX.SetDrawScreen(DX.DX_SCREEN_BACK);
long t0 = Stopwatch.GetTimestamp() / 10, lMax = 0;
int nNGCount = 0;
while (DX.ProcessMessage() == 0)
{
DX.ClearDrawScreen();
if (DX.CheckHitKey(DX.KEY_INPUT_ESCAPE) == 1) break;
DX.DrawString(20, 20, $"Quit To ESC: 5msec以上{nNGCount}回発生: 最大値{lMax}: FPS{DX.GetFPS()}", DX.GetColor(255, 255, 255));
long t1 = Stopwatch.GetTimestamp() / 10;
if (DX.ScreenFlip() != 0) break;
long t2 = Stopwatch.GetTimestamp() / 10;
if (t2 - t1 > lMax) lMax = t2 - t1;
if (t2 - t1 >= 3000) nNGCount++; // 3msec以上でカウント
if (t2 - t0 >= 60 * 1000 * 1000) break; // 60秒で抜ける
// ここの行
}
long t3 = Stopwatch.GetTimestamp() / 10; // ここでSTOPして数値を確認
DX.DxLib_End();
_mutex.ReleaseMutex();
_mutex.Dispose();
timeEndPeriod(1);
}
}
ScreenFlip自体の処理時間を測定してみました。上記の「// ここの行」のところを変えて、
60秒間で「ScreenFlipが3msec以上かかった回数と、かかった時間の最大値」を数回
測定して平均してみたところ
ウェイト 発生回数 最大時間 FPS
-----------------------------------------------------
行なし(ノーウェイト) 0回 1msec 9000〜
Thread.Sleep(1) 25回 5msec 500弱
Thread.Sleep(16) 140回 7msec 60弱
Thread.Sleep(33) 50回 5msec 30弱
のようになりました。
ここまで書いて別のことを試しました。GeForce4070SUPERなのですが、
NVIDIAコントロールパネルで電源管理モードを 標準 ⇒ パフォーマンス優先 に変更すると
Sleep(1)も(16)も発生回数0回、最大時間1msec程度になりました。
・ループ中にSleepがあるとScreenFlipの処理時間が変わる
・GPUの電源設定でこれまた変わる
ということでしょうか?