位图性能优化模式
我发现了几种优化WPF中的位图处理的模式。 但是,我不知道何时使用每种模式。 正如我认为这是一个普遍的问题,我总结了我的理解和我的猜想,并寻求你的帮助。 如果你可以添加模式 ,解释它们的不同之处 ,说明它们是使用CPU还是使用GPU ,以及什么时候使用它们以及如何合并它们 ,那将会是一个巨大的帮助!
上下文 – 图片“网格”场景:
我的应用程序必须显示许多位图图像。 图像显示在屏幕上的行和列网格组织(不一定是网格或UniformGrid类,认为窗口媒体播放器的相册视图)。 图像可能会在不同的网格单元之间移 任意单元格中的一些图像可能被其他单元replace。 图像应该是可点击的,应该提供一个上下文菜单,应该是可select的,可拖拽的等等。换句话说,“将小编辑器合并成一个大的位图”是不适用的,至less不是天真的。
模式0:黑客
把这些小杂烩混合成一个位图(如何绘制上下文?),并将其作为背景。 覆盖这与图像与空的内容,将处理命中,上下文菜单,事件等
优点是我们只在这里讲两个位图:当前显示的和应该replace的位图。 这应该是非常快的。 但是,我多年的经验提高了危险的红旗。 你的评论?
模式1:缩小图像大小
当你事先知道要调整的图像大小,以及当你准备失去performance的细节(颜色)时,这是一个不容小觑的过程:
- 使用BitmapImage.DecodePixelWidth减小位图大小
- 使用FormatConvertedBitmap.DestinationFormat减less颜色信息
- 将控件的缩放行为设置Image.Stretch设置为Stretch.None
- 将图像的SetBitmapScalingMode设置为LowQuality。
- 冻结bug子手
在这里看代码。
模式2:背景预取
当您认为您可以利用用户凝视屏幕上的图像并准备好接下来要显示的图像时,此模式适用。 除了内存开销之外,项目的缺点是必须支持.Net Framework 4目标,而不仅仅是客户端configuration文件,因此可能会导致客户端上的安装。 你自己将不得不忍受asynchronous编程的痛苦。
在这种模式下,您可以创build所需数量的图像控件。 当需要添加,移动或删除位图时,只能修改图像控件的位图源。 BackgroundWorker任务负责预取BitmapSource(可能使用上面的“缩小图像大小”模式)并将其插入到MemoryCache中。
为此,您必须将BitmapImage的CacheOption设置为OnLoad,以便工作被卸载到后台工作者。
模式3:绘图上下文
这是Microsoft支持部门的Sheldon Ziao在MSDN WPF论坛上提出的build议。 请参阅Adam Nathan的WPF 4的第15章“2Dgraphics”中的494页,了解DrawingContext的描述。 我不能说我明白这一点。 根据这里的答案,我会认为这将改善处理几何图纸,而不是位图。 接下来,我不认为这将支持图像的焦点和事件要求(我不好意思,因为没有在论坛上更好地解释要求)而且,我担心这本书的总结性的句子:“请注意,使用DrawingContext不会改变您在保留模式系统中运行的事实。 指定的绘图不会立即发生; 这些命令被WPF所保留,直到需要为止。“这意味着,一旦我们的处理程序重新开始,我们就不能像”后台预取“那样利用并行性。
模式4:可写位图
这里的MSDN文档将其描述为双缓冲系统:您的UI线程更新缓冲区; WPF的呈现线程将其移动到video内存。
预期用法(请参见此处 )适用于像显示器之类的video影片中变化很大的位图。 我不确定,但可能会被黑客入侵,并与后台预取模式相结合,并在网格场景中使用。
模式5:caching的位图
没有太多的信息在MSDN( 这里 )。 在WPF论坛档案( 在这里 ),它解释说:“BitmapCache API的目的是caching你的内容(当硬件渲染时)在video内存,这意味着它驻留在你的GPU。 这样可以节省您在绘制到屏幕上时重新呈现内容的成本。“这似乎是一个好主意。 但是,我不确定是什么缺陷以及如何使用它。
模式6:RenderTargetBitmap
RenderTargetBitmap将Visual转换成位图。 我不确定这里是否有关。 看到这里 。
编辑 :关于保罗Hoenecke问题:我写了“我的应用程序必须显示许多位图iages”。 我没有提到我需要同时显示大约800个图像。
我可以阅读有关性能问题涉及到我的问题WPF位图性能和如何使显示图像在WPF更“活泼”?
我修改了模式1的描述来突出显示图像控件不被创build或删除的概念(除非我们想显示一个更大或更小的网格)。 只有它们的源被设置为不同的,新的或空的BitmapSource。
编辑 : 这个问题张贴在WPF支持论坛上 ,有一些MS人员的答案。
我无法在您的post中find具体的问题,除了要求对以下方法发表评论之外。 我不会声称知道上面的所有内容,但是我会告诉你我曾经使用WPF和Silverlight开发高性能UI的一段时间。
模式0:黑客。 将所有图像合并成一个图像
如果可能,我会避免这种情况。 这听起来像是要显示一个包含数千个小图像的大型包装面板。 因此,每个图像都是一个缩略图(因为一次不能显示1000个大图像)。 因此,我主张caching/调整组合。
模式1:缩小图像大小
如果您一次在屏幕上显示1,000张图像,请考虑可用的屏幕空间。 平均显示器是1280×1024像素,或者刚刚超过130万像素。 1000张图片显示,您将获得每张图片最大1300像素或36 * 36的大小。 可以说你的图像尺寸是32 * 32。 您一定要在屏幕上创build该图像大小的缩略图,然后点击(或其他动作)显示全尺寸的图像。
还要考虑调整大图像的渲染开销,而不是将大图像发送到GPU来resize。 该数据需要带宽发送。 大图像可能是几兆字节,而32 * 32大小的缩略图可能是几千字节。
如果您需要dynamicresize,那么您需要尝试创build多个缩略图或dynamic生成缩略图。
模式2:背景预取
这是我从来没有听说过的技术,但似乎是合理的。 你的应用程序的开销是多less? 是它更新Image.Source属性或创build一个新的图像,镶嵌,执行布局和发送信息呈现给GPU?
除了最终的渲染之外,以上所有都发生在CPU上。 通过减lessCPU端的开销并更新源代码,你可能会得到一些东西。 结合WriteableBitmap作为一个源代码,你可以进一步提高性能(见下文)。
模式3:绘图上下文
好吧,所有这一切都允许你使用“OnPaint”风格的语法排队保留的模式绘图调用,这与旧的GDI OnPaint没什么两样。 在我的经验中,OnRender并没有提高性能,但它确实允许在绘制和何时绘制细粒度的灵活性。 OnRender为您提供了一个具有DrawImagefunction的上下文,允许将BitmapSource绘制到渲染pipe道,而不需要Image控件。 这是很好的,因为它消除了一些开销,但是引入了类似于Pattern0中的问题(你将失去布局并且必须计算所有图像的位置)。 如果你这样做,你也可以调用模式0,我build议不要这样做。
模式4:可写位图
WriteableBitmaps是WPF中的一个有点使用和非常强大的子系统。 我使用它们来创build一个能够实时渲染大量数据的图表组件。 我会build议检查一下WriteableBitmapEx的codeplex项目Disclosure,我曾经为此做出过贡献,并且看看你是否可以将它与其他模式结合起来。 特别是Blitfunction,它可以让你将caching的位图写入图像上的位图源。
例如,一个好的技术可能是模式1 + 2 + 4。
在网格控件的设置位置,屏幕上可以有N个图像控件的网格。 这些都是静态的,不会滚动查看,所以没有创build/删除操作。 现在,在这之上,调整你的图像的大小,并写入一个WriteableBitmap,它被设置为每个图像的Source属性。 在滚动时,获取下一个/上一个缩略图,并使用WriteableBitmapEx.Blit更新源。 啪! 虚拟化,caching,multithreading的成像优势。
模式5:caching的位图
正如我上面所讨论的,这是微软试图做1 + 2 + 4的尝试。 它试图做的是在布局(CPU端),曲面细分(CPU端),向GPU(CPU端)发送保留模式渲染指令,渲染(GPU端)caching渲染元素的栅格图像之后,在下一个渲染过程中使用。 是的,关于WPF一个鲜为人知的事实是,美妙的GPU动力引擎是非常慢,因为它的大部分工作在CPU上:P
我会试用BitmapCache,看看它是如何执行的。 有一些注意事项,当你更新你的UIElement时,它必须重新创buildcaching,所以静态元素的执行要比dynamic更好。 此外,我还没有看到使用这种性能的显着改善,而WriteableBitmap风格的技术可以提供一个数量级的改善。
模式6:RenderTargetBitmap
这个最后的技术可以让你把一个UIElement渲染成一个位图 – 你知道的 – 但有趣的是这可以执行一个糟糕的缩略图生成器(或resize)。 例如,使用全尺寸图像的BitmapSource设置图像。 现在将Image控件的大小设置为32 * 32并呈现给位图。 瞧! 您可以将BitmapSource缩略图与某些交换(模式2)和/或可写位图结合使用。
好吧,只是想说你的要求将推动WPF的极限,但有办法让它执行。 正如我所说,我已经构build了一个系统,通过使用WriteableBitmap这个美妙的解决方法,可以同时在屏幕上显示数千或数百万个元素。 去标准的WPF路线将导致performance地狱,所以你将不得不做一些异国情调来解决这个问题。
正如我所说我的build议是1 + 2 + 4。 你必须调整缩略图的大小,我毫不怀疑。 有一个静态网格的图像控件和更新源的想法是非常好的。 使用WriteableBitmap(特别是WriteableBitmapEx blit函数)更新源代码的想法也值得探讨。
祝你好运!