首页 > 解决方案 > CGContextDrawPath 挂在 aa_distribute_edges

问题描述

用户报告我的应用程序冻结并且他必须强制退出。该应用程序不会崩溃,它只是挂起。他给我发了文件和条件,但我无法在我的电脑上重现这个挂起。他给我发了很长的崩溃日志,但似乎问题发生在 CGContextDrawPath 期间。下边是

 17 CGContextDrawPath + 199(CoreGraphics + 323893)[0x7fff515da135] 1 - 17
         17 ripc_DrawPath + 318(CoreGraphics + 324473)[0x7fff515da379] 1 - 17
         17 ripc_Render + 381(CoreGraphics + 99778)[0x7fff515a35c2] 1 - 17
         17 RIPRenderCoverage + 1639(CoreGraphics + 101667)[0x7fff515a3d23] 1 - 17
         17 aa_render + 632(CoreGraphics + 559938)[0x7fff51613b42] 1 - 17
         1 aa_distribute_edges + 436(CoreGraphics + 404181)[0x7fff515edad5](running) 1
         2 aa_distribute_edges + 302(CoreGraphics + 404047)[0x7fff515eda4f](running) 2 - 3
         1 aa_distribute_edges + 436(CoreGraphics + 404181)[0x7fff515edad5](running) 4
         5 aa_distribute_edges + 302(CoreGraphics + 404047)[0x7fff515eda4f](running) 5 - 9
         1 aa_distribute_edges + 436(CoreGraphics + 404181)[0x7fff515edad5](running) 10
         3 aa_distribute_edges + 302(CoreGraphics + 404047)[0x7fff515eda4f](running) 11 - 13
         2 aa_distribute_edges + 436(CoreGraphics + 404181)[0x7fff515edad5](running) 14 - 15
         1 aa_distribute_edges + 302(CoreGraphics + 404047)[0x7fff515eda4f](running) 16
         1 aa_distribute_edges + 436(CoreGraphics + 404181)[0x7fff515edad5](running) 17

发生这种情况的例程是绘制一个图,路径只是点之间的线。用户告诉我,对于点数较少的地块,问题就消失了。挂起应用程序的情节有60,000或更多点。我向他发送了一个版本,该版本在此代码的不同位置写入日志文件,似乎冻结发生在 CGContextDrawPath 内部。到达该行代码后,日志中不会出现任何内容。我无法在无法访问他的计算机的情况下进行调试。任何人都有任何建议。我有其余的崩溃(挂起)日志,如果有帮助的话。

更新:2019 年 8 月 12 日虽然我无法在我的 macbook 上重现这个“挂起”,但我确实安装了 xcode 并在我妻子的 macbook 上运行了该项目,并且可以在 Xcode 中重现这个“挂起”。导致挂起的代码是

CGPathRef path = CreatePath();
if(path) {
    CGContextAddPath(context, path);
    CFRelease(path);
    CGContextSaveGState(context);
    CGContextSetStrokeColorWithColor(context, foreColor.CGColor);
    CGContextSetLineWidth(context,0.75);
    CGContextDrawPath(context, kCGPathStroke);
    CGContextRestoreGState(context);
    return true;
}

CreatePath() 函数是一个 MoveTo,后跟一个 LineTo 循环。我使用 CGPathApply 并打印出整个元素列表,每个元素正是我所期望的,即 MoveTo 后跟一堆 LineTo。

代码挂在 CGContextDrawPath 中,当我在 Xcode 中暂停应用程序时,它总是在“aa_distribute_edges”内

我不仅无法弄清楚导致挂起的原因,而且我也不知道为什么相同的代码可以在没有挂在我的笔记本电脑上的情况下工作。

更新:2019 年 8 月 13 日仍然卡住。在两台计算机上运行 Xcode,我可以进入汇编代码并按照相同的应用程序绘制相同的文档步骤,一直到 RIPRenderCoverage。但是在这个例程中,没有挂起的计算机不会进入 aa_render,而挂起的计算机会进入 aa_render。一旦进入 aa_render,挂起的计算机就会进入 aa_distribute_edges,然后似乎进入了一个看似无限循环的状态。

