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