在iOS上,缓存绘制的屏幕图像并显示它的最快方法是什么?

时间:2012-05-27 18:36:49

标签: ios calayer quartz-graphics cglayer

我不是每次都让drawRect重绘数千个点,而是认为有几种方法可以“在屏幕上缓存图像”和任何其他绘图,我们会添加到该图像中,并且只显示该图像时现在是drawRect

的时候了
  1. 使用BitmapContext并绘制到位图,并在drawRect中绘制此位图。

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

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

  4. 所以似乎有三种方法,我认为方法(3)中的CALayer只能使用CGImage来实现它。 CALayer本身无法缓存屏幕图像,与(2)中的CGLayer不同。

    方法(2)是否是这三种方法中最快的,还有其他方法可以实现这一目标吗?我实际上计划动画几个屏幕图像,(循环5或6个),并尝试使用CADisplayLink尝试最高60fps的帧速率。方法(1),(2)或(3)中的任何一个是否使用显卡中的内存,因此不使用RAM,因此也不太可能从iOS获得内存警告?

2 个答案:

答案 0 :(得分:11)

根据您提出的最后几个问题,看起来您完全混淆了CGLayers和CALayers。它们是不同的概念,并没有真正相互关联。 CGLayer是一个核心图形构造,它有助于在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中缓存场景的哪些部分。想想老式动画如何使用cels来包含他们将要移动的部分场景,而不是让动画师重绘场景中的每一个变化。

在现代iOS设备上,您可以轻松地让数百个CALayers在屏幕上动画制作动画。但是,如果你想为像粒子系统这样的东西做几千个点,你可以通过移动到OpenGL ES并使用GL_POINTS进行渲染来获得更好的服务。这将需要更多的代码来设置,但它可能是获得你所询问的“数千个点”的可接受性能的唯一方法。

答案 1 :(得分:5)

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

(1)创建您自己的位图支持的图形上下文,绘制到它,然后在任何时候修改它(根据需要逐渐增加一个点或数千个点,等等)。不幸的是,它将是不可见的,因为没有办法直接将任何位图连接到iOS设备上的显示器。

所以,另外,

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

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