DrawingVisual与Canvas.OnRender的性能,适用于许多不断变化的形状

时间:2021-06-15 04:13:18

I'm working on a game-like app which has up to a thousand shapes (ellipses and lines) that constantly change at 60fps. Having read an excellent article on rendering many moving shapes, I implemented this using a custom Canvas descendant that overrides OnRender to do the drawing via a DrawingContext. The performance is quite reasonable, although the CPU usage stays high.

我正在开发一款类似游戏的应用程序,它有多达千种形状(椭圆和线条),不断变换为60fps。阅读了一篇关于渲染许多移动形状的优秀文章后,我使用自定义Canvas后代实现了这一点,该后代覆盖OnRender以通过DrawingContext进行绘制。虽然CPU使用率很高,但性能非常合理。

However, the article suggests that the most efficient approach for constantly moving shapes is to use lots of DrawingVisual instances instead of OnRender. Unfortunately though it doesn't explain why that should be faster for this scenario.

但是,文章表明,不断移动形状的最有效方法是使用大量的DrawingVisual实例而不是OnRender。不幸的是,虽然它没有解释为什么在这种情况下应该更快。

Changing the implementation in this way is not a small effort, so I'd like to understand the reasons and whether they are applicable to me before deciding to make the switch. Why could the DrawingVisual approach result in lower CPU usage than the OnRender approach in this scenario?

以这种方式改变实现并不是一件小事,所以我想在决定进行切换之前了解原因以及它们是否适用于我。为什么在这种情况下,DrawingVisual方法会导致CPU使用率低于OnRender方法?

4 个解决方案

#1


7  

I thought Petzold explains in this paragraph;

我认为Petzold在这一段中解释过;

The ScatterPlotVisual class works by creating a DrawingVisual object for each DataPoint. When the properties of a DataPoint object change, the class only needs to alter the DrawingVisual associated with that DataPoint.

ScatterPlotVisual类的工作原理是为每个DataPoint创建一个DrawingVisual对象。当DataPoint对象的属性发生更改时,该类只需要更改与该DataPoint关联的DrawingVisual。

Which builds on an earlier explanation;

这建立在早先的解释之上;

Whenever the ItemsSource property changes, or the collection changes, or a property of the DataPoint objects in the collection changes, ScatterPlotRender calls InvalidateVisual. This generates a call to OnRender, which draws the entire scatter plot.

每当ItemsSource属性更改,或者集合发生更改,或者集合中的DataPoint对象的属性发生更改时,ScatterPlotRender都会调用InvalidateVisual。这会生成一个OnRender调用,它会绘制整个散点图。

Is this what your asking about?

这是你要问的吗?

By the way, this is a fairly recent high-performance WPF tutorial, many tens of thousands of points in that plot, it is 3D rendered and animated also (even uses mouse input to drive some of the transforms).

顺便说一句,这是一个相当新的高性能WPF教程,在该绘图中有数万个点,它也是3D渲染和动画(甚至使用鼠标输入来驱动一些变换)。

#2


15  

From Pro WPF in C# 2008:

来自C#2008中的Pro WPF:

The problem posed by these applications isn't the complexity of the art, but the sheer number of individual graphic elements. Even if you replace your Path elements with lighter weight Geometry objects, the overhead will still hamper the application's performance. The WPF solution for this sort of situation is to use the lower-level visual layer model. The basic idea is that you define each graphical element as a Visual object, which is an extremely lightweight ingredient that has less overhead than a Geometry object or a Path object.

这些应用程序带来的问题不是艺术的复杂性,而是单个图形元素的绝对数量。即使用较轻的Geometry对象替换Path元素,开销仍然会妨碍应用程序的性能。针对这种情况的WPF解决方案是使用较低级别的可视层模型。基本思想是将每个图形元素定义为Visual对象,这是一个非常轻量级的成分,其开销比Geometry对象或Path对象少。

What it boils down to is that every single one of those ellipses and lines you're creating is a separate FrameworkElement; that means it supports not only hit testing, but also layout, input, focus, events, styles, data-binding, resources, and animation. That's a pretty heavy-weight object for what you're trying to do! The Visual object skips all of that and inherits directly from DependencyObject. It still provides support for hit-testing, coordinate transformation, and bounding-box calculations, but none of the other stuff that the shapes support. It's far more lightweight and would probably improve your performance immensely.

