GDI+ 性能技巧

4

有没有人知道一些可靠(而且希望是全面的)关于 GDI+ 性能讨论的书籍/网站(除了显而易见的内容)?

例如,我最近看到了 这个优秀的实验。我也最近注意到 Graphics.FillPath()Graphics.DrawPath() 快得多。我很想知道还有哪些重要的信息我错过了。

祝好, 大卫

2个回答

21

嗯。如果你需要画路径的轮廓,那么知道FillPath比DrawPath更快是没有意义的!

优化GDI+的最佳方法与优化任何其他代码的方法完全相同:首先不要优化它。而是:

  • 首先编写简单的代码,确保其能够正常工作,然后再决定它是否过慢。
  • 然后检查您的“算法”:
    • 简化您正在绘制的内容(减少您正在绘制的物体数量),这样可以使速度更快,并且大多数情况下减少了混乱感。
    • 您是否每次都在绘制整个显示屏,还是使用剪辑矩形来避免绘制不需要更新的图像部分?
    • 检查您绘制的方式。您是否为每次重新绘制创建和销毁资源(例如画笔和笔刷)?将它们缓存到成员变量中。是否重复多次绘制同一个像素?(例如,在背景上绘制位图,然后在其上绘制矩形 - 您可能可以避免一些重绘)。当窗口滚动时,您是否让操作系统移动现有图像,以便仅需要重新绘制新暴露的条带,还是浪费时间重新绘制整个窗口?
    • 您是否使用转换,还是在代码中进行冗长的定位计算?
    • 检查任何循环,并确保您尽可能将更多代码移出它们 - 预计算您在循环内使用的值等。确保您的循环在CPU缓存友好的方向/方式上遍历您的数据。
    • 在重新绘制期间,您是否正在处理数据中的任何内容?也许其中一些可以预先计算或以更渲染优化的形式组织。例如:使用不同类型的列表枚举数据会更快吗?您是否在处理1000个数据项以查找您需要绘制的10个数据项?
  • 你能用不同的方法实现相同的外观吗?例如,您可以通过绘制 64 个交替黑白的正方形来绘制棋盘。通过连续绘制 32 个黑色和 32 个白色正方形,可以更快地绘制它,以避免矩形之间的状态更改。但是,您实际上可以使用白色背景清除、4 个黑色矩形和 4 个 XOR 矩形来绘制它(而不是 64 个矩形 => 更快的算法)。
  • 图像中是否有不经常更改的部分?尝试将它们缓存到离屏位图中,以最小化每次重绘所需“重构”的数量。请记住,您仍然可以渲染离屏位图,然后在其上叠加图形原语,因此您可能会发现比您意识到的更多“静态”图像区域。
  • 您正在渲染位图图像吗?尝试将它们转换为屏幕的像素格式并以那种“本机”形式缓存它们,而不是让 GDI+ 每次绘制时都进行转换。
  • 如果您对更快的渲染效果感到满意,请降低质量设置的值。
  • 完成所有这些事情后,您可以开始寻找有关优化渲染的书籍。当然,如果仍然太慢。运行分析器以找出渲染中最慢的部分。

    在您认为可能能够获得收益的地方,尝试以不同的方式渲染物体(例如,Graphics.Clear() 很可能比使用 FillRectangle() 填充背景更快),或者不同的渲染顺序(首先绘制所有颜色相同的东西,以防状态更改花费时间 - 用现代图形卡通常很重要。单次调用绘制多个多边形通常比进行多个单多边形调用更快,因此您是否可以将所有多边形累积到延迟渲染缓冲区中,然后在渲染传递的结束时提交它们?)

    之后,您可能不得不考虑使用 GDI 或 DirectX,以更接近硬件。


    嗨,Jason,感谢你的详细回答。让我逐一回答这些问题,或许可以澄清我卡在哪里了: - David Rutten
    1
    听起来你已经解决了许多更明显的问题。你说你正在渲染大量的位图,所以看看是否有任何可以合并的位图(绘制一个大位图通常比绘制两个半大小的位图更快)-可以在预处理中合并,或者只是缓存,这样它们不会每次都从头开始重新绘制。 - Jason Williams
    1
    如果您正在绘制大量对象,则可以考虑限制绘制的数量(例如,在某个缩放级别下,采用简化的渲染方案,以显示更少的细节但保持可接受的性能)。另一个可能性是使用背景或延迟渲染方法(因此在滚动和缩放时呈现非常简化的视图,然后在过程结束时以高质量重新绘制,因此在需要交互性时保留交互性,但当您停止查看显示时,它会显示完整的细节。 - Jason Williams
    1
    另一种类似的方法是将显示区域分成“瓷砖”,因此您可以快速渲染中心(注意力的主要焦点)区域,然后随着时间的推移绘制外围的瓷砖来填充视图。这再次允许高互动性,但只要用户暂停一两秒钟,显示屏就会以高质量填充。 - Jason Williams
    在我的机器上,绘制一个全屏缓存图像需要5毫秒。因此,如果直接绘制到屏幕上需要小于5毫秒,就没有必要像背景网格一样将其缓存起来。所以当平移时会有大约5毫秒的延迟。 - GorillaApe
    显示剩余9条评论

    12

    这可能不是您要找的答案,但请考虑完全不使用GDI+。最近我不得不重写2D-CAM应用程序的渲染堆栈,最重要的要求是快速获得良好的抗锯齿线(抗锯齿是促使我们重写现有的GDI渲染器的原因)。

    下面是200个项目的渲染结果(每个项目本身都包含一些线条和小的填充形状标记)。这些是帧速率(在Windows 7 上),因此数字越高越好:

    200个项目: GDI=51,GDI+=20,D2D=59,WPF=33,GL=59。

    (D2D是Direct2D,GL是OpenGL)。你已经可以看到GDI+落后了。WPF可能在保留模式API方面有些劣势,但是OpenGL也是双缓冲的,看起来同样平滑。

    在1000个项目时,差异更加明显:

    GDI=23,GDI+=5,D2D=17,WPF=2,GL=40。

    没错,WPF现在只有2帧每秒,而GDI+每秒只能爬行5次。所以请考虑D2D,或者简单地回到OpenGL。这就是我们现在正在使用的,重写3个月后,我认为我们做出了正确的选择。编程模型本身比D2D要干净得多。

    请注意,我们使用的WPF渲染已经高度优化;没有回调,没有事件,没有绑定。我们只需获取DrawingContext并在每个帧上使用最低级别的原语将所有内容绘制在其上,因此没有事件开销。

    如果您有兴趣,请告诉我,我可以向您发送我使用的测试套件,以便您可以自己尝试。

    (我可能要避免GDI+的一个原因是它不太可能被硬件加速)。


    Tarydon,恐怕这不再是一个选项了。我已经花了超过2年的时间开发这个图形用户界面,并且其中相当一部分时间都用来编写GDI+代码。在大部分情况下,它的运行速度还算快,但我也遇到过帧率低于10fps的情况。最近我一直在对代码进行性能分析,并发现了一些有趣(即意外)的瓶颈,但我原本希望这方面的工作已经由其他人完成了。不过,还是要给你的帮助加1。谢谢。 - David Rutten
    1
    我对Win7感到困惑,我认为在这个平台上GDI是100%的软件。但是当我看到您的数字甚至比GDI更慢时,我想知道MSDN标记消息背后的真相。目前我只使用GDI来加载/保存图像代码。 - Lothar
    1
    @Lothar 显然他没有正确使用 D2D。 - johnathan
    大多数廉价的显卡,如集成显卡,在OpenGL方面表现非常糟糕。例如GMA950等。 - GorillaApe

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