9

与其让drawRect每次重绘数千个点,我认为有几种方法可以“在屏幕上缓存图像”和任何其他绘图,我们将添加到该图像,并在需要时显示该图像drawRect

  1. 使用 BitmapContext 绘制位图,然后在drawRect中绘制此位图。

  2. 使用CGLayer并绘制CGLayerin drawRect,这可能比方法 1 更快,因为此图像缓存在显卡中(并且它不会计入 iOS 上“内存警告”的 RAM 使用量?)

  3. 绘制到 a CGImage,并使用视图的图层:view.layer.contents = (id) cgimage;

所以好像有3种方法,我觉得CALayer方法(3)中只能用aCGImage来实现。 CALayer本身不能缓存屏幕图像,不像CGLayer(2)中那样。

方法(2)是三种方法中最快的吗,还有其他方法可以做到吗?我实际上计划制作一些屏幕图像的动画(循环超过 5 或 6 个),并将尝试使用CADisplayLink60fps 的最高帧速率。方法(1)、(2)或(3)中的任何一种是否会使用显卡中的内存,因此不使用 RAM,因此也不太可能从 iOS 收到内存警告?

4

2 回答 2

12

根据您提出的最后几个问题,您似乎完全混淆了 CGLayers 和 CALayers。它们是不同的概念,彼此之间并没有真正的关系。CGLayer 是一种 Core Graphics 构造,它有助于在 Core Graphics 上下文的画布内重复呈现内容,并且被限制在单个视图、位图或 PDF 上下文中。我很少需要使用 CGLayer。

CALayer 是一个核心动画层,iOS 中的每个 UIView 都有一个支持(以及 Mac 上支持层的 NSView)。你一直在 iOS 上处理这些,因为它们是 UI 架构的基础部分。每个 UIView 实际上是围绕 CALayer 的轻量级包装器,而每个 CALayer 反过来实际上是围绕 GPU 上的纹理四边形的包装器。

在屏幕上显示 UIView 时,第一次需要渲染内容(或触发完全重绘时)Core Graphics 用于获取线条、弧线和其他矢量绘图(有时也包括光栅位图)和光栅化他们到一个位图。然后通过 CALayer 将该位图上传并缓存在 GPU 上。

对于界面的变化,例如视图被移动、旋转、缩放等,这些视图或图层不需要重新绘制,这是一个昂贵的过程。相反,它们只是在 GPU 上转换并在新位置合成。这就是在整个 iOS 界面中看到流畅的动画和滚动的原因。

因此,如果您想获得最佳性能,您将希望避免使用 Core Graphics 重绘任何内容。缓存你可以在 CALayers 或 UIViews 中的场景的哪些部分。想想老式动画如何使用 cel 来包含他们将移动的场景部分,而不是让动画师重新绘制场景中的每一个变化。

您可以轻松获得数百个 CALayer,以便在现代 iOS 设备上流畅地为屏幕制作动画。但是,如果您想为粒子系统之类的东西做数千个点,那么通过迁移到 OpenGL ES 并使用 GL_POINTS 进行渲染会更好。这将需要更多的代码来设置,但这可能是为您询问的“数千点”获得可接受的性能的唯一方法。

于 2012-05-27T21:47:13.490 回答
5

一种允许缓存图形和修改这些缓存的图形内容的快速方法是您的方法 (1) 和 (3) 的混搭。

(1) 创建您自己的基于位图的图形上下文,对其进行绘制,然后根据需要随时修改(增量添加一个点或不时增加数千个点等)。不幸的是,它将是不可见的,因为无法直接将任何位图显示在 iOS 设备上。

所以,此外,

(3) 在某个帧率(60 Hz、30 Hz 等)下,如果位图脏(已修改),则将位图上下文转换为 CGImage 并将该图像分配给 CALayer 的内容。这会将整个位图的内存转换并复制到 GPU 纹理缓存(这是慢速部分)。然后使用核心动画对图层执行任何您想要的操作(刷新、合成、在窗口周围飞行等)以显示由位图制成的纹理。在幕后,Core 动画最终会让 GPU 使用该纹理将四边形投射到一些合成的窗口块上,这些块最终将被发送到设备显示器(这种描述可能会遗漏图形和 GPU 管道中的一大堆阶段)。在主 UI 运行循环中冲洗并根据需要重复。我关于这种方法的博客文章在这里.

无法部分修改正在使用的现有 GPU 纹理的内容。您要么必须用一个完整的新纹理上传替换它,要么在纹理层的顶部合成另一个层。因此,您最终将使用双倍内存,一些在 CPU 地址空间中,一些在 GPU 纹理缓存中。

于 2012-05-28T06:28:22.533 回答