互联网科技

GPU vs CPU in iOS

作者:金沙国际官网    发布时间:2020-04-20 07:27     浏览次数 :137

[返回]

一直以来,我们做产品的时候并没有特别的去考虑CPU/GPU的使用,最近为了提升可视化功能的性能,发现合理使用GPU也是一个可以好好研究的部分,这里总结一下一些有用的信息。

本系列文章的重点是关注在总结iOS图形图像的原理和性能优化的常规解决方案。

事先声明,本文绝大多数概念和内容均来源于已有素材,但是均经过作者消化后总结归纳。如果你不想麻烦地自己去网络一一搜索资料,那么本文将是一个很好的总结笔记。阅读前提是读者已经基本了解View和Layer的操作。如果你对iOS 2D图像优化已经熟练,可以忽略本文。

About OpenGL ES

The Open Graphics Library (OpenGL) is used for visualizing 2D and 3D data. It is a multipurpose open-standard graphics library that supports applications for 2D and 3D digital content creation, mechanical and architectural design, virtual prototyping, flight simulation, video games, and more. You use OpenGL to configure a 3D graphics pipeline and submit data to it. Vertices are transformed and lit, assembled into primitives, and rasterized to create a 2D image. OpenGL is designed to translate function calls into graphics commands that can be sent to underlying graphics hardware. Because this underlying hardware is dedicated to processing graphics commands, OpenGL drawing is typically very fast.

开放图形库(OpenGL)用于2D和3D数据的可视化。 它是一个多用途的开放标准图形库,支持2D和3D数字内容创建,机械和建筑设计,虚拟原型,飞行模拟,视频游戏等应用。 您可以使用OpenGL配置3D图形流水线并提交数据。 顶点被转换和点亮,组合成原始图像,并进行光栅化以创建2D图像。 OpenGL旨在将函数调用转换为可发送到底层图形硬件的图形命令。 因为这个底层硬件专门用于处理图形命令,因此OpenGL绘图通常非常快。

OpenGL for Embedded Systems (OpenGL ES) is a simplified version of OpenGL that eliminates redundant functionality to provide a library that is both easier to learn and easier to implement in mobile graphics hardware.

OpenGL for Embedded Systems(OpenGL ES)是OpenGL的简化版本,消除了冗余功能,从而提供更容易学习和易于在移动图形硬件中实现的库。

图片 1

中央处理器 CPU (Central Processing Unit) 是通用型处理器,可以用于处理任何计算,可称为软件层面计算;而图形处理器 GPU (Graphics Processing Unit) 是专用设计来处理图形图像,它基于硬件对高度并行浮点计算做了优化,称为硬件层面计算,所以GPU处理屏幕渲染的任务会更高效。

前一段时间在review code的时候,看到一段对于Collection View的滑动性能有一定影响的处理代码,以前对于UI性能这部分有一些了解,但是比较粗浅,于是就想系统学习下iOS关于2D 图形图像性能的资料,结果一翻不得了,相关内容越挖越深,扩展开来感觉进入了一个浩大的领域。这个话题的火热程度可以举个简单例子:在简书上搜索“Quartz 2D” 或者 “Core Graphic”就有大约5、6百篇文章,如果搜索“iOS 优化”甚至有超过1万条记录。经过前后大约2-3周的零碎时间,我仔细搜集整理了20余篇优秀资料总结出本文,给自己留个研究笔记,也希望能帮助到需要的人。

At a Glance

OpenGL ES allows an app to harness the power of the underlying graphics processor. The GPU on iOS devices can perform sophisticated 2D and 3D drawing, as well as complex shading calculations on every pixel in the final image. You should use OpenGL ES if the design requirements of your app call for the most direct and comprehensive access possible to GPU hardware. Typical clients for OpenGL ES include video games and simulations that present 3D graphics.

OpenGL ES允许应用程序利用底层图形处理器的功能。 iOS设备上的GPU可以执行复杂的2D和3D绘图,以及最终图像中每个像素的复杂阴影计算。 您应该使用OpenGL ES,如果您的应用程序的设计要求要求最直接和全面的访问可能对GPU硬件。 OpenGL ES的典型客户端包括视频游戏和3D图形模拟。

OpenGL ES is a low-level, hardware-focused API. Though it provides the most powerful and flexible graphics processing tools, it also has a steep learning curve and a significant effect on the overall design of your app. For apps that require high-performance graphics for more specialized uses, iOS provides several higher-level frameworks:

  • The Sprite Kit framework provides a hardware-accelerated animation system optimized for creating 2D games. (See Sprite Kit Programming Guide.)
  • The Core Image framework provides real-time filtering and analysis for still and video images. (See Core Image Programming Guide.)
  • Core Animation provides the hardware-accelerated graphics rendering and animation infrastructure for all iOS apps, as well as a simple declarative programming model that makes it simple to implement sophisticated user interface animations. (See Core Animation Programming Guide.)

You can add animation, physics-based dynamics, and other special effects to Cocoa Touch user interfaces using features in the UIKit framework.

OpenGL ES是一个低级的,以硬件为重点的API。 虽然它提供了最强大和最灵活的图形处理工具,但它也具有"陡峭的学习曲线",对您的应用程序的整体设计有重大的影响。 对于需要高性能图形用于更专业化应用的应用,iOS提供了几个更高级别的框架:

  • Sprite Kit框架提供了一个为创建2D游戏而优化的硬件加速动画系统。 (请参见“Sprite Kit编程指南”。)
  • Core Image框架为静态和视频图像提供实时过滤和分析。
  • 核心动画为所有iOS应用程序提供了硬件加速的图形渲染和动画基础设施,以及一个简单的声明式编程模型,使得实现复杂的用户界面动画变得简单。