天哪,我难住了。在这里迫切需要任何建议。

更新:2019 年 8 月 13 日(下午)。刚刚发现,如果我减少线宽,即 CGContextSetLineWidth(context,0.5),而不是我上面使用的,我可以避免挂起。我对 Core Graphics 有什么不了解的地方?

更新:2019 年 8 月 19 日。我发现挂起取决于所使用的显示器。具体来说,视网膜显示器挂起。如果我将 NSHighResolutionCapable 添加到应用程序的 info.plist 并将其设置为 NO,那么问题就会消失,我可以使用任何线宽。因此,我的代码中的某些内容对于高分辨率绘图是错误的。

更新:2019 年 8 月 24 日。我确实让它“挂”了一个多小时,但它从未在高分辨率模式下完成。关闭 NSHighResolutionCapable 的相同绘制是即时的(几毫秒)。如果我在每个点绘制一个小圆圈而不是用线条连接,我也会对 NSHighResolutionCapable 感到困惑。

更新:2019 年 9 月 6 日。关闭 NSHighResolutionCapable 可以防止我的应用程序在 Retina 显示器上崩溃,但用户抱怨现在在 Retina 显示器上的窗口看起来有点模糊。

标签: cgcontextcgpath

解决方案


我认为 Core Graphics 正遭受严重的透支情况

这个答案是基于您在问题中提供的证据的推测性猜测。

我相信这个挂起是一个长期挂起,但不是完全死锁,基于我读到的类似报告,在 macOS 上 R 语言的绘图性能很差

假设您的问题是相似的,那么如果您让它运行很长时间,它最终会呈现。

在 CoreGraphics/Quartz2D 中,我认为绘图发生在 CPU 上。也就是说,我无法从Apple 当前的文档中找到关于 Core Graphics 是使用 CPU 还是 GPU 的确切信息,但是有一些旧信息说只有一种名为 Quartz Extreme 的旧技术才允许在 Core Graphics 中进行 GPU 渲染,但是默认情况下被禁用。由于挂起是在一个名为的函数中ripc_Render,它看起来确实是在 CPU 上渲染线条。

由于无法检查 Core Graphics 的源代码,我将不得不推测,但这是我认为正在发生的事情:

  1. 当您调用 时CGContextAddLineToPoint,它会将一个新点附加到一个点数组中。到目前为止,工作O(n)相对于点数是线性的。
  2. 调用CGContextDrawPath将数据发送到 CoreGraphics CPU 渲染器,它本质上是渲染位图图像。渲染线条所需的工作是多于 n(点数)的函数,它也是线条宽度、屏幕密度和线条长度的函数。这就是为什么减少线宽或在非视网膜屏幕上运行可以提高性能的原因。

解决方案:减少积分

4K 显示器的分辨率为 3840x2160 (WxH)。如果您正在绘制一个函数(在数学意义上,这意味着所有 X 值都是唯一的),那么您只能在 4K 显示器上显示这些点中的 3840 个而不重叠。

一旦你的点数超过了屏幕上的容量,通过减少调用来消除一些路径点CGContextAddLineToPoint。您可以使用以下(未经测试的)puedo 代码算法之一,其中DP[]是您的数据点数组,WxH 是以像素为单位的屏幕分辨率。

  • SKIP:如果DP.length > W,则只绘制每个(DP.length/W)数据点
  • AVERAGE:如果DP.length > W,则绘制W数据点,其中每个WDP.length/W数据点的平均值。
  • RANGE BAR:如果,则在每组数据点DP.length > W的最小值和最大值之间画一条垂直线。YDP.length/W

使用任何一种算法都会为您绘制的线数设置一个上限,这应该会显着减少花费在CGContextDrawPath.


推荐阅读