使用 BitmapSource 的 WPF 渲染性能

发布于 2024-07-19 02:13:15 字数 787 浏览 2 评论 0原文

我创建了一个 WPF 控件(继承自 FrameworkElement),它显示可以平移的平铺图形。 每个图块为 256x256 像素,24bpp。 我已经重写了 OnRender。 在那里,我加载所有新图块(如 BitmapFrame),然后使用 DrawingContext.DrawImage 绘制所有可见图块。

现在,每当每个渲染周期有多个新图块时,帧速率就会从 60fps 下降到零,持续大约一秒钟。 这不是由加载图像(需要几毫秒)引起的,也不是由 DrawImage(根本不需要时间,因为它只填充一些中间渲染数据结构)引起的。

我的猜测是,每当渲染线程获得大量(~20)新的 BitmapSource 实例(即尚未缓存的实例)时,渲染线程本身就会阻塞。 要么花费大量时间将它们转换为某种内部 DirectX 兼容格式,要么可能是缓存问题。 它不能耗尽视频 RAM; Perforator 显示峰值低于 60MB,我有 256MB。 另外,Perforator 表示所有渲染目标都是硬件加速的,所以这也不可能。

任何见解将不胜感激!

预先感谢

丹尼尔

@RandomEngy:
BitmapScalingMode.LowQuality 稍微减少了问题,但并没有消除它。 我已经以预期的分辨率加载图块。 而且不可能是显卡驱动程序,它是最新的(Nvidia)。
我有点惊讶地发现扩展需要这么多时间。 按照我的理解,位图(无论其大小)只是作为 Direct3D 纹理加载,然后进行硬件缩放。 事实上,一旦位图第一次渲染,我就可以改变它的旋转和缩放,而不会再出现任何冻结。

I've created a WPF control (inheriting from FrameworkElement) that displays a tiled graphic that can be panned. Each tile is 256x256 pixels at 24bpp. I've overridden OnRender. There, I load any new tiles (as BitmapFrame), then draw all visible tiles using drawingContext.DrawImage.

Now, whenever there are more than a handful new tiles per render cycle, the framerate drops from 60fps to zero for about a second. This is not caused by loading the images (which takes in the order of milliseconds), nor by DrawImage (which takes no time at all, as it merely fills some intermediate render data structure).

My guess is that the render thread itself chokes whenever it gets a large number (~20) of new BitmapSource instances (that is, ones it had not already cached). Either it spends a lot of time converting them to some internal DirectX-compatible format or it might be a caching issue. It cannot be running out of video RAM; Perforator shows peaks at below 60MB, I have 256MB. Also, Perforator says all render targets are hardware-accelerated, so that can't be it, either.

Any insights would be appreciated!

Thanks in advance

Daniel

@RandomEngy:
BitmapScalingMode.LowQuality reduced the problem a little, but did not get rid of it. I am already loading tiles at the intended resolution. And it can't be the graphics driver, which is up-to-date (Nvidia).
I'm a little surprised to learn that scaling takes that much time. The way I understood it, a bitmap (regardless of its size) is just loaded as a Direct3D texture and then hardware-scaled. As a matter of fact, once the bitmap has been rendered for the first time, I can change its rotation and scale without any further freezes.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

活泼老夫 2024-07-26 02:13:15

这不仅仅是大量的图像。 只需一张大图像就足以在加载之前阻止渲染,当图像尺寸开始增加到数千时,这一点可能会非常明显。

我同意你的观点,这可能是渲染线程的问题:我做了一个测试,UI 线程仍然愉快地调度消息,而渲染延迟是由于尝试显示完全预缓存的 BitmapImage 而发生的。

它一定是在对图像进行某种转换或准备,就像您推测的那样。 我试图在我的应用程序中通过“渲染”但隐藏图像来缓解这种情况,然后在需要显示时显示它。 然而,这并不理想,因为渲染冻结无论如何都会发生。

(编辑)

