timeGetTime() 开始变量大于结束变量
timeGetTime() start variable is bigger than end variable
我正在使用 timeGetTime()
将帧速率限制为每秒 60 帧。我打算这样做的方法是获取渲染所述 60 帧所需的时间,然后使用 Sleep 等待第二秒的剩余时间。但是由于某种原因,timeGetTime()
第一次调用它时返回的数字比我在渲染 60 帧后调用它时返回的数字大得多。
代码如下:
Header
#ifndef __TesteMapa_h_
#define __TesteMapa_h_
#include "BaseApplication.h"
#include "Mundo.h"
class TesteMapa : public BaseApplication{
public:
TesteMapa()
virtual ~TesteMapa();
protected:
virtual void createScene();
virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);
virtual bool frameEnded(const Ogre::FrameEvent& evt);
virtual bool keyPressed(const OIS::KeyEvent &evt);
virtual bool keyReleased(const OIS::KeyEvent &evt);
private:
Mundo mundo = Mundo(3,3,3);
short altura, largura, passos, balanca, framesNoSegundo=0;
Ogre::SceneNode *noSol, *noSolFilho, *noCamera;
DWORD inicioSegundo = 0, finala;//inicioSegundo is the start variable and finala the ending variable
};
#endif
CPP相关函数
bool TesteMapa::frameEnded(const Ogre::FrameEvent& evt){
framesNoSegundo++;
if (inicioSegundo == 0)
inicioSegundo = timeGetTime();
else{
if (framesNoSegundo == 60){
finala = timeGetTime(); //getting this just to see the value being returned
Sleep(1000UL - (timeGetTime() - inicioSegundo));
inicioSegundo = 0;
framesNoSegundo = 0;
}
}
return true;
}
我在主函数中使用 timeBeginPeriod(1)
和 timeEndPeriod(1)
。
如果您使用的是 Visual Studio 2013 或更早版本,std::chrono 使用 64hz 自动收报机(每个刻度 15.625 毫秒),速度很慢。 VS 2015 应该会解决这个问题。您可以改用 QueryPerformanceCounter。这是以固定频率运行而没有漂移的示例代码,因为延迟是基于计数器的原始读数。 dwLateStep 是一种调试辅助工具,如果一个或多个步骤花费的时间太长,它会增加。该代码与 Windows XP 兼容,其中 Sleep(1) 最多可能需要 2 毫秒,这就是为什么代码仅在有 2 毫秒或更多延迟时间时才进行休眠的原因。
typedef unsigned long long UI64; /* unsigned 64 bit int */
#define FREQ 60 /* frequency */
DWORD dwLateStep; /* late step count */
LARGE_INTEGER liPerfFreq; /* 64 bit frequency */
LARGE_INTEGER liPerfTemp; /* used for query */
UI64 uFreq = FREQ; /* thread frequency */
UI64 uOrig; /* original tick */
UI64 uWait; /* tick rate / freq */
UI64 uRem = 0; /* tick rate % freq */
UI64 uPrev; /* previous tick based on original tick */
UI64 uDelta; /* current tick - previous */
UI64 u2ms; /* 2ms of ticks */
UI64 i;
/* ... */ /* wait for some event to start thread */
QueryPerformanceFrequency(&liPerfFreq);
u2ms = ((UI64)(liPerfFreq.QuadPart)+499) / ((UI64)500);
timeBeginPeriod(1); /* set period to 1ms */
Sleep(128); /* wait for it to stabilize */
QueryPerformanceCounter(&liPerfTemp);
uOrig = uPrev = liPerfTemp.QuadPart;
for(i = 0; i < (uFreq*30); i++){
/* update uWait and uRem based on uRem */
uWait = ((UI64)(liPerfFreq.QuadPart) + uRem) / uFreq;
uRem = ((UI64)(liPerfFreq.QuadPart) + uRem) % uFreq;
/* wait for uWait ticks */
while(1){
QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
uDelta = (UI64)(liPerfTemp.QuadPart - uPrev);
if(uDelta >= uWait)
break;
if((uWait - uDelta) > u2ms)
Sleep(1);
}
if(uDelta >= (uWait*2))
dwLateStep += 1;
uPrev += uWait;
/* fixed frequency code goes here */
/* along with some type of break when done */
}
timeEndPeriod(1); /* restore period */
甚至没有阅读完整的问题,以下内容:
using timeGetTime()
t limit the framerate to 60 frames per second
...
Sleep for the remainder of the second
可以用坚定的回答"You are doing it wrong"。换句话说,就此打住,换一种方式。
timeGetTime
也没有必要的精度(即使使用 timeBeginPeriod(1)
也不行),Sleep
也没有所需的精度,Sleep
也没有给出任何保证最大持续时间,Sleep
的语义甚至与您期望的相差甚远,也不是通过睡眠来限制帧速率的正确方法。
另外,计算秒的余数将不可避免地引入系统误差,该误差会随着时间的推移而累积。
限制帧率的唯一正确方法是使用垂直同步。
如果您需要以其他方式将模拟限制为特定速率,使用可等待计时器是正确的方法。这仍然会受到调度程序精度的影响,但它会避免累积系统错误,并且优先级提升将 至少 提供事实上的软实时保证。
为了理解为什么您正在做的事情(除了精度和累积错误)在概念上是错误的,请考虑两件事:
- 不同的计时器,即使它们 运行 频率明显相同,也会发生分歧(因此,使用除垂直同步中断之外的任何计时器都是 错误的 来限制帧速度)。观看红色交通信号灯处的汽车,以进行现实类比。他们的眼罩将总是不同步。
Sleep
使当前线程 "not ready" 变为 运行,最终,在指定时间过去后的某个时间,再次使线程 "ready"。这并不意味着该线程将再次 运行 。事实上,这并不一定意味着线程将 运行 在任何有限的时间内完全。
- 分辨率通常在 16 毫秒左右(如果您调整调度程序的粒度,则为 1 毫秒,这是一种反模式——一些最近的架构通过使用未记录的 Nt API 支持 0.5 毫秒),这对于某些东西来说太粗糙了在 1/60 秒范围内。
我正在使用 timeGetTime()
将帧速率限制为每秒 60 帧。我打算这样做的方法是获取渲染所述 60 帧所需的时间,然后使用 Sleep 等待第二秒的剩余时间。但是由于某种原因,timeGetTime()
第一次调用它时返回的数字比我在渲染 60 帧后调用它时返回的数字大得多。
代码如下:
Header
#ifndef __TesteMapa_h_
#define __TesteMapa_h_
#include "BaseApplication.h"
#include "Mundo.h"
class TesteMapa : public BaseApplication{
public:
TesteMapa()
virtual ~TesteMapa();
protected:
virtual void createScene();
virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);
virtual bool frameEnded(const Ogre::FrameEvent& evt);
virtual bool keyPressed(const OIS::KeyEvent &evt);
virtual bool keyReleased(const OIS::KeyEvent &evt);
private:
Mundo mundo = Mundo(3,3,3);
short altura, largura, passos, balanca, framesNoSegundo=0;
Ogre::SceneNode *noSol, *noSolFilho, *noCamera;
DWORD inicioSegundo = 0, finala;//inicioSegundo is the start variable and finala the ending variable
};
#endif
CPP相关函数
bool TesteMapa::frameEnded(const Ogre::FrameEvent& evt){
framesNoSegundo++;
if (inicioSegundo == 0)
inicioSegundo = timeGetTime();
else{
if (framesNoSegundo == 60){
finala = timeGetTime(); //getting this just to see the value being returned
Sleep(1000UL - (timeGetTime() - inicioSegundo));
inicioSegundo = 0;
framesNoSegundo = 0;
}
}
return true;
}
我在主函数中使用 timeBeginPeriod(1)
和 timeEndPeriod(1)
。
如果您使用的是 Visual Studio 2013 或更早版本,std::chrono 使用 64hz 自动收报机(每个刻度 15.625 毫秒),速度很慢。 VS 2015 应该会解决这个问题。您可以改用 QueryPerformanceCounter。这是以固定频率运行而没有漂移的示例代码,因为延迟是基于计数器的原始读数。 dwLateStep 是一种调试辅助工具,如果一个或多个步骤花费的时间太长,它会增加。该代码与 Windows XP 兼容,其中 Sleep(1) 最多可能需要 2 毫秒,这就是为什么代码仅在有 2 毫秒或更多延迟时间时才进行休眠的原因。
typedef unsigned long long UI64; /* unsigned 64 bit int */
#define FREQ 60 /* frequency */
DWORD dwLateStep; /* late step count */
LARGE_INTEGER liPerfFreq; /* 64 bit frequency */
LARGE_INTEGER liPerfTemp; /* used for query */
UI64 uFreq = FREQ; /* thread frequency */
UI64 uOrig; /* original tick */
UI64 uWait; /* tick rate / freq */
UI64 uRem = 0; /* tick rate % freq */
UI64 uPrev; /* previous tick based on original tick */
UI64 uDelta; /* current tick - previous */
UI64 u2ms; /* 2ms of ticks */
UI64 i;
/* ... */ /* wait for some event to start thread */
QueryPerformanceFrequency(&liPerfFreq);
u2ms = ((UI64)(liPerfFreq.QuadPart)+499) / ((UI64)500);
timeBeginPeriod(1); /* set period to 1ms */
Sleep(128); /* wait for it to stabilize */
QueryPerformanceCounter(&liPerfTemp);
uOrig = uPrev = liPerfTemp.QuadPart;
for(i = 0; i < (uFreq*30); i++){
/* update uWait and uRem based on uRem */
uWait = ((UI64)(liPerfFreq.QuadPart) + uRem) / uFreq;
uRem = ((UI64)(liPerfFreq.QuadPart) + uRem) % uFreq;
/* wait for uWait ticks */
while(1){
QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
uDelta = (UI64)(liPerfTemp.QuadPart - uPrev);
if(uDelta >= uWait)
break;
if((uWait - uDelta) > u2ms)
Sleep(1);
}
if(uDelta >= (uWait*2))
dwLateStep += 1;
uPrev += uWait;
/* fixed frequency code goes here */
/* along with some type of break when done */
}
timeEndPeriod(1); /* restore period */
甚至没有阅读完整的问题,以下内容:
using timeGetTime() t limit the framerate to 60 frames per second
...
Sleep for the remainder of the second
可以用坚定的回答"You are doing it wrong"。换句话说,就此打住,换一种方式。
timeGetTime
也没有必要的精度(即使使用 timeBeginPeriod(1)
也不行),Sleep
也没有所需的精度,Sleep
也没有给出任何保证最大持续时间,Sleep
的语义甚至与您期望的相差甚远,也不是通过睡眠来限制帧速率的正确方法。
另外,计算秒的余数将不可避免地引入系统误差,该误差会随着时间的推移而累积。
限制帧率的唯一正确方法是使用垂直同步。
如果您需要以其他方式将模拟限制为特定速率,使用可等待计时器是正确的方法。这仍然会受到调度程序精度的影响,但它会避免累积系统错误,并且优先级提升将 至少 提供事实上的软实时保证。
为了理解为什么您正在做的事情(除了精度和累积错误)在概念上是错误的,请考虑两件事:
- 不同的计时器,即使它们 运行 频率明显相同,也会发生分歧(因此,使用除垂直同步中断之外的任何计时器都是 错误的 来限制帧速度)。观看红色交通信号灯处的汽车,以进行现实类比。他们的眼罩将总是不同步。
Sleep
使当前线程 "not ready" 变为 运行,最终,在指定时间过去后的某个时间,再次使线程 "ready"。这并不意味着该线程将再次 运行 。事实上,这并不一定意味着线程将 运行 在任何有限的时间内完全。- 分辨率通常在 16 毫秒左右(如果您调整调度程序的粒度,则为 1 毫秒,这是一种反模式——一些最近的架构通过使用未记录的 Nt API 支持 0.5 毫秒),这对于某些东西来说太粗糙了在 1/60 秒范围内。