Ogre引擎源码——Timer
2012-09-21 06:15
411 查看
http://blog.csdn.net/hunter8777/article/details/6204719
一个图形绘制引擎底层有很多工具类(utility),通过读基础工具类源码可以学习到不少东西。
Ogre引擎中与Timer相关的文件大致不多,大致如下(只列出头文件)
WIN32/OgreTimerImp.h
GLX/OgreTimerImp.h
OSX/OgreTimerImp.h
iPhone/OgreTimerImp.h
OgreTimer.h
其实这部分代码很少,真正意义上的头文件就是OgreTimer.h,以下是这个头文件的全部了
[cpp]
view plaincopyprint?
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
# include "WIN32/OgreTimerImp.h"
#elif (OGRE_PLATFORM == OGRE_PLATFORM_LINUX) || (OGRE_PLATFORM == OGRE_PLATFORM_SYMBIAN)
# include "GLX/OgreTimerImp.h"
#elif OGRE_PLATFORM == OGRE_PLATFORM_APPLE
# include "OSX/OgreTimerImp.h"
#elif OGRE_PLATFORM == OGRE_PLATFORM_IPHONE
# include "iPhone/OgreTimerImp.h"
#endif
(1)mZeroClock
程序启动后所执行的时间。
[cpp]
view plaincopyprint?
mZeroClock = clock();
(3)mLastTime
在调用读取时间函数 unsigned long getMilliseconds() / unsigned long getMicroseconds() 后记录当前读取的最后时间,作用是当下一次时间读取错误时,进行调整。(关于时间读取错误后面会有展开。)
(4)mStartTime / mFrequency
用于高精度计时器时间读取 分别通过函数QueryPerformanceCounter和QueryPerformanceFrequency获得count数以及frequency。
这里需要注意的是,Timer里应用两套计时度量方法。
一套是通过使用clock()获得cpu中时钟tick,在获取时间时,使用的api如下
[cpp]
view plaincopyprint?
unsigned long getMillisecondsCPU();
unsigned long getMicrosecondsCPU();
在后面会讨论下这些api值得关注的细节。
(5)mTimerMask
这个变量看名字有点不明白,其实这个变量和高精度计时器读取时间有关。
查询msdn中QueryPerformanceCounter函数的注解中可以发现,这个函数调用环境如果是多处理器(CPU)的话,由于BIOS或者HAL的原因,切换到不同CPU上的线程调用后会造成不正确的结果。所以必须保证每次调用这个函数都是在同一个CPU上进行。这个mask就是将线程调用限定在某一个CPU上。
[cpp]
view plaincopyprint?
HANDLE thread = GetCurrentThread();
// Set affinity to the first core
DWORD_PTR oldMask = SetThreadAffinityMask(thread, mTimerMask);
// Get the constant frequency
QueryPerformanceFrequency(&mFrequency);
// Query the timer
QueryPerformanceCounter(&mStartTime);
mStartTick = GetTickCount();
// Reset affinity
SetThreadAffinityMask(thread, oldMask);
注:此处省略了构造函数和析构函数,构造函数就简单调用reset函数,析构函数为空函数
(1) bool setOption( const String& strKey, const void* pValue )
这个函数的作用是设置mTimerMask,方法是通过GetProcessAffinityMask()获取能够执行程序的CPU标示,以mask形式给出,然后取最低标示位标示的CPU作为mTimerMask的值,作为限定CPU执行高精度计时读取。
这个函数在Ogre中并没有被调用,mTimerMask在构造函数中初始化为0,并在reset函数中有进一步的赋值。
(2)void reset()
顾名思义,重置一次计时状态,也用于第一次Timer的初始化。
这个函数主要做两件事情
1、设置mTimerMask
[cpp]
view plaincopyprint?
// Get the current process core mask
DWORD_PTR procMask;
DWORD_PTR sysMask;
GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);
...
// Find the lowest core that this process uses
if( mTimerMask == 0 )
{
mTimerMask = 1;
while( ( mTimerMask & procMask ) == 0 )
{
mTimerMask <<= 1;
}
}
可以看到使用上面提到过的方式,将CPU限定后,读取了两种度量下的时间,并将所有成员变量进行初始化。
(3)时间读取函数
剩下四个函数就是两套度量方式读取时间的函数,每套方式有读取毫秒和百分之一秒两个API。
两种度量方式也已经提到过,这里讲下各自的方法。
1、CPU tick度量
基本一句代码就可以实现,就是通过clock读取当前的tick数,减去Timer初始化的mZeroClock就可以得到毫秒数,并不需要进行CPU限定。
2、高精度计时器度量
这个度量步骤和CPU度量相似,在CPU限定的情况下,读取count数,进行相减。但是这里有个需要提及,就是在讲成员变量mLastTime时提到的错误时间调整。
通过查询Microsoft KB: Q274323(http://support.microsoft.com/kb/274323/en-us),由于芯片组的设计缺陷,QueryPerformanceCounter函数返回的结果会意外的产生跳跃的错误时间。这也就是不能单独使用高精度计时器的原因,必须有另一个计时方法来进行补偿调整。
[cpp]
view plaincopyprint?
unsigned long check = GetTickCount() - mStartTick;
signed long msecOff = (signed long)(newTicks - check);
if (msecOff < -100 || msecOff > 100)
{
LONGLONG adjust = (std::min)(msecOff * mFrequency.QuadPart / 1000, newTime - mLastTime);
mStartTime.QuadPart += adjust;
newTime -= adjust;
// Re-calculate milliseconds
newTicks = (unsigned long) (1000 * newTime / mFrequency.QuadPart);
}
// Record last time for adjust
mLastTime = newTime;
unsigned long check = GetTickCount() - mStartTick;
signed long msecOff = (signed long)(newTicks - check);
if (msecOff < -100 || msecOff > 100)
{
LONGLONG adjust = (std::min)(msecOff * mFrequency.QuadPart / 1000, newTime - mLastTime);
mStartTime.QuadPart += adjust;
newTime -= adjust;
// Re-calculate milliseconds
newTicks = (unsigned long) (1000 * newTime / mFrequency.QuadPart);
}
// Record last time for adjust
mLastTime = newTime;
这个是读取完高精度时间后需要进行的检验。
check变量是CPU tick度量方式得到的时间,然后将两种方式得到的时间相减,检查这个差值的绝对值,如果过大就需要进行调整。然后重新计算newTick就可以了。
Ogre中的Timer是很具有代表性的,笔者网上搜了下,win环境下的Timer实现基本就是这样的方式。在将来的编程过程中,如有需要,可以直接考虑拿来重用。
一个图形绘制引擎底层有很多工具类(utility),通过读基础工具类源码可以学习到不少东西。
Ogre引擎中与Timer相关的文件大致不多,大致如下(只列出头文件)
WIN32/OgreTimerImp.h
GLX/OgreTimerImp.h
OSX/OgreTimerImp.h
iPhone/OgreTimerImp.h
OgreTimer.h
其实这部分代码很少,真正意义上的头文件就是OgreTimer.h,以下是这个头文件的全部了
[cpp]
view plaincopyprint?
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
# include "WIN32/OgreTimerImp.h"
#elif (OGRE_PLATFORM == OGRE_PLATFORM_LINUX) || (OGRE_PLATFORM == OGRE_PLATFORM_SYMBIAN)
# include "GLX/OgreTimerImp.h"
#elif OGRE_PLATFORM == OGRE_PLATFORM_APPLE
# include "OSX/OgreTimerImp.h"
#elif OGRE_PLATFORM == OGRE_PLATFORM_IPHONE
# include "iPhone/OgreTimerImp.h"
#endif
[cpp] view plaincopyprint? clock_t mZeroClock; DWORD mStartTick; LONGLONG mLastTime; LARGE_INTEGER mStartTime; LARGE_INTEGER mFrequency; DWORD_PTR mTimerMask; clock_t mZeroClock; DWORD mStartTick; LONGLONG mLastTime; LARGE_INTEGER mStartTime; LARGE_INTEGER mFrequency; DWORD_PTR mTimerMask;
(1)mZeroClock
程序启动后所执行的时间。
[cpp]
view plaincopyprint?
mZeroClock = clock();
[cpp] view plaincopyprint? mStartTick = GetTickCount(); mStartTick = GetTickCount();
(3)mLastTime
在调用读取时间函数 unsigned long getMilliseconds() / unsigned long getMicroseconds() 后记录当前读取的最后时间,作用是当下一次时间读取错误时,进行调整。(关于时间读取错误后面会有展开。)
(4)mStartTime / mFrequency
用于高精度计时器时间读取 分别通过函数QueryPerformanceCounter和QueryPerformanceFrequency获得count数以及frequency。
这里需要注意的是,Timer里应用两套计时度量方法。
一套是通过使用clock()获得cpu中时钟tick,在获取时间时,使用的api如下
[cpp]
view plaincopyprint?
unsigned long getMillisecondsCPU();
unsigned long getMicrosecondsCPU();
[cpp] view plaincopyprint? unsigned long getMilliseconds(); unsigned long getMicroseconds(); unsigned long getMilliseconds(); unsigned long getMicroseconds();
在后面会讨论下这些api值得关注的细节。
(5)mTimerMask
这个变量看名字有点不明白,其实这个变量和高精度计时器读取时间有关。
查询msdn中QueryPerformanceCounter函数的注解中可以发现,这个函数调用环境如果是多处理器(CPU)的话,由于BIOS或者HAL的原因,切换到不同CPU上的线程调用后会造成不正确的结果。所以必须保证每次调用这个函数都是在同一个CPU上进行。这个mask就是将线程调用限定在某一个CPU上。
[cpp]
view plaincopyprint?
HANDLE thread = GetCurrentThread();
// Set affinity to the first core
DWORD_PTR oldMask = SetThreadAffinityMask(thread, mTimerMask);
// Get the constant frequency
QueryPerformanceFrequency(&mFrequency);
// Query the timer
QueryPerformanceCounter(&mStartTime);
mStartTick = GetTickCount();
// Reset affinity
SetThreadAffinityMask(thread, oldMask);
[cpp] view plaincopyprint? bool setOption( const String& strKey, const void* pValue ); /** Resets timer */ void reset(); /** Returns milliseconds since initialisation or last reset */ unsigned long getMilliseconds(); /** Returns microseconds since initialisation or last reset */ unsigned long getMicroseconds(); /** Returns milliseconds since initialisation or last reset, only CPU time measured */ unsigned long getMillisecondsCPU(); /** Returns microseconds since initialisation or last reset, only CPU time measured */ unsigned long getMicrosecondsCPU(); bool setOption( const String& strKey, const void* pValue ); /** Resets timer */ void reset(); /** Returns milliseconds since initialisation or last reset */ unsigned long getMilliseconds(); /** Returns microseconds since initialisation or last reset */ unsigned long getMicroseconds(); /** Returns milliseconds since initialisation or last reset, only CPU time measured */ unsigned long getMillisecondsCPU(); /** Returns microseconds since initialisation or last reset, only CPU time measured */ unsigned long getMicrosecondsCPU();
注:此处省略了构造函数和析构函数,构造函数就简单调用reset函数,析构函数为空函数
(1) bool setOption( const String& strKey, const void* pValue )
这个函数的作用是设置mTimerMask,方法是通过GetProcessAffinityMask()获取能够执行程序的CPU标示,以mask形式给出,然后取最低标示位标示的CPU作为mTimerMask的值,作为限定CPU执行高精度计时读取。
这个函数在Ogre中并没有被调用,mTimerMask在构造函数中初始化为0,并在reset函数中有进一步的赋值。
(2)void reset()
顾名思义,重置一次计时状态,也用于第一次Timer的初始化。
这个函数主要做两件事情
1、设置mTimerMask
[cpp]
view plaincopyprint?
// Get the current process core mask
DWORD_PTR procMask;
DWORD_PTR sysMask;
GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);
...
// Find the lowest core that this process uses
if( mTimerMask == 0 )
{
mTimerMask = 1;
while( ( mTimerMask & procMask ) == 0 )
{
mTimerMask <<= 1;
}
}
[cpp] view plaincopyprint? HANDLE thread = GetCurrentThread(); // Set affinity to the first core DWORD_PTR oldMask = SetThreadAffinityMask(thread, mTimerMask); // Get the constant frequency QueryPerformanceFrequency(&mFrequency); // Query the timer QueryPerformanceCounter(&mStartTime); mStartTick = GetTickCount(); // Reset affinity SetThreadAffinityMask(thread, oldMask); mLastTime = 0; mZeroClock = clock(); HANDLE thread = GetCurrentThread(); // Set affinity to the first core DWORD_PTR oldMask = SetThreadAffinityMask(thread, mTimerMask); // Get the constant frequency QueryPerformanceFrequency(&mFrequency); // Query the timer QueryPerformanceCounter(&mStartTime); mStartTick = GetTickCount(); // Reset affinity SetThreadAffinityMask(thread, oldMask); mLastTime = 0; mZeroClock = clock();
可以看到使用上面提到过的方式,将CPU限定后,读取了两种度量下的时间,并将所有成员变量进行初始化。
(3)时间读取函数
剩下四个函数就是两套度量方式读取时间的函数,每套方式有读取毫秒和百分之一秒两个API。
两种度量方式也已经提到过,这里讲下各自的方法。
1、CPU tick度量
基本一句代码就可以实现,就是通过clock读取当前的tick数,减去Timer初始化的mZeroClock就可以得到毫秒数,并不需要进行CPU限定。
2、高精度计时器度量
这个度量步骤和CPU度量相似,在CPU限定的情况下,读取count数,进行相减。但是这里有个需要提及,就是在讲成员变量mLastTime时提到的错误时间调整。
通过查询Microsoft KB: Q274323(http://support.microsoft.com/kb/274323/en-us),由于芯片组的设计缺陷,QueryPerformanceCounter函数返回的结果会意外的产生跳跃的错误时间。这也就是不能单独使用高精度计时器的原因,必须有另一个计时方法来进行补偿调整。
[cpp]
view plaincopyprint?
unsigned long check = GetTickCount() - mStartTick;
signed long msecOff = (signed long)(newTicks - check);
if (msecOff < -100 || msecOff > 100)
{
LONGLONG adjust = (std::min)(msecOff * mFrequency.QuadPart / 1000, newTime - mLastTime);
mStartTime.QuadPart += adjust;
newTime -= adjust;
// Re-calculate milliseconds
newTicks = (unsigned long) (1000 * newTime / mFrequency.QuadPart);
}
// Record last time for adjust
mLastTime = newTime;
unsigned long check = GetTickCount() - mStartTick;
signed long msecOff = (signed long)(newTicks - check);
if (msecOff < -100 || msecOff > 100)
{
LONGLONG adjust = (std::min)(msecOff * mFrequency.QuadPart / 1000, newTime - mLastTime);
mStartTime.QuadPart += adjust;
newTime -= adjust;
// Re-calculate milliseconds
newTicks = (unsigned long) (1000 * newTime / mFrequency.QuadPart);
}
// Record last time for adjust
mLastTime = newTime;
这个是读取完高精度时间后需要进行的检验。
check变量是CPU tick度量方式得到的时间,然后将两种方式得到的时间相减,检查这个差值的绝对值,如果过大就需要进行调整。然后重新计算newTick就可以了。
Ogre中的Timer是很具有代表性的,笔者网上搜了下,win环境下的Timer实现基本就是这样的方式。在将来的编程过程中,如有需要,可以直接考虑拿来重用。
相关文章推荐
- Ogre引擎源码——Timer
- Ogre引擎源码——文件管理
- Ogre引擎源码——资源之Font
- 在Ogre中使用Havok物理引擎(源码)
- Ogre引擎源码——UTFString
- Ogre引擎源码——资源之GpuProgram
- Ogre引擎源码——内存管理 .
- Ogre引擎源码——资源管理
- Ogre引擎源码——场景查询
- Ogre引擎源码——String
- Ogre引擎源码——资源之Skeleton
- Ogre引擎源码——资源之Material
- Ogre引擎源码——资源之Skeleton
- 在Ogre中使用Havok物理引擎(源码)
- Ogre引擎源码——场景查询
- Ogre引擎源码——资源之GpuProgram
- Ogre引擎源码——资源之Font
- 转: 在Ogre中使用Havok物理引擎(源码)
- Ogre引擎源码——资源之Font
- Ogre引擎源码——文件管理