当前位置: 首页 > 编程日记 > 正文

iOS 的离屏渲染

原文链接:http://www.imlifengfeng.com/blog/?p=593
OpenGL ES 是一套多功能开放标准的用于嵌入系统的 C-based 的图形库,用于 2D 和 3D 数据的可视化。OpenGL 被设计用来转换一组图形调用功能到底层图形硬件(GPU),由 GPU 执行图形命令,用来实现复杂的图形操作和运算,从而能够高性能、高帧率利用 GPU 提供的 2D 和 3D 绘制能力。iOS 系统默认支持 OpenGl ES1.0、ES2.0 以及 ES3.0 3 个版本,三者之间并不是简单的版本升级,设计理念甚至完全不同。GPU 屏幕渲染方式中有一种方式为离屏渲染,处理不好离屏渲染往往会对 APP 的性能产生较大的影响。 —— 由膜法小编分享

一、概述

OpenGL ES是一套多功能开放标准的用于嵌入系统的C-based的图形库,用于2D和3D数据的可视化。OpenGL被设计用来转换一组图形调用功能到底层图形硬件(GPU),由GPU执行图形命令,用来实现复杂的图形操作和运算,从而能够高性能、高帧率利用GPU提供的2D和3D绘制能力。iOS系统默认支持OpenGl ES1.0、ES2.0以及ES3.0 3个版本,三者之间并不是简单的版本升级,设计理念甚至完全不同。GPU屏幕渲染方式中有一种方式为离屏渲染,处理不好离屏渲染往往会对APP的性能产生较大的影响。

二、当前屏幕渲染与离屏渲染

OpenGL中,GPU屏幕渲染有两种方式:

(1)On-Screen Rendering (当前屏幕渲染)

指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区进行。

(2)Off-Screen Rendering (离屏渲染)

指的是在GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作。

当前屏幕渲染不需要额外创建新的缓存,也不需要开启新的上下文,相对于离屏渲染性能更好。但是受当前屏幕渲染的局限因素限制(只有自身上下文、屏幕缓存有限等),当前屏幕渲染有些情况下的渲染解决不了的,就使用到离屏渲染。

相比于当前屏幕渲染,离屏渲染的代价是很高的,主要体现在两个方面:

(1)创建新缓冲区

要想进行离屏渲染,首先要创建一个新的缓冲区。

(2)上下文切换

离屏渲染的整个过程,需要多次切换上下文环境:先是从当前屏幕(On-Screen)切换到离屏(Off-Screen),等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上有需要将上下文环境从离屏切换到当前屏幕。而上下文环境的切换是要付出很大代价的。

特殊的“离屏渲染”:CPU渲染

如果我们重写了drawRect方法,并且使用任何Core Graphics的技术进行了绘制操作,就涉及到了CPU渲染。整个渲染过程由CPU在App内同步地完成,渲染得到的bitmap(位图)最后再交由GPU用于显示。

Designing for iOS: Graphics & Performance 这篇文章也提到了使用 Core Graphics API 会触发离屏渲染。 苹果 iOS 4.1-8 时期的 UIKit 组成员Andy Matuschak也曾对这个说法进行解释:「Core Graphics 的绘制 API 的确会触发离屏渲染,但不是那种 GPU 的离屏渲染。使用 Core Graphics 绘制 API 是在 CPU 上执行,触发的是 CPU 版本的离屏渲染。」

三、为什么要有离屏渲染

