1

さて、メッセージループの奇妙な点を見つけました。

まず、このコードを以下にロックします

MSG msg = {0};
while( WM_QUIT != msg.message )
{
    if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    else
    {
        Render();  // Do some rendering
    }
}

directxのチュートリアルで、この部分はメッセージループの一部です。

マウスをクリックすると、メッセージとしてキューに入れられます。

したがって、このような入力は win api の proc 関数で処理されるはずです。

peekMessage が true を返すようになったので、クリックしたときに render() がフレーム内で呼び出されなくなりました。

クリックするとレンダリングの if~else が if~if に変わると思います。

これ説明できますか??

4

3 に答える 3

4

あなたの理解は近いですが、完全には正しくありません。ループはフレームごとに 1 回実行されません。むしろ、ループの反復ごとに、1 つのメッセージが処理されるか、Render が呼び出されます。これにより、実質的にレンダリングの優先度が最も低くなりますが、アプリケーションの応答性は維持されます。ループは、実行する作業の量に応じて、描画される各フレームに対して何度も実行されることもあれば、数回実行されることもあります。

Render は Present を直接呼び出しますか? それとも、ウィンドウを無効にしますか? ウィンドウを無効にする場合、レンダリング間でウィンドウを再描画しないリスクがあるため、言及したように常に Render を呼び出すように変更したくないでしょう。

于 2013-05-06T15:01:34.113 に答える
2

基本的に、このループはウィンドウの保留中の Win32 メッセージを処理し、メッセージがない場合はフレームをレンダリングします。メッセージがWM_QUIT表示されると、ループを終了してアプリを終了します。

レンダリングが保留中のフレームが既に 3 つある場合、DirectX Present はスレッドをブロックする (つまり、一時停止する) ため、「スロットル」は必要ありません。

このモデルは、'Render' 呼び出しごとに 1 フレームの 'Update' を実行していることを前提としています。これは、ゲームにとっては現実的ではありませんが、チュートリアルでは単純です。StepTimerを使用してチュートリアル ループを拡張すると、次のようになります。

#include “StepTimer.h
DX::StepTimer g_timer;

...

MSG msg = {0};
while( WM_QUIT != msg.message )
{
    if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    else
    {
        g_timer.Tick([&]()
        {
            Update(g_timer); // Update world/game state
        });
        Render();  // Do some rendering
    }
}

...

void Render();
void Update(DX::StepTimer& timer);

StepTimerデフォルトでは可変ステップ更新を使用します。これはUpdate、フレームごとに 1 回呼び出され、時間のデルタが何であれ、その後 1 回呼び出されることを意味しますRender

次のように、固定ステップの更新 (たとえば、1 秒間に 60 回) を使用できます。

g_timer.SetFixedTimeStep(true);
g_timer.SetTargetElapsedSeconds(1.f / 60.f);

このモードでは、保留中のすべての Win32 メッセージが処理され、Update1 秒あたり平均 60 回の固定ステップ更新を維持するために必要に応じて呼び出され、その後 1 回Render呼び出されます。

于 2015-01-04T00:10:48.263 に答える
2

else 内の Render() は基本的に、レンダリングよりもキュー内のメッセージの処理を優先します。directx でレンダリングされたウィンドウの上にマウスを移動すると、メッセージがメッセージ キューにすばやく追加されますが、レンダリングが遅れるほど速くはありません。繰り返しが頻繁に発生するため、各繰り返しでレンダリングする利点はありませ各フレームがスワップチェーンで生成されるよりも速く、新しいメッセージがキューを圧倒するよりもはるかに高速です。今日のほとんどのコンピューターは、このループを 1 ミリ秒あたり 1 回以上実行し、マウスオーバー イベントでさえこれよりも少ない頻度で発生します。すべての反復でレンダリングすることは間違いではありません。それは単に不必要です。この例を実行した状態で、directx ウィンドウ上でマウスをできるだけ速く移動すると、このループの反復の 10% 未満でメッセージを処理し、レンダリングを遅らせることができます。

このメッセージ ループは可能な限り迅速に実行され、スワップチェーンのレンダリング準備が整ったことを検出する機能はありません。PeekMessage は、キューにメッセージがあるかどうかを確認します。ある場合は処理し、ない場合はレンダリングします。あなたが心配しているのは、一連のウィンドウ イベントによってレンダリングが遅延することですが、それは事実上不可能です。メッセージがどれだけ速くキューに送信されても​​、スワップチェーンは 60fps の場合でも、必要な速度の 10 倍以上の速さでレンダリングされます。このループは、CPU 使用率が高くなる原因です。その理由は、チュートリアルを単純化するためかもしれませんが、本質的に複雑な環境であるためです。メッセージ キューがフレームのレンダリングを遅らせることが心配な場合は、別のスレッドでスワップ チェーンを変更できます。

サンプル プログラムの CPU 効率を改善するには、Sleep(8); を追加するだけです。Render() ルーチンの一番下にあります。これにより、メッセージ ハンドラー/レンダリング スレッドが、メッセージの処理とレンダリングのサイクル間で一時停止し、1 秒あたり約 120 回になります。これは、高解像度タイマーとサイクル間の係数ベースのスリープを使用することで改善できます。

この例を改善するための優れた情報源は、ここにあります。

于 2015-01-03T20:41:40.543 に答える