我正在开发一种科学应用程序,该应用程序必须(尽可能最佳地)估计在视频后缓冲区中绘制的对象与该对象实际在屏幕上可见的时间之间的时间差。换句话说,Windows XP +上的DirectX如何处理监视器的垂直刷新周期。

首先,我的视频例程基于SDL 1.3库。结果,我没有立即访问DirectX API的权限,但是如有必要,可以更改此权限。在全屏模式下,将使用D3DSWAPEFFECT_DISCARD,D3DPRESENT_INTERVAL_ONE和BackBufferCount = 1初始化DirectX。这些似乎是最关键的参数,但是如果需要更多信息,我很乐于深入研究SDL代码的其余部分。

D3DPRESENT_INTERVAL_ONE标志可确保每个刷新周期交换后缓冲区和前缓冲区不超过一次,并且绝不会在刷新过程中进行交换(它基本上启用了vsync)。的确,如果我有一个简单的循环仅连续调用IDirect3DDevice9::Present(在我的情况下为SDL_RenderPresent),则此函数将阻塞两个刷新周期之间的毫秒数(60Hz时为16.67ms,100Hz时为10ms,等等)。 。

这是我的问题...假设我在后台缓冲区中绘制了一个白色正方形,然后调用SDL_RenderPresent,它将阻塞16.67毫秒(假设刷新频率为60Hz)。当对SDL_RenderPresent的调用返回时,关于监视器上可见图像的状态,我可以得出什么结论?我看到的是以下几种可能性:

  • 白色正方形刚刚在监视器上绘制。
  • 即将绘制白色正方形(少于1毫秒)。
  • 前面的前端缓冲区刚刚绘制;在我的白色方块出现之前,将需要另一个刷新周期(16.67毫秒)(再次调用SDL_RenderPresent将使我进入情况1)。
  • 上一个缓冲区是在最近的16.67毫秒内绘制的,下一个是我的白色方块,但是直到下一次刷新的确切时间是未知的。

  • 从我已完成的所有阅读中,我倾向于选择3,但是我找不到针对4的任何保证。在我的配置中,Present函数仅应在第二个过程中被第二次调用时阻塞在两个刷新周期之间暂停。由于目标是交换前缓冲区和后缓冲区,因此第二个调用可以做到的最早时间是在刷新监视器之后(刚刚绘制了前缓冲区)。此时,可以将包含我的白色方块的后缓冲区移到前面,但是它必须等待(最多)16.67毫秒,监视器才能实际读取和显示缓冲区内容。理想情况下,我想听到该函数应始终在上一个刷新周期结束后立即返回。

    任何对DirectX经验比较丰富的人都可以提供有关此主题的任何见解吗?我的假设是正确的还是我缺少什么?对于任何具有DirectX支持的系统,这些假设是否总是正确的?或者逻辑会根据视频卡,显示器或其他某些东西而改变吗?

    作为最后一个小问题,回到我的循环,一次又一次地调用SDL_RenderPresent,我注意到前3或4个调用立即返回,而所有后续调用都等待刷新周期。我是否假设D3DPRESENT_INTERVAL_ONE限制在第一次刷新之前就被忽略了(这与我希望拥有的超过2个缓冲区的某种排队方式相反)是正确的吗?

    换句话说,假设以大约8ms的时间进入循环,直到下一个刷新周期。在此期间,它可能能够交换前缓冲区和后缓冲区4次。在第一次刷新发生之前,SDL_RenderPresent将立即返回(因为从技术上来说,我们现在没有任何前缓冲区,因此只有2个后缓冲区),但是只要屏幕上显示了其中一个缓冲区,就会立即开始阻塞。这是有效的解释吗?

    [编辑]

    根据下面的答复,很明显,我使用vsync和Present的方法行不通。我想我找到了另一种方法来达到预期的效果,所以我将其发布在这里,以防有人发现我的想法中的错误,或者只是为其他从事类似问题的人提供信息。

    第一步是摆脱D3DPRESENT_INTERVAL_ONE。这将禁用vsync并确保对SDL_RenderPresent的任何调用将立即返回。接下来,您可以使用IDirect3DDevice9::GetRasterStatus获取有关当前监视器状态的信息。它提供了一个 bool 字段,该字段在两个刷新周期之间的暂停期间设置为true,而另一个字段则在 Activity 刷新期间告诉您当前的扫描线。使用这两条信息,可以实现自己的垂直同步例程,尽管可以通过运行一个不断轮询监视器状态并因此消耗100%CPU的循环来实现。这对于我的需求是可以接受的。

    仍然存在缓冲问题-调用SDL_RenderPresent时,如何知道要在屏幕上绘制哪一帧?我想我找到了一种确定此方法的方法,这取决于我是否知道监视器上正在绘制哪条线。基本逻辑如下:
  • 等待新的刷新周期开始(暂停=否,扫描线= 0)。
  • 用红色填充下一个后备缓冲区,然后调用Present。
  • 等待扫描线达到32。
  • 用绿色填充下一个后备缓冲区,然后调用Present。

  • 依此类推...在我的演示实现中,我使用了红色,绿色,蓝色,最后是黑色。这个想法是,只有在GetRasterStatus提供有关刷新状态的准确信息并且调用SDL_RenderPresent时立即翻转前和后缓冲区时,您才会看到RGB颜色模式。如果不满足这些条件中的任何一个,则您可能看不到任何东西,颜色可能会交换或重叠,等等。另一方面,如果您在屏幕顶部看到每帧恒定的RGB模式,则此证明您可以直接控制绘制的图像。

    我应该补充一点,就是我在今天工作的多台计算机上测试了这一理论。大多数都显示了图案,但至少有一个将整个屏幕涂成了红色。少数几个颜色带会上下跳动,表明交换缓冲区时有些不一致。这通常发生在较旧的计算机上。我认为这是一个很好的校准测试,可以确定硬件是否适合我们的测试目的。

    最佳答案

    我强烈建议您查看Microsoft的GPUView。这是介绍该工具的one of the authors webpage

  • D3D通常将缓冲一帧以上的渲染命令(包括礼物)。有关示例,请参见幻灯片25,我们可以在BumpEarth设备队列中看到约3个帧正在缓冲。这说明3-4个调用立即返回(当前数据包是交叉的数据包)。他们只是排队。
  • 除非您要进行全屏渲染,否则操作系统需要进行一些合成(同一张幻灯片显示了在vsync上发生的合成-蓝色垂直线)

  • 一些后果:
  • 当前返回的内容根本无法保证您刚刚发送的渲染命令将在屏幕上更新。
  • 您的命令渲染帧所花费的时间并不容易确定。我已经看到应用程序依赖于以前渲染的时序,并进行了平滑处理(以防止乒乓渲染变化)。

  • 作为其他注释:
  • 我目睹了现实工作负载中约1.5帧的命令缓冲。
  • 即使在发生vsync且视频卡更新了前缓冲区时,
  • ,监视器仍可以在内部进行一些缓冲(由于我们将CRT留在了后面,因此可以进行更多缓冲)。

  • 我要问,为什么框架在屏幕上显示时您需要精确控制?

    关于windows - 了解IDirect3DDevice9::Present在阻止vsync时的行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3780652/

    10-13 09:13