iOS图像处理的框架可以参见下面的架构图:

知己知彼,百战不殆。

OpenGL ES Is a Platform-Neutral API Implemented in iOS(在iOS中OpenGL ES是平台无关的API)

Because OpenGL ES is a C-based API, it is extremely portable and widely supported. As a C API, it integrates seamlessly with Objective-C Cocoa Touch apps. The OpenGL ES specification does not define a windowing layer; instead, the hosting operating system must provide functions to create an OpenGL ES rendering context, which accepts commands, and a framebuffer, where the results of any drawing commands are written to. Working with OpenGL ES on iOS requires using iOS classes to set up and present a drawing surface and using platform-neutral API to render its contents.

因为OpenGL ES是一个基于C的API,它非常便于集成并得到广泛的支持。 作为C API,它与Objective-C Cocoa Touch应用程序无缝集成。 OpenGL ES规范没有定义窗口层; 相反,托管操作系统必须提供函数来创建可以接受命令的OpenGL ES渲染上下文以及写入任何绘图命令的结果的帧缓冲区。 在iOS上使用OpenGL ES需要使用iOS类来设置和呈现绘图层,并使用平台无关的API来呈现其内容。

Relevant Chapters: Checklist for Building OpenGL ES Apps for iOS, Configuring OpenGL ES Contexts
相关章节

图片 2Core Animation

掌握任何一件事情都需要从原理入手。我们先来巩固iOS 2D Graphic相关的基本概念和知识。

GLKit Provides a Drawing Surface and Animation Support

Views and view controllers, defined by the UIKit framework, control the presentation of visual content on iOS. The GLKit framework provides OpenGL ES–aware versions of these classes. When you develop an OpenGL ES app, you use a GLKView object to render your OpenGL ES content. You can also use a GLKViewController object to manage your view and support animating its contents.

UIKit框架定义的视图和视图控制器控制iOS上的视觉内容的呈现。 GLKit框架提供这些类的OpenGL ES-aware版本。 开发OpenGL ES应用程序时,您可以使用GLKView对象来呈现OpenGL ES内容。 您还可以使用GLKViewController对象来管理视图并支持动画化其内容。

Relevant Chapters: Drawing with OpenGL ES and GLKit

UIKit

1. 关于iOS 图像处理的基本概念

iOS Supports Alternative Rendering Targets(iOS支持替代渲染目标)

Besides drawing content to fill an entire screen or part of a view hierarchy, you can also use OpenGL ES framebuffer objects for other rendering strategies. iOS implements standard OpenGL ES framebuffer objects, which you can use for rendering to an offscreen buffer or to a texture for use elsewhere in an OpenGL ES scene. In addition, OpenGL ES on iOS supports rendering to a Core Animation layer (the CAEAGLLayer class), which you can then combine with other layers to build your app’s user interface or other visual displays.

除了绘制内容以填充整个屏幕或视图层次结构的一部分外,您还可以使用OpenGL ES framebuffer对象进行其他渲染策略。 iOS实现标准的OpenGL ES帧缓冲对象,您可以将其用于渲染到屏幕外缓冲区或纹理,以便在OpenGL ES场景中的其他位置使用。 此外,iOS上的OpenGL ES支持渲染到Core Animation层(CAEAGLLayer类),然后您可以将其与其他图层相结合,构建应用的用户界面或其他视觉显示。

Relevant Chapters: Drawing to Other Rendering Destinations

UIKit 是一组 Objective-C API 用于管理图形用户界面,应用程序开发者一般直接使用UIKit来创建各种UI界面应用,因为UIKIt提供了丰富的界面控件比如 UIImage/UIColor/UIButton/UILabel等,使得开发者不需要自己手动创建这些标准控件。

1.1 Graphic Frameworks

首先看一张来自Apple的描述图形处理模块的经典(烂大街)图:

图片 3

Graphic Technology Framework.png

最上层是UIKit框架,这是服务于Application的最前端框架,封装了所有“UI*”空间类和操作;在其之下是Core Animation框架,借助与这个框架,Apple向开发人员提供了非常方便的动画处理功能和图像渲染功能。再往下,分离成基于GPU绘图的OpenGL ES层和基于CPU绘图的Core Graphic层。最底层的就是支持最终绘图的硬件平台,包括GPU,CPU,缓存,总线等等。

虽然在Apple的文档里,看起来Core Graphic的层级在Core Animation之下,和OpenGL是同一个level,但其实它们三者之间的关系非常紧密,App和它们之间的交互也更加自由,并非被CA层完全拦截在中间。各个部分之间的工作关系如下图:

图片 4

iOS Graphic Stacks.png

GPU Driver 是直接和 GPU 交流的代码块,使不同的GPU在下一个层级上显示的更为统一,典型的下一层级有 OpenGL/OpenGL ES. OpenGL(Open Graphics Library) 是一个提供了 2D 和 3D 图形渲染的 API。OpenGL 和 GPU 密切的工作以提高GPU的能力,并实现硬件加速渲染。OpenGL 之上扩展出很多东西。在 iOS 上,几乎所有的东西都是通过 Core Animation 绘制出来,然而在 OS X 上,绕过 Core Animation 直接使用 Core Graphics 绘制的情况并不少见。对于一些专门的应用,尤其是游戏,程序可能直接和 OpenGL/OpenGL ES 交流。

