在iPad 3上CGContextDrawLayerAtPoint很慢

时间:2012-06-19 12:31:22

标签: ios ipad core-graphics ipad-3

我的应用中有自定义视图(继承自UIView)。自定义视图覆盖

- (void) drawRect:(CGRect) rect

问题是:{3}在iPad 3上的执行次数比在iPad 2上的执行时间长许多倍(iPad 3约为0.1秒,iPad 2为0.003秒)。它慢了约30倍。

基本上,我正在使用一些预先创建的图层并在drawRect:中绘制它们。最后一次电话

drawRect:

占用大部分时间(约占CGContextDrawLayerAtPoint(context, CGPointZero, m_currentLayer); 总时间的95%)

可能会使事情变得如此缓慢,我该如何解决问题?

更新:

没有直接涉及的主题。我在一个帖子中调用drawRect:,从另一个线程调用setNeedsDisplay:,但就是这样。锁也是如此(没有使用锁)。

视图重新绘制以响应触摸(它是着色书应用程序)。在iPad 2上,我可以在触摸和更新屏幕之间获得合理的延迟。我想在iPad 3上实现同样的目标。

2 个答案:

答案 0 :(得分:7)

因此,iPad 3在许多领域肯定都比较慢。我对此有一个理论。 Marco Arment noted方法renderInContext在新iPad上的速度非常慢。我还发现在尝试为自定义文本视图创建放大镜时就是这种情况。最后,我不得不放弃renderInContext进行自定义Core Graphics绘图。

我在查看核心图形绘制中可怕的wait_fences错误时遇到了问题:Only on new iPad 3: wait_fences: failed to receive reply: 10004003

这是我到目前为止所知道的。 iPad 3显然是驱动像素的4倍。这可能会导致两个问题:

  1. 首先是CPU。所有核心图形绘制都由CPU完成。在旋转事件的情况下,如果CPU花费太长时间来绘制,它会遇到wait_fences错误,我认为这只是一个调用,告诉设备等待一段时间来实际执行旋转,因此延迟。
  2. 将图像传输到GPU。 GPU显然可以很好地处理视网膜分辨率(参见Infinity Blade 2)。但是当核心图形绘制时,它会将图像直接绘制到GPU缓冲区以避免memcpy。但是,自从iPad 2以来,GPU缓冲区都没有变化,或者它们只是没有足够大,因为重载这些缓冲区非常容易。当发生这种情况时,我相信CPU会将图像写入标准内存,然后在GPU缓冲区可以处理它时将它们复制到GPU 。我认为是导致性能问题的原因。额外的副本耗费大量像素并且会大大减慢速度。
  3. 为了避免记忆,我推荐了几件事:

    1. 只绘制你需要的东西。避免不惜一切代价在屏幕外绘图。如果您正在绘制一个大视图,但只显示该视图的一部分(例如,覆盖它的子视图),请尝试找到一种仅绘制可见内容的方法。
    2. 如果您需要绘制一个大视图,请考虑将视图分解为子视图或子图层(在您的情况下可能是子图层)。只重绘你需要的东西。例如,使用notability app。当您放大时,您可以逐字地观看它一次重绘一个方格。或者在safari中,您可以在滚动时观看它更新方块。不幸的是,我没有这么做,所以我不确定这种方法。
    3. 尽量保持简单的图纸。我有一个很棒的自定义核心文本视图,必须重绘每个输入的字符。非常慢。我将背景改为简单的白色(在核心图形中),它加速了。更好的是我不会重新绘制背景。
    4. 我想指出我的理论是猜想。 Apple并没有真正解释他们到底做了什么。我的理论仅仅基于他们所说的内容以及iPad如何响应以及我自己的实验。

      更新

      所以Apple现在发布了2012 WWDC开发者视频。他们有两个视频可以帮助您(需要开发者帐户):

      1. iOS App Performance: Responsiveness
      2. iOS App Performance: Graphics and Animation
      3. 我认为他们谈论的一件事可能对您有所帮助:setNeedsDisplayInRect:(CGRect)rect。使用此方法代替普通setNeedsDisplay并确保您的drawRect方法仅绘制给定的矩形可以极大地帮助提高性能。就个人而言,我使用函数CGContextClipToRect(context, rect);将我的绘图仅剪切到提供的矩形。

        作为一个例子,我有一个单独的类,用于使用Core Text直接将文本绘制到我的视图中。我的UIView子类保留对此对象的引用,并使用它来绘制文本,而不是使用UILabel。我曾经在文本更改时刷新整个视图(setNeedsDisplay)。现在我让我的CoreText对象计算更改的CGRect并使用setNeedsDisplayInRect仅更改包含文本的视图部分。滚动时,这确实有助于我的表现。

答案 1 :(得分:1)

我最终使用了@Kurt Revis answer for similar question中描述的方法。

我最小化了使用的图层数量,添加了UIImageView并将其图像设置为UIImage包裹CGImageRef。请阅读上述答案以获取有关该方法的更多详细信息。

最后,我的应用程序变得比以前更简单,并且在iPad 2和iPad 3上的速度几乎相同。