美文网首页
OpenGL离屏渲染

OpenGL离屏渲染

作者: 我是卖报的小行家 | 来源:发表于2020-07-07 11:55 被阅读0次

先看一个例子
1.背景图片

    UIButton * btn1  = [UIButton buttonWithType:UIButtonTypeCustom];
    btn1.frame = CGRectMake(100, 100, 100, 100);
    btn1.layer.cornerRadius = 50;
    [btn1 setImage:[UIImage imageNamed:@"截屏2020-07-07 上午11.05.22"] forState:0];
    [self.view addSubview:btn1];
    btn1.clipsToBounds = YES; 

2.背景颜色加背景图片

    btn2.frame = CGRectMake(100, 230, 100, 100);
    btn2.layer.cornerRadius = 50;
    btn2.backgroundColor = [UIColor systemBlueColor];
    [btn2 setImage:[UIImage imageNamed:@"截屏2020-07-07 上午11.05.05"] forState:0];
    [self.view addSubview:btn2];
    btn2.clipsToBounds = YES;

3.不存在背景图片

    btn3.frame = CGRectMake(100, 350, 100, 100);
    btn3.layer.cornerRadius = 50;
    btn3.backgroundColor = [UIColor systemBlueColor];
    [self.view addSubview:btn3];
    btn3.clipsToBounds = YES;

4.只设置图片无背景色

    btn4.frame = CGRectMake(100, 480, 100, 100);
    btn4.layer.cornerRadius = 50;
    [btn4 setImage:[UIImage imageNamed:@"截屏2020-07-07 上午11.05.05"] forState:0];
    [self.view addSubview:btn4];
    btn4.clipsToBounds = YES;
打开模拟器离屏渲染

其中1,3,4产生了离屏渲染


模拟器离屏渲染

离屏渲染的原理
1.app渲染流程


渲染流水线示意图

看上图得知Application以及RenderServer部分是运行在CPU上的,Application处理好数据后,提交到Render Server,然后Core Animation在会将具体操作转换成GPU的Draw calls(OpenGL/Metal);也就是CPU+GPU共同完成渲染工作。

这个时候我们再来看什么叫离屏渲染
如果要在显示屏上显示内容,我们至少需要一块与屏幕像素数据量一样大的frame buffer,作为像素数据存储区域,而这也是GPU存储渲染结果的地方。如果有时因为面临一些限制,无法把渲染结果直接写入frame buffer,而是先暂存在另外的内存区域,之后再写入frame buffer,那么这个过程被称之为离屏渲染(offscreen buffer)

一般的渲染流程简化图


渲染流程简化图

离屏渲染流程简化图


离屏渲染流程简化图

离屏渲染性能问题:
1),需要额外的存储空间,
2),将结果从Offscreen buffer转存到Frame Buffer中也是需要时间的
既然离屏渲染容易带来性能问题,为什么还要用呢?
1.非常多的特殊效果,并不能一次用一个图层就能画出来,所以需要使用额外的offscreen Buffer来保持中间状态(不得不使用),比如:圆角,阴影

2.能带来效率的优势:既然效果会多次出现在屏幕上,可提前渲染好,保存在offscreen Buffer中,从而达到复用的结果 比如:光珊化shouldRasterize

引发离屏渲染的情况
1.使用了 mask 的 layer (layer.mask)
2.需要进行裁剪的 layer (layer.masksToBounds /view.clipsToBounds)
设置了layer.cornerRadius,只会设置backgroundColor和border的圆角,不会设置content的圆角,除非同时设置了layer.masksToBounds
3.设置了组透明度为 YES,并且透明度不为 1 的layer (layer.allowsGroupOpacity/ layer.opacity)
4.添加了投影的 layer (layer.shadow*)
5.采用了光栅化的 layer (layer.shouldRasterize)
1).如果layer不被复用,则没有必要打开光栅化
2).如果layer不是静态的,需要被频繁修改,比如处于动画之中,那么开启离屏渲染会影响动画效果了。
3).离屏渲染缓存有时间限制,缓存内容100ms内如果没有被使用,那么它就会丢弃,就无法再复用了。
4).离屏渲染缓存空间有限,超过2.5倍屏幕像素大小也会失效。且无法进行复用了。
6.绘制了文字的 layer (UILabel, CATextLayer, Core Text 等)

最后来点干货
iOS上的圆⻆处理⼿段参考⽅案
圆⻆触发的离屏渲染 offscreen Rendering


圆⻆触发的离屏渲染

GPU离屏渲染,“画家画法”

通过渲染流水线示意图中我们可以看到,主要的渲染操作都是由CoreAnimation的Render Server模块,通过调用显卡驱动所提供的OpenGL/Metal接口来执行的。通常对于每一层layer,Render Server会遵循“画家算法”,按次序输出到frame buffer,后一层覆盖前一层,就能得到最终的显示结果(值得一提的是,与一般桌面架构不同,在iOS中,设备主存和GPU的显存共享物理内存,这样可以省去一些数据传输开销)