顺便提一下,也许你会在网上看到类似这样一幅图:

图片 5

Graphic Technology Framework - 2.png

我个人觉得这幅图是有一定问题的,因为这会给人一个错觉,好像图形的处理对于CPU和GPU而言是分离的,但是实际上,CPU处理完的数据最终都需要提交到GPU。不管是iOS还是Mac,最终所有的绘图操作都会通过OpenGL层去操作GPU。

但是这个图也有一个好处,是它通常意义上诠释了:Core Animation操作基于GPU进行“硬绘图”的OpenGL ES,和基于CPU进行“软绘图”的Core Graphic。使用Core Graphic绘制会让Core Animation 使用CPU创建一张Content"图片",CPU处理生成这张图片后交给GPU进行显示。一般来说,GPU做Rendering,Tilering,Compositing,而CPU更多的时候是做图层布局处理和协助GPU做预处理(预绘制,动画准备,动画提交,计算动画中间值等等)。根据这张图你可以有个直观的印象就是:调用CG开头的API会触发CPU去处理图像,也就是说这个会涉及到后文中关于影响性能的一个重要概念:CPU密集型(CPU bound)操作。

CPU和GPU之间真正的关系可以看下图:

图片 6

Paste_Image.png

GPU 需要将每一个 frame 的纹理(位图)合成在一起(一秒60次)。每一个纹理会占用 VRAM(video RAM),所以需要给 GPU 同时保持纹理的数量做一个限制。GPU 在合成方面非常高效,但是某些合成任务却比其他更复杂,并且 GPU在 16.7ms(1/60s)内能做的工作也是有限的。为了让 GPU 访问数据,需要将数据从 RAM 移动到 VRAM 上,一些大型的纹理却会非常耗时。

举个例子,比如显示文本,对 CPU来说会调用 Core Text 和 Core Graphics 框架更紧密的集成来根据文本生成一个位图。一旦准备好,它将会被作为一个纹理上传到 GPU 并准备显示出来。当你滚动或者在屏幕上移动文本时,同样的纹理能够被复用,CPU 只需简单的告诉 GPU 新的位置就行了,所以 GPU 就可以重用存在的纹理了。CPU 并不需要重新渲染文本,并且位图也不需要重新上传到 GPU。

在这里,有必要再讨论下Quartz。Quartz这个名词其实是一个从Mac上过来的历史遗留物,而其本身是Apple窗口服务器和描画技术的一般叫法。在Apple的《Quartz 2D Programming Guide》中,开篇一句话已经基本解释了Quartz和Core Graphic的关系:

Quartz 2D is an advanced, two-dimensional drawing engine available for iOS application development and to all Mac OS X application environments outside of the kernel. The Quartz 2D API is part of the Core Graphics framework, so you may see Quartz referred to as Core Graphics or, simply, CG.

Quartz 2D是iPhone OS和Mac OS X环境下的二维绘图引擎。它其实是Core Graphic的一个核心部分。使用Quartz 2D API,你可以:基于路径的绘图,透明度绘图,遮盖,阴影,透明层,颜色管理,防锯齿渲染,生成PDF,以及PDF元数据相关处理。在Mac OS X下,Quartz 2D能与其它图形图像技术相结合——Core Image,Core Video,OpenGL,以及Quick Time。类似的,在iPhone OS下的Quartz 2D也能与其它的图像和动画技术相结合——Core Animation,OpenGL ES,以及UIKit类。

另外,这篇回答详细的讨论了和Quartz/Core Graphic相关的framework的列表:

  • CoreGraphics.framework
    Quartz 2D : API manages the graphic context and implements drawing.
    Quartz Services : API provides low level access to the window server. This includes display hardware, resolution, refresh rate, and others.
  • QuartzCore.framework
    Core Animation : Objective-C API to do 2D animation.
    Core Image: image and video processing (filters, warp, transitions).iOS 5
  • Quartz.framework (OS X only)
    Image Kit: display and edit images.
    PDF Kit: display and edit PDFs.
    Quartz Composer: display Quartz Composer compositions.
    QuickLookUI: preview media elements.
  • 其它一些Quartz技术:
    Quartz Extreme: GPU acceleration for Quartz Composer.
    QuartzGL (aka "Quartz 2D Extreme"): GPU acceleration for Quartz 2D.

只需要记住一点,从某种意义上来说,Quartz 和CG可以互相混淆,事实上,上文的作者也嘲笑了一句“如果Apple的目的是想把大家给搞糊涂那么他们成功地做到了!”

上面主要针对的是CA层,CG层和GPU、CPU之间的关系,那么我们再从CA层往上走,来看看CA层和UI层之间的关系。

Apps Require Additional Performance Tuning(应用程序需要额外的性能调优)

Graphics processors are parallelized devices optimized for graphics operations. To get great performance in your app, you must carefully design your app to feed data and commands to OpenGL ES so that the graphics hardware runs in parallel with your app. A poorly tuned app forces either the CPU or the GPU to wait for the other to finish processing commands.

You should design your app to efficiently use the OpenGL ES API. Once you have finished building your app, use Instruments to fine tune your app’s performance. If your app is bottlenecked inside OpenGL ES, use the information provided in this guide to optimize your app’s performance.

Xcode provides tools to help you improve the performance of your OpenGL ES apps.