它归结为你正在创建的那些省略号和行中的每一个都是一个单独的FrameworkElement;这意味着它不仅支持命中测试,还支持布局,输入,焦点,事件,样式,数据绑定,资源和动画。对于你想要做的事情来说,这是一个非常重量级的对象! Visual对象跳过所有这些并直接从DependencyObject继承。它仍然支持命中测试,坐标转换和边界框计算,但没有形状支持的其他东西。它的重量更轻,可能会极大地提高您的性能。

EDIT:

编辑:

Ok, I misread your question the first time around.

好的,我第一次误读了你的问题。

In the case that you are using OnRender, it really depends how you are creating the visuals and displaying them. If you are using a DrawingContext and adding all of the visuals to a single element, this is no different than using the DrawingVisual approach. If you were creating a separate element for each Visual created, then this would be a problem. It seems to me that you are doing things the right way.

在您使用OnRender的情况下,它实际上取决于您如何创建视觉效果并显示它们。如果您正在使用DrawingContext并将所有视觉效果添加到单个元素,则这与使用DrawingVisual方法没有什么不同。如果您为每个创建的Visual创建一个单独的元素,那么这将是一个问题。在我看来,你正在以正确的方式做事。

#3


8  

Everyone in the answers got it wrong. The question is whether rendering shapes directly in the drawing context is faster than creating DrawingVisual. The answer is obviously 'yes'. Functions such as DrawLine, DrawEllipse, DrawRectangle etc. do not create any UI Element. DrawingVisual is much slower because it does create a UI Element, although a lightweight one. The confusion in the answers is because people simply copy/paste the DrawingVisual performs better than distinct UIElement shapes statement from MSDN.

答案中的每个人都错了。问题是在绘图上下文中直接渲染形状是否比创建DrawingVisual更快。答案显然是'是'。 DrawLine,DrawEllipse,DrawRectangle等函数不会创建任何UI元素。 DrawingVisual要慢得多,因为它确实创建了一个UI元素,尽管它是一个轻量级元素。答案中的混淆是因为人们只是复制/粘贴DrawingVisual比MSDN中的不同UIElement形状语句表现更好。

#4


2  

In my tests however (panning animations), I notice no difference in speed. I would say that using a host element for many drawing visuals is a bit faster. This approach where you build your visual tree with many visuals gives you more control. Moreover, when you want to do a complex hit testing, the filtering process is faster because you can skip entire "branches" of visuals

然而,在我的测试中(平移动画),我注意到速度没有区别。我会说使用主机元素来绘制很多视觉效果要快一些。这种使用许多视觉效果构建可视树的方法可以让您获得更多控制权。此外,当您想要进行复杂的命中测试时,过滤过程会更快,因为您可以跳过视觉效果的整个“分支”

#1


7  

I thought Petzold explains in this paragraph;

我认为Petzold在这一段中解释过;

The ScatterPlotVisual class works by creating a DrawingVisual object for each DataPoint. When the properties of a DataPoint object change, the class only needs to alter the DrawingVisual associated with that DataPoint.

ScatterPlotVisual类的工作原理是为每个DataPoint创建一个DrawingVisual对象。当DataPoint对象的属性发生更改时,该类只需要更改与该DataPoint关联的DrawingVisual。

Which builds on an earlier explanation;

这建立在早先的解释之上;

Whenever the ItemsSource property changes, or the collection changes, or a property of the DataPoint objects in the collection changes, ScatterPlotRender calls InvalidateVisual. This generates a call to OnRender, which draws the entire scatter plot.

每当ItemsSource属性更改,或者集合发生更改,或者集合中的DataPoint对象的属性发生更改时,ScatterPlotRender都会调用InvalidateVisual。这会生成一个OnRender调用,它会绘制整个散点图。

Is this what your asking about?

这是你要问的吗?

By the way, this is a fairly recent high-performance WPF tutorial, many tens of thousands of points in that plot, it is 3D rendered and animated also (even uses mouse input to drive some of the transforms).