大家高中物理应该学过显示器是如何显示图像的:需要显示的图像经过CRT电子枪以极快的速度一行一行的扫描,扫描出来就呈现了一帧画面,随后电子枪又会回到初始位置循环扫描,形成了我们看到的图片或视频。
为了让显示器的显示跟视频控制器同步,当电子枪新扫描一行的时候,准备扫描的时发送一个水平同步信号(HSync信号),显示器的刷新频率就是HSync信号产生的频率。然后CPU计算好frame等属性,将计算好的内容交给GPU去渲染,GPU渲染好之后就会放入帧缓冲区。然后视频控制器会按照HSync信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器,就显示出来了。具体的大家自行查找资料或询问相关专业人士,这里只参考网上资料做一个简单的描述。
离屏渲染的代价很高,想要进行离屏渲染,首选要创建一个新的缓冲区,屏幕渲染会有一个上下文环境的一个概念,离屏渲染的整个过程需要切换上下文环境,先从当前屏幕切换到离屏,等结束后,又要将上下文环境切换回来。这也是为什么会消耗性能的原因了。
由于垂直同步的机制,如果在一个 HSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是 界面卡顿的原因。

既然离屏渲染这么耗性能,为什么有这套机制呢?

有些效果被认为不能直接呈现于屏幕,而需要在别的地方做额外的处理预合成。图层属性的混合体没有预合成之前不能直接在屏幕中绘制,所以就需要屏幕外渲染。屏幕外渲染并不意味着软件绘制,但是它意味着图层必须在被显示之前在一个屏幕外上下文中被渲染(不论CPU还是GPU)。

下面的情况或操作会引发离屏渲染:

  • 为图层设置遮罩(layer.mask)
  • 将图层的layer.masksToBounds / view.clipsToBounds属性设置为true
  • 将图层layer.allowsGroupOpacity属性设置为YES和layer.opacity小于1.0
  • 为图层设置阴影(layer.shadow *)。
  • 为图层设置layer.shouldRasterize=true
  • 具有layer.cornerRadius,layer.edgeAntialiasingMask,layer.allowsEdgeAntialiasing的图层
  • 文本(任何种类,包括UILabel,CATextLayer,Core Text等)。
  • 使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现。

四、优化方案

官方对离屏渲染产生性能问题也进行了优化:

iOS 9.0 之前UIimageView跟UIButton设置圆角都会触发离屏渲染。

iOS 9.0 之后UIButton设置圆角会触发离屏渲染,而UIImageView里png图片设置圆角不会触发离屏渲染了,如果设置其他阴影效果之类的还是会触发离屏渲染的。

1、圆角优化

在APP开发中,圆角图片还是经常出现的。如果一个界面中只有少量圆角图片或许对性能没有非常大的影响,但是当圆角图片比较多的时候就会APP性能产生明显的影响。

我们设置圆角一般通过如下方式:

imageView.layer.cornerRadius = CGFloat(10);
imageView.layer.masksToBounds = YES;

这样处理的渲染机制是GPU在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作,也就是离屏渲染,这会给我们带来额外的性能损耗,如果这样的圆角操作达到一定数量,会触发缓冲区的频繁合并和上下文的的频繁切换,性能的代价会宏观地表现在用户体验上——掉帧。

优化方案1:使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; 
imageView.image = [UIImage imageNamed:@"myImg"]; 
//开始对imageView进行画图 
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0); 
//使用贝塞尔曲线画出一个圆形图 
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip];
[imageView drawRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext(); 
//结束画图 
UIGraphicsEndImageContext();
[self.view addSubview:imageView];

优化方案2:使用CAShapeLayer和UIBezierPath设置圆角

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; 
imageView.image = [UIImage imageNamed:@"myImg"]; 
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init]; 
//设置大小 
maskLayer.frame = imageView.bounds; 
//设置图形样子 
maskLayer.path = maskPath.CGPath;
imageView.layer.mask = maskLayer; 
[self.view addSubview:imageView];

对于方案2需要解释的是:

  • CAShapeLayer继承于CALayer,可以使用CALayer的所有属性值;
  • CAShapeLayer需要贝塞尔曲线配合使用才有意义(也就是说才有效果)
  • 使用CAShapeLayer(属于CoreAnimation)与贝塞尔曲线可以实现不在view的drawRect(继承于CoreGraphics走的是CPU,消耗的性能较大)方法中画出一些想要的图形
  • CAShapeLayer动画渲染直接提交到手机的GPU当中,相较于view的drawRect方法使用CPU渲染而言,其效率极高,能大大优化内存使用情况。

