Avi*_*Avi 36 wpf optimization performance design-patterns bitmap
我发现了几种用于优化WPF中位图处理的模式.但是,我不了解何时使用每种模式.我认为这是一个常见问题,我总结了我的理解和猜测,并请求您的帮助.如果您可以添加模式,解释它们的不同之处,解释它们是否使用CPU或GPU,并教授何时使用它们以及如何组合它们,这将是一个巨大的帮助!
上下文 - 图像"网格"场景:
我的应用程序必须显示许多位图图像.图像以行和列网格状组织显示在屏幕上(不一定是Grid或UniformGrid类,想想Window Media Player的Album视图).图像可能在不同的网格单元之间移动 任意单元格中的某些图像可能会被其他图像替换.图像应该是可点击的,应该提供一个上下文菜单,应该是可选择的,可拖动的等等.换句话说,"将小虫子组合成一个大的位图"是不适用的,至少不是天真的.
模式0:黑客
将小虫子组合成位图(如何绘制上下文?),并将其用作背景.使用空内容处理命中,上下文菜单,事件等的图像叠加此图像.
优点是我们这里只讨论两个位图:当前显示的位图和应该替换它的位图.这应该非常快.然而,我多年的经历引发了危险的危险.你的评论?
模式1:减小图像大小
如果您事先知道要调整大小的图像大小,以及当您准备丢失性能的详细信息(颜色)时,这是一个明智的做法:
见代码在这里.
模式2:背景预取
当您认为可以利用用户注视屏幕上的图像时,此模式适用,并提前准备要显示的下一个图像.除了内存开销之外,项目的缺点是它必须支持.Net Framework 4目标而不仅仅是客户端配置文件,因此它可能会在客户端上进行安装.你自己将不得不忍受异步编程的痛苦.
在此模式中,您可以精确创建所需数量的图像控件.当需要添加,移动或删除位图时,您只需修改Image控件的BitmapSource(s).BackgroundWorker任务负责预取BitmapSource(可能使用上面的"Reduce Image Size"模式)并将它们插入MemoryCache.
为此,您必须将BitmapImage的CacheOption设置为OnLoad,以便将工作卸载到后台工作程序.
模式3:绘制上下文
这是从在MSDN WPF论坛Microsoft支持建议谢尔登Ziao 这里.有关DrawingContext的说明,请参阅Adam Nathan的WPF 4中的第494页,第15章"2D图形".我不能说我理解它.根据这里的答案,我认为这将改善几何图纸的处理,而不是位图.接下来,我认为这不会支持图像的焦点和事件要求(我不好在论坛上没有更好地解释要求)而且,我很担心这本书的总结句:"注意使用DrawingContext不会改变您在保留模式系统中运行的事实.指定的图纸不会立即发生; 命令由WPF保留,直到需要它们为止."这意味着一旦我们的偶数处理程序恢复,我们就无法利用"背景预取"中的并行性.
模式4:可写位图
这里的MSDN文档将其描述为双缓冲系统:您的UI线程更新缓冲区; WPF的渲染线程将其移动到视频内存.
预期用法(参见此处)适用于在显示等视频电影中发生重大变化的位图.我不确定,但可能会被黑客攻击并与背景预取模式结合并在网格场景中使用.
模式5:缓存的位图
关于MSDN的信息不多(这里).在WPF论坛存档(这里),它解释说"BitmapCache API旨在缓存你的内容(当在硬件中渲染时)在视频内存中,这意味着它仍然驻留在你的GPU上.这样可以节省在将内容绘制到屏幕时重新呈现内容的成本."这似乎是一个好主意.但是,我不确定是什么陷阱以及如何使用它.
模式6:RenderTargetBitmap
RenderTargetBitmap将Visual转换为位图.我不确定这里是否相关.看到这里.
编辑:关于Paul Hoenecke的问题:我写过"我的应用程序必须显示许多位图".我没有提到,我需要显示约800图像并发.
人们可以阅读我的问题所涉及的性能问题WPF Bitmap性能以及如何使WPF上的图像显示更加"活泼"?
我已经修改了模式1的描述,以突出显示未创建或删除图像控件的概念(除非我们想要显示更大或更小的网格).只有它们的Sources被设置为不同的,新的或null的BitmapSources.
编辑:这个问题发布在WPF支持论坛上,有MS人员的一些答案.
Dr.*_*ABT 19
除了要求对以下方法发表评论之外,我无法在您的帖子中找到具体问题.我不会声称知道上面的所有内容,但我会告诉你我知道我曾经使用WPF和Silverlight开发高性能UI的工作.
模式0:黑客.将所有组合成一个图像
如果可能的话,我会避免这种情况.听起来你想要显示一个包含数千个小图像的大型包装面板.因此,每个图像都是缩略图(因为您不能同时显示1000个大图像).因此,我主张缓存/调整组合.
模式1:减小图像大小
如果您一次在屏幕上显示1,000张图像,请考虑可用的屏幕空间.平均显示器为1280x1024像素,或略高于130万像素.1000张图像表明每张图像的最大尺寸为1300像素,或36*36.让我们说你的图像大小为32*32.你绝对应该创建一个图像大小的缩略图来在屏幕上渲染,然后点击(或其他动作)显示完整大小的图像.
此外,不仅要考虑调整大图像大小的渲染开销,还要考虑将大图像发送到GPU以调整大小.该数据需要发送带宽.大图像可以是几兆字节,而大小为32*32的缩略图可以是几千字节.
如果您需要动态调整大小,那么您需要尝试创建多个缩略图或动态生成它们.
模式2:背景预取
这是一种我没有听说过的技术,但看起来似乎有道理.您的申请中的开销是多少?是更新Image.Source属性还是创建新图像,细分,执行布局并发送信息以将其呈现给GPU?
以上所有内容都发生在CPU上,但最终渲染除外.通过减少CPU端的开销并更新源,您可能会遇到一些问题.将此与WriteableBitmap结合使用可以进一步提高性能(见下文).
模式3:绘制上下文
好的,所有这一切都允许您使用"OnPaint"样式语法排队保留模式绘制调用,这与旧的GDI OnPaint完全不同.根据我的经验,OnRender并没有提高性能,但它确实允许在绘制时间和时间上实现细粒度的灵活性.OnRender为您提供了一个上下文,它具有DrawImage功能,允许将BitmapSource绘制到渲染管道而无需Image控件.这很好,因为它消除了一些开销,但是引入了类似于Pattern0中看到的问题(你将丢失布局并且必须计算所有图像的位置).如果你这样做,你也可以调用模式0,我建议不要这样做.
模式4:可写位图
WriteableBitmaps在WPF中是一个使用不多且功能非常强大的子系统.我使用它们来创建一个能够实时渲染大量数据的图表组件.我建议查看WriteableBitmapEx codeplex项目Disclosure,我已经为此做过一次贡献,看看你是否可以将它与其他模式结合起来.特别是Blit函数,它允许您将缓存的位图写入图像上的位图源.
例如,一种好的技术可能是模式1 + 2 + 4.
您可以在网格控件中的设置位置屏幕上显示N个图像控件的网格.其中每个都是静态的,不会滚出视图,因此没有创建/删除.现在,最重要的是,调整图像大小并写入WriteableBitmap,该映射在每个图像上设置为Source属性.滚动时,获取下一个/上一个缩略图并使用WriteableBitmapEx.Blit更新源.啪!虚拟化,缓存,多线程成像优势.
模式5:缓存的位图
这是微软尝试做1 + 2 + 4,如上所述.它尝试做的是在布局(CPU侧),曲面细分(CPU侧),向GPU(CPU侧)发送保留模式渲染指令和渲染(GPU侧)它缓存渲染元素的光栅图像之后用于下一个渲染过程.是的,一个关于WPF的一个鲜为人知的事实是,由于它在CPU上完成了大部分工作,所以GPU驱动的引擎非常慢:P
我将尝试使用BitmapCache并查看它的执行情况.有一些警告,它们是当你更新你的UIElement时它必须重新创建缓存,所以静态元素将比动态更好.此外,我没有看到使用它的性能有显着改善,而WriteableBitmap样式技术可以提供一个数量级的改进.
模式6:RenderTargetBitmap
这个最终技术让你可以将UIElement渲染到位图 - 你知道 - 但有趣的是,它可以执行一个糟糕的缩略图生成器(或调整大小).例如,使用全尺寸图像的BitmapSource设置图像.现在将Image控件的大小设置为32*32并渲染为位图.瞧!您可以将BitmapSource缩略图与一些交换(模式2)和/或可写位图结合使用.
好吧最后,只是想说你的要求会将WPF推到极限,但是有办法让它发挥作用.就像我说的那样,我通过使用WriteableBitmap的精彩解决方案构建了一次在屏幕上呈现数千或数百万个元素的系统.沿着标准的WPF路线走下去会导致性能下降,所以你必须做一些异国情调才能解决这个问题.
正如我所说,我的建议是1 + 2 + 4.您必须调整缩略图的大小,我毫不怀疑.拥有静态网格的Image控件和更新源代码的想法非常好.使用WriteableBitmap(特别是WriteableBitmapEx blit函数)来更新源代码的想法也值得探讨.
祝好运!