草庐IT

c++ - 直线运动卡顿

coder 2023-11-17 原文

我使用 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/

有关c++ - 直线运动卡顿的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  3. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  4. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  5. ruby - 在 Ruby 中,垂直线是什么? - 2

    1.upto(9){|x|printx}为什么这行不通?{printx|x}}y呢? 最佳答案 它用于传递给您的block的参数。即在您的示例中,upto将使用1到9中的每个数字调用您的block,当前值可作为x获得。block参数可以有任何名称,就像方法参数一样。例如1.upto(9){|num|putsnum是有效的。就像一个方法的参数一样,一个block也可以有多个参数。例如hash.each_pair{|key,value|puts"#{key}is#{value}"} 关于ru

  6. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

  7. += 的 Ruby 方法 - 2

    有没有办法让Ruby能够做这样的事情?classPlane@moved=0@x=0defx+=(v)#thisiserror@x+=v@moved+=1enddefto_s"moved#{@moved}times,currentxis#{@x}"endendplane=Plane.newplane.x+=5plane.x+=10putsplane.to_s#moved2times,currentxis15 最佳答案 您不能在Ruby中覆盖复合赋值运算符。任务在内部处理。您应该覆盖+,而不是+=。plane.a+=b与plane.a=

  8. ruby - Sinatra + Heroku + Datamapper 使用 dm-sqlite-adapter 部署问题 - 2

    出于某种原因,heroku尝试要求dm-sqlite-adapter,即使它应该在这里使用Postgres。请注意,这发生在我打开任何URL时-而不是在gitpush本身期间。我构建了一个默认的Facebook应用程序。gem文件:source:gemcuttergem"foreman"gem"sinatra"gem"mogli"gem"json"gem"httparty"gem"thin"gem"data_mapper"gem"heroku"group:productiondogem"pg"gem"dm-postgres-adapter"endgroup:development,:t

  9. ruby - Ruby 中字符串运算符 + 和 << 的区别 - 2

    我是Ruby和这个网站的新手。下面两个函数是不同的,一个在函数外修改变量,一个不修改。defm1(x)x我想确保我理解正确-当调用m1时,对str的引用被复制并传递给将其视为x的函数。运算符当调用m2时,对str的引用被复制并传递给将其视为x的函数。运算符+创建一个新字符串,赋值x=x+"4"只是将x重定向到新字符串,而原始str变量保持不变。对吧?谢谢 最佳答案 String#+::str+other_str→new_strConcatenation—ReturnsanewStringcontainingother_strconc

  10. ruby - rails 3.2.2(或 3.2.1)+ Postgresql 9.1.3 + Ubuntu 11.10 连接错误 - 2

    我正在使用PostgreSQL9.1.3(x86_64-pc-linux-gnu上的PostgreSQL9.1.3,由gcc-4.6.real(Ubuntu/Linaro4.6.1-9ubuntu3)4.6.1,64位编译)和在ubuntu11.10上运行3.2.2或3.2.1。现在,我可以使用以下命令连接PostgreSQLsupostgres输入密码我可以看到postgres=#我将以下详细信息放在我的config/database.yml中并执行“railsdb”,它工作正常。开发:adapter:postgresqlencoding:utf8reconnect:falsedat

随机推荐