3

我制作了一个游戏内图形分析器(CPU 和 GPU),Nvidia 驱动程序有一个奇怪的行为,我不确定如何处理。

这是正常情况下的截图: GPU Profiler, vysnc 开启 您可以在这里看到连续 3 帧,GPU 在顶部,CPU 在底部。两个图是同步的。

“END FRAME”栏仅包含对 的调用SwapBuffers。在 GPU 完成所有工作之前它一直处于阻塞状态似乎很奇怪,但这就是驱动程序有时在 vsync 开启时选择做的事情,并且所有工作(CPU 和 GPU)都可以在 16 毫秒内完成(AMD 也是如此)。我的猜测是它这样做是为了最大限度地减少输入滞后。

现在我的问题是它并不总是这样做。根据帧中发生的情况,图表有时看起来像这样: GPU Profiler, vysnc on, V2 这里实际发生的是,第一个 OpenGL 调用是阻塞的,而不是对SwapBuffers. 在这种特殊情况下,阻塞调用是glBufferData. 如果我添加一个可以做到这一点的虚拟代码(创建一个统一缓冲区,用随机值加载它并销毁它),它会更加明显:

GPU Profiler, vysnc on, V2 with dummy code

这是一个问题,因为这意味着图表中的条形可能会无缘无故变得非常大。看到这一点的人可能会得出关于某些代码运行缓慢的错误结论。

所以我的问题是,我该如何处理这种情况?我需要一种始终显示有意义的 CPU 计时的方法。

添加一个加载统一缓冲区的虚拟代码不是很优雅,并且可能不适用于未来版本的驱动程序(如果驱动程序只阻塞绘图调用怎么办?)。

与 a 同步glClientWaitSync看起来也不是一件好事,因为如果帧速率下降,驱动程序将停止阻塞以允许 CPU 和 GPU 帧并行运行,我需要检测到停止调用glClientWaitSync(但我'不知道该怎么做。)

(欢迎提出更好的标题建议。)

编辑:当 GPU 成为瓶颈时,这是没有 vsync 时会发生的情况: GPU Profiler, vysnc 关闭, V2 GPU 帧比 CPU 帧花费的时间更长,因此驱动程序决定在glBufferDataGPU 赶上之前阻塞 CPU。

条件不一样,但问题是:CPU时序是“错误的”,因为驱动程序做了一些OpenGL功能块。这实际上可能是一个比打开 vsync 的例子更容易理解的例子。

4

1 回答 1

3

这实际上按预期工作。由于 VSYNC 导致的阻塞不一定要在调用 期间发生SwapBuffers (...),VSYNC 导致阻塞的原因有几个,它们几乎完全不受您的控制。

当交换链充满等待交换的后备缓冲区时(通常只有 1 个后备缓冲区),在交换完成之前不得执行修改帧缓冲区的命令。这会导致管道停顿,并且是第一次罢工。请记住,即使管道停止,GL 仍可能在此状态下排队命令。

在大多数平台上,没有 API 允许您显式请求窗口系统交换链中的后备缓冲区数量。您可以请求缓冲或缓冲,驱动程序可能会将双缓冲解释为 2 或更多(您会在某些驱动程序中看到此标记为“启用三重缓冲” )。

罢工二来自被称为“提前渲染”的东西。这是 GL 在拒绝接受新命令之前将排队的特定于驱动程序的工作量。再说一次,您作为 OpenGL 软件的开发者,对此没有任何控制权。在某些驱动程序中,您可以深入挖掘并手动配置它。增加该值将允许 CPU 在管道停止时排队更多工作,但往往会增加延迟(特别是 D3D 实现它的方式,它禁止丢帧)。

一旦渲染管道停止等待缓冲区交换并且您耗尽了渲染提前限制,即罢工三。调用线程将阻塞下一个 GL 命令,直到 VBLANK 滚动并疏通管道。


glClientWaitSync (...),正如您所描述的,将有效地消除所有提前渲染。这可能是为了最大限度地减少时间变化,但如果您无法达到刷新率,则会对整体帧率产生负面影响。

自适应 VSYNC 应该是你追求的第一件事。在支持此功能的驱动程序上,您可以通过设置负交换间隔来启用它,当您无法维持刷新率时,它将避免阻塞。实际上,自适应 VSYNC 的目的是在绘制速度过快时限制渲染。如果您的绘图速度超过了您的显示器可以处理的速度,那么分析 GL API 调用似乎并不特别重要。

在最坏的情况下,您始终可以完全禁用 VSYNC。在像 Windows Vista 这样的现代合成窗口管理器中,无论您是否启用 VSYNC,都可以在窗口模式下防止撕裂。在这种情况下,VSYNC 确实可以节省电力,将其关闭以进行更准确的分析可能是一个可以接受的折衷方案。您可以轻松实现自己的节流机制,以防止引擎以高得离谱的帧速率进行绘制,而不会出现 VSYNC 引入的不可预测的行为。

于 2015-02-22T23:09:10.657 回答