2

現在、c#で(.NETを使用して)作成しているアプリケーションがあります。このアプリケーションでは、ユーザーが画面に画像を表示したらすぐに、キーを押して応答するまでタイマーを開始する必要があります。

モニターの入力遅延と応答時間、キーボードがメッセージを物理的に送信するのにかかる時間、メッセージを処理するためのOSなどを考えると、これは実際には非常に難しいことに気付きました。

しかし、私はそれをほぼ一定のエラーに減らすために最善を尽くしています(応答時間の結果は、あるユーザーを次のユーザーと比較するために使用されるため、一定のエラーは実際には問題ではありません)。ただし、厄介なハードルはモニターのリフレッシュレートによって引き起こされる変数です。これは、onPaintメッセージが呼び出されて処理されたときに収集されるため、画像が実際に処理されてグラフィックバッファーから送信されたという意味ではありませんか?

残念ながら、時間制限やその他のコミットメントにより、Windows用のc#でこのタスクを続行することが現実的に制限されます。

では、私が疑問に思っていたのは、OpenGLまたはDirectXですべての描画を処理するのか、それともOpenGLまたはDirectXのいずれかを使用して画面が更新されたときにイベントを作成できるのであれば、それでもよいのでしょうか。

以前に私に与えられたもう1つの提案は、V-Syncに関するものでした。これをオフにすると、画像は描画されるとすぐに送信されますか?モニターのリフレッシュレートに同期した設定レートで画像を送信するのとは対照的に?

4

2 に答える 2

4

次のことを行うには、グラフィックを別のスレッドでレンダリングする必要があります。

  • 垂直同期を使用して、画像を効果的に表示する正確なタイミングを設定します。
  • ユーザー入力の正確なタイミングを取得します(ユーザーインターフェイスはレンダリングループと同じスレッド上にないため)。

Direct3Dを初期化して、レンダリング中にVSyncを有効にします。

// DirectX example
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.BackBufferCount = 1;
presentParams.PresentationInterval = PresentInterval.One;

device = new Device(...

別のスレッドでレンダリングを実行します。

Thread renderThread = new Thread(RenderLoop);
renderThread.Start();

shouldDisplayImageEvent = new AutoResetEvent();

次に、次のレンダリングループを使用します。

void RenderLoop()
{
    while(applicationActive)
    {
          device.BeginScene();

        // Other rendering task

        if (shouldDisplayImageEvent.WaitOne(0))
        {
            // Render image
            // ...

            userResponseStopwatch = new Stopwatch();
            userResponseStopwatch.Start();
        }

        device.EndScene();

        device.Present();
    }
}

次に、ユーザー入力を処理します。

void OnUserInput(object sender, EventArgs e)
{
    if (userResponseStopwatch != null)
    {
        userResponseStopwatch.Stop();

        float userResponseDuration = userResponseStopwatch.ElapsedMillisecond - 1000 / device.DisplayMode.RefreshRate - displayDeviceDelayConstant;
        userResponseStopwatch = null;
    }
}

ここで、shouldDisplayImageEvent.Set()イベントトリガーを使用して、必要に応じて画像を表示し、ストップウォッチを開始します。

于 2012-06-12T14:01:30.073 に答える
2

まず、アプリケーションのアイドルループでVSyncを有効にします。

// DirectX example
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.BackBufferCount = 1;
presentParams.PresentationInterval = PresentInterval.One;

device = new Device(...

Application.Idle += new EventHandler(OnApplicationIdle);

// More on this here : http://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx
internal void OnApplicationIdle(object sender, EventArgs e)
{
    Msg msg = new Msg();
    while (true)
    {
        if (PeekMessage(out msg, IntPtr.Zero, 0, 0, 0))
            break;
    }

    // Clearing render
    // ...

    if (displayImage)
    {
        // Render image
        // ...

        renderTime = DateTime.now();
    }
    device.Present();
}

vsyncを有効にすると、 device.Present関数は次のフレーム同期までブロックされるため、renderTimeとユーザー入力時間の間の時間を計算しディスプレイデバイスの遅延+ 16.67msを削除すると、ユーザーの応答遅延が得られます。

于 2012-06-11T13:08:22.130 に答える