Graphics processors ( 图形处理器)是针对图形操作优化的并行设备。 为了在应用程序中获得出色的表现,您必须仔细设计应用程序以将数据和命令提供给OpenGL ES,以使图形硬件与您的应用程序并行运行。 调整好的应用程序会强制CPU或GPU等待另一个完成处理命令。

您应该设计您的应用程序以有效地使用OpenGL ES API。 完成应用程式制作完成后,使用Instruments 来调整应用程式的成效。 如果您的应用程序在OpenGL ES中出现瓶颈,请使用本指南中提供的信息来优化应用程序的性能。

Xcode提供了帮助您提高OpenGL ES应用程序性能的工具。

Relevant Chapters: OpenGL ES Design Guidelines, Best Practices for Working with Vertex Data, Best Practices for Working with Texture Data, Best Practices for Shaders, Tuning Your OpenGL ES App

Core Animation

1.2 UIView 和 CALayer

“每一个成功的男人背后都有一个女人!”

你在iOS上能看到的,触碰到的所有东西,都是UIView展示出来的。你刚开始接触iOS开发的时候,使用的最多的,也是UIView,看起来UIView无限风光。但是如果认为UIView是绘制出的图像那你就错了!在iOS中,每一个UIView的背后都有一个默认的CALayer,真正为图像做出贡献的,其实是这个layer,UIView只是作为一个视窗容器,用来精简封装layer并对外提供响应操作而已。

iOS中UIView和CALayer的关系如下图:

图片 7

Paste_Image.png

CALayer是UIView的基础,所有实际的绘图工作都是Layer向其backing store里绘制bit map完成的。而操作View的绝大多数图形属性,其实都是直接操作的其拥有的layer属性,比如frame,bounds,backgroundColor等等。

UIView和CALayer都有自己的树状结构,它们都可以有自己的SubView和SubLayer:

图片 8

Layer Tree.png

对于每一个使用Core Animation的App来说,系统实际上维护这3套不同的Layer 树状层级:

  1. layer tree (modal tree):这里是App的代码直接操纵的tree,你所修改的各种属性值都是反映在这个tree里;
  2. presentation tree:这是一个中间层,系统在做动画时,所有动画中间态就都在这一层上更改属性来完成动画的分动作;
  3. render tree:这是直接对应于提交到render server上进行显示的树,屏幕上的内容对应于该层。

图片 9

3 different layer tree copies.png

这让我想起了一句老话:“每一个成功的男人背后都有一个成功的女人”,UIView的无限风光其实离不开CALayer在其背后的支持。但是这种每个View的背后都有一个Layer的设定在OS X上并不总是成立。Apple把support layer的View称作** “layer backed view” ,在OS X上还有一种叫做 "layer hosting view" **。iOS默认的都是layer backed view。其它在此不表。

“既生瑜何生亮?!”

到这里你可能会问,既然有了CALayer为啥还要UIView,这不多余么?!事实上,这种看似多余的设计,其背后的艺术确是非常精妙的,关于这个问题,网上有很多介绍,但是我仍然认为,在设计精髓上,这篇风趣的文章是解释的最深刻的,简单的说,UIView和CALayer既是把Respond和Drawing分离,把Content和Action分离,把机制策略分离。Layer能够使得iOS更有效的处理绘图,高帧率的动画,UIView能够更方便的处理事件和响应链。它们彼此合作,互相依赖,缺一不可。另外这篇文章在事件响应和修改属性参数的效果上稍微更进一步的解释了两者的区别。

关于它们的更多内容,可以参考Apple文档《Core Animation Programming Guide》

这里再提一个特殊的UIView: UIImageView。UIImageView继承自UIView,但是其有一个特殊的属性UIImage。在上面我们提到过,每一个UIView的Layer都有一个对应的Backing Store作为其存储Content的实际内容,而这些内容其实就是一个CGImage数据(更确切的说,是bitmap数据),以供GPU读取展示。而UIImage其实是CGImage的一个轻量级封装,于是很自然的,在UIImageView中的UIImage对象直接将自己的CGImage图片数据作为UIVIew的Content,提供给CALayer。

图片 10

UIImageView.png

OpenGL ES May Not Be Used in Background Apps(OpenGL ES可能不会用于后台应用程序)

Apps that are running in the background may not call OpenGL ES functions. If your app accesses the graphics processor while it is in the background, it is automatically terminated by iOS. To avoid this, your app should flush any pending commands previously submitted to OpenGL ES prior to being moved into the background and avoid calling OpenGL ES until it is moved back to the foreground.

在后台运行的应用程序可能不会调用OpenGL ES函数。 如果您的应用程序在后台访问图形处理器,则会由iOS自动终止。 为避免这种情况,您的应用程序应该在挂起前提交OpenGL ES刷新命令,然后才能移动到后台,并避免调用OpenGL ES,直到它移回到前台。

Relevant Chapters: Multitasking, High Resolution, and Other iOS Features

UIKit对于动画的处理是基于Core Animation的:

2. iOS UI上进行Graphic和Animation绘制的原理

现在我们已经了解了关于Graphic的一些基本对象结构,接下来我们看看Graphic的基本工作原理。

OpenGL ES Places Additional Restrictions on Multithreaded Apps(OpenGL ES在多线程应用中的限制)

Designing apps to take advantage of concurrency can be useful to help improve your app’s performance. If you intend to add concurrency to an OpenGL ES app, you must ensure that it does not access the same context from two different threads at the same time.

