What is off-screen rendering

Here is a scenario: Take TioIM’s recent chat session list as an example: The profile picture on each cell needs to be cropped. Set rounded corners as follows

Images are loaded asynchronously

imageView.backgroundColor = [UIColor whiteColor];
imageView.layer.cornerRadius = 50;
imageView.layer.masksToBounds = YES;
Copy the code

When sliding the list, and when the amount of data is relatively large, fast sliding will find that the list is stuck, we perform the following operations, check

Turn on the emulator’s off-screen render and you’ll see

The imageView turns yellow, indicating an off-screen rendering.

Let’s modify the code to get rid of clipping masksToBounds

imageView.backgroundColor = [UIColor whiteColor];
imageView.layer.cornerRadius = 50;
// imageView.layer.masksToBounds = YES;
Copy the code

The result is as follows: The content(image) is not clipped and no off-screen rendering is seen

// imageView.backgroundColor = [UIColor whiteColor];
imageView.layer.cornerRadius = 50;
imageView.layer.masksToBounds = YES;
Copy the code

The yellow part disappears

So setting a cornerRadius doesn’t necessarily result in an off-screen rendering,

What happens to the render display after an off-screen render occurs?

There are no steps for off-screen rendering

The rendering step after an off-screen rendering occurs

Why is there more off-screen rendering before the frame cache?

Render pipeline workflow from Core Animation to Display

If the rendered picture is complex, such as the case above, the backgroundColor backgroundColor needs to be rendered, and the content(image) also needs to be clipped. If the off-screen rendering step is not followed, the remote background should be displayed first according to the painter’s algorithm (the one displayed first is at the bottom, which is analogous to the normal sub-control layout) :

  • The rendered bitmap enters the frame buffer -> screen first, and the frame buffer’s backgroundColor is cleared
  • The bitmap of the content(image) is then entered into the frame buffer -> screen and the content(image) of the frame buffer is cleared.
  • Content (image) clipping ???? There’s nothing left to crop, the frame buffer has just been emptied.

Therefore, you need to create an extra buffer and wait for compositing and clipping to complete -> frame buffer -> screen display. So, this additional place for processing complex render data is the Offscreen Buffer. Therefore, there is an extra off-screen render buffer before the frame buffer.

Before RenderServer enters the GPU and displays, the above text is visualized in the form of a flowchart:

  • The first step is to render the Texture. The Texture can be interpreted as the content, which in imageView is the image.
  • Step 2: Render the background green
  • Step 3: In the open off-screen render Buffer, combine texture and background -> Frame Buffer -> screen display

Off-screen rendering does not occur without a cornerRadius

  • The rendered bitmap enters the frame buffer -> screen first, and the frame buffer’s backgroundColor is cleared
  • The bitmap of the content(image) is then entered into the frame buffer -> screen and the content(image) of the frame buffer is cleared.
  • The end of the

Instead of waiting for the off-screen render buffer to do extra processing, just go through the artist’s algorithm layer by layer to the screen.

If the cell has an off-screen rendering of the TableView, it will get stuck while swiping fast

Once we know why an off-screen rendering is triggered and at what point in the rendering process it happens, let’s look at the tableView lag problem. Of course, there are a lot of reasons why TableViews can get stuck, but we’ll just talk about the lag caused by off-screen rendering.

If each cell has one or even several controls that will be rendered off-screen, and each render is given to the frame buffer to wait for display after the off-screen rendering is completed, look at the flow of the rendering pipeline in the figure above, the GPU without off-screen rendering is 16.67ms. If there is off-screen rendering processing, it will quickly scroll through a large number of cells. The resulting cell is constantly being re-rendered, and the multiple off-screen renderings inside are doing a lot of time-consuming processing, which is scary. So in this case, we need to optimize the off-screen rendering.

3. When will off-screen rendering be triggered/in what scenes will off-screen rendering be triggered?

Many of you will be very familiar with the cornerRadius+masksToBounds scene at the beginning of this article.

1, the cornerRadius + masksToBounds

