none
多个WPF窗口连续显示图片有严重的闪烁问题,该如何解决? RRS feed

  • 问题

  • 详细描述:主窗口中有5个窗口,每个窗口刷新的图像都来自同一个三维图像数据源,只是每个窗口的视角和渲染方式不同。渲染算法是自编代码实现的,使用的是CUDA架构在显卡中运算。连续刷新图像的时候,每个界面都有闪烁现象。图像刷新使用的就是drawingvisual类,用RenderOpen获取DrawContext,然后调用DrawImage方法,最后Close掉DrawContext。

    环境:显卡GTS450,驱动程序2011版的,非常新。系统Windows7,.Net Framework4。

    WPF不是已经内置了双缓冲了吗?怎么还闪的这么严重。另外,闪烁的区域仅限于画图区域,我测试过如果画图范围不占用整个子窗口的话,那么只是画图像的区域闪,而其他区域不闪。

    2012年5月4日 8:25

答案

  • 我猜想是DrawImage性能问题,你可以用 WPF Performance Suite:http://msdn.microsoft.com/en-us/library/aa969767.aspx 跟踪测试下。 不过,介于你的需求,我倒是建议你直接Host DX3D去渲染, D3DImage 类型可以帮你,具体参考:http://msdn.microsoft.com/zh-cn/library/cc656710.aspx

    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    • 已标记为答案 Eric789808 2012年5月9日 6:29
    2012年5月7日 3:41
    版主
  • Winform是基于GDI渲染绘制的,最理想状态,也就能够达到15帧每秒,而我们WPF基于DX渲染绘制,是能很轻松超过这个值的。所以WPF并不是通过 WM_Paint 这类消息机制去控制渲染的,完全是在DX的管理下进行的,他会针对目前已经失效或者需要更新的区域进行更新。 这些现象你通过WPF Performence Tool进行测试时候都是能够看见的。

    尝试将一个Winform的UserControl嵌入到WPF中,在这个控件上画图,不过这样的话,同一个平面内的WPF控件都接收不到任何UI消息了。 由于属于两种不同技术,Winform还是基于消息机制,所以它所能收到的消息是不会同时发送给WPF层,所以这两者的消息通讯需要我们手动地去完成,比较复杂。 

    我的建议,这样的需求一方面看看DX3D能否做。 如果只是要现实图片,在WPF或者Silverlight中,还提供了一种方案:CompositionTarget.Rendering Event How To: http://msdn.microsoft.com/en-us/library/ms748838.aspx

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    • 已标记为答案 Eric789808 2012年5月9日 6:29
    2012年5月8日 9:59
    版主
  • 因为你在你的实际环境中,图片通过显卡生成,那么你是否有缓存,是否图片过于离散, 假如说显卡生成速度比显示满,那么你就需要做一些显示上的延时,或者缓存, 让一段图片连续后再开始显示. 其实方法还有其他,比如我们曾经做过一个例子,是在WPF中显示GIF的,他所用的方法 你就可以借鉴下:http://code.msdn.microsoft.com/windowsdesktop/CSWPFAnimatedGIF-3029aeb9

    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    • 已标记为答案 Eric789808 2012年5月9日 6:29
    2012年5月9日 3:10
    版主
  • 谢谢,现在问题解决了。我们的错误在于对WriteableBitmap理解不够,每次DrawImage都用的是一个新创建出来的WriteableBitmap对象,这是导致闪烁的原因。而修改的方法就是不更换WriteableBitmap,需要更新内容时,只要将新的WriteableBitmap的内存复制一下到原先的WriteableBitmap中即可。然后用AddDirtyRect通知下刷新。理解的误差在于:WPF的Writeablebitmap本身就有双缓冲区,DrawImage也不是就把图像直接覆盖到屏幕上,而是在渲染的时候从WriteableBitmap的前缓冲区去数据到显存。我们频繁的更换WriteableBitmap对象,导致某些时候它的前缓冲区还没有数据就被刷新到了屏幕上,就看到了闪烁。

    非常感谢您的帮助,使我对WPF渲染机制的理解上升了一个层次,虽然本来就不怎么理解。


    Eric

    • 已标记为答案 Eric789808 2012年5月9日 6:29
    2012年5月9日 6:13