设计应用程序以利用并发功能可以帮助您提高应用程序的性能。 如果您打算在OpenGL ES应用程序中添加并发性,则必须确保它不会同时从两个不同的线程访问相同的上下文。

Relevant Chapters: Concurrency and OpenGL ES

Core Animation is a graphics rendering and animation infrastructure available on both iOS and OS X that you use to animate the views and other visual elements of your app.

With Core Animation, most of the work required to draw each frame of an animation is done for you. All you have to do is configure a few animation parameters (such as the start and end points) and tell Core Animation to start. Core Animation does the rest, handing most of the actual drawing work off to the onboard graphics hardware to accelerate the rendering. This automatic graphics acceleration results in high frame rates and smooth animations without burdening the CPU and slowing down your app.

2.1 图像的绘制

How to Use This Document

Begin by reading the first three chapters: Checklist for Building OpenGL ES Apps for iOS, Configuring OpenGL ES Contexts, Drawing with OpenGL ES and GLKit. These chapters provide an overview of how OpenGL ES integrates into iOS and all the details necessary to get your first OpenGL ES apps up and running on an iOS device.

If you’re familiar with the basics of using OpenGL ES in iOS, read Drawing to Other Rendering Destinations and Multitasking, High Resolution, and Other iOS Features for important platform-specific guidelines. Developers familiar with using OpenGL ES in iOS versions before 5.0 should study Drawing with OpenGL ES and GLKit for details on new features for streamlining OpenGL ES development.

Finally, read OpenGL ES Design Guidelines, Tuning Your OpenGL ES App, and the following chapters to dig deeper into how to design efficient OpenGL ES apps.
Unless otherwise noted, OpenGL ES code examples in this book target OpenGL ES 3.0. You may need to make changes to use these code examples with other OpenGL ES versions

首先阅读前三章:构建用于iOS的OpenGL ES应用程序的清单,配置OpenGL ES上下文,使用OpenGL ES和GLKit进行绘图。这些章节概述了OpenGL ES如何整合到iOS中,以及在iOS设备上启动和运行第一个OpenGL ES应用程序所需的所有细节。

如果您熟悉在iOS中使用OpenGL ES的基础知识,请阅读绘图到其他呈现目的地和多任务,高分辨率和其他iOS功能,以获取重要的平台特定指南。熟悉在5.0之前的iOS版本中使用OpenGL ES的开发人员应该研究使用OpenGL ES和GLKit绘图,以获得有关简化OpenGL ES开发的新功能的详细信息。

最后,阅读OpenGL ES设计指南,调整OpenGL ES应用程序以及以下章节,深入了解如何设计高效的OpenGL ES应用程序。

除非另有说明,本书中的OpenGL ES代码示例以OpenGL ES 3.0为目标。您可能需要对其他OpenGL ES版本进行更改以使用这些代码示例。

Core Animation 也是一套Objective-C API,是iOS平台上负责图形渲染与动画的基础设施,可以实现视图和其他的可视元素的动画,比如提供显示内容的图层类 CALayer、动画和计时类 Animation/Timing等。是一组相对UIKit和动画绘制和动画自由度更大的API,构建于Core Graphics之上。iOS上的UIKit和动画效果大部分都是通过Core Animation实现的,比如UIView可以通过某些属性即可实现的动画。

* 2.1.1 Core Animation Pipeline *

前文中的Graphic Framework一节已经说到App通过Core Animation调度GPU和CPU,最终绘制图像到屏幕上。那么具体到绘制细节中,都需要经过哪些具体的步骤呢?2014年的WWDC上Apple向我们提供了一些技术细节可以让我们管窥一斑。从App调用Core Animation开始,一直到最终显示,是一个严格遵循时间顺序的管道(Pipeline)过程,叫做Core Animation Pipeline:

图片 11

Core Animation Pipeline.png

可以看到除Display之外几个关键的参与者:App, Render Server, GPU。前两者中Core Animation都有介入,App中Core Animation的作用是做具体绘制前的准备工作,在Render server中Core Animation更多的是负责具体的绘制。GPU主要负责硬件阶段的具体渲染。Render server其实是一个独立的进程,在 iOS 5 以前这个进程叫 SpringBoard,在 iOS 6 之后叫 BackBoard。

图中的每一个竖线代表的是一个VSync信号。一般而言视频控制器都是通过VSync信号来进行显示同步的,每一个VSync信号间隔是固定的,这个时间是16.67ms,也就是1秒钟刷新60帧。在 VSync 信号到来后,系统图形服务会通过 CADisplayLink 等机制通知 App,App 主线程开始在 CPU 中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。随后 GPU 会把渲染结果提交到帧缓冲区去,等待下一次 VSync 信号到来时显示到屏幕上。

Prerequisites

Before attempting use OpenGL ES, you should already be familiar with general iOS app architecture. See Start Developing iOS Apps Today.

This document is not a complete tutorial or a reference for the cross-platform OpenGL ES API. To learn more about OpenGL ES, consult the references below.

在尝试使用OpenGL ES之前,您应该已经熟悉了一般的iOS应用程序架构。 请参阅今天开始开发iOS应用程序。

本文档不是完整的教程或跨平台OpenGL ES API的参考。 要了解有关OpenGL ES的更多信息,请参阅以下参考。

Core Animation对应的是QuartzCore.framework,参看 QuartzCore.h 可见这个头文件包括 #include <QuartzCore/CoreAnimation.h>,而检查 CoreAnimation.h,可见:

* 2.1.2 Core Animation Commit Transaction *

