preface

Before introducing off-screen rendering, let’s first understand the concept, what off-screen rendering is, why it happens, and how to avoid it. There are two ways to render a GPU screen:

(1) On-screen Rendering

Refers to the fact that the RENDERING operation of the GPU is performed in the screen buffer currently used for display.

(2) Off-screen Rendering

This refers to creating a buffer on the GPU outside the current screen buffer for rendering operations.

The current screen rendering does not require the creation of additional new caches, nor does it require the opening of a new context, providing better performance than off-screen rendering. However, due to the limitations of the current screen rendering (only its own context, limited screen cache, etc.), the current screen rendering in some cases cannot solve the rendering, the use of off-screen rendering.

Reasons for off-screen rendering:

The Apple system cannot process the view one at a time, but needs to process the view one by one, so it will need to open an off-screen cache to store the processed view one by one, which will lead to off-screen rendering. If off-screen rendering is so performance-intensive, why is there such a mechanism?

Some effects are not considered to be directly on-screen and require additional processing elsewhere. The blend of layer properties cannot be drawn directly on screen without pre-composition, so it needs to be rendered off-screen. Off-screen rendering does not mean software rendering, but it does mean that layers must be rendered in an off-screen context (whether CPU or GPU) before they can be displayed. The following conditions or actions cause an off-screen rendering:

  • Set the layer mask (layer.mask)

– Set layer.masksToBounds/view.clipsToBounds to true

– Set the layer layer.allowsGroupOpacity property to YES and layer.opacity less than 1.0

– Set the shadow for the layer (layer.shadow *).

– Set the raster layer for the layer. ShouldRasterize =true

– a layer. The cornerRadius, layer edgeAntialiasingMask, layer. AllowsEdgeAntialiasing layer Of course not all rounded corners will lead to an off-screen rendering

– Text (any kind, including UILabel, CATextLayer, Core Text, etc.)

– Using CGContext to draw in the drawRect: method will most likely result in an off-screen rendering, or even just an empty implementation.

How to avoid off-screen rendering:

1. Optimization of rounded corners:

Rounded corner images are common in APP development. If there are only a few rounded corner images in an interface, the performance may not be greatly affected, but when there are a lot of rounded corner images, the performance of the APP will be significantly affected.

We set rounded corners in the following way:

imageView.layer.cornerRadius = CGFloat(10);

imageView.layer.masksToBounds = YES;

Render such processing mechanism of the GPU in the current screen buffer outside a new settlement render buffer, which is from the screen, it will bring us additional performance loss, if such a rounded operation to achieve a certain number, will trigger a buffer frequent mergers and context of frequent switching, the performance of the price will be macroscopic manifested in user experience – frame drop.

Optimization 1: Draw a rounded corner using the Bezier curve UIBezierPath and Core Graphics framework

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100,100,100,100)];

imageView.image = [UIImage imageNamed:@”myImg”]; // Start drawing the imageView

UIGraphicsBeginImageContextWithOptions (imageView. Bounds. The size, NO, 1.0);

// Draw a circle using bezier curves

[[UIBezierPath bezierPathWithRoundedRect:imageView.boundscornerRadius:imageView.frame.size.width]addClip];

[imageView drawRect:imageView.bounds];

imageView.image=UIGraphicsGetImageFromCurrentImageContext();

// Finish drawing

UIGraphicsEndImageContext();

[self.view addSubview:imageView];

2. Use CAShapeLayer and UIBezierPath to set rounded corners

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];

// Set the size

maskLayer.frame = imageView.bounds;

// Set the graphic appearance

maskLayer.path = maskPath.CGPath;

imageView.layer.mask = maskLayer;

[self.view addSubview:imageView];

What needs to be explained for Scheme 2 is:

CAShapeLayer inherits from CALayer and can use all the attribute values of CALayer.

CAShapeLayer needs Bessel curves to work together to make sense.

Using CAShapeLayer and Bezier curves, it is possible to draw some desired graphics without using the View’s drawRect method

CAShapeLayer animation rendering is directly submitted to the phone’s GPU, which is highly efficient compared to view’s drawRect method using CPU rendering and can greatly optimize memory usage.

** In general, CAShapeLayer has less memory consumption and faster rendering speed. It is recommended to use optimization scheme 2. 天安门事件

2, shadow:

For Shadow, if the layer is a simple geometric shape or rounded shape, we can optimize the performance by setting shadowPath, which can greatly improve the performance. The following is an example:

imageView.layer.shadowColor=[UIColorgrayColor].CGColor;

ImageView. Layer. ShadowOpacity = 1.0;

ImageView. Layer. ShadowRadius = 2.0;

UIBezierPath *path=[UIBezierPathbezierPathWithRect:imageView.frame];

imageView.layer.shadowPath=path.CGPath;

We can also force off-screen rendering by setting the shouldRasterize property to YES. It’s really Rasterization. If off-screen rendering is so bad, why do we force it on? When an image is mixed with multiple layers, these layers have to be recomposed every frame with every move, which can be very performance consuming. When rasterization is enabled, a bitmap cache is generated for the first time and reused when used again. However, the bitmap cache is regenerated if the layer changes. Therefore, this function cannot be used in UITableViewCell, because cell reuse degrades performance. Best used for graphics with more layers of static content. There is also a limit to the size of the bitmap cache that can be generated, typically 2.5 screen sizes. If the cache is not used within 100ms, the cache will also be deleted. So it depends on the scenario.

3. Some other optimization suggestions

When we need a rounded corner effect, we can use a transparent image in the middle to mask it

Use ShadowPath to specify the layer shadow effect path

Layer rendering using asynchrony (AsyncDisplayKit, Facebook’s open source asynchronous drawing framework)

Set the opaque value of the layer to YES to reduce complex layer composition

Try to use image resources that do not contain alpha channels

Try to set the size of layer to an integer value

The most efficient solution is to have the artist cut the image into rounded corners

In many cases, users upload images for display, allowing the server to handle rounded corners

Use code to manually generate the rounded Image set to the View to display, using UIBezierPath (CoreGraphics framework) to draw the rounded Image