2D绘图性能(GDI+ vs SlimDX)

10
我是一个团队的一部分,我们创建了一个工具来查看和与C# / WPF中的非常大且高度相互关联的图表进行交互。通过一个自定义控件来查看和与图表进行交互,该控件接收一组DrawingVisuals并在画布上显示它们。图表中的节点可以使用我们的编辑器创建自定义形状。当前控件运行良好,与我们的程序相当耦合,但考虑到更大的图表(20,000+个节点和许多连接)时,存在性能方面的合理担忧。
经过一些研究,似乎有两种方法:
1. GDI +路线,其中将图形绘制到WriteableBitmap或InteropBitmap中。 2. SlimDX或DirectX变体(托管在D3DImage中)
考虑以下因素,哪种方法最好:
1. 即使查看整个图表,与图表的交互也必须快速。 2. 更新视觉效果应该很快(颜色或大小更改)。 3. 命中测试必须快速(点和矩形)。 4. 必须及时完成开发。
你会选择哪种方法?为什么?
编辑:
看起来有一个相似的问题被提出了,但没有得到回答。

GDI+慢。GDI快但丑陋。D2D更好但较慢。SlimDX是一个巨大的依赖项和大量的抽象学习曲线。第三个选择是使用DX9或者(通过DXGI)DX11,因为它与WPF D3DImage接口进行握手。虽然有一定的学习曲线,但是创建3DX设备、上下文、缓冲区、着色器并通过Direct3D渲染您的应用程序更易于维护。文档很糟糕,需要进行一些研究和尝试错误,但并没有看起来那么糟糕。点击这里下载一个真实的小型18MB演示文件,包括WPF WinForm和MFC,其中主要是WAV和GIS数据。 - Robert
3个回答

10
我会翻译中文。以下是需要翻译的内容:

我在我的制图应用程序中使用了GDI。虽然GDI+比DirectX等其他引擎速度慢,但我发现有很多技巧和方法可以用来加速。在绘制数据之前,大量CPU用于准备数据本身,因此GDI不应该是唯一的瓶颈。

需要注意的事项(这些内容足够通用,适用于其他图形引擎):

首先:进行度量。使用分析器查看代码中真正的瓶颈在哪里。 重用GDI原语。这非常重要。如果您必须绘制100,000个外观相同或相似的图形对象,请使用相同的PenBrush等。创建这些原语是昂贵的。 缓存渲染数据 - 例如:如果您不需要重新计算gfx元素位置,则不要重新计算。 在平移/缩放时,使用较低的GDI+质量(并且不使用昂贵的GDI操作)绘制场景。有许多Graphics对象设置可以降低质量。用户停止平移后,再以高质量绘制场景。 还有很多小细节可以提高性能。我已经开发了这个应用程序2-3年了(或者已经4年了?),现在我仍然找到改进事物的方法:)。这就是为什么度量很重要 - 代码会改变,这会影响性能,因此您需要对新代码进行度量。

另外一件事:我没有使用过SlimDX,但我尝试了Direct2D(我指的是Microsoft.WindowsAPICodePack.DirectX.Direct2D1)。在我的情况下,性能比GDI+快得多,但是我在渲染位图方面遇到了一些问题,从未有时间找到正确的解决方案。


项目负责人对采用GDI+/Bitmap技术路线持谨慎态度,因为他过去的经验表明它速度较慢(他可能是对的或错的)。他希望有一个基于性能的解决方案,并且一直认为DirectX是解决方案。您认为GDI+在30fps和与100k个对象交互时足够快吗? - CBent
@CBent:很难说,这非常取决于你正在绘制的内容的性质(当然还有硬件)。但是我刚刚偶然发现了一些东西,将会在我的应用程序中进行实验:http://code.google.com/p/sharpdx/。声称是最快的托管DirectX库-如果它比WindowsAPICodePack更快,那么它真的是真正快速的! - Igor Brejc
@CBent: 对,如果你需要快速图形处理,通常使用DirectX是一个很好的选择,所以我想你的项目领导的想法是正确的。我选择了GDI+路线,因为它是跨平台的——我的应用程序在Linux和Mac上运行。 - Igor Brejc
感谢您的贡献。事实上,我已经开始尝试使用SharpDX了。不幸的是,学习曲线非常陡峭,并且我们公司没有具备这种经验的开发人员,所以我只能自己学习。我们的应用程序仅针对Windows,因此我们不必担心可移植性问题。 - CBent
晚了。你使用双精度还是单精度的数字? - GorillaApe

3

最近我将一些绘图代码移植到了DirectX上,并对结果感到非常满意。我们主要使用位操作渲染位图,而现在的渲染时间可以缩短到1-2秒,而之前渲染时间可能需要数分钟。

虽然我们是从C++中的位操作转移到了C#中的SlimDX Direct3D,因此不能直接与您的用法进行比较,但我相信您也会看到性能提升,即使它们并不像我们看到的那样成倍增长。

我建议您尝试使用SlimDX的Direct2D。由于某些原因,您需要使用DirectX 10.1,因为Direct2D与DirectX 11不兼容。如果您已经使用过WPF的绘图API,则应该已经熟悉Direct2D,因为其API基于WPF绘图API。Direct2D的主要问题是缺乏文档,并且仅适用于Vista及以后版本。