针对准备阶段的Commit Transaction部分,可以细分为4个步骤:

图片 12

屏幕快照 2016-06-22 4.35.39 PM.png

  • 布局Layout:在这个阶段,程序设置 View / Layer 的层级信息,设置 layer 的属性,如 frame,background color 等等。[UIView layoutSubViews][CALayer layoutSublayers] 就是在这个阶段调用的。
  • 显示Display:在这个阶段程序会创建 layer 的 backing image,无论是通过 setContents 将一个 image 传給 layer,还是通过 drawRect:drawLayer: inContext: 来画出来的。所以 drawRect: 等函数是在这个阶段被调用的。注意不要混淆这里的Display和最终的显示Display;

关于使用drawRect和使用ImageView

  • 使用 -drawRect :
    如果你的视图类实现了 -drawRect:,他们将像这样工作:
    • 当你调用 -setNeedsDisplay,UIKit 将会在这个视图的图层上调用-setNeedsDisplay。这为图层设置了一个标识,标记为 dirty,但还显示原来的内容。它实际上没做任何工作,所以多次调用 -setNeedsDisplay并不会造成性能损失。
    • 下面,当渲染系统准备好,它会调用视图图层的-display方法.此时,图层会装配它的后备存储。
    • 然后建立一个 Core Graphics 上下文(CGContextRef),将后备存储对应内存中的数据恢复出来,绘图会进入对应的内存区域,并使用CGContextRef 绘制。当你使用 UIKit 的绘制方法,例如: UIRectFill() 或者-[UIBezierPath fill]代替你的 -drawRect: 方法,他们将会使用这个上下文。此时,UIKit 将后备存储的 CGContextRef 推进他的 graphics context stack,也就是说,它会将那个上下文设置为当前的。(UIGraphicsGetCurrent() 将会返回那个对应的上下文),这样,UIKit 使用当前上下文将绘图绘入到图层的后备存储。如果你想直接使用 Core Graphics 方法,你可以自己调用 UIGraphicsGetCurrent() 得到相同的上下文,并且将这个上下文传给 Core Graphics 方法。
    • 从现在开始,图层的后备存储将会被不断的渲染到屏幕上。直到下次再次调用视图的-setNeedsDisplay ,将会依次将图层的后备存储更新到视图上。
  • 不使用 -drawRect:
    当你用一个 UIImageView 时,事情略有不同,这个视图仍然有一个 CALayer,但是图层却没有申请一个后备存储。取而代之的是使用一个 CGImageRef 作为他的内容,并且渲染服务将会把图片的数据绘制到帧的缓冲区,比如,绘制到显示屏。在这种情况下,将不会继续重新绘制。我们只是简单的将位图数据以图片的形式传给了 UIImageView,然后 UIImageView 传给了 Core Animation,然后轮流传给渲染服务。
  • 准备Prepare:在这个阶段,Core Animation 框架准备要渲染的 layer 的各种属性数据,以及要做的动画的参数,准备传递給 render server。同时在这个阶段也会解压要渲染的 image。(除了用 imageNamed:方法从 bundle 加载的 image 会立刻解压之外,其他的比如直接从硬盘读入,或者从网络上下载的 image 不会立刻解压,只有在真正要渲染的时候才会解压)。在这个阶段你可以看到类似CA::Layer::prepare_commitRender::prepare_image,Render::copy_image,Render::create_image等等这样的操作;
  • 提交Commit:在这个阶段,Core Animation 打包 layer 的信息以及需要做的动画的参数,通过 IPC(inter-Process Communication)传递給 render server。这是一个递归操作,根据打包的Layer层级复杂度来决定递归的次数。大量连续递归的CA::Layer::commit_if_needed调用是这个阶段的显著特征。

See Also

OpenGL ES is an open standard defined by the Khronos Group. For more information about the OpenGL ES standard, please consult their web page at http://www.khronos.org/opengles/.

  • OpenGL® ES 3.0 Programming Guide, published by Addison-Wesley, provides a comprehensive introduction to OpenGL ES concepts.
  • OpenGL® Shading Language, Third Edition, also published by Addison-Wesley, provides many shading algorithms useable in your OpenGL ES app. You may need to modify some of these algorithms to run efficiently on mobile graphics processors.
  • OpenGL ES API Registry is the official repository for the OpenGL ES specifications, the OpenGL ES shading language specifications, and documentation for OpenGL ES extensions.
  • OpenGL ES Framework Reference describes the platform-specific functions and classes provided by Apple to integrate OpenGL ES into iOS.
  • iOS Device Compatibility Reference provides more detailed information on the hardware and software features available to your app.
  • GLKit Framework Reference describes a framework provided by Apple to make it easier to develop OpenGL ES 2.0 and 3.0 apps.

OpenGL ES是由Khronos集团定义的开放标准。有关OpenGL ES标准的更多信息,请访问他们的网页http://www.khronos.org/opengles/。

  • Addison-Wesley发布的OpenGL®ES 3.0编程指南全面介绍了OpenGL ES的概念。
  • OpenGL®着色语言,第三版,也由Addison-Wesley出版,提供了许多可用于您的OpenGL ES应用程序的着色算法。您可能需要修改其中一些算法才能在移动图形处理器上高效运行。
  • OpenGL ES API注册表是OpenGL ES规范的官方存储库,-
    OpenGL ES着色语言规范以及OpenGL ES扩展的文档。
  • OpenGL ES Framework Reference介绍了Apple将OpenGL ES集成到iOS中提供的平台特定功能和类。
  • iOS设备兼容性参考提供了有关您的应用程序可用的硬件和软件功能的更详细信息。
  • “GLKit框架参考”介绍了由Apple提供的一个框架,可以更轻松地开发OpenGL ES 2.0和3.0应用程序。