总的来说就是用CAShapeLayer的内存消耗少,渲染速度快,建议使用优化方案2。

2、shadow优化

对于shadow,如果图层是个简单的几何图形或者圆角图形,我们可以通过设置shadowPath来优化性能,能大幅提高性能。示例如下:

imageView.layer.shadowColor = [UIColor grayColor].CGColor;
imageView.layer.shadowOpacity = 1.0;
imageView.layer.shadowRadius = 2.0;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:imageView.frame];
imageView.layer.shadowPath = path.CGPath;

我们还可以通过设置shouldRasterize属性值为YES来强制开启离屏渲染。其实就是光栅化(Rasterization)。既然离屏渲染这么不好,为什么我们还要强制开启呢?当一个图像混合了多个图层,每次移动时,每一帧都要重新合成这些图层,十分消耗性能。当我们开启光栅化后,会在首次产生一个位图缓存,当再次使用时候就会复用这个缓存。但是如果图层发生改变的时候就会重新产生位图缓存。所以这个功能一般不能用于UITableViewCell中,cell的复用反而降低了性能。最好用于图层较多的静态内容的图形。而且产生的位图缓存的大小是有限制的,一般是2.5个屏幕尺寸。在100ms之内不使用这个缓存,缓存也会被删除。所以我们要根据使用场景而定。

3、其他的一些优化建议

  • 当我们需要圆角效果时,可以使用一张中间透明图片蒙上去
  • 使用ShadowPath指定layer阴影效果路径
  • 使用异步进行layer渲染(Facebook开源的异步绘制框架AsyncDisplayKit)
  • 设置layer的opaque值为YES,减少复杂图层合成
  • 尽量使用不包含透明(alpha)通道的图片资源
  • 尽量设置layer的大小值为整形值
  • 直接让美工把图片切成圆角进行显示,这是效率最高的一种方案
  • 很多情况下用户上传图片进行显示,可以让服务端处理圆角
  • 使用代码手动生成圆角Image设置到要显示的View上,利用UIBezierPath(CoreGraphics框架)画出来圆角图片

五、Core Animation工具检测离屏渲染

对于离屏渲染的检测,苹果为我们提供了一个测试工具Core Animation。可以在Xcode->Open Develeper Tools->Instruments中找到,如下图:

201703080040

Core Animation工具用来监测Core Animation性能,提供可见的FPS值,并且提供几个选项来测量渲染性能。如下图:

201703080041

下面我们来说明每个选项的功能:
Color Blended Layers:这个选项如果勾选,你能看到哪个layer是透明的,GPU正在做混合计算。显示红色的就是透明的,绿色就是不透明的。

Color Hits Green and Misses Red:如果勾选这个选项,且当我们代码中有设置shouldRasterize为YES,那么红色代表没有复用离屏渲染的缓存,绿色则表示复用了缓存。我们当然希望能够复用。

Color Copied Images:按照官方的说法,当图片的颜色格式GPU不支持的时候,Core Animation会
拷贝一份数据让CPU进行转化。例如从网络上下载了TIFF格式的图片,则需要CPU进行转化,这个区域会显示成蓝色。还有一种情况会触发Core Animation的copy方法,就是字节不对齐的时候。如下图:

201703080043

Color Immediately:默认情况下Core Animation工具以每毫秒10次的频率更新图层调试颜色,如果勾选这个选项则移除10ms的延迟。对某些情况需要这样,但是有可能影响正常帧数的测试。

Color Misaligned Images:勾选此项,如果图片需要缩放则标记为黄色,如果没有像素对齐则标记为紫色。像素对齐我们已经在上面有所介绍。

Color Offscreen-Rendered Yellow:用来检测离屏渲染的,如果显示黄色,表示有离屏渲染。当然还要结合Color Hits Green and Misses Red来看,是否复用了缓存。