我没有尝试过DirectX 10/WPF互操作,但我认为这是可能的(https://dev59.com/2UjSa4cB1Zd3GeqPItAx

编辑:我想给您一个比较,来自我们绘制简单多边形的代码。首先是WPF版本:

        StreamGeometry geometry = new StreamGeometry();

        using (StreamGeometryContext ctx = geometry.Open())
        {
            foreach (Polygon polygon in mask.Polygons)
            {
                bool first = true;

                foreach (Vector2 p in polygon.Points)
                {
                    Point point = new Point(p.X, p.Y);

                    if (first)
                    {
                        ctx.BeginFigure(point, true, true);
                        first = false;
                    }
                    else
                    {
                        ctx.LineTo(point, false, false);
                    }
                }
            }
        }

现在是Direct2D版本:
        Texture2D maskTexture = helper.CreateRenderTexture(width, height);

        RenderTargetProperties props = new RenderTargetProperties
        {
            HorizontalDpi = 96,
            PixelFormat = new PixelFormat(SlimDX.DXGI.Format.Unknown, AlphaMode.Premultiplied),
            Type = RenderTargetType.Default,
            Usage = RenderTargetUsage.None,
            VerticalDpi = 96,
        };

        using (SlimDX.Direct2D.Factory factory = new SlimDX.Direct2D.Factory())
        using (SlimDX.DXGI.Surface surface = maskTexture.AsSurface())
        using (RenderTarget target = RenderTarget.FromDXGI(factory, surface, props))
        using (SlimDX.Direct2D.Brush brush = new SolidColorBrush(target, new SlimDX.Color4(System.Drawing.Color.Red)))
        using (PathGeometry geometry = new PathGeometry(factory))
        using (SimplifiedGeometrySink sink = geometry.Open())
        {
            foreach (Polygon polygon in mask.Polygons)
            {
                PointF[] points = new PointF[polygon.Points.Count()];
                int i = 0;

                foreach (Vector2 p in polygon.Points)
                {
                    points[i++] = new PointF(p.X * width, p.Y * height);
                }

                sink.BeginFigure(points[0], FigureBegin.Filled);
                sink.AddLines(points);
                sink.EndFigure(FigureEnd.Closed);
            }

            sink.Close();
            target.BeginDraw();
            target.FillGeometry(geometry, brush);
            target.EndDraw();
        }

正如您所看到的,Direct2D版本需要稍微多做一些工作(并且依赖于我编写的几个帮助函数),但实际上它非常相似。


我希望通过使用DirectX改进我们的应用程序渲染效果,目前正在考虑使用SharpDX。我们绘制简单的线条和2D多边形(但是它们太多了,以至于WPF开始出问题)。你用什么资源进行SlimDX研究? - CBent
很抱歉,这是一年前完成的,所以我无法记住我使用的确切资源。不过,SlimDX非常接近Direct2D COM API,因此任何C++中的示例都可以轻松转换为C# / SlimDX,这就是我所做的。 - Grokys
哇!我刚刚收到了你编辑过的帖子,其中包含代码。它们确实非常相似。你是否需要更改任何多边形格式以使它们与SlimDX兼容?你的应用程序是全屏应用程序、单个DX表面,还是同时显示许多表面? - CBent
我的应用程序实际上是一个离线渲染器,它将渲染结果输出到.png文件,因此实际上没有在屏幕上显示任何内容。我使用的是由SlimDX.Vector2结构组成的多边形格式,其中使用浮点数,不过如果你不介意额外开销的话,也可以使用WPF多边形对象并在运行时将双精度转换为浮点数。 - Grokys
@Groky,您能否请发一下这行代码后面的代码: Texture2D maskTexture = helper.CreateRenderTexture(width, height);我也在尝试做同样的事情,但我的RenderTexture创建方法出现了一些错误,我无法解决:http://stackoverflow.com/questions/22934324/e-invalidarg-an-invalid-parameter-was-passed-to-the-returning-function - Ivan Peric
显示剩余3条评论

1

让我试着列出每种方法的优缺点,这或许能帮助你决定使用哪种方法。

GDI 优点

  • 易于绘制矢量图形
  • 无需包含额外的库

GDI 缺点

  • 比 DX 慢
  • 需要限制“花哨”的绘图(如渐变等),否则可能会减慢速度
  • 如果图表需要交互性,则可能不是一个很好的选择

SlimDX 优点

  • 可以进行一些花哨的绘图,同时比 GDI 更快
  • 如果绘图具有交互性,则此方法将更好
  • 由于您绘制基元,因此可以在每个缩放级别上控制质量

SlimDX 缺点

  • 不太容易绘制简单的形状-您需要编写自己的抽象或使用帮助您绘制形状的库
  • 如果您以前没有使用过它,那么使用起来不像 GDI 那样简单

也许还有更多我忘记放在这里的内容,但这些或许已经足够了吧?

-A.


谢谢您的回复!您有使用SlimDX(wpf集成)的经验吗?它可以渲染的物品数量是否有上限?我正在尝试权衡SlimDX的学习曲线和性能优势与GDI路线之间的差异。 - CBent
我相信SlimDX有一个WPF互操作示例(http://code.google.com/p/slimdx/source/browse/trunk/samples/Direct3D10/WpfSample10/)。此外,虽然SlimDX / DirectX可以呈现的内容没有上限,但请记住,性能只取决于显卡性能和渲染效率 - 实际上,这对GDI和DX都是如此。如果性能不符合您的需求,我建议首先选择GDI路线,然后采用SlimDX方案。 - Ani
我很好奇你为什么推荐使用“GDI”而不是SlimDX。 SlimDX的学习曲线有那么高吗?实现GDI方式(渲染,快速空间选择和集成)可能会浪费时间,因为最初采用GPU加速路线就可以解决问题。再次感谢您的回复。 - CBent
我建议一开始使用GDI,因为使用SlimDX时,你需要编写比使用GDI更多的低级绘图和管理代码,除非有充分理由,否则个人不会选择这条路。我建议进行一些数字和负载实验,以确保最终应用程序中可能遇到的情况。 - Ani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接