#include <QuartzCore/CABase.h>#include <QuartzCore/CATransform3D.h>#import <Foundation/Foundation.h>#import <QuartzCore/CAAnimation.h>#import <QuartzCore/CADisplayLink.h>#import <QuartzCore/CAEAGLLayer.h>#import <QuartzCore/CAMetalLayer.h>#import <QuartzCore/CAEmitterCell.h>#import <QuartzCore/CAEmitterLayer.h>#import <QuartzCore/CAGradientLayer.h>#import <QuartzCore/CALayer.h>#import <QuartzCore/CAMediaTiming.h>#import <QuartzCore/CAMediaTimingFunction.h>#import <QuartzCore/CAReplicatorLayer.h>#import <QuartzCore/CAScrollLayer.h>#import <QuartzCore/CAShapeLayer.h>#import <QuartzCore/CATextLayer.h>#import <QuartzCore/CATiledLayer.h>#import <QuartzCore/CATransaction.h>#import <QuartzCore/CATransform3D.h>#import <QuartzCore/CATransformLayer.h>#import <QuartzCore/CAValueFunction.h>
* 2.1.3 GPU Rendering *

当App调用Core Animation(甚至是Core Graphic)将所有准备工作完成后,将参数和数据提交到下层OpenGL层,再传输给GPU做真正的渲染处理:

  • 块渲染 Tile Based Rendering

块渲染的根本思路是将设备屏幕分割为包含N*N个像素的块状(Tile)区域。每一个Tile可以适配到SoC的cache中。

图片 13

Tiler Based Render 1.png

每一个屏幕上的图像对象,都将被这些Tile再次切割成独立的碎块,然后针对每一个碎块进行三角形顶点着色。

图片 14

Tiler and Vertex Shader.png

块渲染是目前移动GPU的主流渲染方式,因为这种方式更好地适配了移动设备的耗电和性能的平衡问题。究其原因,是因为GPU在运算时对数据带宽消耗的极高要求:OPENGL的虚拟管线需要大量的显存带宽来支持, 为了减少这个凶残的带宽需求,大多数移动GPU都使用了tiled-based渲染。在最基础的层面,这些GPU将帧缓存(framebuffer),包括深度缓存,多采样缓存等等,从主内存移到了一块超高速的on-chip存储器上,计算芯片就能以远低于常规消耗的电能来读写存储器。但是on-chip的存储器都不可能很大,否则GPU芯片的大小将大的吓人,在有些GPU中小到只能容纳16x16个像素,于是将OPENGL的帧缓存切割成16x16的小块(这就是tile-based渲染的命名由来),然后一次就渲染一块。对于每一块tile: 将有用的几何体提交进去,当渲染完成时,将tile的数据拷贝回主内存。这样,带宽的消耗就只来自于写回主内存了,那是一个较小的消耗,消耗极高的深度/模板测试和颜色混合完全的在计算芯片上就完成了。

有关Tile Based Rendering的更多内容,有兴趣可以简单翻一下这篇译文《Performance Tunning for Tile-Based Architecture》

  • 渲染通道(流水线) Render Pass

Core Animation将包装的好的数据提交给OpenGL之后,后续的流程基本上就属于OpenGL ES的范畴了,你在这里可以看到顶点着色器(Vertex Shader)和像素着色器(Pixel Shader)。

图片 15

Render Pass.png

顶点着色器定义了在 2D 或者 3D 场景中几何图形是如何处理的。一个顶点指的是 2D 或者 3D 空间中的一个点。顶点着色器设置顶点的位置,并且把位置和纹理坐标这样的参数发送到Pixel Shader。然后 GPU 使用Pixel Shader在对象或者图片的每一个像素上进行计算,最终计算出每个像素的最终颜色。

能够实现Vertex Shader和Pixel Shader的显卡的图形处理流水线被称作为是可编程的,相对而言,在此之前的图形处理流水线被称作为是固定功能(fixed function)。虽然如此,但是实际上可编程的只有流水线的一部分,正如Vertex Shader 和Pixel Shader的字面意思一样,现在可编程的部分只有处理顶点的和处理象素的单元。但是这两个着色器是OpenGL ES 2.0定义的两个缺一不可的单元。

Vertex Shader 和 Pixel Shader在不同的文档里面有不同的叫法,Nvidia在自己的OpenGL扩展中把Vertex Shader叫做Vertex Program、把Pixel Shader叫做Texture Shader,3Dlabs在自己提出一份OpenGL 2.0的提议里面把这两者分别叫做Vertex Shader和Fragment(片段)Shader

《GPU-Accelerated Image Processing》 和 这段回答 或许能给你更多关于GPU Shader的内容。

  • 多通道渲染(Render Passes)示例:Masking

在实际绘图过程中,由于多个Layer的存在,最终图像实际上是由多个Render Pass并发绘制后再组合渲染的(Compositing and Blending)。以一个带有蒙板的组合图像为例,实际发生的Render pass有3个,mask layer和content layer各自渲染,最后再做一次Render pass组合出最终图像:

图片 16

Render Passes.png

另注:

2.2 图层的动画 Animation