一些后续工作:在对 MS WPF 别名进行讨论后,我发现了导致延迟的原因。 在我的 Server 2008 计算机上,它是不支持新 WDDM 驱动程序模型的旧视频驱动程序和调整图像大小的延迟的组合。

如果源图像大小与显示大小不同,则会在图像显示之前延迟渲染线程。 默认情况下,图像设置为最高质量,但您可以通过调用 RenderOptions.SetBitmapScalingMode(uiImage, BitmapScalingMode.LowQuality); 来更改渲染的缩放选项。 一旦我这样做了,显示图像之前的神秘冻结就消失了。 如果您不喜欢缩放时的质量下降,另一种方法是加载 BitmapImage,其 DecodePixelWidth/Height 等于其显示的大小。 然后,如果您在后台线程上加载 BitmapImage,则显示它应该没有延迟。

It's not just with a large number of images. Just one large image is enough to hold up rendering until it has been loaded in, and that can be quite noticable when your image dimensions start getting up in the thousands.

I do agree with you that it's probably the render thread: I did a test and the UI thread was still happily dispatching messages while this render delay was taking place from trying to display a fully pre-cached BitmapImage.

It must be doing some sort of conversion or preparation on the image, like you were speculating. I've tried to mitigate this in my app by "rendering" but hiding the image, then revealing it when I need to show it. However this is less than ideal because the rendering freezes happen anyway.

(Edit)

Some followup: After a discussion on the MS WPF alias I found what was causing the delays. On my Server 2008 machine it was a combination of old video drivers that don't support the new WDDM driver model and a delay for resizing the image.

If the source image size is different from the display size, that will delay the render thread before the image shows up. By default an image is set to the highest quality, but you can change the scaling options for rendering by calling RenderOptions.SetBitmapScalingMode(uiImage, BitmapScalingMode.LowQuality); . Once I did that, the mysterious freeze before displaying an image went away. An alternative, if you don't like the quality drop in scaling, is to load the BitmapImage with DecodePixelWidth/Height equal to the size it will be displayed at. Then if you load the BitmapImage on a background thread, you should have no delay in displaying it.

超可爱的懒熊 2024-07-26 02:13:15

也可以尝试这些;

/* ivis is declared in XAML <Image x:Name="iVis" UseLayoutRounding="True" SnapsToDevicePixels="True" /> */

iVis.Stretch = Stretch.None;
RenderOptions.SetBitmapScalingMode(iVis, BitmapScalingMode.NearestNeighbor);
RenderOptions.SetEdgeMode(iVis, EdgeMode.Aliased);
VisualBitmapScalingMode = BitmapScalingMode.NearestNeighbor;
iVis.Source = **** your bitmap source ****

当使用大量的“A”通道颜色时,我在性能方面遇到了一些问题,等到图像按比例渲染后,它对我来说效果更好。

另外,正如您所说,您使用平铺图形?

您通常会使用 TileBrush 来简单地将其设置为画笔框架元素。 如果您要动态设置它们的动画或添加新的画笔,您可以生成画笔,然后在手动操作时将它们应用到您的对象上,如果可以的话,请务必冻结它们。 此外,VisualBitmapScalingMode 是任何 Visual 的属性。

Also try these;

/* ivis is declared in XAML <Image x:Name="iVis" UseLayoutRounding="True" SnapsToDevicePixels="True" /> */

iVis.Stretch = Stretch.None;
RenderOptions.SetBitmapScalingMode(iVis, BitmapScalingMode.NearestNeighbor);
RenderOptions.SetEdgeMode(iVis, EdgeMode.Aliased);
VisualBitmapScalingMode = BitmapScalingMode.NearestNeighbor;
iVis.Source = **** your bitmap source ****

I was having some trouble with performance when using a huge amount of "A" channel color's, waiting until after the image had rendered to scale it worked much better for me.

Also, as you said your using a tiled graphic?

You would usually use a TileBrush to simply set as the Brush on your FrameworkElement. If you are animating them or adding new ones dynamically, you could generate your brushes then apply them to your object as you go manually too, be sure to Freeze them if you can. Also, VisualBitmapScalingMode is a property of any Visual.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文