【问题标题】:CGContextDrawLayerAtPoint is slow on iPad 3CGContextDrawLayerAtPoint 在 iPad 3 上很慢
【发布时间】:2012-06-19 12:31:21
【问题描述】:

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

- (void) drawRect:(CGRect) rect

问题是:drawRect: 在 iPad 3 上的执行时间比在 iPad 2 上长很多倍(在 iPad 3 上大约 0.1 秒,在 iPad 2 上大约 0.003 秒)。它慢了大约 30 倍。

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

CGContextDrawLayerAtPoint(context, CGPointZero, m_currentLayer);

占用大部分时间(大约占drawRect: 总时间的 95%)

是什么让事情变得如此缓慢,我应该如何解决这个原因?

更新:

没有直接涉及的线程。我确实在一个线程中调用了setNeedsDisplay:,而drawRect: 是从另一个线程调用的,仅此而已。锁也是如此(没有使用锁)。

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

【问题讨论】:

  • 只是一个猜测,但也许您的像素数量突然增加了四倍这一事实与它有关(视网膜显示器和所有)。
  • @borrrden 我认为你是对的,在 iPad 3 上应该有很多工作要做,但整个过程已经慢了大约 30 倍。
  • 我认为没有人可以仅凭这些信息进行合理的猜测。这是否使用多个线程的任何部分?互斥锁?另外,为什么这对您来说是个问题?您的视图是否经常重绘?如果是这样,为什么? DrawLayerAtPoint 可能总是最重的功能(它也在我的应用程序中,虽然我使用的是 iPad 2,所以我不能保证它在第三代的速度)。

标签: ios ipad core-graphics ipad-3


【解决方案1】:

所以,iPad 3 在很多方面肯定要慢一些。我对此有一个理论。 Marco Arment noted 方法 renderInContext 在新 iPad 上速度慢得离谱。在尝试为自定义文本视图创建放大镜时,我也发现了这种情况。最后,我不得不放弃 renderInContext 进行自定义核心图形绘图。

我在此处的核心图形绘图上遇到了可怕的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。然而,要么 GPU 缓冲区自 iPad 2 以来没有变化,要么只是没有使它们变得足够大,因为这些缓冲区很容易过载。发生这种情况时,我相信 CPU 会将图像写入标准内存,然后在 GPU 缓冲区可以处理它时将它们复制到 GPU。我认为这是导致性能问题的原因。额外的副本对于如此多的像素非常耗时,并且会大大减慢速度。

为了避免 memcpy,我推荐几件事:

  1. 只画你需要的东西。不惜一切代价避免在屏幕外画任何东西。如果您正在绘制一个大视图,但只显示该视图的一部分(例如覆盖它的子视图),请尝试找到一种仅绘制可见内容的方法。
  2. 如果您必须绘制大视图,请考虑将视图分解为子视图或子层(在您的情况下可能是子层)。并且只重绘你需要的东西。以知名度应用程序为例。当您放大时,您可以从字面上看到它一次重绘一个正方形。或者在 Safari 中,您可以在滚动时看到它更新方块。不幸的是,我不必这样做,所以我不确定方法。
  3. 尽量保持您的绘图简单。我有一个很棒的自定义核心文本视图,必须在输入的每个字符上重新绘制。非常慢。我将背景更改为简单的白色(在核心图形中),它加速得很好。我最好不要重绘背景。

我想指出我的理论是猜想。苹果并没有真正解释他们到底做了什么。我的理论只是基于他们所说的以及 iPad 的响应方式以及我自己的实验。

更新

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

  1. iOS App Performance: Responsiveness
  2. iOS App Performance: Graphics and Animation

他们谈论的一件事我认为可能对您有所帮助,那就是使用以下方法:setNeedsDisplayInRect:(CGRect)rect。使用此方法而不是普通的 setNeedsDisplay 并确保您的 drawRect 方法仅绘制给定的矩形可以极大地提高性能。就个人而言,我使用以下功能:CGContextClipToRect(context, rect); 将我的绘图仅剪辑到提供的矩形。

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

【讨论】:

  • 感谢您的回答。我认为这是一条有价值的信息,即使我在我的案例中使用了其他建议来源。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 2016-02-06
    • 2012-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-04
    • 1970-01-01
    相关资源
    最近更新 更多