1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/viz/service/display/ca_layer_overlay.h"
6 
7 #include <algorithm>
8 
9 #include "base/metrics/histogram_macros.h"
10 #include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
11 #include "components/viz/common/quads/solid_color_draw_quad.h"
12 #include "components/viz/common/quads/stream_video_draw_quad.h"
13 #include "components/viz/common/quads/texture_draw_quad.h"
14 #include "components/viz/common/quads/tile_draw_quad.h"
15 #include "components/viz/service/display/display_resource_provider.h"
16 #include "gpu/GLES2/gl2extchromium.h"
17 #include "third_party/skia/include/core/SkDeferredDisplayList.h"
18 
19 namespace viz {
20 
21 namespace {
22 
23 // The CoreAnimation renderer's performance starts suffering when too many
24 // quads are promoted to CALayers. At extremes, corruption can occur.
25 // https://crbug.com/1022116
26 constexpr size_t kTooManyQuads = 128;
27 
28 // If there are too many RenderPassDrawQuads, we shouldn't use Core
29 // Animation to present them as individual layers, since that potentially
30 // doubles the amount of work needed to present them. cc has to blit them into
31 // an IOSurface, and then Core Animation has to blit them to the final surface.
32 // https://crbug.com/636884.
33 const int kTooManyRenderPassDrawQuads = 30;
34 
35 // This enum is used for histogram states and should only have new values added
36 // to the end before COUNT.
37 enum CALayerResult {
38   CA_LAYER_SUCCESS = 0,
39   CA_LAYER_FAILED_UNKNOWN = 1,
40   // CA_LAYER_FAILED_IO_SURFACE_NOT_CANDIDATE = 2,
41   CA_LAYER_FAILED_STREAM_VIDEO_NOT_CANDIDATE = 3,
42   // CA_LAYER_FAILED_STREAM_VIDEO_TRANSFORM = 4,
43   CA_LAYER_FAILED_TEXTURE_NOT_CANDIDATE = 5,
44   // CA_LAYER_FAILED_TEXTURE_Y_FLIPPED = 6,
45   CA_LAYER_FAILED_TILE_NOT_CANDIDATE = 7,
46   CA_LAYER_FAILED_QUAD_BLEND_MODE = 8,
47   // CA_LAYER_FAILED_QUAD_TRANSFORM = 9,
48   // CA_LAYER_FAILED_QUAD_CLIPPING = 10,
49   CA_LAYER_FAILED_DEBUG_BORDER = 11,
50   CA_LAYER_FAILED_PICTURE_CONTENT = 12,
51   // CA_LAYER_FAILED_RENDER_PASS = 13,
52   CA_LAYER_FAILED_SURFACE_CONTENT = 14,
53   CA_LAYER_FAILED_YUV_VIDEO_CONTENT = 15,
54   CA_LAYER_FAILED_DIFFERENT_CLIP_SETTINGS = 16,
55   CA_LAYER_FAILED_DIFFERENT_VERTEX_OPACITIES = 17,
56   // CA_LAYER_FAILED_RENDER_PASS_FILTER_SCALE = 18,
57   CA_LAYER_FAILED_RENDER_PASS_BACKDROP_FILTERS = 19,
58   // CA_LAYER_FAILED_RENDER_PASS_MASK = 20,
59   CA_LAYER_FAILED_RENDER_PASS_FILTER_OPERATION = 21,
60   CA_LAYER_FAILED_RENDER_PASS_SORTING_CONTEXT_ID = 22,
61   CA_LAYER_FAILED_TOO_MANY_RENDER_PASS_DRAW_QUADS = 23,
62   // CA_LAYER_FAILED_QUAD_ROUNDED_CORNER = 24,
63   CA_LAYER_FAILED_QUAD_ROUNDED_CORNER_CLIP_MISMATCH = 25,
64   CA_LAYER_FAILED_QUAD_ROUNDED_CORNER_NOT_UNIFORM = 26,
65   CA_LAYER_FAILED_TOO_MANY_QUADS = 27,
66   CA_LAYER_FAILED_COUNT,
67 };
68 
FilterOperationSupported(const cc::FilterOperation & operation)69 bool FilterOperationSupported(const cc::FilterOperation& operation) {
70   switch (operation.type()) {
71     case cc::FilterOperation::GRAYSCALE:
72     case cc::FilterOperation::SEPIA:
73     case cc::FilterOperation::SATURATE:
74     case cc::FilterOperation::HUE_ROTATE:
75     case cc::FilterOperation::INVERT:
76     case cc::FilterOperation::BRIGHTNESS:
77     case cc::FilterOperation::CONTRAST:
78     case cc::FilterOperation::OPACITY:
79     case cc::FilterOperation::BLUR:
80     case cc::FilterOperation::DROP_SHADOW:
81       return true;
82     default:
83       return false;
84   }
85 }
86 
FromRenderPassQuad(DisplayResourceProvider * resource_provider,const AggregatedRenderPassDrawQuad * quad,const base::flat_map<AggregatedRenderPassId,cc::FilterOperations * > & render_pass_filters,const base::flat_map<AggregatedRenderPassId,cc::FilterOperations * > & render_pass_backdrop_filters,CALayerOverlay * ca_layer_overlay)87 CALayerResult FromRenderPassQuad(
88     DisplayResourceProvider* resource_provider,
89     const AggregatedRenderPassDrawQuad* quad,
90     const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
91         render_pass_filters,
92     const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
93         render_pass_backdrop_filters,
94     CALayerOverlay* ca_layer_overlay) {
95   if (render_pass_backdrop_filters.count(quad->render_pass_id)) {
96     return CA_LAYER_FAILED_RENDER_PASS_BACKDROP_FILTERS;
97   }
98 
99   if (quad->shared_quad_state->sorting_context_id != 0)
100     return CA_LAYER_FAILED_RENDER_PASS_SORTING_CONTEXT_ID;
101 
102   auto it = render_pass_filters.find(quad->render_pass_id);
103   if (it != render_pass_filters.end()) {
104     for (const auto& operation : it->second->operations()) {
105       bool success = FilterOperationSupported(operation);
106       if (!success)
107         return CA_LAYER_FAILED_RENDER_PASS_FILTER_OPERATION;
108     }
109   }
110 
111   ca_layer_overlay->rpdq = quad;
112   ca_layer_overlay->contents_rect = gfx::RectF(0, 0, 1, 1);
113 
114   return CA_LAYER_SUCCESS;
115 }
116 
FromStreamVideoQuad(DisplayResourceProvider * resource_provider,const StreamVideoDrawQuad * quad,CALayerOverlay * ca_layer_overlay)117 CALayerResult FromStreamVideoQuad(DisplayResourceProvider* resource_provider,
118                                   const StreamVideoDrawQuad* quad,
119                                   CALayerOverlay* ca_layer_overlay) {
120   unsigned resource_id = quad->resource_id();
121   if (!resource_provider->IsOverlayCandidate(resource_id))
122     return CA_LAYER_FAILED_STREAM_VIDEO_NOT_CANDIDATE;
123   ca_layer_overlay->contents_resource_id = resource_id;
124   ca_layer_overlay->contents_rect =
125       BoundingRect(quad->uv_top_left, quad->uv_bottom_right);
126   return CA_LAYER_SUCCESS;
127 }
128 
FromSolidColorDrawQuad(const SolidColorDrawQuad * quad,CALayerOverlay * ca_layer_overlay,bool * skip)129 CALayerResult FromSolidColorDrawQuad(const SolidColorDrawQuad* quad,
130                                      CALayerOverlay* ca_layer_overlay,
131                                      bool* skip) {
132   // Do not generate quads that are completely transparent.
133   if (SkColorGetA(quad->color) == 0) {
134     *skip = true;
135     return CA_LAYER_SUCCESS;
136   }
137   ca_layer_overlay->background_color = quad->color;
138   return CA_LAYER_SUCCESS;
139 }
140 
FromTextureQuad(DisplayResourceProvider * resource_provider,const TextureDrawQuad * quad,CALayerOverlay * ca_layer_overlay)141 CALayerResult FromTextureQuad(DisplayResourceProvider* resource_provider,
142                               const TextureDrawQuad* quad,
143                               CALayerOverlay* ca_layer_overlay) {
144   unsigned resource_id = quad->resource_id();
145   if (!resource_provider->IsOverlayCandidate(resource_id))
146     return CA_LAYER_FAILED_TEXTURE_NOT_CANDIDATE;
147   if (quad->y_flipped) {
148     // The anchor point is at the bottom-left corner of the CALayer. The
149     // transformation that flips the contents of the layer without changing its
150     // frame is the composition of a vertical flip about the anchor point, and a
151     // translation by the height of the layer.
152     ca_layer_overlay->shared_state->transform.preTranslate(
153         0, ca_layer_overlay->bounds_rect.height(), 0);
154     ca_layer_overlay->shared_state->transform.preScale(1, -1, 1);
155   }
156   ca_layer_overlay->contents_resource_id = resource_id;
157   ca_layer_overlay->contents_rect =
158       BoundingRect(quad->uv_top_left, quad->uv_bottom_right);
159   ca_layer_overlay->background_color = quad->background_color;
160   for (int i = 1; i < 4; ++i) {
161     if (quad->vertex_opacity[i] != quad->vertex_opacity[0])
162       return CA_LAYER_FAILED_DIFFERENT_VERTEX_OPACITIES;
163   }
164   ca_layer_overlay->shared_state->opacity *= quad->vertex_opacity[0];
165   ca_layer_overlay->filter = quad->nearest_neighbor ? GL_NEAREST : GL_LINEAR;
166   return CA_LAYER_SUCCESS;
167 }
168 
FromTileQuad(DisplayResourceProvider * resource_provider,const TileDrawQuad * quad,CALayerOverlay * ca_layer_overlay)169 CALayerResult FromTileQuad(DisplayResourceProvider* resource_provider,
170                            const TileDrawQuad* quad,
171                            CALayerOverlay* ca_layer_overlay) {
172   unsigned resource_id = quad->resource_id();
173   if (!resource_provider->IsOverlayCandidate(resource_id))
174     return CA_LAYER_FAILED_TILE_NOT_CANDIDATE;
175   ca_layer_overlay->contents_resource_id = resource_id;
176   ca_layer_overlay->contents_rect = quad->tex_coord_rect;
177   ca_layer_overlay->contents_rect.Scale(1.f / quad->texture_size.width(),
178                                         1.f / quad->texture_size.height());
179   ca_layer_overlay->filter = quad->nearest_neighbor ? GL_NEAREST : GL_LINEAR;
180   return CA_LAYER_SUCCESS;
181 }
182 
183 class CALayerOverlayProcessorInternal {
184  public:
FromDrawQuad(DisplayResourceProvider * resource_provider,const gfx::RectF & display_rect,const DrawQuad * quad,const base::flat_map<AggregatedRenderPassId,cc::FilterOperations * > & render_pass_filters,const base::flat_map<AggregatedRenderPassId,cc::FilterOperations * > & render_pass_backdrop_filters,CALayerOverlay * ca_layer_overlay,bool * skip,bool * render_pass_draw_quad)185   CALayerResult FromDrawQuad(
186       DisplayResourceProvider* resource_provider,
187       const gfx::RectF& display_rect,
188       const DrawQuad* quad,
189       const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
190           render_pass_filters,
191       const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
192           render_pass_backdrop_filters,
193       CALayerOverlay* ca_layer_overlay,
194       bool* skip,
195       bool* render_pass_draw_quad) {
196     if (quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver)
197       return CA_LAYER_FAILED_QUAD_BLEND_MODE;
198 
199     // Early-out for invisible quads.
200     if (quad->shared_quad_state->opacity == 0.f ||
201         quad->visible_rect.IsEmpty()) {
202       *skip = true;
203       return CA_LAYER_SUCCESS;
204     }
205 
206     // Support rounded corner bounds when they have the same rect as the clip
207     // rect, and all corners have the same radius. Note that it is entirely
208     // possible to make rounded corner rects independent of clip rect (by adding
209     // another CALayer to the tree). Handling non-single border radii is also,
210     // but requires APIs not supported on all macOS versions.
211     if (quad->shared_quad_state->mask_filter_info.HasRoundedCorners()) {
212       DCHECK(quad->shared_quad_state->is_clipped);
213       if (quad->shared_quad_state->mask_filter_info.rounded_corner_bounds()
214               .GetType() > gfx::RRectF::Type::kSingle) {
215         return CA_LAYER_FAILED_QUAD_ROUNDED_CORNER_NOT_UNIFORM;
216       }
217     }
218 
219     // Enable edge anti-aliasing only on layer boundaries.
220     ca_layer_overlay->edge_aa_mask = 0;
221     if (quad->IsLeftEdge())
222       ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_LEFT_CHROMIUM;
223     if (quad->IsRightEdge())
224       ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_RIGHT_CHROMIUM;
225     if (quad->IsBottomEdge())
226       ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_BOTTOM_CHROMIUM;
227     if (quad->IsTopEdge())
228       ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_TOP_CHROMIUM;
229 
230     if (most_recent_shared_quad_state_ != quad->shared_quad_state) {
231       most_recent_shared_quad_state_ = quad->shared_quad_state;
232       most_recent_overlay_shared_state_ = new CALayerOverlaySharedState;
233       // Set rect clipping and sorting context ID.
234       most_recent_overlay_shared_state_->sorting_context_id =
235           quad->shared_quad_state->sorting_context_id;
236       most_recent_overlay_shared_state_->is_clipped =
237           quad->shared_quad_state->is_clipped;
238       most_recent_overlay_shared_state_->clip_rect =
239           gfx::RectF(quad->shared_quad_state->clip_rect);
240       most_recent_overlay_shared_state_->rounded_corner_bounds =
241           quad->shared_quad_state->mask_filter_info.rounded_corner_bounds();
242 
243       most_recent_overlay_shared_state_->opacity =
244           quad->shared_quad_state->opacity;
245       most_recent_overlay_shared_state_->transform =
246           quad->shared_quad_state->quad_to_target_transform.matrix();
247     }
248     ca_layer_overlay->shared_state = most_recent_overlay_shared_state_;
249 
250     ca_layer_overlay->bounds_rect = gfx::RectF(quad->rect);
251 
252     *render_pass_draw_quad =
253         quad->material == DrawQuad::Material::kAggregatedRenderPass;
254     switch (quad->material) {
255       case DrawQuad::Material::kTextureContent:
256         return FromTextureQuad(resource_provider,
257                                TextureDrawQuad::MaterialCast(quad),
258                                ca_layer_overlay);
259       case DrawQuad::Material::kTiledContent:
260         return FromTileQuad(resource_provider, TileDrawQuad::MaterialCast(quad),
261                             ca_layer_overlay);
262       case DrawQuad::Material::kSolidColor:
263         return FromSolidColorDrawQuad(SolidColorDrawQuad::MaterialCast(quad),
264                                       ca_layer_overlay, skip);
265       case DrawQuad::Material::kStreamVideoContent:
266         return FromStreamVideoQuad(resource_provider,
267                                    StreamVideoDrawQuad::MaterialCast(quad),
268                                    ca_layer_overlay);
269       case DrawQuad::Material::kDebugBorder:
270         return CA_LAYER_FAILED_DEBUG_BORDER;
271       case DrawQuad::Material::kPictureContent:
272         return CA_LAYER_FAILED_PICTURE_CONTENT;
273       case DrawQuad::Material::kAggregatedRenderPass:
274         return FromRenderPassQuad(
275             resource_provider, AggregatedRenderPassDrawQuad::MaterialCast(quad),
276             render_pass_filters, render_pass_backdrop_filters,
277             ca_layer_overlay);
278       case DrawQuad::Material::kSurfaceContent:
279         return CA_LAYER_FAILED_SURFACE_CONTENT;
280       case DrawQuad::Material::kYuvVideoContent:
281         return CA_LAYER_FAILED_YUV_VIDEO_CONTENT;
282       default:
283         break;
284     }
285 
286     return CA_LAYER_FAILED_UNKNOWN;
287   }
288 
289  private:
290   const SharedQuadState* most_recent_shared_quad_state_ = nullptr;
291   scoped_refptr<CALayerOverlaySharedState> most_recent_overlay_shared_state_;
292 };
293 
294 }  // namespace
295 
CALayerOverlay()296 CALayerOverlay::CALayerOverlay() : filter(GL_LINEAR) {}
297 
298 CALayerOverlay::CALayerOverlay(const CALayerOverlay& other) = default;
299 
300 CALayerOverlay::~CALayerOverlay() = default;
301 
302 CALayerOverlay& CALayerOverlay::operator=(const CALayerOverlay& other) =
303     default;
304 
ProcessForCALayerOverlays(DisplayResourceProvider * resource_provider,const gfx::RectF & display_rect,const QuadList & quad_list,const base::flat_map<AggregatedRenderPassId,cc::FilterOperations * > & render_pass_filters,const base::flat_map<AggregatedRenderPassId,cc::FilterOperations * > & render_pass_backdrop_filters,CALayerOverlayList * ca_layer_overlays) const305 bool CALayerOverlayProcessor::ProcessForCALayerOverlays(
306     DisplayResourceProvider* resource_provider,
307     const gfx::RectF& display_rect,
308     const QuadList& quad_list,
309     const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
310         render_pass_filters,
311     const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
312         render_pass_backdrop_filters,
313     CALayerOverlayList* ca_layer_overlays) const {
314   CALayerResult result = CA_LAYER_SUCCESS;
315 
316   size_t num_visible_quads = quad_list.size();
317   for (const auto* quad : quad_list) {
318     if (quad->shared_quad_state->opacity == 0.f ||
319         quad->visible_rect.IsEmpty()) {
320       num_visible_quads--;
321     }
322   }
323   if (num_visible_quads < kTooManyQuads)
324     ca_layer_overlays->reserve(num_visible_quads);
325   else
326     result = CA_LAYER_FAILED_TOO_MANY_QUADS;
327 
328   int render_pass_draw_quad_count = 0;
329   CALayerOverlayProcessorInternal processor;
330   for (auto it = quad_list.BackToFrontBegin();
331        result == CA_LAYER_SUCCESS && it != quad_list.BackToFrontEnd(); ++it) {
332     const DrawQuad* quad = *it;
333     CALayerOverlay ca_layer;
334     bool skip = false;
335     bool render_pass_draw_quad = false;
336     result = processor.FromDrawQuad(
337         resource_provider, display_rect, quad, render_pass_filters,
338         render_pass_backdrop_filters, &ca_layer, &skip, &render_pass_draw_quad);
339     if (result != CA_LAYER_SUCCESS)
340       break;
341 
342     if (render_pass_draw_quad) {
343       ++render_pass_draw_quad_count;
344       if (render_pass_draw_quad_count > kTooManyRenderPassDrawQuads) {
345         result = CA_LAYER_FAILED_TOO_MANY_RENDER_PASS_DRAW_QUADS;
346         break;
347       }
348     }
349 
350     if (skip)
351       continue;
352 
353     // It is not possible to correctly represent two different clipping settings
354     // within one sorting context.
355     if (!ca_layer_overlays->empty()) {
356       const CALayerOverlay& previous_ca_layer = ca_layer_overlays->back();
357       if (ca_layer.shared_state->sorting_context_id &&
358           previous_ca_layer.shared_state->sorting_context_id ==
359               ca_layer.shared_state->sorting_context_id) {
360         if (previous_ca_layer.shared_state->is_clipped !=
361                 ca_layer.shared_state->is_clipped ||
362             previous_ca_layer.shared_state->clip_rect !=
363                 ca_layer.shared_state->clip_rect) {
364           result = CA_LAYER_FAILED_DIFFERENT_CLIP_SETTINGS;
365           break;
366         }
367       }
368     }
369 
370     ca_layer_overlays->push_back(ca_layer);
371   }
372 
373   UMA_HISTOGRAM_ENUMERATION("Compositing.Renderer.CALayerResult", result,
374                             CA_LAYER_FAILED_COUNT);
375 
376   if (result != CA_LAYER_SUCCESS) {
377     ca_layer_overlays->clear();
378     return false;
379   }
380   return true;
381 }
382 
383 }  // namespace viz
384