在使用DWM组合的窗口上使用GDI绘图时,是否可以防止撕裂伪影?

时间:2014-10-02 13:49:26

标签: windows winapi gdi dwm

我正在窗口上,在启用了DWM合成的系统上使用双缓冲GDI绘制动画,并在屏幕上看到清晰可见的tearing。有办法防止这种情况吗?

详细

动画采用相同的图像,并在屏幕上从右向左移动;使用timeGetTime和{1ms resolution来确定当前时间与动画开始时间和结束时间之间的差异,以获得应用于整个窗口宽度的分数。 3}}。动画绘制循环而不处理应用程序消息;它调用(VCL库)方法Repaint,该方法在内部失效,然后针对相关窗口调用UpdateWindow,直接使用WM_PAINT调用消息过程。绘制处理程序的VCL实现使用BeginBufferedPaint。绘画本身是双重缓冲的。

这样做的目的是让帧速率尽可能高,以在屏幕上获得平滑的动画效果。 (该图使用双缓冲来消除闪烁并确保整个图像或帧在任何时间都在屏幕上。它通过调用消息过程直接无效和更新,而不进行其他消息处理。绘画使用现代技术实现(例如,BeginBufferedPaint)用于Aero组合。)在这里,绘画是在几个BitBlt调用中完成的(一个用于动画的左侧,即在屏幕外移动,一个用于动画的右侧,即什么在屏幕上移动。)

观看动画时,显而易见tearing在具有不同图形卡的多个系统上,Windows Vista,7和8.1上会出现这种现象。

我处理这个问题的方法是降低绘图速度,或者在再次绘制之前尝试等待VSync。这可能是错误的方法,所以这个问题的答案可能是"完全做其他事情:X"。如果是这样,很棒:))

(我真正喜欢的是一种让DWM仅为这个特定窗口编写/使用完全绘制的帧的方法。)

我尝试过以下方法,但都没有删除所有可见的撕裂。因此问题是,使用DWM组合时是否可以避免撕裂,如果是这样的话?

尝试了方法:

  • 通过GetDeviceCaps(Application.MainForm.Handle, VREFRESH)获取显示器刷新率;睡眠1 /刷新率毫秒。尽可能快地改善绘画,但可能是一厢情愿的想法。感觉上动画率稍微不那么平滑。 (调整:正常Sleep和使用timeGetTime的高分辨率旋转等待。)

  • 使用DwmSetPresentParameters尝试将更新限制为代码绘制的相同速率。 (变化:大量缓冲区(cBuffer = 8)(无可见效果);使用上面的代码指定监视器刷新率/ 1和睡眠的源速率(与尝试休眠方法相同);指定每帧的刷新次数1,10等(无可见效果);更改源帧覆盖范围(无可见效果)。

  • 以各种方式使用DwmGetCompositionTimingInfo

      • cFramesPending> 0,旋转;
      • 获取cFrame(框架组成)并旋转,而此数字不会改变;
      • 获取cFrameDisplayed并旋转,但这不会改变;
      • 通过添加qpcVBlank + qpcRefreshPeriod来计算入住时间,然后当QueryPerformanceCounter返回的时间少于此时,旋转
  • 所有这些方法也通过绘画,然后旋转/睡觉而变化,然后再次绘画;或者相反:睡觉然后画画。

似乎没有任何明显的效果,并且有什么效果很难获得资格,可能只是帧速率较低的结果。没有防止撕裂,即没有人使DWM用"整体"组成窗口。窗口内容的副本。

建议赞赏:)

1 个答案:

答案 0 :(得分:2)

由于您正在使用BitBlt,请确保您的DIB为4字节/像素。在3字节/像素的情况下,当DWM运行时,GDI非常慢,这可能是你撕裂的根源。我遇到的另一个BitBlt问题,如果您的DIB比BitBlt电话稍微大一些,则会花费很长时间。如果将一个调用分成较小的调用而不是仅绘制一部分数据,则可能有所帮助。这两个项目对我的情况都有帮助,只是因为BitBlt本身运行得太慢,从而导致视频工件。