位图性能优化模式 [英] Bitmap Performance-Optimization Patterns

查看:179
本文介绍了位图性能优化模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现了几种模式来优化WPF中的位图处理。但是,我不知道什么时候使用每种模式。因为我认为这是一个常见的问题,所以我总结了我的理解和想法,并请求你的帮助。如果您可以添加模式,请说明它们的区别,说明他们是否使用 CPU或GPU ,并教导何时使用每个如何组合它们,这将是一个巨大的帮助!



上下文 - 图像网格场景:



我的应用程序必须显示许多位图图像。图像在屏幕上以类似网格的组织(不一定是Grid或UniformGrid类,视窗口媒体播放器的相册视图)显示。图像可能在不同的网格单元之间移动任意单元格中的某些图像可能被其他图像所取代。图像应该是可点击的,应该提供一个上下文菜单,应该是可选择的,可拖动的等等。换句话说,将小bug成为一个大的位图是不适用的,至少不是天真的。



模式0:黑客



将小错误组合到一个位图(如何绘制上下文?) ,并使用它作为背景。覆盖具有空的内容的图像,将处理命中,上下文菜单,事件等。



优点在于我们只谈两个位图:目前显示一个,应该替换它。这应该是真的很快。但是,我多年的经验提高了红旗的危险。您的意见?



模式1:减少图片大小



这是一个no - 当你提前知道图像大小以调整大小时,以及当您准备丢失细节(颜色)以获得性能时:


  1. 使用BitmapImage.DecodePixelWidth
  2. 减少位图大小
  3. 使用FormatConvertedBitmap.DestinationFormat减少颜色信息

  4. 设置控件的缩放行为设置Image.Stretch to Stretch没有一个

  5. 将图像的SetBitmapScalingMode设置为LowQuality。

  6. 冻结bugger

请参阅代码此处



模式2:背景预取



当您认为可以利用用户注意屏幕上的图像,并准备提前显示下一张图像。除了内存开销之外,您的项目的缺点是它必须支持.Net Framework 4目标,而不仅仅是客户端配置文件,因此可能会导致客户端上的安装。你自己将不得不遭受异步编程的痛苦。



在这种模式下,您创建所需数量的图像控件。当位图需要添加,移动或删除时,您只能修改Image控件的BitmapSource。 BackgroundWorker任务负责预取BitmapSource(可能使用上述减小图像大小模式)并将其插入到MemoryCache中。



为此,您必须将BitmapImage的CacheOption设置为OnLoad,以便将工作卸载到后台工作人员。



模式3:绘图上下文



这是MSDN WPF论坛上由Microsoft支持的Sheldon Ziao提出的建议href =http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/b9300929-e32f-4c0c-a871-3e5a45fd5841 =noreferrer> here 。参见Adam Nathan的WPF 4中的第494页第15章2D图形释放了对于DrawingContext的描述。我不能说我明白了根据这里的答案,我会假设这将改善几何图形的处理,而不是位图。接下来,我不认为这将支持图像的焦点和事件要求(我的论坛不能更好地解释要求)此外,我担心本书的总结语句:请注意,使用DrawingContext不会改变您在保留模式系统中运行的事实。指定的图形不会立即发生;这些命令由WPF持久化,直到需要它们为止。这意味着,一旦我们的偶数处理程序被重新使用,我们就不能像背景预取那样利用并行性。



模式4:可写位图



MSDN文档这里将其描述为双缓冲系统:您的UI线程更新缓冲区; WPF的渲染线程将其移动到视频内存。



预期用途(请参阅这里)是用于在诸如显示器的视频电影中改变很多位图。我不确定,但可能会被黑客入侵,并结合使用背景预取模式,并在网格情景中使用。



模式5:缓存位图



