Dem*_*ion 7 c++ game-physics directx-9
我创建了简单,独立于帧,可变时间步长,Direct3D9使用中的线性运动ID3DXSprite.大多数用户都无法注意到它,但在某些(包括我的)计算机上,它经常发生,有时它会很多口吃.
VSync启用和禁用会发生口吃.
我发现在OpenGL渲染器中也会发生同样的情况.
它不是浮点问题.
似乎问题只存在于AERO Transparent Glass窗口模式中(在全屏,无边界全屏窗口或禁用航空器时很好或至少不那么明显),当窗口失去焦点时甚至更糟.
编辑:
即使发生口吃,帧增量时间也不会留出16 ... 17 ms的界限.
好像我的帧增量时间测量日志代码被窃听了.我现在修好了.
(我只在应用程序出口处转储一次日志,而不是在运行,渲染时,因此它不会影响性能)
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);
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
编辑#2:
到目前为止,我尝试了三种更新方法:
1)可变时间步
x += velocity * deltaTime;
Run Code Online (Sandbox Code Playgroud)
2)固定时间步
x += 4;
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
所有方法都工作得非常相似,固定时间步长看起来更好,但它不是一个完全依赖帧速率的选项,它不能完全解决问题(仍然很少跳跃(断断续续)).
到目前为止,禁用AERO Transparent Glass或全屏显示只是重大的积极变化.
我正在使用NVIDIA最新的驱动程序GeForce 332.21 Driver和Windows 7 x64 Ultimate.
部分解决方案是一个简单的精确数据类型问题.用常数交换速度计算,你会看到一个非常平滑的运动.分析计算表明您将结果存储在QueryPerformanceCounter()浮点内.QueryPerformanceCounter()在我的计算机上返回一个看起来像这样的数字:724032629776.此数字至少需要存储5个字节.但是如何float使用4个字节(实际数量只有24位)来存储该值.所以,当你转换的结果精度丢失QueryPerformanceCounter()到float.有时这会导致deltaTime零点导致口吃.
这部分解释了为什么有些用户没有遇到这个问题.这一切都取决于结果QueryPerformanceCounter()是否适合a float.
这部分问题的解决方案是:使用double(或者作为Ben Voigt建议:存储初始性能计数器,并在转换之前从新值中减去它float.这会给你至少更多的头部空间,但可能最终会达到float分辨率应用程序运行很长时间后再次限制(取决于性能计数器的增长速度).)
解决这个问题后,口吃的情况要少得多,但并没有完全消失.分析运行时行为表明偶尔会跳过一个帧.刷新应用程序GPU命令缓冲区,Present但是当前命令保留在应用程序上下文队列中,直到下一个vsync(即使Present在vsync(14ms)之前很久就被调用).进一步的分析表明,背景过程(f.lux)告诉系统偶尔设置伽马斜坡.此命令要求完整的GPU队列在执行之前运行.可能是为了避免副作用.这个GPU刷新是在'present'命令移动到GPU队列之前启动的.系统阻止视频调度直到GPU干涸.这直到下一个vsync.因此,在下一帧之前,当前数据包不会移动到GPU队列.这可见效果:口吃.
您也不太可能在计算机上运行f.lux.但是你可能正在经历类似的背景介入.您需要自己在系统上查找问题的根源.我写了一篇关于如何诊断帧跳过的博客文章:在DirectX应用程序中诊断帧跳过和断断续续.你也会发现将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
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2867 次 |
| 最近记录: |