Direct3D 10学习笔记(二)——计时器
本篇将简单整理Direct3D 10的计时器实现,具体内容参照《 Introduction to 3D Game Programming with DirectX 10》(中文版有汤毅翻译的电子书《DirectX 10 3D游戏编程入门》)。
1.高精度性能计数器
Direct3D10使用高精度性能计数器(精度达微秒级)实现精确时间测量,为了调用下面介绍的两个Win32计数器API,需要添加包含语句“#include <windows.h>”
BOOL QueryPerformanceCounter(LARGE_INTEGER * lpPerformanceCount);
lpPerformanceCount:参数指向计数器的值,即该参数将用于返回当前的计时,为一个64位整型。其计时单位称为计数,即所谓的“滴答”。
由于性能计数器与系统有关,需要知道每秒滴答声的个数,即滴答的频率,然后用前后两次计时的差值去除以频率才能得到这段时间一共走过了多少秒,故还需要计算计数频率。
BOOL QueryPerformanceFrequency(LARGE_INTEGER * lpFrequency);
lpFrequency:参数指向计数器频率的值,即该参数将用于返回系统计数频率。
计时示例:
long long preTime,currTime,valueInSecs,valueInCounts;
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&preTime));
long long CountsPerSec;
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&CountsPerSec));
//计算出转换因子SecondsPerCount,避免重复进行除法计算
double SecondsPerCount = 1.0f / static_cast<double>(countsPerSec);
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&currTime));
//计算两次计时差值
valueInCounts = currTime - preTime;
//将两次计时差值转换成以秒为单位
valueInSecs = valueInCounts * SecondsPerCount;
2.设计游戏计时器类(GameTimer类)
类的头文件为GameTimer.h,具体实现放在GameTimer.cpp中。
一个计时器,需要的功能有初始化、开始计时、停止计时、恢复计时、计算两次计时时间差以及计算总计时时长。
初始化由构造函数完成,该过程中可将计时频率、计时转换因子等必要的值先求出来。需要添加的成员有:每次计时所需时间SecondsPerCount,初始计时BaseTime等。
计时过程中可一并计算两次计时的时间差。需要添加的成员有:前一次计时PreTime,当前计时CurrTime,两次计时时间间隔DeltaTime。
停止计时需要用一个状态量来阻止计时函数的执行,同时还应记录停止时刻的CurrTime,则需要添加的成员有:StopTime,StoppedState。
恢复计时需要额外计算暂停了多长时间,则需要添加的成员有:PauseTime。
同时,还需要添加一些返回计时的函数以及重置函数。
所以其头文件可以设计如下:
GameTimer.h #include <windows.h> class GamerTimer
{
public:
GamerTimer();
~GamerTimer(); FLOAT getGameTime()const;
FLOAT getDeltaTime()const;
VOID reset();
VOID start();
VOID stop();
VOID tick(); private:
DOUBLE m_dSecondsPerCount;
DOUBLE m_dDeltaTime; LONGLONG m_llBaseTime;
LONGLONG m_llPauseTime;
LONGLONG m_llStopTime;
LONGLONG m_llPreTime;
LONGLONG m_llCurrTime; BOOL m_bStopped;
};
下面逐个讲解类方法的实现:
GamerTimer::GamerTimer()
:m_dSecondsPerCount(0.0f), m_dDeltaTime(0.0f), m_llBaseTime(),
m_llPauseTime(), m_llStopTime(), m_llPreTime(), m_llCurrTime(),
m_bStopped(FALSE)
{
LONGLONG countsPerSec;
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&countsPerSec));
m_dSecondsPerCount = 1.0f / static_cast<DOUBLE>(countsPerSec);
}
在这个构造函数中,将所有数据初始化完毕后,还需计算出前面重复提到的转换因子,当计时单位由计数转换为秒时,就需要乘以这个转换因子。获取频率时用了reinterpret_cast进行指针类型的强制转换,不能用static_cast等,这属于C++的内容,今后不再累赘。
VOID GamerTimer::tick()
{
//计时停止则重置时间间隔
if (m_bStopped)
{
m_dDeltaTime = 0.0f;
return;
} //否则,计算时间间隔 //获取当前帧时间计数
LONGLONG currTime;
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&currTime));
m_llCurrTime = currTime; //计算当前帧与上一帧时间差,单位为秒
m_dDeltaTime = (m_llCurrTime - m_llPreTime)*m_dSecondsPerCount; //为下一帧计时做准备
m_llPreTime = m_llCurrTime; //负值处理,当处理器进入省电模式或切换到另一处理器,时间间隔可能为负
if (m_dDeltaTime < 0.0f)
{
m_dDeltaTime = 0.0f;
} return;
}
大部分内容都在注释里呈现了,值得留意的是,该方法的负值处理部分,提到了切换到另一处理器,意味着我们可能会使用多线程计时,只要保证计时器在子线程内一直正常运作,条件是该子线程未结束或未被阻塞/锁上(当然也还是有办法通过使用额外的计时来消除这段误差)。C++11/14标准提供了很方便的线程库,所以将计时器分离出来或许是个不错的尝试。
FLOAT GamerTimer::getDeltaTime()const
{
return static_cast<FLOAT>(m_dDeltaTime);
}
返回两次计时的时间差,单位为秒。
VOID GamerTimer::reset()
{
LONGLONG currTime;
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&currTime)); m_llBaseTime = currTime;
m_llPreTime = currTime;
m_llStopTime = ;
m_bStopped = FALSE; return;
}
用于重置计时器,而不必销毁原来的计时器再构造一个。
VOID GamerTimer::stop()
{
//如果已停止,不做任何操作;否则停止计时
if (!m_bStopped)
{
LONGLONG currTime;
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&currTime)); //记录停止时刻,更新状态
m_llStopTime = currTime;
m_bStopped = TRUE;
} return;
}
停止/暂停计时的方法,记录调用时刻,并将停止状态设置为TRUE。
VOID GamerTimer::start()
{
LONGLONG startTime;
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&startTime)); //从初始状态开始计时或从暂停状态恢复计时
if (m_bStopped)
{
//计算暂停时长
m_llPauseTime = startTime - m_llStopTime; //由于中途暂停,上一帧时刻已无效,需更新到恢复计时时刻
m_llPreTime = startTime; //更新停止状态
m_llStopTime = ;
m_bStopped = FALSE;
} return;
}
开始/恢复计时的方法,主要注意需计算出暂停的时长。该方法将开始计时和恢复计时合并,原因是两种计时方法的前一时刻的停止状态m_bStopped均为FALSE,故它们能有相同的行为。
FLOAT GamerTimer::getGameTime()const
{
//如果处于停止状态
if (m_bStopped)
{
return static_cast<FLOAT>((m_bStopped - m_llBaseTime - m_llPauseTime)*m_dSecondsPerCount);
}
//如果仍在计时
else
{
return static_cast<FLOAT>((m_llCurrTime - m_llBaseTime - m_llPauseTime)*m_dSecondsPerCount);
}
}
计算出总计时时长的方法。计算时注意应该剔除暂停总共花去的时间。
完整的实现代码如下:
GameTimer.cpp #include "GameTimer.h" GamerTimer::GamerTimer()
:m_dSecondsPerCount(0.0f), m_dDeltaTime(0.0f), m_llBaseTime(),
m_llPauseTime(), m_llStopTime(), m_llPreTime(), m_llCurrTime(),
m_bStopped(FALSE)
{
LONGLONG countsPerSec;
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&countsPerSec));
m_dSecondsPerCount = 1.0f / static_cast<DOUBLE>(countsPerSec);
} VOID GamerTimer::tick()
{
//计时停止则重置时间间隔
if (m_bStopped)
{
m_dDeltaTime = 0.0f;
return;
} //否则,计算时间间隔 //获取当前帧时间计数
LONGLONG currTime;
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&currTime));
m_llCurrTime = currTime; //计算当前帧与上一帧时间差,单位为秒
m_dDeltaTime = (m_llCurrTime - m_llPreTime)*m_dSecondsPerCount; //为下一帧计时做准备
m_llPreTime = m_llCurrTime; //负值处理,当处理器进入省电模式或切换到另一处理器,时间间隔可能为负
if (m_dDeltaTime < 0.0f)
{
m_dDeltaTime = 0.0f;
} return;
} FLOAT GamerTimer::getDeltaTime()const
{
return static_cast<FLOAT>(m_dDeltaTime);
} VOID GamerTimer::reset()
{
LONGLONG currTime;
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&currTime)); m_llBaseTime = currTime;
m_llPreTime = currTime;
m_llStopTime = ;
m_bStopped = FALSE; return;
} VOID GamerTimer::stop()
{
//如果已停止,不做任何操作;否则停止计时
if (!m_bStopped)
{
LONGLONG currTime;
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&currTime)); //记录停止时刻,更新状态
m_llStopTime = currTime;
m_bStopped = TRUE;
} return;
} VOID GamerTimer::start()
{
LONGLONG startTime;
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&startTime)); //从初始状态开始计时或从暂停状态恢复计时
if (m_bStopped)
{
//计算暂停时长
m_llPauseTime = startTime - m_llStopTime; //由于中途暂停,上一帧时刻已无效,需更新到恢复计时时刻
m_llPreTime = startTime; //更新停止状态
m_llStopTime = ;
m_bStopped = FALSE;
} return;
} FLOAT GamerTimer::getGameTime()const
{
//如果处于停止状态
if (m_bStopped)
{
return static_cast<FLOAT>((m_bStopped - m_llBaseTime - m_llPauseTime)*m_dSecondsPerCount);
}
//如果仍在计时
else
{
return static_cast<FLOAT>((m_llCurrTime - m_llBaseTime - m_llPauseTime)*m_dSecondsPerCount);
}
} GamerTimer::~GamerTimer(){}
Direct3D 10学习笔记(二)——计时器的更多相关文章
- Direct3D 10学习笔记(三)——文本输出
本篇将简单整理Direct3D 10的文本输出的实现,具体内容参照< Introduction to 3D Game Programming with DirectX 10>(中文版有汤毅 ...
- Direct3D 10学习笔记(四)——Windows编程
本篇将简单整理基本的Windows应用程序的实现,并作为创建Direct3D 10应用程序的铺垫.具体内容参照< Introduction to 3D Game Programming with ...
- Direct3D 10学习笔记(一)——初始化
本篇将简单整理Direct3D 10的初始化,具体内容参照< Introduction to 3D Game Programming with DirectX 10>(中文版有汤毅翻译的电 ...
- WPF的Binding学习笔记(二)
原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...
- java之jvm学习笔记二(类装载器的体系结构)
java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...
- NumPy学习笔记 二
NumPy学习笔记 二 <NumPy学习笔记>系列将记录学习NumPy过程中的动手笔记,前期的参考书是<Python数据分析基础教程 NumPy学习指南>第二版.<数学分 ...
- Learning ROS for Robotics Programming Second Edition学习笔记(二) indigo tools
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...
- Redis学习笔记二 (BitMap算法分析与BitCount语法)
Redis学习笔记二 一.BitMap是什么 就是通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身.我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省 ...
- Django学习笔记二
Django学习笔记二 模型类,字段,选项,查询,关联,聚合函数,管理器, 一 字段属性和选项 1.1 模型类属性命名限制 1)不能是python的保留关键字. 2)不允许使用连续的下划线,这是由dj ...
随机推荐
- Python笔记总结week6
关于创建.调用模块 1.我们创建一个模块commons.py, 并且在文件中写以下三个函数: def login(): print('登录') def logout(): print('退出') d ...
- Leetcode 详解(Valid Number)
Validate if a given string is numeric. Some examples:"0" => true" 0.1 " => ...
- 【Python】【学习笔记】1.快速入门
1.软件安装 从官网下载相应版本的安装包,一般不大. https://www.python.org/ 安装一路默认即可 2. 参考教程:快速入门:十分钟学会Python 本文的内容介于教程(Totur ...
- typeof(self) 的作用
block对于其变量都会形成strong reference,对于self也会形成strong reference ,而如果self本身对block也是 strong reference 的话,就会形 ...
- ARC和MRC混编
在targets的build phases选项下Compile Sources下选择要不使用arc编译的文件,双击它,输入 -fno-objc-arc 即可 MRC工程中也可以使用ARC的类.方法如下 ...
- java 排序
class Employee { private String name; private String id; private String salary; public static void m ...
- Web——在淘宝搜索到看到商品
[摘自]http://blog.renren.com/blog/254459622/799372165 浏览器首先查询DNS服务器,将www.taobao.com转换成ip地址.负载均衡的第一步,将你 ...
- 浅谈c语言结构体
对于很多非计算机专业来说,c语言课程基本上指针都不怎么讲,更别说后面的结构体了.这造成很多学生对结构体的不熟悉.这里我就浅谈一下我对结构体的认识. 结构体,就是我们自己定义出一种新的类型,定义好之后, ...
- js拖拽效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Win7 64位ORACLE取数字乱码的解决
参见网址http://www.2cto.com/database/201304/201767.html 首先是PLSQL DEVELOPER 直接报错 NLS_LANG 错误 第一步是在命令行下测试 ...