MSDN上没有多少信息(这里)。在WPF论坛存档(这里

模式6:RenderTargetBitmap



RenderTargetBitmap将Visual转换为位图。我不知道这是否与此相关。请参阅此处



编辑:关于Paul Hoenecke问题:我写道:我的应用程序必须显示许多位图iage。我没有提到我需要显示大约800张图片并发



我们可以阅读我的问题所涉及的性能问题 WPF位图性能如何在WPF上显示图像更snappy?



我已经修改了模式1的描述,以突出显示未创建或删除图像控件的概念(除非我们要显示更大或更小的网格)。



修改在WPF支持论坛上发布的这个问题,以及MS人员的一些答案

解决方案

除了要求对以下方法的评论外,我在您的帖子中找不到具体问题。我不会声称知道上面的一切,但我会告诉你,我知道在WPF和Silverlight开发高性能UI时一直在做的工作。


图案0:黑客。将全部组合成一个图像


如果可能,我会避免这种情况。听起来你想要显示数以千计的小图像的大型包装面板。因此,每个图像都是缩略图(因为您不能一次显示1000张大图像)。因此,我主张缓存/调整大小组合。


图案1:缩小图片大小


如果您一次在屏幕上显示1000张图像,请考虑可用的屏幕不动产。平均显示器为1280x1024像素,或仅为1.3像素。 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中看到的问题(您将失去布局并且必须计算所有图像的位置)。如果你这样做,你也可以调用Pattern 0,我反对。


模式4:可写位图


WriteableBitmaps是WPF中有一点使用和非常强大的子系统。我使用它们非常有效地创建了能够实时渲染大量数据的图表组件。我建议检查WriteableBitmapEx编码项目披露,我已经贡献了一次,看看是否可以将其与其他模式组合。具体来说,Blit函数可以让您将缓存的位图写入图像上的位图源。



例如,一个好的技术可能是Pattern 1 + 2 + 4。



你可以有一个网格的N图像控件在屏幕上的设置位置在网格控件中。这些都是静态的,不会被滚动出来,所以没有创建/删除。现在,最重要的是调整您的图像大小并写入一个WriteableBitmap,它被设置为每个图像上的Source属性。当您滚动,获取下一个/以前的缩略图,并使用WriteableBitmapEx.Blit更新源。啪!虚拟化,高速缓存,多线程成像优点。


模式5:缓存位图


如上所述,这是微软尝试做1 + 2 + 4的尝试。尝试做的是在布局(CPU侧),细分(CPU Side)之后,将保留模式渲染指令发送到GPU(CPU侧)和渲染(GPU侧),它缓存渲染元素的光栅图像,用于下一个渲染过程。是的,有一个关于WPF的一个有名的事实是,美妙的GPU驱动的引擎是非常慢的,因为它的大部分工作在CPU上:P



我会尝试BitmapCache,看到怎么执行有一些注意事项,当您更新UIElement时,它必须重新创建缓存,因此静态元素的性能远优于动态。此外,我还没有看到使用这方面的性能有显着改善,而WriteableBitmap风格的技术可以提供一个数量级的改进。


模式6:RenderTargetBitmap


最终的技术可以让你将一个UIElement渲染为一个位图 - 你知道 - 但有趣的是这可以执行一个可怜的缩略图生成器(或调整大小)。例如,使用您的全尺寸图像的BitmapSource设置图像。现在将图像控件的大小设置为32 * 32并渲染为位图。瞧!您的BitmapSource缩略图与一些交换(模式2)和/或可写位图结合使用。



最后,只想说出你将要将WPF推到极限的要求,但是有办法让它执行。像我说的,我已经建立了一个系统,通过使用WriteableBitmap的奇妙解决方法,立即在屏幕上呈现了数千或数百万个元素。降低标准的WPF路线将导致性能下降,所以你必须做一些异国情调来解决这个问题。



正如我所说,我的建议是1 + 2 + 4。你必须调整缩略图大小,我毫不怀疑。拥有静态网格图像控件和更新源的想法非常好。使用WriteableBitmap(特别是WriteableBitmapEx blit函数)来更新源的想法也是值得探讨的。



祝你好运!


I found several patterns for optimizing Bitmaps handling in WPF. I do not, however, understand when to use each patterns. As I think this is a common problem, I summarized what I understand and what I guess and ask for your help. If you can add patterns, explain how they differ, explain if they use the CPU or the GPU, and teach when to use each and how to combine them, it’d be a tremendous help!

Context – the Images "Grid" Scenario:

My application has to display many bitmap images. The images are displayed on the screen in a rows-and-columns grid-like organization (not necessarily the Grid or UniformGrid classes, think Window Media Player’s Album view). Images might move between different grid cells. Some images at arbitrary cells may be replaced by others. Images should be clickable, should provide a context menu, should be selectable, drag-able etc. In other words, "combine the little buggers into one big bitmap" is not applicable, at least not naively.

Pattern 0: The Hack

Do combine the little buggers into a bitmap (how? drawing context?), and use this as the background. Overlay this with images with empty content that will handle the hits, context menus, events etc.

The advantage is that we're only speaking about two bitmaps here: The currently displayed one and the one that should replace it. This should be really fast. However, my years of experience raise the red flag of danger. Your comments?

Pattern 1: Reduce Image Size

This is a no-brainer when you know in advance the image size to resize to, and when you’re prepared to lose details (color) for performance:

  1. Reduce the bitmap size using BitmapImage.DecodePixelWidth
  2. Reduce the color information using FormatConvertedBitmap.DestinationFormat
  3. Set the control’s scaling behavior setting Image.Stretch to Stretch.None
  4. Set the SetBitmapScalingMode for the image to LowQuality.
  5. Freeze the bugger

See code here.

Pattern 2: Background pre-fetch

This pattern is applicable when you think you can take advantage of the user gazing at the images on the screen, and prepare ahead the next images to display. The cons for your project, in addition to the memory overhead, is that it has to support the .Net Framework 4 target and not just the client profile, so it might incur an installation on the client’s. You yourself will have to suffer the async programming pain.

In this pattern you create exactly the required number of Image controls. When bitmaps need to be added, moved or deleted you only modify the Image controls' BitmapSource(s). A BackgroundWorker task is responsible for pre-fetching the BitmapSource(s) (possibly using the "Reduce Image Size" pattern above) and inserting them into MemoryCache.

For this to work you have to set the BitmapImage’s CacheOption to OnLoad, so that the work is off-loaded to the background worker.

Pattern 3: Drawing Context

This was suggested by Sheldon Ziao from Microsoft Support on the MSDN WPF forum here. See page 494, Chapter 15 "2D Graphics" in Adam Nathan’s WPF 4 Unleashed for a description of DrawingContext. I can’t say I understand it. According to the answer here, I would assume this would improve handling of Geometry drawings, not bitmaps. Next, I don’t think this will support the focus and events requirements for the images (my bad for not explaining the requirements better at the forum) Moreover, I’m worried by the book’s summary sentence: "Note that the use of DrawingContext doesn’t change the fact that you’re operating within a retained-mode system. The specified drawing doesn’t happen immediately; the commands are persisted by WPF until they are needed." This means that once our even handler re we can’t take advantage of parallelism as in "Background pre-fetch".

Pattern 4: Writeable Bitmaps

The MSDN documentation here describes it as a dual buffer system: Your UI thread updates the buffer; the WPF’s render thread moves this to video memory.

The intended usage (see here) is for bitmaps that change a lot such in a video movie like display. I’m not sure, but possible this could be hacked and combined with the Background Pre-fetch pattern and used in the grid scenario.

Pattern 5: Cached Bitmap

Not much info on the MSDN (here). On the WPF forum archive (here) it’s explained that "The BitmapCache API is designed to cache your content (when rendering in hardware) in video memory, meaning it stays resident on your GPU. This saves you the cost of re-rendering that content when drawing it to the screen." This seems like a great idea. I’m not sure, however, what are the pitfalls and how to use it.

Pattern 6: RenderTargetBitmap

The RenderTargetBitmap converts a Visual to a bitmap. I’m not sure if it’s relevant here. See here.

Edit: Regarding Paul Hoenecke question: I've written that "My application has to display many bitmap iages". I failed to mentions that I need to display about 800 images concurrently.

One can read about the performance issues involved at my SO questions WPF Bitmap performance and How can I make displaying images on WPF more "snappy"?

I've modified the description of pattern 1 to highlight the concept that the image controls aren't created or deleted (unless we want to display a larger or smaller grid). Only their Sources are set to different, new or null BitmapSources.

Edit: This question as posted on the WPF support forum, with some answers from MS personnel.

解决方案

I'm unable to find a specific question in your post, other than asking for comments on the approaches below. I won't claim to know everything above but I'll tell you what I do know having worked for a while developing high-performance UIs using WPF and Silverlight.

Pattern 0: The Hack. Combining all into one image

I'd avoid this if possible. It sounds like you want to display a large wrap-panel of thousands of small images. Each image is therefore a thumbnail (as you cannot display 1000s of large images at once). As a result, I'd advocate caching/resize over combination.

Pattern 1: Reduce Image Size

If you are displaying 1,000 images on screen at once, consider the available screen real-estate. The average monitor is 1280x1024 pixels, or just over 1.3MPixels. 1000 images suggests you will get a maximum size of 1300 pixels per image, or 36*36. Lets say your images are 32*32 in size. You should definitely be creating a thumbnail of that image size to render on screen, then on click (or other action) show the full size image.

Also consider not only the render overhead of resizing a large image, but of sending a large image to the GPU to resize. That data requires bandwidth to send. A large image can be several megabytes whereas a thumbnail of size 32*32 could be a few kilobytes.

If you require dynamic sizing, fine, but you'll need to experiment with creating multiple thumbnails or generating them on the fly.

Pattern 2: Background pre-fetch

This is a technique I've not heard of, however it seems plausible. What is the overhead in your application? is it updating the Image.Source property or creating a new Image, tessellating, performing Layout and sending the information to render it to the GPU?

All the above occur on the CPU except for the final render. By reducing the overhead on the CPU side and updating the source you might be on to something. Combine this with WriteableBitmap as a source and you could further gain a performance improvement (see below).

Pattern 3: Drawing Context

Ok, all this does is allow you to queue up retained mode drawing calls using an "OnPaint" style syntax which is nothing like the old GDI OnPaint. In my experience OnRender doesn't improve performance, but it does allow for fine grained flexibility over what is drawn and when. OnRender provides you with a context, which has a DrawImage function, allowing a BitmapSource to be drawn to the rendering pipeline without the need for an Image control. This is good as it removes some overhead, however introduces problems similar to those seen in Pattern0 (you will lose layout and have to compute position of all your images). If you do this, you might as well invoke Pattern 0, which I advised against.

Pattern 4: Writeable Bitmaps

WriteableBitmaps are a little used and extraordinarily powerful subsystem within WPF. I use them to great effect to create a charting component capable of rendering large amounts of data in real-time. I would suggest checking out the WriteableBitmapEx codeplex project Disclosure, I have contributed to this once and seeing if you can combine it with other patterns. Specifically the Blit function which would let you write a cached bitmap to a bitmap source on an image.

For instance, a good technique might be Pattern 1 + 2 + 4.

You could have a grid of N Image controls on the screen at set locations in a grid control. Each of these is static and doesn't get scrolled out of view so there are no creations/deletions going on. Now, on top of this, resize your image and write to a WriteableBitmap which is set as the Source property on each image. As you scroll, get the next/previous thumbnails and update the sources using WriteableBitmapEx.Blit. Pow! virtualized, cached, multi-threaded imaging goodness.

Pattern 5: Cached Bitmap

This is an attempt by microsoft to do 1+2+4 as I discussed above. What it tries to do is after layout (CPU side), tessellation (CPU Side), sending retained mode render instructions to the GPU (CPU side) and rendering (GPU side) it caches a raster image of the rendered element which is re-used on the next rendering pass. Yes a little known fact about WPF is that wonderful GPU powered engine is terribly slow as it does most of its work on the CPU :P

I would experiment with BitmapCache and see how it performs. There are caveats, and they are that when you update your UIElement it has to recreate the cache so static elements will perform far better than dynamic. Also I've not seen a significant improvement in performance from using this whereas WriteableBitmap style techniques can give an order of magnitude improvement.

Pattern 6: RenderTargetBitmap

This final technique lets you render a UIElement to a bitmap - you know that - but what is interesting is this can perform a poor-mans thumbnail generator (or resize). For instance, set an Image with BitmapSource of your full size image. Now set the size of the Image control to 32*32 and render to bitmap. Voila! You have your BitmapSource thumbnail to use in conjunction with some swapping (Pattern 2) and/or writeable bitmaps.

Ok finally, just wanted to say the requirement you have will push WPF to its limits, however there are ways to get it to perform. Like I said, I have build systems which rendered thousands or millions of elements on the screen at once by using the wonderful workaround that is WriteableBitmap. Going down the standard WPF route will result in performance hell so you will have to do something exotic to solve this.

As I said my recommendation is 1+2+4. You must resize a thumbnail, of that I have no doubt. The idea of having a static grid of Image controls and updating the sources is very good. The idea of using WriteableBitmap (specifically WriteableBitmapEx blit function) to update the sources is also one worth exploring.

Good luck!

这篇关于位图性能优化模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