CPU(中央处理器)、GPU(图像处理器)、显示器的关系如下:
-- CPU 负责计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等,计算好显示内容会提交到 GPU
--GPU负责进行变换、合成、渲染,完成后将渲染结果放入帧缓冲区
--视频控制器会按照 VSync 信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示
具体到一个动画整个处理流程如下:
布局(CPU)- 这是准备你的视图/图层的层级关系,以及设置图层属性(位置,背景色,边框等等)的阶段。
显示(CPU)- 这是图层的寄宿图片被绘制的阶段。绘制有可能涉及你的-drawRect:和-drawLayer:inContext:方法的调用路径。
准备(CPU)- 这是Core Animation准备发送动画数据到渲染服务的阶段。这同时也是Core Animation将要执行一些别的事务例如解码动画过程中将要显示的图片的时间点。
提交(CPU)- 这是最后的阶段,Core Animation打包所有图层和动画属性,然后通过IPC(内部处理通信)发送到渲染服务进行显示。
渲染(CPU)-对所有的图层属性计算中间值(图层叠加后应显示的值),设置OpenGL几何形状(纹理化的三角形)来执行渲染
绘制(GPU)-在屏幕上渲染可见的三角形
上述步骤中仅最后一步使用GPU完成,但GPU仍然做了很多工作,如:几何转换和光照处理、立方环境材质贴图和顶点混合、纹理压缩和凹凸映射贴图、双重纹理四像素256位渲染引擎等。
GPU、CPU的调度都由底层自动处理,无需开发者关心,但并不意味着开发者完全不能插手处理过程。
--大多数CALayer的属性都是用GPU来绘制,如contents图片的裁剪、遮罩、阴影、光栅化,都由GPU处理。
--视频中硬解码将由GPU处理
GPU能够提升性能,但GPU处理能力达到最大时将会大大降低性能即使CPU仍空闲,因此提升性能的本质在于如何有效调度CPU、GPU。以下处理都可能降低性能。
--离屏绘制:圆角,图层遮罩,阴影或者是图层光栅化都会强制Core Animation提前渲染图层的离屏绘制。
--加载过大的图片:如果视图绘制超出GPU支持的2048x2048或者4096x4096尺寸的纹理,就必须要用CPU在图层每次显示之前对图片预处理,同样也会降低性能。
--自动布局:会增加CPU开销
--对视图实现了-drawRect:方法,或者CALayerDelegate的-drawLayer:inContext:方法,那么在绘制任何东西之前都会产生一个巨大的性能开销
--解压图片 - PNG或者JPEG压缩之后的图片文件会比同质量的位图小得多。但是在图片绘制到屏幕上之前,必须把它扩展成完整的未解压的尺寸(通常等同于图片宽 x 长 x 4个字节)。为了节省内存,iOS通常直到真正绘制的时候才去解码图片。因此可以提前解压并缓存,代码如下:
- (void)awakeFromNib {
if(!self.image) {
self.image = [UIImage imageNamed:@"random.jpg"];
if(NULL != UIGraphicsBeginImageContextWithOptions)
UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0);
else
UIGraphicsBeginImageContext(imageSize);
[image drawInRect:imageRect];
self.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
在SDWebImage中就使用了此方法进行了优化:
@implementationUIImage (ForceDecode)
+ (UIImage*)decodedImageWithImage:(UIImage*)image {
if(image.images) {
return image;
}
CGImageRefimageRef = image.CGImage;
CGSizeimageSize =CGSizeMake(CGImageGetWidth(imageRef),CGImageGetHeight(imageRef));
CGRectimageRect = (CGRect){.origin=CGPointZero, .size= imageSize};
CGColorSpaceRefcolorSpace =CGColorSpaceCreateDeviceRGB();
CGBitmapInfobitmapInfo =CGImageGetBitmapInfo(imageRef);
intinfoMask = (bitmapInfo &kCGBitmapAlphaInfoMask);
BOOLanyNonAlpha = (infoMask ==kCGImageAlphaNone||
infoMask ==kCGImageAlphaNoneSkipFirst||
infoMask ==kCGImageAlphaNoneSkipLast);
if(infoMask ==kCGImageAlphaNone&&CGColorSpaceGetNumberOfComponents(colorSpace) >1) {
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |=kCGImageAlphaNoneSkipFirst;
}
else if(!anyNonAlpha &&CGColorSpaceGetNumberOfComponents(colorSpace) ==3) {
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |=kCGImageAlphaPremultipliedFirst;
}
CGContextRefcontext =CGBitmapContextCreate(NULL,
imageSize.width,
imageSize.height,
CGImageGetBitsPerComponent(imageRef),
0,
colorSpace,
bitmapInfo);
CGColorSpaceRelease(colorSpace);
if(!context)returnimage;
CGContextDrawImage(context, imageRect, imageRef);
CGImageRefdecompressedImageRef =CGBitmapContextCreateImage(context);
CGContextRelease(context);
UIImage*decompressedImage = [UIImageimageWithCGImage:decompressedImageRefscale:image.scaleorientation:image.imageOrientation];
CGImageRelease(decompressedImageRef);
returndecompressedImage;
}
@end