然而有些场景并没有那么简单。作为“画家”的GPU虽然可以一层一层往画布上进行输出,但是无法在某一层渲染完成之后,再回过头来擦除/改变其中的某个部分——因为在这一层之前的若干层layer像素数据,已经在渲染中被永久覆盖了。这就意味着,**对于每一层****layer****,要么能找到一种通过单次遍历就能完成渲染的算法,要么就不得不另开一块内存,借助这个临时中转区域来完成一些更复杂的、多次的修改,剪裁操作。

方案1

_imageView.clipsToBounds=YES;
_imageView.layer.cornerRadius=4.0;

方案2


圆角处理2

方案3


圆角处理3[图片上传中...(截屏2020-07-07 下午2.16.40.png-ea8b3a-1594102603503-0)]

方案4


圆角处理4
- (UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius 
 corners:(UIRectCorner)corners 
 borderWidth:(CGFloat)borderWidth 
 borderColor:(UIColor *)borderColor 
 borderLineJoin:(CGLineJoin)borderLineJoin { 
 if (corners != UIRectCornerAllCorners) { 
 UIRectCorner tmp = 0; 
 if (corners & UIRectCornerTopLeft) tmp |= UIRectCornerBottomLeft; 
 if (corners & UIRectCornerTopRight) tmp |= UIRectCornerBottomRight; 
 if (corners & UIRectCornerBottomLeft) tmp |= UIRectCornerTopLeft; 
 if (corners & UIRectCornerBottomRight) tmp |= UIRectCornerTopRight; 
 corners = tmp; 
 } 
 UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale); 
 CGContextRef context = UIGraphicsGetCurrentContext(); 
 CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height); 
 CGContextScaleCTM(context, 1, -1); 
 CGContextTranslateCTM(context, 0, -rect.size.height); 
 CGFloat minSize = MIN(self.size.width, self.size.height); 
 if (borderWidth < minSize / 2) { 
 UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth) byRoundingCorners:corners 
cornerRadii:CGSizeMake(radius, borderWidth)]; 
 [path closePath]; 
 CGContextSaveGState(context); 
 [path addClip]; 
 CGContextDrawImage(context, rect, self.CGImage); 
 CGContextRestoreGState(context); 
 } 
 if (borderColor && borderWidth < minSize / 2 && borderWidth > 0) { 
 CGFloat strokeInset = (floor(borderWidth * self.scale) + 0.5) / self.scale; 
 CGRect strokeRect = CGRectInset(rect, strokeInset, strokeInset); 
 CGFloat strokeRadius = radius > self.scale / 2 ? radius - self.scale / 2 : 0; 
 UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadii:CGSizeMake(strokeRadius, 
borderWidth)]; 
 [path closePath]; 
 path.lineWidth = borderWidth; 
 path.lineJoinStyle = borderLineJoin; 
 [borderColor setStroke];

相关文章

  • Android OpenGL ES 九. FBO离屏渲染(转载补

    离屏渲染(FrameBufferObject)今天为大家介绍离屏渲染的概念。在OpenGL中,GPU屏幕渲染有以下...

  • iOS 界面流畅 - 离屏渲染

    离屏渲染往往会带来界面卡顿的问题,这里将会讨论 当前屏幕渲染、离屏渲染 以及 CPU 渲染 在 OpenGL 中,...

  • OpenGL离屏渲染

    先看一个例子1.背景图片 2.背景颜色加背景图片 3.不存在背景图片 4.只设置图片无背景色 其中1,3,4产生了...

  • iOS触发离屏渲染

    在OpenGL中,GPU有2种渲染方式 离屏渲染消耗性能的原因需要创建新的缓冲区离屏渲染的整个过程,需要多次切换上...

  • 离屏渲染与OpenGL渲染结构

    大纲 理解离屏渲染 OpenGL渲染结构 着色器 OpenGL 基础图元/基本图元连接 理解离屏渲染 正常渲染流程...

  • OpenGL之离屏渲染

    什么是离屏渲染 Off-Screen Rendering意为离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓...

  • OpenGL之离屏渲染

    作者:范大可链接:https://juejin.cn/post/6899635441201643534[https...

  • iOS--离屏渲染

    离屏渲染(Offscreen rendering) 离屏渲染的定义 离屏渲染(offscreen-renderin...

  • 离屏渲染之我知

    相比于当前屏幕渲染,离屏渲染的代价是很高的,这也是iOS移动端优化的必要部分。 OpenGL中,GPU屏幕渲染有以...

  • 图层性能-离屏渲染、光栅化等

    一.离屏渲染 1.在屏渲染、离屏渲染: On-Screen Rendering/在屏渲染:在屏渲染:指的是GPU的...

网友评论

      本文标题:OpenGL离屏渲染

      本文链接:https://www.haomeiwen.com/subject/xszoqktx.html