In the previous session list of several different cases, we can already see that a cornerRadius+masksToBounds does not always trigger an off-screen rendering, only when. The key is to look at the layers of render data: backgroundcolor, content(image, text, etc.).

    // img1 content masksToBounds
    UIImageView *img1 = [UIImageView.alloc initWithFrame:CGRectMake(100, 30, 100, 100)];
    img1.image = [UIImage imageNamed:@"btn.png"]; img1.layer.cornerRadius = 50; img1.layer.masksToBounds = YES; [self.view addSubview:img1]; UIImageView *img2 = [UIImageView.alloc initWithFrame:CGRectMake(100, 180, 100, 100)]; img2.layer.cornerRadius = 50; img2.backgroundColor = UIColor.redColor; [self.view addSubview:img2]; // content + backgroundColor UIImageView *img3 = [UIImageView.alloc initWithFrame:CGRectMake(100, 320, 100, 100)]; img3.image = [UIImage imageNamed:@"btn.png"];
    img3.backgroundColor = UIColor.redColor;
    img3.layer.cornerRadius = 50;
    [self.view addSubview:img3];
    
    // content + backgroundColor + masksToBounds
    UIImageView *img4 = [UIImageView.alloc initWithFrame:CGRectMake(100, 480, 100, 100)];
    img4.image = [UIImage imageNamed:@"btn.png"];
    img4.backgroundColor = UIColor.redColor;
    img4.layer.cornerRadius = 50;
    img4.layer.masksToBounds = YES;
    [self.view addSubview:img4];
Copy the code

The cornerRadius feature does not work with Content (Image), according to Apple: The cornerRadius documentation makes it clear that the cornerRadius setting only applies to CALayer backgroundColor and borderWidth and borderColor.

CornerRadius +masksToBounds Will only render off-screen when content is set and the background is not transparent.

If you must use a cornerRadius+masksToBounds approach for cutting images, do not set backgroundColor.

2. Fuzzy effect of ground glass

The rendered bitmap cannot be displayed directly in the frame buffer, but must be blurred before the final rendered data -> frame buffer -> is displayed.

3, shadow

To enable shadow effect, first of all, what kind of existence is shadow? How is it rendered?

Shadow is a rectangle, it’s a background color, it’s the background of the layer, so it’s underneath the layer. Shadow is derived from layer, so you need to know the layer before you know the size of shadow.

If there is no off-screen rendering, the shadow must be placed in the frame cache and displayed first according to the painter algorithm as above. However, layer does not, so it is impossible to render shadow first. We can only use the off-screen rendering buffer, wait for shadow, layer and other rendering and merging to be completed, and then send it into the frame cache for display.

shouldRasterize

The official documentation for CALayer attributes states:

This means that if enabled, the final effect of layer rendering, including shadow, crop, etc. will be turned into bitmap and put into off-screen buffer for reuse. However, the size of the off-screen buffer cannot exceed 2.5 times the size of the screen, otherwise it will be released. Second, if the layer is not static, such as the image of the imageView needs to change, the text of the label will change frequently, etc., shouldRasterize will affect the efficiency of off-screen rendering. Also, the off-screen buffer has a time limit and will be released if it is not used for more than 100ms.

So we should use shouldRasterize:

  • If layer is not static, do not enable it
  • If the layer cannot be reused, do not enable this function. Cell is reused
  • If the value exceeds 100ms, the service is not overused and this function is not recommended
  • It is not recommended to enable an off-screen render control that is 2.5 times larger than its screen pixels

5, Group opacity

6. Using masks

The maskLayer acts as a mask and is displayed on top of the larger layer and all sublayer children of the larger layer. The masklayer may also have transparency, shape (for example, to show the contents of a specified area), and so on.

Faced with the above situation, Image and Mask must be cropped and processed in the off-screen rendering buffer to display the final Masked Image -> frame buffer.

Round corners cut/cut asynchronously loaded network pictures

In my actual work, I have encountered the rounding cutting of network pictures after asynchronous loading, which is specifically written in my blog SDWebImage rounding cutting and non-deformation processing of network pictures. If you are interested, you can have a look. Main solutions:

  • UIImageView asynchronously downloads network images without stretching and distortion
  • No rounded clipping for off-screen rendering
  • Network picture fillet cropping

Part of the core code cutting

- (instancetype)imageWithCornerRadius:(CGFloat)cornerRadius size:(CGSize)newSize { UIImage *originImage = self; CGRect bounds = CGRectMake(0, 0, newsie.width, newsie.height); UIGraphicsBeginImageContextWithOptions(newSize, NO, UIScreen.mainScreen.scale); CGContextRef context = UIGraphicsGetCurrentContext(); UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:cornerRadius]; CGContextAddPath(context, path.CGPath); CGContextClip(context); [originImage drawInRect:bounds]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();return image;
}

Copy the code

For details, please go to SDWebImage network image fillet cutting and non-deformation processing