修复了VSync打开时出现的时间步长停顿问题
Fixed timestep stuttering with VSync on
在我实现的2D OpenGL引擎中,我有一个固定的时间步长,如著名的修复时间步长文章中所述,以及混合。
我有一个垂直移动的测试对象(y轴)。动作中有口吃(预先编程的动作,而不是来自用户输入)。这意味着对象不能在屏幕上平滑移动。
请参阅我正在链接的未压缩视频:链接
游戏帧速率保持在60fps(从Nvidia驱动程序打开Vsync)
游戏逻辑以我设定的每秒20次更新/滴答的固定速度更新。这很正常。对象每次更新移动50个像素。
然而,屏幕上的动作却非常断断续续。
编辑:我在上面一帧一帧的录制视频中注意到,口吃是由一帧显示两次引起的。
编辑2:在任务管理器中将应用程序优先级设置为"实时",完全消除了卡顿!然而,这显然不是一个解决方案。
下面是在VSync关闭的情况下,对象y在不同时间的移动增量第一列是自上一帧以来经过的时间,单位为微秒(不包括4403
)第二列是自上一帧以来对象在y轴上的移动。实际上,物体每秒移动1000个像素,下面的日志证实了这一点
time since last frame: 4403 ypos delta since last frame: 4.403015
time since last frame: 3807 ypos delta since last frame: 3.806976
time since last frame: 3716 ypos delta since last frame: 3.716003
time since last frame: 3859 ypos delta since last frame: 3.859009
time since last frame: 4398 ypos delta since last frame: 4.398010
time since last frame: 8961 ypos delta since last frame: 8.960999
time since last frame: 7871 ypos delta since last frame: 7.871002
time since last frame: 3985 ypos delta since last frame: 3.984985
time since last frame: 3684 ypos delta since last frame: 3.684021
现在打开了垂直同步
time since last frame: 17629 ypos delta since last frame: 17.628906
time since last frame: 15688 ypos delta since last frame: 15.687988
time since last frame: 16641 ypos delta since last frame: 16.641113
time since last frame: 16657 ypos delta since last frame: 16.656738
time since last frame: 16715 ypos delta since last frame: 16.715332
time since last frame: 16663 ypos delta since last frame: 16.663086
time since last frame: 16666 ypos delta since last frame: 16.665771
time since last frame: 16704 ypos delta since last frame: 16.704102
time since last frame: 16626 ypos delta since last frame: 16.625732
我会说他们看起来不错。
这几天来一直让我抓狂,我错过了什么?
下面是我在循环中调用的Frame函数:
void Frame()
{
static sf::Time t;
static const double ticksPerSecond = 20;
static uint64_t stepSizeMicro = 1000000 / ticksPerSecond; // microseconds
static sf::Time accumulator = sf::seconds(0);
gElapsedTotal = gClock.getElapsedTime();
sf::Time elapsedSinceLastFrame = gElapsedTotal - gLastFrameTime;
gLastFrameTime = gElapsedTotal;
if (elapsedSinceLastFrame.asMicroseconds() > 250000 )
elapsedSinceLastFrame = sf::microseconds(250000);
accumulator += elapsedSinceLastFrame;
while (accumulator.asMicroseconds() >= stepSizeMicro)
{
Update(stepSizeMicro / 1000000.f);
gGameTime += sf::microseconds(stepSizeMicro);
accumulator -= sf::microseconds(stepSizeMicro);
}
uint64_t blendMicro = accumulator.asMicroseconds() / stepSizeMicro;
float blend = accumulator.asMicroseconds() / (float) stepSizeMicro;
if (rand() % 200 == 0) Trace("blend: %f", blend);
CWorld::GetInstance()->Draw(blend);
}
评论中要求的更多信息:
在全屏1920x1080和窗口模式1600x900 中都会出现卡顿
设置是一个简单的SFML项目。我不知道它在渲染纹理矩形时是否在内部使用VBO/VAO
没有在我的电脑上做任何其他事情。请记住,这个问题也发生在其他计算机上,这不仅仅是我的装备
正在主显示器上运行。显示器并没有什么区别。该问题在全屏和窗口模式下都会发生。
我已经介绍了自己的代码。问题是我的代码中有一个区域由于缓存未命中而偶尔出现性能峰值。这导致我的循环耗时超过16.6666毫秒,这是在60Hz下平滑显示所需的最长时间。这只是一帧,偶尔一帧。那帧导致了口吃。代码逻辑本身是正确的,这被证明是一个性能问题。
为了将来的参考,希望这能帮助其他人,我是如何调试的,我放了一个
if ( timeSinceLastFrame > 16000 ) // microseconds
{
Trace("Slow frame detected");
DisplayProfilingInformation();
}
在我的帧代码中。当if
被触发时,它会显示上一帧中函数的评测统计信息,以查看上一帧使用时间最长的函数。因此,我能够将性能缺陷精确定位为不适合其使用的结构。一个大而讨厌的映射映射,它产生了大量缓存未命中,偶尔性能会飙升。
我希望这能帮助未来不幸的灵魂。
您似乎没有将60Hz帧循环与GPU的60Hz VSync同步。是的,您已经在Nvidia中启用了Vsync,但这只会导致Nvidia使用在Vsync上交换的后缓冲区。
您需要将交换间隔设置为1,并执行glFinish()
以等待Vsync。
这是一个棘手的问题,但从上面来看,这似乎不是"帧速率"问题,而是"动画"代码中的某个地方。另一个观察结果是行"Update(stepSizeMicro/1000000.f);"。除以100000000.f可能意味着由于浮点数字位分辨率的限制,您正在失去分辨率,所以四舍五入可能是您的杀手?
- 如何在不到O(N)的时间内解决这个问题?
- asio::read() 需要很长时间,使用 asio::write 没有问题
- 我们能否降低最大赢家问题的时间复杂度?
- 在 MacOS 上C++:显示日期和时间问题
- 解决这个问题的时间复杂性是多少
- 图问题:找出两个节点是否在每个节点的O(1)时间和O(2)存储中共享同一分支
- 为什么我的代码在"decrease to zero"问题中被时间超过了
- std::chrono 的问题:重置时间
- 递归回文问题的时间复杂度,C++
- time_t的时钟周期和获取时间问题
- 简单的最大除数问题 c++ 实现抛出时间限制已超出
- 提升,从字符串问题中获得时间
- Linux 测量时间问题! std::chrono, QueryPerformanceCounter, clock_ge
- 扩展中的日期时间对象方法C++问题
- 尝试构造包装器测量函数调用时间时出现问题
- 一个足够大的问题大小 C++ 需要 0 个运行时间
- 我的开关菜单在一段时间循环中出现问题
- 如何降低此问题中的时间复杂度
- 从文件读取/写入时出现内存和时间问题
- 执行Pong时Allegro中的时间问题