Color OpenGL Fast Path Blue:这个选项对那些使用OpenGL的图层才有用,像是GLKView或者 CAEAGLLayer,如果不显示蓝色则表示使用了CPU渲染,绘制在了屏幕外,显示蓝色表示正常。

Flash Updated Regions:当对图层重绘的时候回显示黄色,如果频繁发生则会影响性能。可以用增加缓存来增强性能。

具体的使用截图就不发出来了,大家自己可以试试。

参考:Drawing to Other Rendering Destinations

原创文章,转载请注明: 转载自李峰峰博客

本文链接地址: iOS的离屏渲染


相关文章:

MySQL 常见操作指令

什么是SQL? SQL(Structured Query Language)用于访问和操作数据库的结构化查询语言。 数据库包含一个或多个表,每个表均有名称标识,包含数据的记录(行)。 典型的SQL语句 1. SELEC语句 SELE…

iOS 实现点击微信头像效果

来源:伯乐在线 - 小良 如有好文章投稿,请点击 → 这里了解详情 如需转载,发送「转载」二字查看说明 公司产品需要实现点击个人主页头像可以放大头像、缩放头像、保存头像效果(和点击微信个人头像类似),故找…

HDU 4292 Food(dinic +拆点)

题目链接 我做的伤心了&#xff0c;不知是模版效率低&#xff0c;还是错了&#xff0c;交上就是TLE&#xff0c;找了份别人的代码&#xff0c;改了好几下终于过了。。 1 #include <cstdio>2 #include <cstring>3 #include <queue>4 #include <map>5 #i…

jQuery中用ajax访问php接口文件

