情報アイランド

「情報を制する者は世界を制す」をモットーに様々な情報を提供することを目指すブログです。現在はプログラミング関連情報が多めですが、投資関連情報も取り扱っていきたいです。

C#でWIN32API step9 FPSを測定する

step8:ここまでのまとめ(その1)

今回から何回かに亘って描画処理をしてみることにします。.NET Frameworkには描画処理のためにSystem.Drawing名前空間が用意されていますが、この連載記事では敢えてWIN32APIを使用します。

最初にFPSを測定するようにしておきましょう。

メッセージループの処理を少し変更するだけです。

GetMessage関数を直接使わず、PeekMessage関数でキュー上のメッセージの有無を調べて、メッセージが存在する場合にだけGetMessage関数を呼び出します。メッセージが存在しない場合は、カウンタと表示の処理を行った後InvalidateRect関数により再描画を実施し、最後に暫くの間眠ります。まだ文字描画について解説していないので、FPS値はコンソール画面に表示するようにしました。

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
int counter = 0;

WIN32.Window.MSG msg = new WIN32.Window.MSG();
while (true)
{
    if (WIN32.Window.PeekMessage(out msg, IntPtr.Zero, 0, 0, WIN32.Window.PM.PM_NOREMOVE))
    {
        if (!WIN32.Window.GetMessage(out msg, IntPtr.Zero, 0, 0))
            break;

        WIN32.Window.TranslateMessage(ref msg);
        WIN32.Window.DispatchMessage(ref msg);
    }
    else
    {
        counter++;
        if (stopwatch.ElapsedMilliseconds > 1000)
        {
            Console.WriteLine(counter.ToString());

            stopwatch.Reset();
            stopwatch.Start();
            counter = 0;
        }

        WIN32.Window.InvalidateRect(hWnd, IntPtr.Zero, false);
        WIN32.Window.UpdateWindow(hWnd);

        Thread.Sleep(1);
    }
}

StopwatchクラスやThread.Sleepメソッドを使用せず、敢えて対応するWIN32APIであるQueryPerformanceFrequency関数/QueryPerformanceCounter関数とSleep関数を使用する場合は以下のようになります。

int counter = 0;
long frequency = 0, time1 = 0, time2 = 0;
WIN32.Common.QueryPerformanceFrequency(out frequency);
WIN32.Common.QueryPerformanceCounter(out time1);

WIN32.Window.MSG msg = new WIN32.Window.MSG();
while (true)
{
    if (WIN32.Window.PeekMessage(out msg, IntPtr.Zero, 0, 0, WIN32.Window.PM.PM_NOREMOVE))
    {
        if (!WIN32.Window.GetMessage(out msg, IntPtr.Zero, 0, 0))
            break;

        WIN32.Window.TranslateMessage(ref msg);
        WIN32.Window.DispatchMessage(ref msg);
    }
    else
    {
        counter++;

        WIN32.Common.QueryPerformanceCounter(out time2);
        if ((time2 - time1) / frequency >= 1)
        {
            Console.WriteLine(counter.ToString());

            WIN32.Common.QueryPerformanceCounter(out time1);
            counter = 0;
        }

        WIN32.Window.InvalidateRect(hWnd, IntPtr.Zero, false);
        WIN32.Window.UpdateWindow(hWnd);

        WIN32.Common.Sleep(1);
    }
}

こちらが、P/I用のコードです(前回からの追加部分)。

using System;
using System.Runtime.InteropServices;

namespace Anttn.WIN32
{
    public static class Common
    {
    (省略)

        [DllImport("kernel32.dll")]
        public static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

        [DllImport("kernel32.dll")]
        public static extern bool QueryPerformanceFrequency(out long lpFrequency);

        [DllImport("kernel32.dll")]
        public static extern void Sleep(uint dwMilliseconds);

    (省略)
    }
}
using System;
using System.Runtime.InteropServices;

namespace Anttn.WIN32
{
    public static partial class Window
    {
    (省略)

        [DllImport("user32.dll")]
        public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, PM wRemoveMsg);

        [DllImport("user32.dll")]
        public static extern bool InvalidateRect(IntPtr hWnd, IntPtr rect, bool bErase);

    (省略)

        [Flags]
        public enum PM : uint
        {
            PM_NOREMOVE = 0x0000,
            PM_REMOVE = 0x0001,
            PM_NOYIELD = 0x0002,
            PM_QS_INPUT = QS.QS_INPUT << 16,
            PM_QS_POSTMESSAGE = (QS.QS_POSTMESSAGE | QS.QS_HOTKEY | QS.QS_TIMER) << 16,
            PM_QS_PAINT = QS.QS_PAINT << 16,
            PM_QS_SENDMESSAGE = QS.QS_SENDMESSAGE << 16
        }

    (省略)
    }
}

当方の環境では900FPS程度になりました。

pizyumi
プログラミング歴19年のベテランプログラマー。業務システム全般何でも作れます。現在はWeb系の技術を勉強中。
スポンサーリンク

-C#, WIN32API