改进DrawingContext性能渲染几何(多边形和折线)

时间:2013-02-11 07:02:08

标签: c# wpf performance drawingcontext

这是我的第一个问题,但是我很长一段时间潜伏着。我将这分为两部分,一部分解释我正在做什么以及为什么我认为这是要走的路,第二部分是我无法为自己解决的实际问题

我在做什么? 我目前正在开发一个用于渲染二维特征的框架,以便实时显示。您可以在浏览器中考虑Google地图等应用程序,但该框架旨在呈现各种地理数据(而不仅仅是轴对齐的栅格数据,如Google Tiles)。

该框架将集成到我们(公司的)最新产品中,该产品是台式机和笔记本电脑的WPF应用程序。

因此我选择WPF实际渲染几何; ;可见性和遮挡剔除由我自己完成,输入处理(鼠标拾取),移动相机等。

作为实时应用程序,它需要达到至少30 FPS。渲染图像时框架表现得足够:我可以每帧绘制几千个位图而不会出现问题,但是多边形数据是一个主要问题。

实际问题 我使用WPF渲染了相当数量的折线和多边形数据,特别是使用DrawingContext和StreamGeometry。到目前为止,我的理解是,如果我需要表现,这就是我要采取的方法。但是,我无法达到我期望的结果。

这就是我用实际数据填充StreamGeometry的方法:

using (StreamGeometryContext ctx = Geometry.Open())
{
        foreach (var segment in segments)
    {
        var first = ToWpf(segment[0]);
        ctx.BeginFigure(first, false, false);

        // Skip the first point, obviously
        List<Point> points = segment.Skip(1).Select(ToWpf).ToList();
        ctx.PolyLineTo(points, true, false);
    }
}
    Geometry.Freeze();

这就是我绘制几何体的方法:

_dc.PushTransform(_mercatorToView);
_dc.DrawGeometry(null, _pen, polyline);
_dc.Pop();

作为测试,我将OpenStreetMap中的ESRI形状加载到我的应用程序中以测试其性能,但是我根本不满意: 我的测试数据包含~3500个线段,总共~20k线。

将每个段映射到它自己的StreamGeometry表现得非常糟糕,但我已经预料到了:渲染大约需要14秒。

然后,我尝试使用多个数字将更多细分包装到同一个StreamGeometry中: 80 StreamGeometry,渲染大约需要50ms。

然而,我无法获得比这更好的结果。将行数增加到大约100k使我的应用程序几乎无法使用:渲染需要超过100毫秒。 在渲染矢量数据时,除了冻结几何图形和笔之外还能做些什么呢?

我现在更倾向于使用DirectX,而不是依赖WPF,因为有些东西似乎非常错误。

修改

进一步阐明我在做什么:应用程序实时显示地理数据,非常就像浏览器中的Google地图应用程序一样:但它应该可视化很多,更多数据。如您所知,Google地图允许缩放和平移,这需要&gt; 25 FPS让它看起来像一个流畅的动画;更少的东西不会流畅。

* 对不起,我不应该在实际产品发布之前上传这个视频。然而,您可以设想像谷歌地图这样的东西,但是有大量的矢量数据(多边形和折线)。 *

有两种解决方案,其中一种经常被陈述:

在位图中缓存大量图纸

实现看起来有点简单,但我发现这种方法存在一些问题:为了正确实现平移,我需要避免每帧绘制繁重的东西,因此我留下了选择在平移摄像机时不更新缓存的位图,或者创建覆盖比视口更大的区域的位图,这样我只需要经常更新缓存的位图。

第二个&#34;问题&#34; 与缩放有关。然而,它更像是一个视觉神器而不是一个真正的问题:由于缓存的位图无法以30 FPS正确更新,因此我需要在缩放时避免这种情况。我可以在缩放时很好地缩放位图,只在缩放结束时创建一个新的位图,但折线的宽度具有恒定的厚度,尽管它们应该。

这种方法似乎确实被MapInfo使用,但是我不能说我太喜欢它了。它似乎是最容易实现的。

将几何体分割为不同的绘图视觉效果

这种方法似乎以不同的方式处理问题。我不确定这种方法是否有效:这取决于我是否正确理解WPF应该如何在这个领域发挥作用。 我不应该使用一个DrawingVisual来绘制所有需要绘制的东西,而应该使用几个,这样每个人都不需要使用RenderOpened()。我可以简单地更改参数,例如上面示例中的矩阵,以反映摄像机平移和移动。 但是我也看到了这种方法的一些问题:平移摄像机将不可避免地将新几何体带入视口,因此我需要执行类似于第一种方法的操作,实际渲染当前不可见的东西,但可能会变得可见由于相机移位;绘制一切都是不可能的,因为对于相当少量的数据可能需要花费大量时间。

与这两种方法相关的问题 这些方法都无法解决的一个大问题是,即使整体帧速率稳定,偶尔也可以在更新缓存的位图时进行连接(好吧,这不适用于缓存的位图仅在摄像机不再平移时更新,或者调用RenderOpen来绘制可见的几何体块,似乎是不可避免的。

到目前为止我的想法

由于这是我遇到过的唯一两个解决方案(我已经用了超过一年的谷歌搜索),我想到目前为止唯一的解决办法是接受帧速率的提升甚至是功能最强大的GPU(应该能够每秒光栅化数亿个基元),延迟更新视口(如果位图只在视口不再移动时更新)或者不使用WPF所有并直接使用DirectX。

我很高兴能得到帮助,但是我无法说到目前为止我对WPF呈现效果印象深刻。

1 个答案:

答案 0 :(得分:2)

要提高2D WPF渲染性能,您可以查看RenderTargetBitmap(对于WPF&gt; = 3.5)或BitmapCache类(对于WPF&gt; = 4)。

这些类用于Cached Composition

来自MSDN

  

通过使用新的BitmapCache和BitmapCacheBrush类,您可以将可视树的复杂部分缓存为位图,从而大大缩短渲染时间。位图仍然响应用户输入,例如鼠标点击,您可以像任何画笔一样将其绘制到其他元素上。