全部回复

  • 我猜想是DrawImage性能问题,你可以用 WPF Performance Suite:http://msdn.microsoft.com/en-us/library/aa969767.aspx 跟踪测试下。 不过,介于你的需求,我倒是建议你直接Host DX3D去渲染, D3DImage 类型可以帮你,具体参考:http://msdn.microsoft.com/zh-cn/library/cc656710.aspx

    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    • 已标记为答案 Eric789808 2012年5月9日 6:29
    2012年5月7日 3:41
    版主
  • 谢谢,我觉得问题和Winform窗口的闪烁类似,画图时需要擦除背景导致闪烁。Winform中设置双缓冲以及AllPaintingInWmPaint可以解决闪烁,WPF的渲染已是默认的双缓冲模式,但是却没有和AllPaintingInWmPaint类似的设置选项。DrawImage再慢,即使画面有滞留也不应该出现闪烁。不知道为什么WPF不提供类似AllPaintingInWmPaint的设置选项?我现在尝试将一个Winform的UserControl嵌入到WPF中,在这个控件上画图,不过这样的话,同一个平面内的WPF控件都接收不到任何UI消息了。如果这个方案不行的话,我再尝试下DX3D。

    谢谢!


    蒋兴华

    2012年5月8日 8:05
  • Winform是基于GDI渲染绘制的,最理想状态,也就能够达到15帧每秒,而我们WPF基于DX渲染绘制,是能很轻松超过这个值的。所以WPF并不是通过 WM_Paint 这类消息机制去控制渲染的,完全是在DX的管理下进行的,他会针对目前已经失效或者需要更新的区域进行更新。 这些现象你通过WPF Performence Tool进行测试时候都是能够看见的。

    尝试将一个Winform的UserControl嵌入到WPF中,在这个控件上画图,不过这样的话,同一个平面内的WPF控件都接收不到任何UI消息了。 由于属于两种不同技术,Winform还是基于消息机制,所以它所能收到的消息是不会同时发送给WPF层,所以这两者的消息通讯需要我们手动地去完成,比较复杂。 

    我的建议,这样的需求一方面看看DX3D能否做。 如果只是要现实图片,在WPF或者Silverlight中,还提供了一种方案:CompositionTarget.Rendering Event How To: http://msdn.microsoft.com/en-us/library/ms748838.aspx

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    • 已标记为答案 Eric789808 2012年5月9日 6:29
    2012年5月8日 9:59
    版主
  • 谢谢!我尝试了Rendering Event方案,我的方法是在事件处理函数中更新窗口的Background为一个ImageBrush,而这个ImageBrush的source就是要刷新的图片。我写了一个测试程序,图片先从磁盘加载到内存,然后在界面上快速刷新,看不到闪烁。但是我们的实际工程项目中,图片是通过显卡实时计算出来的,在这个环境下测试结果仍然闪烁。看来这是WPF的硬伤,用WPF中的技术是无法解决了,我是不是太悲观了?

    谢谢。


    蒋兴华

    2012年5月9日 2:38
  • 因为你在你的实际环境中,图片通过显卡生成,那么你是否有缓存,是否图片过于离散, 假如说显卡生成速度比显示满,那么你就需要做一些显示上的延时,或者缓存, 让一段图片连续后再开始显示. 其实方法还有其他,比如我们曾经做过一个例子,是在WPF中显示GIF的,他所用的方法 你就可以借鉴下:http://code.msdn.microsoft.com/windowsdesktop/CSWPFAnimatedGIF-3029aeb9

    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    • 已标记为答案 Eric789808 2012年5月9日 6:29
    2012年5月9日 3:10
    版主
  • 谢谢,现在问题解决了。我们的错误在于对WriteableBitmap理解不够,每次DrawImage都用的是一个新创建出来的WriteableBitmap对象,这是导致闪烁的原因。而修改的方法就是不更换WriteableBitmap,需要更新内容时,只要将新的WriteableBitmap的内存复制一下到原先的WriteableBitmap中即可。然后用AddDirtyRect通知下刷新。理解的误差在于:WPF的Writeablebitmap本身就有双缓冲区,DrawImage也不是就把图像直接覆盖到屏幕上,而是在渲染的时候从WriteableBitmap的前缓冲区去数据到显存。我们频繁的更换WriteableBitmap对象,导致某些时候它的前缓冲区还没有数据就被刷新到了屏幕上,就看到了闪烁。

    非常感谢您的帮助,使我对WPF渲染机制的理解上升了一个层次,虽然本来就不怎么理解。


    Eric

    • 已标记为答案 Eric789808 2012年5月9日 6:29
    2012年5月9日 6:13