2

まず、このトピックに関する多くの情報を見つけましたが、残念ながら問題を解決する解決策はありませんでした.

私は単純に、C++ プログラムを 1 秒あたり 60 回の反復で実行するように調整しようとしています。規制を緩和するために GetClockTicks() から GetLocalTime() まですべてを試しましたが、Windows Server 2008 マシンでプログラムを実行するたびに、ローカル マシンよりも遅くなり、その理由がわかりません!

「クロック」ベースの関数呼び出しは実行に費やされる CPU 時間を返すことを理解しているので、GetLocalTime に移動し、開始時刻と停止時刻を区別してから Sleep((FPS / 1000) - millisecondExecutionTime) を呼び出します。

私のローカル マシンはサーバーの CPU よりもかなり高速であるため、明らかに CPU ティックがオフになっていると考えられていましたが、GetLocalTime が機能しない理由は説明できません。私はこのメソッドをhttp://www.lazyfoo.net/SDL_tutorials/lesson14/index.phpに基づいて get_ticks() を変更し、常にウェブで見つけた関数を返します。

たとえば、次のコードを使用します。

#include <Windows.h>
#include <time.h>
#include <string>
#include <iostream>

using namespace std;

int main() {
    int tFps = 60;
    int counter = 0;

    SYSTEMTIME gStart, gEnd, start_time, end_time;

    GetLocalTime( &gStart );

    bool done = false;
    while(!done) {
        GetLocalTime( &start_time );

        Sleep(10);
        counter++;

        GetLocalTime( &end_time );

        int startTimeMilli = (start_time.wSecond * 1000 + start_time.wMilliseconds);
        int endTimeMilli = (end_time.wSecond * 1000 + end_time.wMilliseconds);

        int time_to_sleep = (1000 / tFps) - (endTimeMilli - startTimeMilli);


        if (counter > 240)
            done = true;

        if (time_to_sleep > 0)
            Sleep(time_to_sleep);
    }

    GetLocalTime( &gEnd );

    cout << "Total Time: " << (gEnd.wSecond*1000 + gEnd.wMilliseconds) - (gStart.wSecond*1000 + gStart.wMilliseconds) << endl;
    cin.get();
}

このコード スニペットを自分のコンピューター (3.06 GHz) で実行すると、合計時間 (ms) が 3856 になり、サーバー (2.53 GHz) では 6256 になります。つまり、比率は 2.53 ですが、プロセッサの速度である可能性があります。 /3.06 はわずか .826797386 であるのに対し、3856/6271 は .614893956 です。

スリープ機能が予想とは大幅に異なることを行っているかどうかはわかりませんが、その理由はわかりません。それとも、それが時間を取得するための私の方法なのか (時計ではなく世界時間 (ms) である必要があります)サイクルタイム.どんな助けでも大歓迎です, ありがとう.

4

5 に答える 5

3

1 つには、Sleep のデフォルトの解像度は、コンピューターのクォータの長さです。Windows のエディションに応じて、通常は 10 ミリ秒または 15 ミリ秒です。たとえば、1 ミリ秒の分解能を得るには、timeBeginPeriod(1) を発行する必要があります。これは、タイマー ハードウェアを再プログラムして、ミリ秒ごとに (大まかに) 1 回起動します。

于 2012-05-14T23:22:21.650 に答える
1

メインループでは次のことができます

int main() 
{
    // Timers
    LONGLONG curTime = NULL;
    LONGLONG nextTime = NULL;

    Timers::GameClock::GetInstance()->GetTime(&nextTime);
    while (true) {    
        Timers::GameClock::GetInstance()->GetTime(&curTime);
        if ( curTime > nextTime  && loops <= MAX_FRAMESKIP ) { 
            nextTime += Timers::GameClock::GetInstance()->timeCount;

            // Business logic goes here and occurr based on the specified framerate
        }
    }
}

この時間ライブラリを使用して

include "stdafx.h"

LONGLONG cacheTime;

Timers::SWGameClock* Timers::SWGameClock::pInstance = NULL;

Timers::SWGameClock* Timers::SWGameClock::GetInstance ( ) { 
    if (pInstance == NULL) { 
        pInstance = new SWGameClock();
    }
    return pInstance;
}


Timers::SWGameClock::SWGameClock(void) {
    this->Initialize ( );
}

