【问题标题】:iOS CGPath PerformanceiOS CGPath 性能
【发布时间】:2012-07-12 20:02:16
【问题描述】:

更新

我通过使用 OpenGL 绘制所有内容来解决 CG 的限制。仍然有一些小故障,但到目前为止,它的工作速度要快得多。

一些有趣的点:

  • GLKView :这是一个特定于 iOS 的视图,它对设置 OpenGL 上下文和渲染循环有很大帮助。如果你不在 iOS 上,恐怕你自己一个人。
  • 着色器精度:当前版本的 OpenGL ES (2.0) 中着色器变量的精度为16-bit。这对我的目的来说有点低,所以我用一对 16 位变量模拟了 32 位算术。
  • GL_LINES : OpenGL ES 可以原生绘制简单的线条。不太好(没有关节,没有大写字母,请参见下面屏幕截图顶部的紫色/灰色线),但要改善这一点,您必须编写自定义着色器,将每一行转换为一个三角形条,祈祷它有效! (当浏览器告诉你 Canvas2D 是 GPU 加速时,假设这就是浏览器的做法)

                                                   

  • 尽可能少地画。我认为这是有道理的,但是您可以经常避免渲染例如在视口之外的东西。
  • OpenGL ES 支持填充多边形,因此您必须自己对它们进行细分。考虑使用 iPhone-GLU :这是 MESA 代码的一个端口,它非常好,虽然它有点难使用(没有标准的 Objective-C 接口)。

原始问题

我试图在我的滚动视图的drawRect 方法中绘制大量CGPath(通常超过1000 个),当用户用手指平移时会刷新。我在浏览器的 JavaScript 中有相同的应用程序,我正在尝试将其移植到 iOS 本机应用程序。

iOS 测试代码是(100 行操作,path 是预制的CGMutablePathRef):

- (void) drawRect:(CGRect)rect {
    // Start the timer
    BSInitClass(@"Renderer");
    BSStartTimedOp(@"Rendering");

    // Get the context
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 2.0);
    CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]);
    CGContextSetStrokeColorWithColor(context, [[UIColor blueColor] CGColor]);
    CGContextTranslateCTM(context, 800, 800);

    // Draw the points
    CGContextAddPath(context, path);
    CGContextStrokePath(context);

    // Display the elapsed time
    BSEndTimedOp(@"Rendering");
}

在JavaScript中,供参考,代码为(10000行操作):

window.onload = function() {
  canvas = document.getElementById("test");
  ctx = canvas.getContext("2d");

  // Prepare the points before drawing
  var data = [];
  for (var i = 0; i < 100; i++) data.push ({x: Math.random()*canvas.width, y: Math.random()*canvas.height});

  // Draw those points, and write the elapsed time
  var __start = new Date().getTime();
  for (var i = 0; i < 100; i++) {
    for (var j = 0; j < data.length; j++) {
      var d = data[j];
      if (j == 0) ctx.moveTo (d.x, d.y);
      else ctx.lineTo(d.x,d.y)
    }
  }
  ctx.stroke();
  document.write ("Finished in " + (new Date().getTime() - __start) + "ms");
};

现在,我在优化 JavaScript 方面比在 iOS 方面更加精通,但是,经过一些分析后,与 JavaScript 相比,CGPath 的开销似乎绝对非常糟糕。两个 sn-ps 在真正的 iOS 设备上以大约相同的速度运行,并且 JavaScript 代码的行操作数是 Quartz2D 代码的 100 倍!

编辑:这是 Instruments 中时间分析器的顶部:

Running Time   Self             Symbol Name
6487.0ms       77.8%  6487.0    aa_render
449.0ms        5.3%   449.0     aa_intersection_event
112.0ms        1.3%   112.0     CGSColorMaskCopyARGB8888
73.0ms         0.8%   73.0      objc::DenseMap<objc_object*, unsigned long, true, objc::DenseMapInfo<objc_object*>, objc::DenseMapInfo<unsigned long> >::LookupBucketFor(objc_object* const&, std::pair<objc_object*, unsigned long>*&) const
69.0ms         0.8%   69.0      CGSFillDRAM8by1
66.0ms         0.7%   66.0      ml_set_interrupts_enabled
46.0ms         0.5%   46.0      objc_msgSend
42.0ms         0.5%   42.0      floor
29.0ms         0.3%   29.0      aa_ael_insert

据我了解,这在 iOS 上应该会快得多,仅仅是因为代码是原生的……所以,你知道吗:

  • ...我在这里做错了什么?
  • ...如果有其他更好的解决方案来实时绘制那么多线?

非常感谢!

【问题讨论】:

  • 仪器告诉你什么?
  • 尝试在非滚动视图中绘制它,看看它有多慢。 UIScrollView 有时会使绘图变得很慢。如果是这种情况,您也许可以在普通的 UIView 中进行绘图,然后将该 UIView 放入滚动视图中。此外,如果可能,请确保视图是不透明的。
  • 你说是你的滚动视图的drawRect方法,当用户平移时刷新。这是否意味着每次触摸移动时您都会调用 setNeedsDisplay?
  • 您在 JavaScript 中绘制了一次画布。您尝试在 Objective-C 中每秒最多执行 60 次。 JavaScript 画布 2D 上下文几乎可以肯定是在 Mac 和 iOS 上的 Safari 中的 CGBitmapContext 之上构建的。
  • 愿意分享您创建的 GL 代码作为替代品吗?

标签: ios drawing drawrect


【解决方案1】:

正如您在问题中所描述的,使用 OpenGL 是正确的解决方案。 理论上,你可以用OpenGL模拟各种图形绘制,但你需要自己实现所有形状算法。例如,您需要自己延长线的边角。 OpenGL 中没有 lines 的概念。画线是一种实用功能,几乎只用于调试。您应该将所有内容视为一组三角形。

我相信 16 位浮点数对于大多数绘图来说已经足够了。如果您使用大数坐标,请考虑将空间划分为多个扇区以使坐标数更小。当浮点数变得非常大或非常小时,它的精度就会变差。

更新

如果您尝试在 OpenGL 显示上显示 UIKit,我认为您很快就会遇到这个问题。不幸的是,我也找不到解决方案。

【讨论】:

  • 为了子孙后代接受,OpenGL 是未来的发展方向,但我不知道什么时候适合我们,因为优先事项暂时从 iOS 转移了:)
【解决方案2】:

您使用 CGContextAddPath 破坏了 CGPath 性能。

Apple 明确表示这将运行缓慢 - 如果您希望它快速运行,则需要将 CGPath 对象附加到 CAShapeLayer 实例。

您正在执行动态的运行时绘图 - 阻止了 Apple 的所有性能优化。尝试切换到 CALayer(尤其是 CAShapeLayer),您应该会看到性能大幅提升。

(注意:CG 渲染中还有其他可能影响此用例的性能错误,例如 CG/Quartz/CA 中的模糊默认设置,但是...您需要先摆脱 CGContextAddPath 上的瓶颈)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-30
    • 2011-04-17
    • 2013-03-17
    • 2014-01-19
    • 1970-01-01
    相关资源
    最近更新 更多