顺便说一句,这是一个相当新的高性能WPF教程,在该绘图中有数万个点,它也是3D渲染和动画(甚至使用鼠标输入来驱动一些变换)。

#2


15  

From Pro WPF in C# 2008:

来自C#2008中的Pro WPF:

The problem posed by these applications isn't the complexity of the art, but the sheer number of individual graphic elements. Even if you replace your Path elements with lighter weight Geometry objects, the overhead will still hamper the application's performance. The WPF solution for this sort of situation is to use the lower-level visual layer model. The basic idea is that you define each graphical element as a Visual object, which is an extremely lightweight ingredient that has less overhead than a Geometry object or a Path object.

这些应用程序带来的问题不是艺术的复杂性,而是单个图形元素的绝对数量。即使用较轻的Geometry对象替换Path元素,开销仍然会妨碍应用程序的性能。针对这种情况的WPF解决方案是使用较低级别的可视层模型。基本思想是将每个图形元素定义为Visual对象,这是一个非常轻量级的成分,其开销比Geometry对象或Path对象少。

What it boils down to is that every single one of those ellipses and lines you're creating is a separate FrameworkElement; that means it supports not only hit testing, but also layout, input, focus, events, styles, data-binding, resources, and animation. That's a pretty heavy-weight object for what you're trying to do! The Visual object skips all of that and inherits directly from DependencyObject. It still provides support for hit-testing, coordinate transformation, and bounding-box calculations, but none of the other stuff that the shapes support. It's far more lightweight and would probably improve your performance immensely.

它归结为你正在创建的那些省略号和行中的每一个都是一个单独的FrameworkElement;这意味着它不仅支持命中测试,还支持布局,输入,焦点,事件,样式,数据绑定,资源和动画。对于你想要做的事情来说,这是一个非常重量级的对象! Visual对象跳过所有这些并直接从DependencyObject继承。它仍然支持命中测试,坐标转换和边界框计算,但没有形状支持的其他东西。它的重量更轻,可能会极大地提高您的性能。

EDIT:

编辑:

Ok, I misread your question the first time around.

好的,我第一次误读了你的问题。

In the case that you are using OnRender, it really depends how you are creating the visuals and displaying them. If you are using a DrawingContext and adding all of the visuals to a single element, this is no different than using the DrawingVisual approach. If you were creating a separate element for each Visual created, then this would be a problem. It seems to me that you are doing things the right way.

在您使用OnRender的情况下,它实际上取决于您如何创建视觉效果并显示它们。如果您正在使用DrawingContext并将所有视觉效果添加到单个元素,则这与使用DrawingVisual方法没有什么不同。如果您为每个创建的Visual创建一个单独的元素,那么这将是一个问题。在我看来,你正在以正确的方式做事。

#3


8  

Everyone in the answers got it wrong. The question is whether rendering shapes directly in the drawing context is faster than creating DrawingVisual. The answer is obviously 'yes'. Functions such as DrawLine, DrawEllipse, DrawRectangle etc. do not create any UI Element. DrawingVisual is much slower because it does create a UI Element, although a lightweight one. The confusion in the answers is because people simply copy/paste the DrawingVisual performs better than distinct UIElement shapes statement from MSDN.

答案中的每个人都错了。问题是在绘图上下文中直接渲染形状是否比创建DrawingVisual更快。答案显然是'是'。 DrawLine,DrawEllipse,DrawRectangle等函数不会创建任何UI元素。 DrawingVisual要慢得多,因为它确实创建了一个UI元素,尽管它是一个轻量级元素。答案中的混淆是因为人们只是复制/粘贴DrawingVisual比MSDN中的不同UIElement形状语句表现更好。

#4


2  

In my tests however (panning animations), I notice no difference in speed. I would say that using a host element for many drawing visuals is a bit faster. This approach where you build your visual tree with many visuals gives you more control. Moreover, when you want to do a complex hit testing, the filtering process is faster because you can skip entire "branches" of visuals

然而,在我的测试中(平移动画),我注意到速度没有区别。我会说使用主机元素来绘制很多视觉效果要快一些。这种使用许多视觉效果构建可视树的方法可以让您获得更多控制权。此外,当您想要进行复杂的命中测试时,过滤过程会更快,因为您可以跳过视觉效果的整个“分支”