到此为止,我们已经基本了解了在iOS上,从App递交一个绘图请求开始一直到图像出现在屏幕上的基本过程,但这都是静态的图片的绘制,那么动画Animation是怎么做到的呢?

Apple的做法很简单直接,对于每一个Animation,前期的准备阶段和单独的图像提交基本相同,但是Render Server在渲染时,将根据Core Animation的动画参数自动计算出动画所需要的每一帧图像,然后一帧一帧的渲染显示,最终呈现的就是动画效果。也就是说App只需要告诉Render Server动画的起始和终止状态,它自动将
中间过程计算出来并替你完成commit的动作。联想到前文中关于Layer Tree的3份不同Copy,你只需要操作起始和终止状态的Modal Layer属性,在Presentation Layer中即完成所有中间过程的计算。

我不知道Core Animation的这套机制和老乔的Pixar动画公司是否有着千丝万缕的关系,但是无论如何你都能在这套机制设计看到传统动画制作的思路。

一个Animation的完整过程如下图:

图片 17

Animation steps.png

每个视图UIView都包含了一个图层CALayer属性,图层CALayer不处理用户交互,它是真正用于在屏幕上显示和动画,视图UIVIew是对它的封装,并提供了处理交互的功能及CoreAnimation底层方法的公开接口。

iOS基于UIView和CALayer提供两个平行的层级关系做职责分离,这样也能避免很多重复代码。在iOS和Mac OS两个平台上,事件和用户交互有很多的不同,基于多点触控的用户界面和基于鼠标键盘有着本质的区别,这就是为什么iOS有UIKit和UIView,但是Mac OS有AppKit和NSView的原因

3. iOS Graphic的性能考量

我们现在已经了解了iOS 2D图形绘制的基本过程了。在这些内容中,你或许已经看到了时间对于图形绘制的重要性,尤其让你注意到的,应该是那个“VSync”信号。

上面一节已经提到的Pipeline过程是序列化同步进行的,在每一个VSync信号开始时,所有的参与者都会并发的执行下一个序列,如果在一个 VSync 时间内(16.67ms)每一个步骤都有条不紊的完整执行,那么整个显示过程就能顺利的执行下去:

图片 18

Pipeline Serials.png

但是,如果应为某种原因导致某一步或者多步处理时间过长,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。对应到上图,就是某个时间条超长而越过了VSync信号的边界,这就是掉帧:

图片 19

Frame Drop.png

另外,上一节中,我们提到过Animation的3个步骤,对于普通动画而言,只需要一次1-2步的计算,Render Server在后续的第3步中,保证每一帧能够在16ms内提交到GPU就OK了,但是对于Scrolling来说,有一个特殊的地方是,每一次Scroll的动作,都会单独触发上述1-3步的完整过程:也就是说,Scrolling要求每次1-3步都必须在16ms内完成:

图片 20

Scrolling.png

对于TableView而言,这就意味着每一个新的行(new row)的处理都至少要在16ms内完成,最坏情况下,当快速滑动的时候,Render需要在1帧(16ms)内更新整个屏幕的内容!这就是为什么关于ScrollView和TableView的滑动性能一直是大家关注的热点。

掉帧引起的卡顿感,或许是手机用户最敏感的体验,iOS用户一直对Android引以为豪的体验感,其实就来自于iOS针对图形图像和系统设计的优化。(关于这一点,其实除了iOS本身图像渲染的设计有关,另一个重要的因素其实是和Runloop的调度机制有关,这点有时间可以再为Runloop单开一篇)不管怎样,作为iOS App开发的你,应当不遗余力的优化你的App的性能,改善用户的体验。

这部分内容,请看下篇 ——《iOS 2D Graphic (2)— Performance 性能优化》。

希望对你有所帮助。


[参考资料]:

  1. WWDC2012 Session 238 《iOS App Performance: Graphic and Animation》
  2. [WWDC2014 Session 419《Advanced Graphics and Animation Performance》
  3. iOS Developer Library:《Drawing and Printing Guide for iOS》
  4. iOS Developer Library:《Core Animation Programming Guide》
  5. 《iOS UIView 详解》
  6. 《iOS 事件处理机制与图像渲染过程》
  7. 《iOS 视图---动画渲染机制探究》
  8. 《WWDC心得与延伸:iOS图形性能》
  9. 《Tile-Based架构下的性能调校》
  10. 《Getting Pixels onto the Screen》
  11. StackOverflow: What's the difference between Quartz Core, Core Graphics and Quartz 2D?

2016.7.1 完稿于南京。

Metal

OpenGL ES (Open Graphics Library for Embedded Systems):

Bring amazing graphics to life in your iOS and tvOS apps with the hardware-accelerated OpenGL ES API. The OpenGL ES API is simpler than its desktop counterpart but uses the same key concepts, including programmable shaders and extensions that will make your 3D app or game stand out.

OpenGL ES 是OpenGL的一个简化版本,用于二维/三维数据的可视化,是一种开放标准图形库,提供丰富的图形绘制API,并直接使用底层硬件 处理图形命令,它是 Core Animation 实现的一部分。

Metal 2:

Metal 2 provides near-direct access to the graphics processing unit , enabling you to maximize the graphics and compute potential of your apps on iOS, macOS, and tvOS. Building on an efficient low-overhead architecture with precompiled shaders, fine-grained resource control, and multithreading support, Metal 2 evolves to give the GPU even greater control of its graphics pipeline, accelerate neural network training, and provide powerful new tools that give deep insight into your shader code.

下一篇:没有了