直线运动卡顿

2024-01-27

我创建了简单的、与帧无关的、可变时间步长的线性运动Direct3D9 using ID3DXSprite。大多数用户无法注意到它,但在某些(包括我的)计算机上它经常发生,有时甚至会很卡顿。

  • 口吃发生于VSync启用和禁用。

  • 我发现同样的情况发生在OpenGL渲染器。

  • 它不是浮点问题。

  • 似乎问题只存在于AERO Transparent Glass窗口模式(在全屏、无边框全屏窗口或禁用 Aero 的情况下很好或至少不太明显),当窗口失去焦点时更糟。

EDIT:

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

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

  • 通常启用垂直同步的帧渲染时间为 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 Witch 和 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);
}

EDIT #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 Driver and Windows 7 x64 Ultimate.


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

这部分解释了为什么某些用户没有遇到此问题。这一切都取决于结果是否QueryPerformanceCounter()确实适合float.

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

解决这个问题后,卡顿现象减少了很多,但并没有完全消失。分析运行时行为表明,偶尔会跳过一帧。应用程序 GPU 命令缓冲区被刷新Present但当前命令保留在应用程序上下文队列中,直到下一个垂直同步(即使Present在 vsync (14ms) 之前很久就被调用了。进一步分析表明,后台进程 (f.lux) 告诉系统偶尔设置伽玛斜坡。该命令要求完整的 GPU 队列在执行之前耗尽。可能是为了避免副作用。此 GPU 刷新是在“当前”命令移至 GPU 队列之前启动的。系统会阻塞视频调度,直到 GPU 耗尽。这需要直到下一个垂直同步。因此当前数据包直到下一帧才移至 GPU 队列。这样做的明显效果是:口吃。

您的计算机上不太可能也运行 f.lux。但您可能正在经历类似的后台干预。您需要自己查找系统问题的根源。我写了一篇关于如何诊断跳帧的博客文章:诊断 DirectX 应用程序中的跳帧和卡顿 https://ewirch.github.io/2014/03/diagnose-frame-skips-and-stutter-in-directx.html。您还可以在那里找到诊断 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
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

直线运动卡顿 的相关文章

  • 在 C++ 中分割大文件

    我正在尝试编写一个程序 该程序接受一个大文件 任何类型 并将其分成许多较小的 块 我想我已经有了基本的想法 但由于某种原因我无法创建超过 12 kb 的块大小 我知道谷歌等上有一些解决方案 但我更感兴趣的是了解这个限制的根源是什么 然后实际
  • Blazor 与 Razor

    随着 Blazor 的发明 我想知道这两种语言之间是否存在显着的效率 无论是在代码创建方面还是在代码的实际编译 执行方面 https github com SteveSanderson Blazor https github com Ste
  • 有什么工具可以说明每种方法运行需要多长时间?

    我的程序的某些部分速度很慢 我想知道是否有我可以使用的工具 例如它可以告诉我可以运行 methodA 花了 100ms 等等 或者类似的有用信息 如果您使用的是 Visual Studio Team System 性能工具 中有一个内置分析
  • std::map 和二叉搜索树

    我读过 std map 是使用二叉搜索树数据结构实现的 BST 是一种顺序数据结构 类似于数组中的元素 它将元素存储在 BST 节点中并按其顺序维护元素 例如如果元素小于节点 则将其存储在节点的左侧 如果元素大于节点 则将其存储在节点的右侧
  • 调试内存不足异常

    在修复我制作的小型 ASP NET C Web 应用程序的错误时 我遇到了 OutOfMemoryException 没有关于在哪里查看的提示 因为这是一个编译时错误 如何诊断此异常 我假设这正是内存分析发挥作用的地方 有小费吗 Thank
  • 为什么 BOOST_FOREACH 不完全等同于手工编码的?

    From 增强文档 http www boost org doc libs 1 48 0 doc html foreach html foreach introduction what is literal boost foreach li
  • 如何在 VS 中键入时显示方法的完整文档?

    标题非常具有描述性 是否有任何扩展可以让我看到我正在输入的方法的完整文档 我想查看文档 因为我可以在对象浏览器中看到它 其中包含参数的描述和所有内容 而不仅仅是一些 摘要 当然可以选择查看所有覆盖 它可能是智能感知的一部分 或者我不知道它并
  • 如何用 kevent() 替换 select() 以获得更高的性能?

    来自Kqueue 维基百科页面 http en wikipedia org wiki Kqueue Kqueue 在内核和用户空间之间提供高效的输入和输出事件管道 因此 可以修改事件过滤器以及接收待处理事件 同时每次主事件循环迭代仅使用对
  • 在 C# 中将位从 ulong 复制到 long

    所以看来 NET 性能计数器类型 http msdn microsoft com en us library system diagnostics performancecounter aspx有一个恼人的问题 它暴露了long对于计数器
  • 为什么 std::allocator 在 C++17 中丢失成员类型/函数?

    一边看着std 分配器 http en cppreference com w cpp memory allocator 我看到成员 value type pointer const pointer reference const refer
  • C++派生模板类继承自模板基类,无法调用基类构造函数[重复]

    这个问题在这里已经有答案了 我试图从基类 模板 继承 派生类也是模板 它们具有相同的类型 T 我收到编译错误 非法成员初始化 Base 不是基类或成员 为什么 如何调用基类构造函数 include
  • 单元测试失败,异常代码为 c0000005

    我正在尝试使用本机单元测试项目在 Visual Studios 2012 中创建单元测试 这是我的测试 TEST METHOD CalculationsRoundTests int result Calculations Round 1 0
  • 为什么 FTPWebRequest 或 WebRequest 通常不接受 /../ 路径?

    我正在尝试从 ftp Web 服务器自动执行一些上传 下载任务 当我通过客户端甚至通过 Firefox 连接到服务器时 为了访问我的目录 我必须指定如下路径 ftp ftpserver com AB00000 incoming files
  • 范围和临时初始化列表

    我试图将我认为是纯右值的内容传递到范围适配器闭包对象中 除非我将名称绑定到初始值设定项列表并使其成为左值 否则它不会编译 这里发生了什么 include
  • C# 编译器如何决定发出可重定向的程序集引用?

    NET Compact Framework 引入了可重定向程序集引用 现在用于支持可移植类库 基本上 编译器会发出以下 MSIL assembly extern retargetable mscorlib publickeytoken 7C
  • “MyClass”的类型初始值设定项引发异常

    以下是我的Windows服务代码 当我调试代码时 我收到错误 异常 CSMessageUtility CSDetails 的类型初始值设定项引发异常 using System using System Collections Generic
  • gdb查找行号的内存地址

    假设我已将 gdb 附加到一个进程 并且在其内存布局中有一个文件和行号 我想要其内存地址 如何获取文件x中第n行的内存地址 这是在 Linux x86 上 gdb info line test c 56 Line 56 of test c
  • 如何检测 C# 中该字典键是否存在?

    我正在使用 Exchange Web 服务托管 API 和联系人数据 我有以下代码 即功能性的 但并不理想 foreach Contact c in contactList string openItemUrl https service
  • 我应该在应用程序退出之前运行 Dispose 吗?

    我应该在应用程序退出之前运行 Dispose 吗 例如 我创建了许多对象 其中一些对象具有事件订阅 var myObject new MyClass myObject OnEvent OnEventHandle 例如 在我的工作中 我应该使
  • Swagger 为 ASP.CORE 3 中的字典生成错误的 URL

    当从查询字符串中提取的模型将字典作为其属性之一时 Swagger 会生成不正确的 URL 如何告诉 Swagger 更改 URL 中字典的格式或手动定义输入参数模式而不自动生成 尝试使用 Swashbuckle 和 NSwag 控制器 pu

随机推荐

  • “aspnet_regiis -i”在生产服务器上有多安全?

    我有一个生产 IIS 服务器 它托管许多 Web 应用程序 其中大多数都相对简单 服务器上已安装 NET Framework 版本 4 5 但 IIS 尚未配置为使用它 我想在新应用程序上使用 4 5 这个 Stackoverflow 帖子
  • 打印应用了 CSS 样式的 Angular 网页

    我见过很多帖子询问如何使用 CSS 样式打印网页 但没有一个解决方案对我有用 网页如下所示 Webpage https i stack imgur com FSBKm png 我需要它来打印所有颜色编码 但是当我去打印它时 我遇到了这个混乱
  • 前 5 行按每个类型的引用求和,所有其他行等于 1,按团队 ID 分组

    我有一个 SQL 语句 其中每个 TYPE 的前 5 行是根据 WSF REF 选择的 其中 STATUS Approved 的 WEIGHTS 相加 每个类型的每个引用的所有其他行都被归类为 1 这工作正常 我正在寻找添加第二组 TEAM
  • 如何消除 UIAlertController 的延迟?

    点击表格单元格后 警报视图显示有 4 到 5 秒的延迟 下面是代码 func tableView tableView UITableView didSelectRowAt indexPath IndexPath let cell table
  • 在 Android Oreo 中调用相机意图后正在重新创建父 Activity

    我正在使用媒体意图捕获图像 一旦过程完成 结果将被发送回父级 上述过程在 Nougat Os 上正常工作 但在 Oreo 中 父活动再次重新创建 我该如何解决这个问题 上述过程在 Nougat Os 上正常工作 但在 Oreo 中 父活动再
  • Windows Phone - 如何双击退出?

    我正在学习开发 Windows Phone 应用程序 我按照本教程开始使用基于浏览器的应用程序 http blogs msdn com b jaimer archive 2011 02 04 back button press when u
  • 在 if 语句中声明标量?

    为什么我不能在 if 语句中声明标量变量 和变量的作用域有关系吗 每个街区 在 Perl 中创建一个新的作用域 这包括裸块 子例程块 BEGIN 块 控制结构块 循环结构块 内联块 map grep eval 块和语句修饰符循环体 如果块具
  • 使用类型变量强制转换对象

    当然 下面的方法是行不通的 有没有一种可能的方法 与此非常相似 Type newObjectType typeof MyClass var newObject givenObject as newObjectType newObjectTy
  • Webpack-dev-server 编译文件,但不刷新或使编译后的 javascript 可用于浏览器

    我正在尝试使用 webpack dev server 来编译文件并启动开发 Web 服务器 In my package json我将脚本属性设置为 scripts dev webpack dev server hot inline So t
  • 通过反射从其字符串创建任何类型实例

    想象一下以下场景 用户将一个字符串传递给应用程序 该字符串表示 NET 类型 例如string or System IntPtr 假设所讨论的应用程序有权访问定义给定类型的程序集 它可以根据字符串创建给定类型的实例 我已经成功创建了这样一个
  • 如何查找列中的第一个非零值?

    在 A 列中 第一个值为 0 第二个值为 0 第三个值为 17 第四个值为 0 第五个值为 32 在这种情况下 第一个非零值是17 如何通过公式计算它 在单元格 B1 中 INDEX A1 A5 MATCH TRUE INDEX A1 A5
  • 启动 VScode 时访问被拒绝 Go 代码在未调试和已调试的情况下运行

    我正在尝试为 Go 设置一个新的 Windows 开发机器 这通常是一个非常简单的过程 但遇到了很多麻烦 当我尝试在不调试的情况下运行时 出现以下错误 fork exec C Users CAMIWIL AppData Local Temp
  • enum 与 constexpr 用于类内的实际静态常量

    让我首先陈述我的意图 在过去的 C 时代 我们的代码如下 class C public enum SOME VALUE 27 然后我们可以使用SOME VALUE在我们的代码中作为编译时常量以及编译器会看到的任何地方C SOME VALUE
  • 为什么xpath又找到排除的节点?

    考虑这个页面
  • 来自内部存储的电子邮件

    在我的应用程序中 我将一个文件写入内部存储 如上所述安卓开发者 http developer android com guide topics data data storage html filesInternal 然后我想通过电子邮件将
  • 保证 Ajax 调用参数值正确的最佳方法

    我正在开发一个需要一些 ajax 调用以提高灵活性和性能的网站 我的 ajax 调用是为了排名系统 我需要使用 ajax 处理三个输入值 storeID clientID orderID 要使用 ajax 提交操作 我想确保发送的参数值没有
  • .Net Core 2.1不读取用户机密

    我正在 Mac 上运行 net core 2 1 应用程序 并且尝试访问我的连接字符串 该字符串应该被我的用户机密覆盖 csproj 文件包含一个 guid
  • 使用 Python 将 CSV 文件导入 sqlite3 数据库表

    我有一个 CSV 文件 我想使用 Python 将该文件批量导入到我的 sqlite3 数据库中 命令是 import 但似乎不能这样工作 谁能给我一个如何在 sqlite3 中做到这一点的例子 我使用 Windows 以防万一 谢谢 im
  • 将图像添加到警报视图

    当用户按下添加按钮时 我会弹出一个警报视图 如何将图像添加到警报视图 我添加了一些从堆栈溢出中引用的代码 我的保存按钮被图像替换 并且图像看起来是蓝色的 警报视图代码 var alert UIAlertController title Sp
  • 直线运动卡顿

    我创建了简单的 与帧无关的 可变时间步长的线性运动Direct3D9 using ID3DXSprite 大多数用户无法注意到它 但在某些 包括我的 计算机上它经常发生 有时甚至会很卡顿 口吃发生于VSync启用和禁用 我发现同样的情况发生