我正在尝试找到一种可靠的方法来准确设置我希望我的 OpenGL 应用程序在屏幕上呈现多少 FPS。我可以通过休眠 1000/fps 毫秒来在某种程度上做到这一点,但这并没有考虑渲染所需的时间。 将 fps 限制到所需数量的最一致的方法是什么?
您可以在 opengl 中使用 wglSwapIntervalEXT 同步到 vblank。它不是很好的代码,但它确实有效。
http://www.gamedev.net/topic/360862-wglswapintervalext/#entry3371062
bool WGLExtensionSupported(const char *extension_name) {
PFNWGLGETEXTENSIONSSTRINGEXTPROC _wglGetExtensionsStringEXT = NULL;
_wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
if (strstr(_wglGetExtensionsStringEXT(), extension_name) == NULL) {
return false;
}
return true;
}
和
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = NULL;
if (WGLExtensionSupported("WGL_EXT_swap_control"))
{
// Extension is supported, init pointers.
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
// this is another function from WGL_EXT_swap_control extension
wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT");
}
由于 OpenGL 只是一个低级图形 API,因此您不会直接在 OpenGL 中找到类似的东西。
不过,我觉得你的逻辑有点问题。而不是以下内容:
你应该这样做:
如果你只是等待你应该等待的数量,那么你最终应该非常接近每秒 60(或任何你想要的)帧。
不要使用睡眠。如果你这样做,那么你的应用程序的其余部分必须等待他们完成。
相反,跟踪已经过去了多少时间并仅在达到 1000/fps 时才渲染。如果没有满足计时器,请跳过它并做其他事情。
在单线程环境中,很难确保您以精确的 1000/fps 进行绘制,除非那绝对是您唯一要做的事情。一种更通用和更可靠的方法是在单独的线程中完成所有渲染并在计时器上启动/运行该线程。这是一个更复杂的问题,但会让你最接近你的要求。
此外,跟踪发布渲染需要多长时间将有助于动态调整渲染内容的时间。
static unsigned int render_time=0;
now = timegettime();
elapsed_time = last_render_time - now - render_time;
if ( elapsed_time > 1000/fps ){
render(){
start_render = timegettime();
issue rendering commands...
end_render = timegettime();
render_time = end_render - start_render;
}
last_render_time = now;
}
OpenGL 本身没有任何允许限制帧率的功能。时期。
然而,在现代 GPU 上有很多功能,包括帧速率、帧预测等。John Carmack 的问题是他推动了一些功能可用。还有 NVidia 的自适应同步。
这对你意味着什么?将其留给 GPU。假设绘图是完全不可预测的(当你只坚持使用 OpenGL 时应该如此),你自己为事件计时,并将逻辑更新(例如物理)与绘图分开。这样用户将能够从所有这些先进技术中受益,而您不必再为此担心。
一个简单的方法是使用 GLUT。这段代码大概可以完成这项工作。
static int redisplay_interval;
void timer(int) {
glutPostRedisplay();
glutTimerFunc(redisplay_interval, timer, 0);
}
void setFPS(int fps)
{
redisplay_interval = 1000 / fps;
glutTimerFunc(redisplay_interval, timer, 0);
}
在绘制和调用交换缓冲区之后放置它:
//calculate time taken to render last frame (and assume the next will be similar)
thisTime = getElapsedTimeOfChoice(); //the higher resolution this is the better
deltaTime = thisTime - lastTime;
lastTime = thisTime;
//limit framerate by sleeping. a sleep call is never really that accurate
if (minFrameTime > 0)
{
sleepTime += minFrameTime - deltaTime; //add difference to desired deltaTime
sleepTime = max(sleepTime, 0); //negative sleeping won't make it go faster :(
sleepFunctionOfChoice(sleepTime);
}
如果你想要 60fps,
minFrameTime = 1.0/60.0
(假设时间以秒为单位)。
这不会给你 vsync,但意味着你的应用程序不会失控,这会影响物理计算(如果它们不是固定步长)、动画等。请记住 process input 睡觉后!我尝试过尝试平均帧时间,但到目前为止效果最好。
对于
getElapsedTimeOfChoice()
,我用的是这里提到的,也就是
另一个想法是使用 WaitableTimers(如果可能,例如在 Windows 上)
基本理念:
while (true)
{
SetWaitableTimer(myTimer, desired_frame_duration, ...);
PeekMsg(...)
if (quit....) break;
if (msg)
handle message;
else
{
Render();
SwapBuffers();
}
WaitForSingleObject(myTimer);
}
更多信息:如何限制fps信息
这样做:
auto startTime = chrono::system_clock::now();
while (!glfwWindowShouldClose(window))
{
double deltaSec = std::chrono::duration<double>(chrono::system_clock::now() - startTime).count();
if (deltaSec >= 0.02) {
startTime = chrono::system_clock::now();
......
}
}