这似乎是程序员之间的一个秘密,没有人愿意为此分享他们的代码。为什么呢?
我找不到可以在不使用v-sync的情况下将FPS限制在至少60的FPS限制器。
我当然希望以正确的方式做到这一点。所以我还没有自己做,因为他们都说他们花了一年时间学习fps限制器的所有技巧......
编辑:这里是我的fps限制器代码,这不是完美的,但我能做的最好,它仍然流下眼泪:
timeBeginPeriod(1);
frame_start_time = TimerGetTime();
while(!done){
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
if(msg.message == WM_QUIT){
done = 1;
}else{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}else if(active){
draw_everything();
SwapBuffers(hDC);
// fps limiter:
one_frame_limit = 1000.0f/(float)framerate_limit; // 16.666 ms
while((time_left = one_frame_limit-(TimerGetTime()-frame_start_time)) > 0){
if(time_left >= 6.0f){
Sleep((int)(time_left/6.0f));
}else{
Sleep(0); // sleep less than 1ms
}
}
frame_start_time = TimerGetTime();
}
}
EDIT2:继承我的第二次尝试,使用了建议的等待计时器:
float one_frame_limit = 1000.0f/(float)framerate_limit;
float time_left = one_frame_limit-(TimerGetTime()-frame_start_time); // 4.7432ms
liDueTime.QuadPart = -(LONGLONG)(time_left*10000.0f);
if(SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0)){
WaitForSingleObject(hTimer, INFINITE);
}
while((time_left = one_frame_limit-(TimerGetTime()-frame_start_time)) > 0.003f){
Sleep(0);
}
frame_start_time = TimerGetTime();
我觉得效果更好。但仍然撕裂......请注意,我在那里添加了while循环,因为它比没有它时更容易撕裂。
-
另一个问题:这个初始化安全吗?:
HANDLE hTimer = NULL;
LARGE_INTEGER liDueTime;
liDueTime.QuadPart = -100000LL; // testing timer: wait for 10ms
hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
if(hTimer == NULL){
waitable_timer_supported = 0;
}else if(!SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0)){
waitable_timer_supported = 0;
}else if(WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0){
waitable_timer_supported = 0;
}
即使支持等待计时器,我担心最后2次检查可能会失败...到目前为止它还没有失败。这是检查其支持的正确方法吗?
答案 0 :(得分:2)
将系统计时器设置为每16.6666ms关闭一次并对该事件进行渲染。更好的是,在定时器上进行渲染和翻页并开始渲染下一帧。这不是一个大秘密,你可以使用高分辨率计时器在Windows中进行。
一旦你开始工作,你会看到撕裂并决定等待v-sync而不是任意计时器。
答案 1 :(得分:1)
在DirectX中,您可以简单地将一个参数传递给Present(),该参数将使当前线程空闲,直到它返回,这将使FPS达到刷新率的某个倍数。我已经使用过它,并且可以说我的系统通过使用这个系统实现了大约1%的CPU使用率。您当前的代码基本上是错误的,因为Sleep是最不可靠的函数,简而言之,您几乎不应该使用非零参数。
您可以尝试使用QueryPerformanceCounter和Sleep()。
LARGE_INTEGER freq, begin, end;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&begin);
QueryPerformanceCounter(&end);
const unsigned __int64 waitinticks = static_cast<unsigned __int64>(static_cast<double>(1000)/60));
while(end.QuadPart - begin.QuadPart < waitinticks) {
Sleep(0); // If someone else wants to run, let them.
QueryPerformanceCounter(&end);
}
最好的办法仍然是使用等待计时器 - http://msdn.microsoft.com/en-us/library/ms682492(v=VS.85).aspx。这将使你的线程休眠,直到计时器提醒它。
答案 2 :(得分:1)
请原谅我,如果有一些魔法让我错了,但就我所知,VSync和撕裂是相辅相成的。无论你的帧限制是什么,如果帧缓冲区在帧的中间翻转,你仍然会撕裂。这就是VSync的用途。
对于帧限制代码,这里有一些用于进行生产者/消费者时间步进的技术:
http://gafferongames.com/game-physics/fix-your-timestep/
他的理由似乎集中在物理学上,但它也非常适用于帧限制。