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

Jer*_*y L 7 calayer quartz-graphics cglayer ios

与其让drawRect重绘上千点的每一次,我觉得有几个办法"缓存屏幕上的影像"和任何其他图纸,我们将添加到图像,并只显示图像时,是时候drawRect:

  1. 使用BitmapContext并绘制到位图,然后drawRect绘制此位图.

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

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

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

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

Bra*_*son 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进行渲染来获得更好的服务.这将需要更多的代码来设置,但它可能是获得你所询问的"数千个点"的可接受性能的唯一方法.


hot*_*aw2 5

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

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

所以,另外,

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

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