2

首先,我发现了很多关于这个主题的信息,但不幸的是没有解决这个问题的解决方案。

我只是想调节我的 C++ 程序以每秒 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 仅为 0.826797386,而 3856/6271 为 0.614893956。

我不知道睡眠功能是否正在做一些与预期完全不同的事情,尽管我不明白为什么会这样做,或者它是否是我获取时间的方法(即使它应该是世界时间(毫秒)而不是时钟周期时间。任何帮助将不胜感激,谢谢。

4

5 回答 5

3

一方面,睡眠的默认分辨率是计算机的配额长度——通常是 10 毫秒或 15 毫秒,具体取决于 Windows 版本。例如,要获得 1 毫秒的分辨率,您必须发出 timeBeginPeriod(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

您可以尝试一个WinMain函数并使用该SetTimer函数和一个常规消息循环(您也可以利用 的过滤器机制GetMessage( ... )),您可以在其中使用请求的时间测试WM_TIMER消息,并且当您的计数器达到限制时执行PostQuitMessage(0)终止消息环形。

于 2012-05-15T01:20:18.657 回答
0

基于每帧渲染之间经过的时间渲染(迭代)呢?考虑创建一个void render(double timePassed)函数并根据timePassed参数进行渲染,而不是让程序进入睡眠状态。

例如,想象一下,您想要渲染一个球落下或弹跳。你会知道它是你需要的速度、加速度和所有其他物理。根据timePassed和所有其他物理参数(速度、加速度等)计算球的位置。

render()或者,如果您愿意,如果传递的时间值很小,您可以跳过函数执行,而不是让程序进入睡眠状态。

于 2012-05-15T18:36:04.473 回答
0

对于如此快的占空比,您可以使用高精度计时器(如 QueryPerformanceTimer)和忙等待循环。

如果你有一个低得多的占空比,但仍然需要精度,那么你可以休眠一部分时间,然后用忙等待循环消耗剩余的时间。

另一种选择是使用 DirectX 之类的东西将自己同步到 VSync 中断(几乎总是 60 Hz)。如果您正在编写游戏或 a/v 演示文稿,这可能很有意义。

Windows 不是实时操作系统,因此永远不会有完美的方法来执行此类操作,因为无法保证您的线程将被安排在您需要的时候准确运行。

请注意,在Sleep的注释中,实际时间量将至少比您在线程计划再次运行之前请求的延迟长一个“滴答”,并且可能长一整个“滴答”(然后我们必须假设线程已安排)。根据硬件和 Windows 版本的不同,“tick”可能会有很大差异。它通常在 10-15 毫秒的范围内,我看到它和 19 毫秒一样糟糕。对于 60 Hz,每次迭代需要 16.666 ms,因此这显然不够精确,无法满足您的需求。

于 2012-05-15T18:21:15.090 回答