js代码 function ajax_request(){var result;var articleId new Object();articleIdgetArticleId();$.ajax({url: "/topicPage/getComment.php",//请求php文件的路径data:{id:articleId},//请求中要传送的参数,会自动拼接成一个路径&#xff0c;在php中用get方式获取…

Python 数据库操作 psycopg2

文章目录安装基本使用安装 psycopg 是 Python 语言中 PostpreSQL数据库接口 安装环境&#xff1a; Python&#xff1a;v2.7, v3.4~3.8PostGreSQL&#xff1a;7.4~12 pip install psycopg2基本使用 import psycopg2def connect_db(host: str,port: int,database: str,user:…

Android logcat命令详解

一、logcat命令介绍 1.android log系统 2.logcat介绍 logcat是android中的一个命令行工具&#xff0c;可以用于得到程序的log信息 log类是一个日志类&#xff0c;可以在代码中使用logcat打印出消息 常见的日志纪录方法包括&#xff1a;方法 描述 v(String,String) (vervbose)显…

[iOS]如何重新架构 JPVideoPlayer ?

注意&#xff1a;此文为配合 JPVideoPlayer version 2.0 版本发布而写&#xff0c;如果你想了解 2.0 版本的更新内容和所有实现细节&#xff0c;请点击前往 GitHub。 导言&#xff1a;我几个月前写了一个在 UITableView 中滑动 UITableViewCell 播放视频的框架&#xff0c;类似…

函数项目一个超感人的故事:关于swfupload在某些环境下面session丢失的完美解决方案(看完我哭了)...

查了好多资料&#xff0c;发现还是不全&#xff0c;干脆自己整理吧&#xff0c;至少保证在我的做法正确的&#xff0c;以免误导读者&#xff0c;也是给自己做个记录吧&#xff01; 标题吸引到你了吗&#xff1f; 先说一下这个题问成形的原因。大家都晓得 session是靠cookie中的…

【学习笔记】git 使用文档

安装 git # mac 环境 brew install git检查是否安装成功 ➜ ~ git --version git version 2.20.1 (Apple Git-117)卸载 git ➜ ~ which -a git /usr/bin/git ➜ ~ cd /usr/bin ➜ bin sudo rm -rf git*git init 命令 对一个空文件&#xff0c;git 初始化。文件名称增加…

UIBezierPath和CAShapeLayer创建不规则View(Swift 3.0)

最近一个朋友在做图片处理的 App&#xff0c;想要实现类似 MOLDIV App 拼图的UI效果&#xff08;如何创建不规则的 view&#xff09;&#xff0c;就问我有什么想法。我首先想到的就是 UIBezierPathCAShapeLayer的方式&#xff0c;为了验证自己的想法&#xff0c;写了一个小 dem…

http响应状态

Servlet API&#xff1a; javax.servlet.http.HttpServletResponse 用于创建HTTP响应&#xff0c;包括HTTP协议的状态行、响应头以及消息体 HTTP状态码&#xff1a; 100-199&#xff1a;表示信息性代码&#xff0c;标示客户端应该采取的其他动作&#xff0c;请求正在进行。 200…

antlr.collections.AST.getLine()I问题的起因及解决

在我们的java web 项目中引入hibernate和struts&#xff0c;当我们使用HQL语句进行查询时会报 antlr.collections.AST.getLine()I的错误&#xff0c;导致程序无法继续运行&#xff0c;这并不是我们的程序写的有错误&#xff0c;出现这个异常的原因是因为我们使用的hibernate和s…

2018湖湘杯海选复赛Writeup

2018湖湘杯Writeup0x01 签到题0x02 MISC Flow0x03 WEB Code Check0x04 WEB Readflag0x05 WEB XmeO0x06 Reverse Replace0x07 MISC Disk0x08 Crypto Common Crypto0x09 Reverse HighwayHash640x10 Web Mynot0x01 签到题 关注合天智汇公众号&#xff0c;回复hxb2018得到flag。0x…

Operation Queues并发编程

并发、异步在我们的编程中&#xff0c;见到的太多了。在iOS中&#xff0c;实现并发的主要三个途径Operation Queues、Dispatch Queues、Dispatch Sources&#xff0c;今天我们就来详细介绍Operatin Queues的使用&#xff0c;花了两天时间写这一篇&#xff0c;值得一看。 为什么…

socket 服务器浏览器与服务器客户端实例

一、服务器与浏览器 // 取得本机的loopback网络地址&#xff0c;即127.0.0.1 IPAddress address IPAddress.Loopback; IPEndPoint endPoint new IPEndPoint(address, 49152); Socket socket new Socket(AddressFamily.InterNetwork, Socke…

匹配3位或4位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔...

public bool IsPhone(string input){string pattern "^\\(0\\d{2}\\)[- ]?\\d{8}$|^0\\d{2}[- ]?\\d{8}$|^\\(0\\d{3}\\)[- ]?\\d{7}$|^0\\d{3}[- ]?\\d{7}$";Regex regex new Regex(pattern);return regex.IsMatch(input);} 转载于:https://www.cnblogs.com/…

Mac MySQL配置环境变量的两种方法

第一种&#xff1a; 1.打开终端,输入&#xff1a; cd ~ 会进入~文件夹 2.然后输入&#xff1a;touch .bash_profile 回车执行后&#xff0c; 3.再输入&#xff1a;open -e .bash_profile 会在TextEdit中打开这个文件&#xff08;如果以前没有配置过环境变量&#xff0c;那么这…

linux之x86裁剪移植---字符界面sdl开发入门

linux下有没有TurboC2.0那样的画点、线、圆的图形函数库&#xff0c;有没有grapihcs.h&#xff0c;或者与之相对应或相似的函数库是什么&#xff1f;有没有DirectX这样的游戏开发库&#xff1f;SDL就是其中之一。SDL&#xff08;Simple DirectMedia Layer&#xff09;是一个夸平…

iOS 视频捕获系列Swift之AVFoundation(一)

iOS 视频捕获系列之AVFoundation(一) AVCaptureMovieFileOutput系列 在iOS开发过程中&#xff0c;或多或少的都涉及视频的操作。 尤其在去年直播行业的带动下&#xff0c;移动端对视频的处理也愈来愈发要求严格。 本文也是在 这篇 中参考而来。 Swift 版本哦&#xff01; 本文 …

C#做外挂常用API

using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; //这个肯定要的 namespace WindowsApplication1 {class win32API{public const int OPEN_PROCESS_ALL 2035711;public const int PAGE_READWRITE 4;public con…

phpinfo 信息利用

0x01 基础信息 1.system info:提供详细的操作系统信息&#xff0c;为提权做准备。 2.extension_dir:php扩展的路径 3.$_SERVER[‘HTTP_HOST’]:网站真实IP、CDN什么的都不存在的&#xff0c;找到真实ip&#xff0c;扫一扫旁站&#xff0c;没准就拿下几个站。 4.$_SERVER[‘…

iOS三种录制视频方式详细对比

先附上参考资料 http://www.jianshu.com/p/16cb14f53933 https://developer.apple.com/library/content/samplecode/AVSimpleEditoriOS/Introduction/Intro.html https://github.com/objcio/VideoCaptureDemo https://github.com/gsixxxx/DTSmallVideo https://github.com/Andy…

C# 实现Oracle中的数据与Excel之间的转换

最近项目要求实现数据库之间数据在各个数据库之间导入导出&#xff0c;在此做个笔记 1. 将Oracle中的表导入到Excel中&#xff0c;反之亦然 private static readonly string connectionString ConfigurationManager.ConnectionStrings["OracleConnection"].Connecti…

【转】Word2007中不连续页码设置 多种页码设置

【转】Word2007中不连续页码设置 多种页码设置 页码是论文必不可少的部分。我们看一下如何添加页码&#xff0c;并且针对一些特殊的格式要求怎么应对&#xff1a; 如果是【毕业论文】有多种混合页码&#xff0c;有Ⅰ、Ⅱ、Ⅲ。。。还有1、2、3 。。。请直接看【第二种方法】。 …

vim编辑器异常退出产生备份文件

当非正常关闭vim编辑器时&#xff08;比如直接关闭终端或者电脑断电&#xff09;&#xff0c;会生成一个.swp文件&#xff0c;这个文件是一个临时交换文件&#xff0c;用来备份缓冲区中的内容。 需要注意的是如果你并没有对文件进行修改&#xff0c;而只是读取文件&#xff0c…

从0到1思考与实现iOS-Widget

讲述之前首先看下demo效果图&#xff1a; 基本的展开收起、本App本体交互然后再展示几个效果不错的 Widget app 毒物 && KeepESPNPCalcMusixmatchFantastical 2Carrot Weatherdemo 地址在此&#xff01;欢迎star 比心一、Widget总览 Widget 是 iOS8 推出第一版&…

Android Studio 初体验

Google在I/O2013大会上发布了Android新的开发工具Android Studio&#xff0c;趁周末时间做了一下尝试。有需要的可以在http://developer.android.com/sdk/installing/studio.html下载&#xff0c;当前版本是V0.1。官方解释&#xff1a;Android Studio is anew Android developm…

JAVA面试题(2)

1 String 与 new 的不同 使用“”赋值不一定每次都创建一个新的字符串&#xff0c;而是从“字符串实例池”中查找字符串。使用“new”进行赋值&#xff0c;则每次都创建一个新的字符串。 2 String与StringBuffer String类是不可变类&#xff0c;字符串一旦初始化后&#xff0c…

限制HTTP数据包发送Referer

一般点击一个A标签的时候都会发送 Referer 什么是 Referer&#xff1f; 就是你点击A标签 Referer的信息告诉服务端你从哪里点击出来的 可在HTML上加 <meta name"referrer" content"no-referrer">这样就不发送Referer头了

TCP/IP详解学习笔记(3)-IP协议,ARP协议,RARP协议

把这三个协议放到一起学习是因为这三个协议处于同一层&#xff0c;ARP协议用来找到目标主机的Ethernet网卡Mac地址&#xff0c;IP则承载要发送的消息。数据链路层可以从ARP得到数据的传送信息&#xff0c;而从IP得到要传输的数据信息。 1.IP协议 IP协议是TCP/IP协议的核心&…