这是我的第一个问题,但我是一个很长时间的潜伏者。我将把它分成两部分,一部分解释我在做什么以及为什么我认为这是要走的路,第二部分是我自己无法解决的实际问题。
我在做什么? 我目前正在开发一个用于渲染要实时显示的二维特征的框架。您可以在浏览器中考虑像 Google Maps 这样的应用程序,但该框架旨在呈现各种地理数据(不仅仅是轴对齐的栅格数据,如那些 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 条线段,总共约 2 万条线。
将每个段映射到它自己的 StreamGeometry 表现非常糟糕,但我已经预料到了:渲染大约需要 14 秒。
然后我尝试使用多个数字将更多段打包到同一个 StreamGeometry 中:80 StreamGeometry,渲染大约需要 50 毫秒。
但是,我无法得到比这更好的结果。将行数增加到 100k 左右会使我的应用程序几乎无法使用:渲染时间超过 100 毫秒。 除了在渲染矢量数据时冻结几何体和笔,我还能做什么?
我现在宁愿自己使用 DirectX,也不愿依赖 WPF 来做这件事,因为某些事情似乎出了大问题。
编辑
为了进一步澄清我在做什么:该应用程序实时可视化地理数据,非常类似于浏览器中的谷歌地图等应用程序:但是它应该可视化更多的数据。您可能知道,Google Maps 允许缩放和平移,这需要 > 25 FPS 才能显示为流畅的动画;少了什么都感觉不流畅。
* 抱歉,我不应该在实际产品发布之前上传此视频。然而,您可能会想到类似 Google Maps 的东西,但是有大量的矢量数据(多边形和折线)。*
有两种解决方案,其中一种经常被提及:
在位图中缓存重绘图
实现似乎有点简单,但是我发现这种方法存在一些问题:为了正确实现平移,我需要避免在每帧绘制繁重的东西,因此我可以选择在平移时不更新缓存的位图相机,或者创建一个覆盖比视口更大的区域的位图,所以我只需要每隔一段时间更新一次缓存的位图。
第二个“问题”与缩放有关。然而,它更像是一个视觉伪影而不是一个真正的问题:由于缓存的位图无法以 30 FPS 正确更新,因此在缩放时我也需要避免这种情况。我可以在缩放时很好地缩放位图,仅在缩放结束时创建一个新的位图,但是折线的宽度不会具有恒定的厚度,尽管它们应该。
MapInfo 似乎确实使用了这种方法,但是我不能说我太喜欢它了。不过,它似乎是最容易实现的。
将几何图形拆分为不同的绘图视觉效果
这种方法似乎以不同的方式处理问题。我不确定这种方法是否有效:这取决于我是否正确理解 WPF 在该领域应该如何工作。与其对所有需要绘制的东西使用一个 DrawingVisual,我应该使用几个,这样就不是每个都需要 RenderOpened()。我可以简单地更改参数,例如上面示例中的矩阵,以反映相机平移和移动。但是我也看到了这种方法的一些问题:平移相机将不可避免地将新的几何图形带入视口,因此我需要执行与第一种方法类似的操作,实际渲染当前不可见但可能变得可见的东西由于相机移位;
与这两种方法相关的 问题 这两种方法都无法解决的一个大问题是,即使整体帧速率稳定,在更新缓存位图时偶尔也会出现中断(好吧,如果缓存位图仅当相机不再平移时更新)或调用 RenderOpen 绘制可见的几何块,似乎是不可避免的。
到目前为止我的想法
由于这是我见过的解决这个问题的仅有的两个解决方案(我已经做了一年多的谷歌搜索),我想到目前为止唯一的解决方案是接受即使是最强大的 GPU 上的帧率提升(它应该能够每秒光栅化数亿个图元),视口的延迟更新(在位图仅在视口不再移动时更新的情况下)或根本不使用 WPF 并求助于 DirectX直接地。
我很高兴能得到帮助,但我不能说我对 WPF 的渲染性能印象深刻。