

整体渲染流程可以分为三个阶段:
1、CPU阶段: CPU的计算主要是通过CoreAnimation来处理,通过OpenGL ES/Metal将数据传递给GPU。
2、GPU阶段: GPU渲染主要是将接收到的渲染数据进行一系列渲染之后将帧数据存储在帧缓存(Frame Buffer)里面,供视频控制器调用。
3、屏幕显示: 视频控制器从帧缓存中获取到帧数据显示在屏幕上。


CRT显示器原理主要是通过【电子束】激发屏幕内表面的荧光粉来显示图像,由于荧光粉点亮后很快就会熄灭,所以【电子枪】需要不断的【从上到下】进行扫描,扫描完成后显示器就呈现一帧画面,电子枪回到【初始位置】开始下一次的扫描。
水平同步信号:当电子枪换行扫描时会发出一个水平同步信号。
垂直同步信号:当一帧完成绘制后,电子枪恢复到原来的位置准备扫描下一帧时显示器会发出一个垂直同步信号。
GPU渲染完成后将渲染结果存入帧缓存区,视频控制器根据【垂直同步信号】逐帧读取帧缓冲区的数据,经过数据转换之后由显示器进行显示。
1、屏幕刷新频率
Refresh Rate,单位hz,指的是设备刷新屏幕的频率,这个频率一般是60hz,所以每隔16.67ms屏幕会刷新一次。
2、帧率
Frame Rate,单位fps,指的是GPU生成帧的速率。
3、帧缓存
也叫显存,它是屏幕所显示画面的一个直接映像,也叫做位映射图(bitmap)或光栅,帧缓存的每一存储单元对应屏幕上一个像素,整个帧缓存对应一帧图像。
理想情况下,屏幕刷新频率和帧率完全一致,也就是说当屏幕显示完一帧的时候刚好下一帧画面也生成直接显示在屏幕上,但实际上这两个频率并不完全一致,为了解决这个问题,引入的【帧缓存】的概念。
图像撕裂现象:

垂直同步信号保证GPU的渲染只有等到显示器发出【垂直同步信号】之后才会进行下一帧的渲染。
双缓存保证显示器会交叉读取两个缓存区的内容,相当于是拿空间换时间的一种策略。
好处在于:
- 不浪费CPU、GPU资源,保证提前渲染好的位图有一个缓存区来保存,这样GPU可以就可以进行下一帧的处理。
减少掉帧的出现。
2022080806.jpg
当显示器的【垂直同步信号】发出的时候,GPU没有完成相应的渲染就会出现【卡顿】的现象,这也是为了解决画面撕裂的问题带来的副作用,如下图所示:

补充:
- 掉帧指的是重复渲染同一帧数据而不是指某一帧丢掉了不渲染。
- 为了减少【掉帧】的情况,有的会引入【三缓存】+【垂直同步信号】,比如安卓设备。
当GPU无法直接把渲染结果存放到帧缓存中,而是先是暂时把中间的一个临时状态存放在另外的区域。之后再存放到帧缓存,这个过程叫离屏渲染。
即是说:GPU需要再当前屏幕缓存区以外开辟一个新的缓冲区进行操作。
原因:
GPU渲染采用的是【画家算法】,只能一层一层的输出,所以当一层不能直接生成图片的话就需要额外开辟新的缓冲区来存放这些临时图层直到最后生成了一张完整的图片之后再写入帧缓存里面。
如下:



离屏渲染在当前屏幕缓冲区外新开辟一个缓冲区进行渲染操作,造成其性能损耗的主要原因在于:创建离屏渲染和上下文切换。
切换上下文主要是当发生离屏渲染时,渲染上下文需从当前屏幕缓冲区切换到屏幕外缓冲区然后再完成渲染。
如果一屏元素都发生离屏渲染,这个从当前屏幕缓冲区切换到屏幕外的缓冲区就会发生多次,自然就会有一定的性能损耗。

结论:
满足以下条件的就会发生离屏渲染:
- clipsToBounds开启,圆角 > 0,contents上有内容
- 同时修改了contents+backgroundColor 或 contents+border(iOS9之后)
优化:
- 直接让UI提供带圆角的图片
- 利用UIBezierPath和CAShapeLayer
- 利用UIBezierPath和CoreGraphics

设置遮罩的流程如下:
- 渲染layer的mask纹理
- 渲染layer的content纹理
- 合并操作:合并mask 和 content纹理
结论:
满足以下条件的都会触发离屏渲染:
设置了mask + 任意contents(比如设置UILabel文字、背景颜色、图片等)

结论:
阴影的本质和layer类似,都是在layer下一层多添加一层,根据前面提到的【画家算法】无法一次性生成,所以会发生离屏渲染。
优化:
利用UIBezierPath给视图添加一个阴影路径,相当于提前告诉GPU这个阴影的几何形状,这样阴影就可以独立渲染。

光栅化:
光栅化是一种缓存机制,开启后会缓存这个图片的bitmap,如果对应的layer和sublayers没有发生变化,就可以直接使用缓存而不用GPU再进行渲染,从而提高性能。
注意:
光栅化只能缓存100ms,而且只能存储屏幕大小2.5倍的数据,缓存空间十分有限。

allowsGroupOpacity:
alpha并不是分别应用到每一层上,而是整个layer图层树完成之后,再统一加上alpha,然后和底下其他像素进行融合。
注意:
iOS7之后allowsGroupOpacity默认为YES,这样做的原因是为了保持子视图和父视图保持同样的透明度。
allowsGroupOpacity触发离屏渲染的条件:
- 当视图上有其他子视图
- 视图View的alpha值在0 ~ 1之间
- 视图view.layer.allowsGroupOpacity = YES
离屏渲染的处理仅仅是我们日常所关注的性能中的其中一个点,在处理的时候也要根据具体场景具体分析,要注意并不是所有的离屏渲染都是必须要去避免的,开辟额外的帧缓存虽然有一定的性能损耗,但是保存渲染结果并进行最终的视图显示也是为了保持视图的流畅性。
更多技术文章欢迎移步Sunny的个人技术博客。
我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>
在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上
我在一个简单的RailsAPI中有以下Controller代码:classApi::V1::AccountsControllerehead:not_foundendendend问题在于,生成的json具有以下格式:{id:2,name:'Simpleaccount',cash_flows:[{id:1,amount:34.3,description:'simpledescription'},{id:2,amount:1.12,description:'otherdescription'}]}我需要我生成的json是camelCase('cashFlows'而不是'cash_flows'
我想在我的Controller中使用以下corsheader呈现JSON:'Access-Control-Allow-Origin'='*'.我试过这个:defmy_actionrender(json:some_params)response.headers['Access-Control-Allow-Origin']='*'end但是我得到了一个AbstractController::DoubleRenderError。有没有办法使用header呈现JSON? 最佳答案 您不能在渲染后设置header,因为已发送响应。所以在没有意
我有一个Ruby数组,如何在Rails3.0中将其呈现为JSONView?我的Controller方法是defautocomplete@question=Question.allend 最佳答案 如果自动完成操作仅呈现JSON,您可以将re5et的解决方案简化为:defautocompletequestions=Question.allrender:json=>questionsend(请注意,我将“问题”复数化以反射(reflect)它是一个数组并删除了@符号-一个局部变量就足够了,因为您可能只使用它来呈现内联JSON)作为一种附