void Timers::SWGameClock::GetTime ( LONGLONG * t ) { 
    // Use timeGetTime() if queryperformancecounter is not supported 
    if (!QueryPerformanceCounter( (LARGE_INTEGER *) t)) { 
        *t = timeGetTime();
    }

    cacheTime = *t;
}

LONGLONG Timers::SWGameClock::GetTimeElapsed ( void ) { 
    LONGLONG t; 

    // Use timeGetTime() if queryperformancecounter is not supported
    if (!QueryPerformanceCounter( (LARGE_INTEGER *) &t )) { 
        t = timeGetTime();
    }

    return (t - cacheTime);
}

void Timers::SWGameClock::Initialize ( void ) { 
    if ( !QueryPerformanceFrequency((LARGE_INTEGER *) &this->frequency) ) { 
        this->frequency = 1000; // 1000ms to one second 
    }
    this->timeCount = DWORD(this->frequency / TICKS_PER_SECOND);
}

Timers::SWGameClock::~SWGameClock(void)
{
}

以下を含むヘッダーファイルを使用します。

// Required for rendering stuff on time
#pragma once
#define TICKS_PER_SECOND 60
#define MAX_FRAMESKIP 5

namespace Timers { 
    class SWGameClock
    {
    public:
        static SWGameClock* GetInstance();
        void Initialize ( void );
        DWORD timeCount;

        void GetTime ( LONGLONG* t );
        LONGLONG GetTimeElapsed ( void );
        LONGLONG frequency; 

        ~SWGameClock(void);
    protected:
        SWGameClock(void);

    private:
        static SWGameClock* pInstance;
    }; // SWGameClock
} // Timers

これにより、コードが 60FPS (または入力したもの) で実行されることが保証されますが、この例では実際には実装されていないため、おそらく MAX_FRAMESKIP をダンプできます!

于 2012-05-15T16:19:53.693 に答える
0

各フレームのレンダリング間の経過時間に基づいてレンダリング(反復)するのはどうですか?プログラムをスリープ状態にするのではなくvoid render(double timePassed)、パラメーターに応じて関数を作成してレンダリングすることを検討してください。timePassed

たとえば、ボールを落下またはバウンドさせたいとします。あなたはそれがあなたが必要とする速度、加速そして他のすべての物理学であることを知っているでしょう。timePassedおよび他のすべての物理パラメータ(速度、加速度など)に基づいて、ボールの位置を計算します。

render()または、必要に応じて、経過時間が小さい値の場合は、プログラムをスリープ状態にする代わりに、関数の実行をスキップすることもできます。

于 2012-05-15T18:36:04.473 に答える
0

デューティサイクルが非常に速い場合は、高精度タイマー(QueryPerformanceTimerなど)とビジー待機ループを使用できます。

デューティサイクルがはるかに低いが、それでも精度が必要な場合は、一部の時間スリープしてから、ビジーウェイトループで残りの時間を使い果たすことができます。

もう1つのオプションは、DirectXなどを使用してVSync割り込み(ほとんどの場合60 Hz)に同期することです。これは、ゲームやA/Vプレゼンテーションをコーディングしている場合に非常に意味があります。

WindowsはリアルタイムOSではないため、このようなことを行うための完璧な方法はありません。スレッドが必要なときに正確に実行されるようにスケジュールされる保証はないからです。

スリープに関する注釈では、実際の時間は、スレッドが再び実行されるようにスケジュールされる前に、要求した遅延よりも少なくとも1つの「ティック」であり、場合によっては1つの「ティック」より長くなることに注意してください(その後、スレッドがスケジュールされています)。「目盛り」は、ハードウェアとWindowsのバージョンによって大きく異なります。それは一般的に10-15ミリ秒の範囲にあり、私はそれが19ミリ秒と同じくらい悪いのを見てきました。60 Hzの場合、反復ごとに16.666ミリ秒が必要です。したがって、これは明らかに、必要なものを提供するのに十分な精度ではありません。

于 2012-05-15T18:21:15.090 に答える
0

WinMain関数を試して、関数とSetTimer通常のメッセージ ループ (のフィルタ メカニズムを利用することもできますGetMessage( ... )) を使用して、要求された時間でメッセージをテストし、WM_TIMERカウンターが制限に達したときにPostQuitMessage(0)メッセージを終了することができます。ループ。

于 2012-05-15T01:20:18.657 に答える