iOS CGPath性能

F.X*_*.X. 6 drawing drawrect ios

UPDATE

通过使用OpenGL绘制所有内容,我克服了CG的局限性.还是有一些小故障,但到目前为止它工作得更快,更快.

一些有趣的观点:

  • GLKView:这是一个特定于iOS的视图,它在设置OpenGL上下文和渲染循环方面有很大帮助.如果你不在iOS上,我担心你是独自一人.
  • 着色器精度:当前版本的OpenGL ES(2.0)中着色器变量的精度为16位.这对我来说有点低,所以我模拟了32位算术,其中包含16位变量对.
  • 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");
}
Run Code Online (Sandbox Code Playgroud)

在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");
};
Run Code Online (Sandbox Code Playgroud)

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

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

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
Run Code Online (Sandbox Code Playgroud)

我的理解是,在iOS上这应该快得多,因为代码是原生的...所以,你知道吗:

  • ......我在这里做错了什么?
  • ......如果还有另一个更好的解决方案来实时绘制那么多行?

非常感谢!

Eon*_*nil 0

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

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

更新

我认为如果您尝试通过 OpenGL 显示来显示 UIKit,您很快就会遇到这个问题。不幸的是,我还没有找到解决方案。