gpt4 book ai didi

c++ - 直线运动卡顿

转载 作者:可可西里 更新时间:2023-11-01 18:28:16 31 4
gpt4 key购买 nike

我使用 ID3DXSpriteDirect3D9 中创建了简单的、帧无关的、可变时间步长的线性运动。大多数用户不会注意到它,但在某些(包括我的)计算机上,它经常发生,有时会出现很多断断续续的情况。

  • 在启用和禁用 VSync 时会出现卡顿现象。

  • 我发现 OpenGL 渲染器中也会发生同样的情况。

  • 这不是 float 问题。

  • 似乎问题只存在于AERO Transparent Glass 窗口模式(在全屏、无边框全屏窗口或禁用 aero 时很好或至少不太明显),当窗口丢失时更糟专注。

编辑:

帧增量时间不会超出 16 .. 17 毫秒的范围,即使发生卡顿也是如此。

似乎我的帧增量时间测量日志代码被窃听了。我现在修好了。

  • 通常启用 VSync 的帧渲染时间为 17 毫秒,但有时(可能发生断断续续时)它会跳到 25-30 毫秒。

(我只在应用程序退出时转储日志一次,而不是在运行、渲染时转储,所以它不影响性能)

    device->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 255, 255, 255), 0, 0);

device->BeginScene();

sprite->Begin(D3DXSPRITE_ALPHABLEND);

QueryPerformanceCounter(&counter);

float time = counter.QuadPart / (float) frequency.QuadPart;

float deltaTime = time - currentTime;

currentTime = time;

position.x += velocity * deltaTime;

if (position.x > 640)
velocity = -250;
else if (position.x < 0)
velocity = 250;

position.x = (int) position.x;

sprite->Draw(texture, 0, 0, &position, D3DCOLOR_ARGB(255, 255, 255, 255));

sprite->End();

device->EndScene();

device->Present(0, 0, 0, 0);

由于 Eduard Wirch 和 Ben Voigt 修复了定时器(虽然它没有解决最初的问题)

float time()
{
static LARGE_INTEGER start = {0};
static LARGE_INTEGER frequency;

if (start.QuadPart == 0)
{
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&start);
}

LARGE_INTEGER counter;

QueryPerformanceCounter(&counter);

return (float) ((counter.QuadPart - start.QuadPart) / (double) frequency.QuadPart);
}

编辑#2:

到目前为止,我尝试了三种更新方法:

1)可变时间步

    x += velocity * deltaTime;

2)固定时间步

    x += 4;

3)固定时间步长+插值

    accumulator += deltaTime;

float updateTime = 0.001f;

while (accumulator > updateTime)
{
previousX = x;

x += velocity * updateTime;

accumulator -= updateTime;
}

float alpha = accumulator / updateTime;

float interpolatedX = x * alpha + previousX * (1 - alpha);

所有方法的工作原理几乎相同,固定时间步长看起来更好,但依赖于帧率并不是一个很好的选择,它也不能完全解决问题(仍然很少时不时地跳跃(断断续续))。

到目前为止,禁用 AERO Transparent Glass 或进入全屏只是显着的积极变化。

我正在使用 NVIDIA 最新的驱动程序 GeForce 332.21 驱动程序Windows 7 x64 Ultimate

最佳答案

部分解决方案是一个简单的精度数据类型问题。将速度计算换成一个常量,你会看到极其流畅的运动。分析计算表明您正在存储 QueryPerformanceCounter() 的结果在漂浮物内。 QueryPerformanceCounter()返回一个在我的电脑上看起来像这样的数字:724032629776 .这个数字至少需要存储 5 个字节。 float怎么过使用 4 个字节(实际数字只有 24 位)来存储值。因此,当您转换 QueryPerformanceCounter() 的结果时,精度会丢失至 float .有时这会导致 deltaTime零导致口吃。

这部分解释了为什么有些用户没有遇到此问题。这完全取决于 QueryPerformanceCounter() 的结果。确实适合 float .

这部分问题的解决方法是:使用double (或者按照 Ben Voigt 的建议:存储初始性能计数器,并在转换为 float 之前从新值中减去它。这至少会给你更多的空间,但最终可能会再次达到 float 分辨率限制,当应用程序运行时间较长(取决于性能计数器的增长速度)。

修复此问题后,卡顿现象减轻了很多,但并未完全消失。分析运行时行为表明,有时会跳过一个帧。应用程序 GPU 命令缓冲区由 Present 刷新但是当前命令保留在应用程序上下文队列中,直到下一个垂直同步(即使 Present 在垂直同步(14 毫秒)之前很久就被调用了)。进一步的分析表明,后台进程 (f.lux) 告诉系统偶尔设置 Gamma 斜坡。此命令要求整个 GPU 队列在执行之前干涸。可能是为了避免副作用。这个 GPU 刷新是在“present”命令被移动到 GPU 队列之前开始的。系统阻塞视频调度,直到 GPU 耗尽。这一直持续到下一个 vsync。所以当前数据包直到下一帧才被移动到 GPU 队列。可见的效果:口吃。

您不太可能也在计算机上运行 f.lux。但您可能正在经历类似的后台干预。您需要自己在系统上寻找问题的根源。我写了一篇关于如何诊断跳帧的博文:Diagnose frame skips and stutter in DirectX applications .您还会在那里找到将 f.lux 诊断为罪魁祸首的整个故事。

但是,即使您找到了跳帧的根源,我怀疑在启用 dwm 窗口合成时您能否达到稳定的 60fps。原因是,您没有直接在屏幕上绘图。但是你绘制到 dwm 的共享表面。由于它是一个共享资源,它可以被其他人锁定任意时间,使您无法为您的应用程序保持稳定的帧速率。如果你真的需要一个稳定的帧率,全屏,或者禁用窗口合成(在 Windows 7 上。Windows 8 不允许禁用窗口合成):

#include <dwmapi.h>
...
HRESULT hr = DwmEnableComposition(DWM_EC_DISABLECOMPOSITION);
if (!SUCCEEDED(hr)) {
// log message or react in a different way
}

关于c++ - 直线运动卡顿,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21356302/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com