1 // Copyright 2017 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/dc_layer_overlay.h"
6 
7 #include <limits>
8 #include <utility>
9 
10 #include "base/metrics/histogram_functions.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/threading/thread_task_runner_handle.h"
13 #include "build/build_config.h"
14 #include "cc/base/math_util.h"
15 #include "components/viz/common/display/renderer_settings.h"
16 #include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
17 #include "components/viz/common/quads/debug_border_draw_quad.h"
18 #include "components/viz/common/quads/solid_color_draw_quad.h"
19 #include "components/viz/common/quads/texture_draw_quad.h"
20 #include "components/viz/common/quads/yuv_video_draw_quad.h"
21 #include "components/viz/service/display/display_resource_provider.h"
22 #include "components/viz/service/display/overlay_processor_interface.h"
23 #include "gpu/GLES2/gl2extchromium.h"
24 #include "gpu/config/gpu_finch_features.h"
25 #include "ui/gfx/geometry/insets.h"
26 #include "ui/gfx/geometry/rect.h"
27 #include "ui/gfx/geometry/rect_conversions.h"
28 #include "ui/gl/gl_switches.h"
29 #include "ui/gl/gl_utils.h"
30 #include "ui/gl/gpu_switching_manager.h"
31 
32 namespace viz {
33 
34 namespace {
35 
36 constexpr int kDCLayerDebugBorderWidth = 4;
37 constexpr gfx::Insets kDCLayerDebugBorderInsets = gfx::Insets(-2);
38 
39 // This is used for a histogram to determine why overlays are or aren't used,
40 // so don't remove entries and make sure to update enums.xml if it changes.
41 enum DCLayerResult {
42   DC_LAYER_SUCCESS,
43   DC_LAYER_FAILED_UNSUPPORTED_QUAD,  // not recorded
44   DC_LAYER_FAILED_QUAD_BLEND_MODE,
45   DC_LAYER_FAILED_TEXTURE_NOT_CANDIDATE,
46   DC_LAYER_FAILED_OCCLUDED,
47   DC_LAYER_FAILED_COMPLEX_TRANSFORM,
48   DC_LAYER_FAILED_TRANSPARENT,
49   DC_LAYER_FAILED_NON_ROOT,
50   DC_LAYER_FAILED_TOO_MANY_OVERLAYS,
51   DC_LAYER_FAILED_NO_HW_OVERLAY_SUPPORT,  // deprecated
52   DC_LAYER_FAILED_ROUNDED_CORNERS,
53   DC_LAYER_FAILED_BACKDROP_FILTERS,
54   kMaxValue = DC_LAYER_FAILED_BACKDROP_FILTERS,
55 };
56 
57 enum : size_t {
58   kTextureResourceIndex = 0,
59   kYPlaneResourceIndex = 0,
60   kUVPlaneResourceIndex = 1,
61 };
62 
63 // This returns the smallest rectangle in target space that contains the quad.
ClippedQuadRectangle(const DrawQuad * quad)64 gfx::RectF ClippedQuadRectangle(const DrawQuad* quad) {
65   gfx::RectF quad_rect = cc::MathUtil::MapClippedRect(
66       quad->shared_quad_state->quad_to_target_transform,
67       gfx::RectF(quad->rect));
68   if (quad->shared_quad_state->is_clipped)
69     quad_rect.Intersect(gfx::RectF(quad->shared_quad_state->clip_rect));
70   return quad_rect;
71 }
72 
GetExpandedRectWithPixelMovingFilter(const AggregatedRenderPassDrawQuad * rpdq,float max_pixel_movement)73 gfx::RectF GetExpandedRectWithPixelMovingFilter(
74     const AggregatedRenderPassDrawQuad* rpdq,
75     float max_pixel_movement) {
76   const SharedQuadState* shared_quad_state = rpdq->shared_quad_state;
77   gfx::RectF expanded_rect(rpdq->rect);
78   expanded_rect.Inset(-max_pixel_movement, -max_pixel_movement);
79 
80   // expanded_rect in the target space
81   return cc::MathUtil::MapClippedRect(
82       shared_quad_state->quad_to_target_transform, expanded_rect);
83 }
84 
ValidateYUVQuad(const YUVVideoDrawQuad * quad,const std::vector<gfx::Rect> & backdrop_filter_rects,bool has_overlay_support,int allowed_yuv_overlay_count,int processed_yuv_overlay_count,DisplayResourceProvider * resource_provider)85 DCLayerResult ValidateYUVQuad(
86     const YUVVideoDrawQuad* quad,
87     const std::vector<gfx::Rect>& backdrop_filter_rects,
88     bool has_overlay_support,
89     int allowed_yuv_overlay_count,
90     int processed_yuv_overlay_count,
91     DisplayResourceProvider* resource_provider) {
92   // Note: Do not override this value based on base::Feature values. It is the
93   // result after the GPU blocklist has been consulted.
94   if (!has_overlay_support)
95     return DC_LAYER_FAILED_UNSUPPORTED_QUAD;
96 
97   // Check that resources are overlay compatible first so that subsequent
98   // assumptions are valid.
99   for (const auto& resource : quad->resources) {
100     if (!resource_provider->IsOverlayCandidate(resource))
101       return DC_LAYER_FAILED_TEXTURE_NOT_CANDIDATE;
102   }
103 
104   // Hardware protected video must use Direct Composition Overlay
105   if (quad->protected_video_type == gfx::ProtectedVideoType::kHardwareProtected)
106     return DC_LAYER_SUCCESS;
107 
108   if (quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver)
109     return DC_LAYER_FAILED_QUAD_BLEND_MODE;
110 
111   if (!quad->shared_quad_state->quad_to_target_transform
112            .Preserves2dAxisAlignment()) {
113     return DC_LAYER_FAILED_COMPLEX_TRANSFORM;
114   }
115 
116   if (processed_yuv_overlay_count >= allowed_yuv_overlay_count)
117     return DC_LAYER_FAILED_TOO_MANY_OVERLAYS;
118 
119   // Rounded corner on overlays are not supported.
120   if (quad->shared_quad_state->mask_filter_info.HasRoundedCorners())
121     return DC_LAYER_FAILED_ROUNDED_CORNERS;
122 
123   auto quad_target_rect = gfx::ToEnclosingRect(ClippedQuadRectangle(quad));
124   for (const auto& filter_target_rect : backdrop_filter_rects) {
125     if (filter_target_rect.Intersects(quad_target_rect))
126       return DC_LAYER_FAILED_BACKDROP_FILTERS;
127   }
128 
129   return DC_LAYER_SUCCESS;
130 }
131 
FromYUVQuad(const YUVVideoDrawQuad * quad,const gfx::Transform & transform_to_root_target,DCLayerOverlay * dc_layer)132 void FromYUVQuad(const YUVVideoDrawQuad* quad,
133                  const gfx::Transform& transform_to_root_target,
134                  DCLayerOverlay* dc_layer) {
135   // Direct composition path only supports single NV12 buffer, or two buffers
136   // one each for Y and UV planes.
137   DCHECK(quad->y_plane_resource_id() && quad->u_plane_resource_id());
138   DCHECK_EQ(quad->u_plane_resource_id(), quad->v_plane_resource_id());
139   dc_layer->resources[kYPlaneResourceIndex] = quad->y_plane_resource_id();
140   dc_layer->resources[kUVPlaneResourceIndex] = quad->u_plane_resource_id();
141 
142   dc_layer->z_order = 1;
143   dc_layer->content_rect = gfx::ToNearestRect(quad->ya_tex_coord_rect);
144   dc_layer->quad_rect = quad->rect;
145   // Quad rect is in quad content space so both quad to target, and target to
146   // root transforms must be applied to it.
147   gfx::Transform quad_to_root_transform(
148       quad->shared_quad_state->quad_to_target_transform);
149   quad_to_root_transform.ConcatTransform(transform_to_root_target);
150   // Flatten transform to 2D since DirectComposition doesn't support 3D
151   // transforms.  This only applies when non axis aligned overlays are enabled.
152   quad_to_root_transform.FlattenTo2d();
153   dc_layer->transform = quad_to_root_transform;
154 
155   dc_layer->is_clipped = quad->shared_quad_state->is_clipped;
156   if (dc_layer->is_clipped) {
157     // Clip rect is in quad target space, and must be transformed to root target
158     // space.
159     gfx::RectF clip_rect = gfx::RectF(quad->shared_quad_state->clip_rect);
160     transform_to_root_target.TransformRect(&clip_rect);
161     dc_layer->clip_rect = gfx::ToEnclosingRect(clip_rect);
162   }
163   dc_layer->color_space = quad->video_color_space;
164   dc_layer->protected_video_type = quad->protected_video_type;
165   dc_layer->hdr_metadata = quad->hdr_metadata;
166 }
167 
ValidateTextureQuad(const TextureDrawQuad * quad,const std::vector<gfx::Rect> & backdrop_filter_rects,DisplayResourceProvider * resource_provider)168 DCLayerResult ValidateTextureQuad(
169     const TextureDrawQuad* quad,
170     const std::vector<gfx::Rect>& backdrop_filter_rects,
171     DisplayResourceProvider* resource_provider) {
172   // Check that resources are overlay compatible first so that subsequent
173   // assumptions are valid.
174   for (const auto& resource : quad->resources) {
175     if (!resource_provider->IsOverlayCandidate(resource))
176       return DC_LAYER_FAILED_TEXTURE_NOT_CANDIDATE;
177   }
178 
179   if (quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver)
180     return DC_LAYER_FAILED_QUAD_BLEND_MODE;
181 
182   if (!quad->shared_quad_state->quad_to_target_transform
183            .Preserves2dAxisAlignment()) {
184     return DC_LAYER_FAILED_COMPLEX_TRANSFORM;
185   }
186 
187   // Rounded corner on overlays are not supported.
188   if (quad->shared_quad_state->mask_filter_info.HasRoundedCorners())
189     return DC_LAYER_FAILED_ROUNDED_CORNERS;
190 
191   auto quad_target_rect = gfx::ToEnclosingRect(ClippedQuadRectangle(quad));
192   for (const auto& filter_target_rect : backdrop_filter_rects) {
193     if (filter_target_rect.Intersects(quad_target_rect))
194       return DC_LAYER_FAILED_BACKDROP_FILTERS;
195   }
196 
197   return DC_LAYER_SUCCESS;
198 }
199 
FromTextureQuad(const TextureDrawQuad * quad,const gfx::Transform & transform_to_root_target,DCLayerOverlay * dc_layer)200 void FromTextureQuad(const TextureDrawQuad* quad,
201                      const gfx::Transform& transform_to_root_target,
202                      DCLayerOverlay* dc_layer) {
203   dc_layer->resources[kTextureResourceIndex] = quad->resource_id();
204   dc_layer->z_order = 1;
205   dc_layer->content_rect = gfx::Rect(quad->resource_size_in_pixels());
206   dc_layer->quad_rect = quad->rect;
207   // Quad rect is in quad content space so both quad to target, and target to
208   // root transforms must be applied to it.
209   gfx::Transform quad_to_root_transform;
210   if (quad->y_flipped) {
211     quad_to_root_transform.Scale(1.0, -1.0);
212     quad_to_root_transform.PostTranslate(0.0, dc_layer->content_rect.height());
213   }
214   quad_to_root_transform.ConcatTransform(
215       quad->shared_quad_state->quad_to_target_transform);
216   quad_to_root_transform.ConcatTransform(transform_to_root_target);
217   // Flatten transform to 2D since DirectComposition doesn't support 3D
218   // transforms.  This only applies when non axis aligned overlays are enabled.
219   quad_to_root_transform.FlattenTo2d();
220   dc_layer->transform = quad_to_root_transform;
221 
222   dc_layer->is_clipped = quad->shared_quad_state->is_clipped;
223   if (dc_layer->is_clipped) {
224     // Clip rect is in quad target space, and must be transformed to root target
225     // space.
226     gfx::RectF clip_rect = gfx::RectF(quad->shared_quad_state->clip_rect);
227     transform_to_root_target.TransformRect(&clip_rect);
228     dc_layer->clip_rect = gfx::ToEnclosingRect(clip_rect);
229   }
230   dc_layer->color_space = gfx::ColorSpace::CreateSRGB();
231 }
232 
IsProtectedVideo(const QuadList::Iterator & it)233 bool IsProtectedVideo(const QuadList::Iterator& it) {
234   if (it->material == DrawQuad::Material::kYuvVideoContent) {
235     const auto* yuv_quad = YUVVideoDrawQuad::MaterialCast(*it);
236     return yuv_quad->protected_video_type ==
237                gfx::ProtectedVideoType::kHardwareProtected ||
238            yuv_quad->protected_video_type ==
239                gfx::ProtectedVideoType::kSoftwareProtected;
240   }
241   return false;
242 }
243 
IsUnderlayAllowed(const QuadList::Iterator & it)244 DCLayerResult IsUnderlayAllowed(const QuadList::Iterator& it) {
245   if (it->ShouldDrawWithBlending()) {
246     return DC_LAYER_FAILED_TRANSPARENT;
247   }
248   return DC_LAYER_SUCCESS;
249 }
250 
251 // Any occluding quads in the quad list on top of the overlay/underlay
HasOccludingQuads(const gfx::RectF & target_quad,QuadList::ConstIterator quad_list_begin,QuadList::ConstIterator quad_list_end,const DCLayerOverlayProcessor::FilterOperationsMap & render_pass_filters)252 bool HasOccludingQuads(
253     const gfx::RectF& target_quad,
254     QuadList::ConstIterator quad_list_begin,
255     QuadList::ConstIterator quad_list_end,
256     const DCLayerOverlayProcessor::FilterOperationsMap& render_pass_filters) {
257   for (auto overlap_iter = quad_list_begin; overlap_iter != quad_list_end;
258        ++overlap_iter) {
259     float opacity = overlap_iter->shared_quad_state->opacity;
260     if (opacity < std::numeric_limits<float>::epsilon())
261       continue;
262 
263     const DrawQuad* quad = *overlap_iter;
264     gfx::RectF overlap_rect;
265     // Expand the overlap_rect for the render pass draw quad with pixel moving
266     // foreground filters.
267     bool has_pixel_moving_filter = false;
268     if (!render_pass_filters.empty() &&
269         quad->material == DrawQuad::Material::kAggregatedRenderPass) {
270       const auto* rpdq = AggregatedRenderPassDrawQuad::MaterialCast(quad);
271       auto render_pass_it = render_pass_filters.find(rpdq->render_pass_id);
272       if (render_pass_it != render_pass_filters.end()) {
273         auto* filters = render_pass_it->second;
274         float max_pixel_movement = filters->MaximumPixelMovement();
275         overlap_rect =
276             GetExpandedRectWithPixelMovingFilter(rpdq, max_pixel_movement);
277         has_pixel_moving_filter = true;
278       }
279     }
280 
281     if (!has_pixel_moving_filter)
282       overlap_rect = ClippedQuadRectangle(quad);
283 
284     if (quad->material == DrawQuad::Material::kSolidColor) {
285       SkColor color = SolidColorDrawQuad::MaterialCast(quad)->color;
286       float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity;
287       if (quad->ShouldDrawWithBlending() &&
288           alpha < std::numeric_limits<float>::epsilon())
289         continue;
290     }
291     if (overlap_rect.Intersects(target_quad))
292       return true;
293   }
294   return false;
295 }
296 
CalculateOccludingDamageRect(const SharedQuadState * shared_quad_state,SurfaceDamageRectList * surface_damage_rect_list,const gfx::Rect & quad_rect_in_root_target_space)297 gfx::Rect CalculateOccludingDamageRect(
298     const SharedQuadState* shared_quad_state,
299     SurfaceDamageRectList* surface_damage_rect_list,
300     const gfx::Rect& quad_rect_in_root_target_space) {
301   if (!shared_quad_state->overlay_damage_index.has_value())
302     return quad_rect_in_root_target_space;
303 
304   size_t overlay_damage_index = shared_quad_state->overlay_damage_index.value();
305   if (overlay_damage_index >= surface_damage_rect_list->size()) {
306     DCHECK(false);
307   }
308 
309   // Damage rects in surface_damage_rect_list are arranged from top to bottom.
310   // (*surface_damage_rect_list)[0] is the one on the very top.
311   // (*surface_damage_rect_list)[overlay_damage_index] is the damage rect of
312   // this overlay surface.
313   gfx::Rect occluding_damage_rect;
314   for (size_t i = 0; i < overlay_damage_index; ++i) {
315     occluding_damage_rect.Union((*surface_damage_rect_list)[i]);
316   }
317   occluding_damage_rect.Intersect(quad_rect_in_root_target_space);
318 
319   return occluding_damage_rect;
320 }
321 
RecordVideoDCLayerResult(DCLayerResult result,gfx::ProtectedVideoType protected_video_type)322 void RecordVideoDCLayerResult(DCLayerResult result,
323                               gfx::ProtectedVideoType protected_video_type) {
324   switch (protected_video_type) {
325     case gfx::ProtectedVideoType::kClear:
326       UMA_HISTOGRAM_ENUMERATION(
327           "GPU.DirectComposition.DCLayerResult.Video.Clear", result);
328       break;
329     case gfx::ProtectedVideoType::kSoftwareProtected:
330       UMA_HISTOGRAM_ENUMERATION(
331           "GPU.DirectComposition.DCLayerResult.Video.SoftwareProtected",
332           result);
333       break;
334     case gfx::ProtectedVideoType::kHardwareProtected:
335       UMA_HISTOGRAM_ENUMERATION(
336           "GPU.DirectComposition.DCLayerResult.Video.HardwareProtected",
337           result);
338       break;
339   }
340 }
341 
RecordDCLayerResult(DCLayerResult result,QuadList::Iterator it)342 void RecordDCLayerResult(DCLayerResult result, QuadList::Iterator it) {
343   // Skip recording unsupported quads since that'd dwarf the data we care about.
344   if (result == DC_LAYER_FAILED_UNSUPPORTED_QUAD)
345     return;
346 
347   switch (it->material) {
348     case DrawQuad::Material::kYuvVideoContent:
349       RecordVideoDCLayerResult(
350           result, YUVVideoDrawQuad::MaterialCast(*it)->protected_video_type);
351       break;
352     case DrawQuad::Material::kTextureContent:
353       UMA_HISTOGRAM_ENUMERATION("GPU.DirectComposition.DCLayerResult.Texture",
354                                 result);
355       break;
356     default:
357       break;
358   }
359 }
360 
361 // This function records the damage rect rect of the current frame.
RecordOverlayHistograms(DCLayerOverlayList * dc_layer_overlays,bool has_occluding_surface_damage,const gfx::Rect * damage_rect)362 void RecordOverlayHistograms(DCLayerOverlayList* dc_layer_overlays,
363                              bool has_occluding_surface_damage,
364                              const gfx::Rect* damage_rect) {
365   // If an underlay is found, we record the damage rect of this frame as an
366   // underlay.
367   bool is_overlay = true;
368   for (const auto& dc_layer : *dc_layer_overlays) {
369     if (dc_layer.z_order != 1) {
370       is_overlay = false;
371       break;
372     }
373   }
374 
375   OverlayProcessorInterface::RecordOverlayDamageRectHistograms(
376       is_overlay, has_occluding_surface_damage, damage_rect->IsEmpty());
377 }
378 }  // namespace
379 
380 DCLayerOverlay::DCLayerOverlay() = default;
381 DCLayerOverlay::DCLayerOverlay(const DCLayerOverlay& other) = default;
382 DCLayerOverlay& DCLayerOverlay::operator=(const DCLayerOverlay& other) =
383     default;
384 DCLayerOverlay::~DCLayerOverlay() = default;
385 
DCLayerOverlayProcessor(const DebugRendererSettings * debug_settings,int allowed_yuv_overlay_count,bool skip_initialization_for_testing)386 DCLayerOverlayProcessor::DCLayerOverlayProcessor(
387     const DebugRendererSettings* debug_settings,
388     int allowed_yuv_overlay_count,
389     bool skip_initialization_for_testing)
390     : has_overlay_support_(skip_initialization_for_testing),
391       use_overlay_damage_list_(base::FeatureList::IsEnabled(
392           features::kDirectCompositionUseOverlayDamageList)),
393       allowed_yuv_overlay_count_(
394           use_overlay_damage_list_ ? allowed_yuv_overlay_count : 1),
395       debug_settings_(debug_settings),
396       viz_task_runner_(skip_initialization_for_testing
397                            ? nullptr
398                            : base::ThreadTaskRunnerHandle::Get()) {
399   if (!skip_initialization_for_testing) {
400     UpdateHasHwOverlaySupport();
401     ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
402   }
403 }
404 
~DCLayerOverlayProcessor()405 DCLayerOverlayProcessor::~DCLayerOverlayProcessor() {
406   ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this);
407 }
408 
409 // Called on the Viz Compositor thread
UpdateHasHwOverlaySupport()410 void DCLayerOverlayProcessor::UpdateHasHwOverlaySupport() {
411   DCHECK(viz_task_runner_->BelongsToCurrentThread());
412   has_overlay_support_ = gl::AreOverlaysSupportedWin();
413 }
414 
415 // Not on the Viz Compositor thread
OnDisplayAdded()416 void DCLayerOverlayProcessor::OnDisplayAdded() {
417   viz_task_runner_->PostTask(
418       FROM_HERE,
419       base::BindOnce(&DCLayerOverlayProcessor::UpdateHasHwOverlaySupport,
420                      base::Unretained(this)));
421 }
422 
423 // Not on the Viz Compositor thread
OnDisplayRemoved()424 void DCLayerOverlayProcessor::OnDisplayRemoved() {
425   viz_task_runner_->PostTask(
426       FROM_HERE,
427       base::BindOnce(&DCLayerOverlayProcessor::UpdateHasHwOverlaySupport,
428                      base::Unretained(this)));
429 }
430 
ClearOverlayState()431 void DCLayerOverlayProcessor::ClearOverlayState() {
432   previous_frame_underlay_rect_ = gfx::Rect();
433   previous_frame_overlay_rect_union_ = gfx::Rect();
434   previous_frame_processed_overlay_count_ = 0;
435   previous_frame_overlay_rects_.clear();
436 }
437 
RemoveOverlayDamageRect(const QuadList::Iterator & it,const gfx::Rect & quad_rectangle,const gfx::Rect & occluding_damage_rect,gfx::Rect * damage_rect)438 void DCLayerOverlayProcessor::RemoveOverlayDamageRect(
439     const QuadList::Iterator& it,
440     const gfx::Rect& quad_rectangle,
441     const gfx::Rect& occluding_damage_rect,
442     gfx::Rect* damage_rect) {
443   if (use_overlay_damage_list_) {
444     // This is done by setting the overlay surface damage rect in the
445     // |surface_damage_rect_list_| to zero.
446     if (it->shared_quad_state->overlay_damage_index.has_value()) {
447       size_t overlay_damage_index =
448           it->shared_quad_state->overlay_damage_index.value();
449       if (overlay_damage_index >= surface_damage_rect_list_->size())
450         DCHECK(false);
451       else
452         (*surface_damage_rect_list_)[overlay_damage_index] = gfx::Rect();
453     }
454   } else {
455     // This is done by subtract the overlay rect fromt the root damage rect.
456     damage_rect->Subtract(quad_rectangle);
457     damage_rect->Union(occluding_damage_rect);
458   }
459 }
460 
461 // This is called at the end of Process(). The goal is to get an empty root
462 // damage rect if the overlays are the only damages in the frame.
UpdateRootDamageRect(const gfx::RectF & display_rect,gfx::Rect * damage_rect)463 void DCLayerOverlayProcessor::UpdateRootDamageRect(
464     const gfx::RectF& display_rect,
465     gfx::Rect* damage_rect) {
466   // Check whether the overlay rect union from the previous frame should be
467   // added to the current frame and whether the overlay damages can be removed
468   // from the current damage rect.
469   if (use_overlay_damage_list_) {
470     DCHECK_EQ(static_cast<size_t>(current_frame_processed_overlay_count_),
471               current_frame_overlay_rects_.size());
472     DCHECK_EQ(static_cast<size_t>(previous_frame_processed_overlay_count_),
473               previous_frame_overlay_rects_.size());
474 
475     size_t current_frame_overlay_count = current_frame_overlay_rects_.size();
476     if (current_frame_overlay_count > 0 &&
477         current_frame_overlay_count == previous_frame_overlay_rects_.size() &&
478         display_rect == previous_display_rect_) {
479       bool same_overlays = true;
480       for (size_t i = 0; i < current_frame_overlay_count; ++i) {
481         if (previous_frame_overlay_rects_[i] !=
482             current_frame_overlay_rects_[i]) {
483           same_overlays = false;
484           break;
485         }
486       }
487 
488       if (same_overlays) {
489         // No need to add back the overlay rect union from the previous frame
490         // if no changes in overlays.
491         previous_frame_overlay_rect_union_ = gfx::Rect();
492 
493         // Overlay rects have been previously removed from
494         // |surface_damage_rect_list_|. The union is the result of non-overlay
495         // rects.
496         gfx::Rect root_damage_rect;
497         for (const auto& damage_rect : *surface_damage_rect_list_)
498           root_damage_rect.Union(damage_rect);
499 
500         *damage_rect = root_damage_rect;
501       }
502     }
503   } else {
504     // If the current overlay has changed in size/position from the previous
505     // frame, we have to add the overlay quads from the previous frame to the
506     // damage rect for GL compositor. It's hard to optimize multiple overlays.
507     // So always add the overlay rects back in this case.
508     if (current_frame_processed_overlay_count_ == 1 &&
509         previous_frame_processed_overlay_count_ == 1 &&
510         current_frame_overlay_rect_union_ ==
511             previous_frame_overlay_rect_union_) {
512       previous_frame_overlay_rect_union_ = gfx::Rect();
513     }
514   }
515 
516   damage_rect->Union(previous_frame_overlay_rect_union_);
517   damage_rect->Intersect(gfx::ToEnclosingRect(display_rect));
518 }
519 
InsertDebugBorderDrawQuad(const DCLayerOverlayList * dc_layer_overlays,AggregatedRenderPass * render_pass,const gfx::RectF & display_rect,gfx::Rect * damage_rect)520 void DCLayerOverlayProcessor::InsertDebugBorderDrawQuad(
521     const DCLayerOverlayList* dc_layer_overlays,
522     AggregatedRenderPass* render_pass,
523     const gfx::RectF& display_rect,
524     gfx::Rect* damage_rect) {
525   auto* shared_quad_state = render_pass->CreateAndAppendSharedQuadState();
526 
527   for (const auto& dc_layer : *dc_layer_overlays) {
528     gfx::RectF overlay_rect(dc_layer.quad_rect);
529     dc_layer.transform.TransformRect(&overlay_rect);
530     if (dc_layer.is_clipped)
531       overlay_rect.Intersect(gfx::RectF(dc_layer.clip_rect));
532 
533     // Overlay:red, Underlay:blue.
534     SkColor border_color = dc_layer.z_order > 0 ? SK_ColorRED : SK_ColorBLUE;
535 
536     auto& quad_list = render_pass->quad_list;
537     auto it =
538         quad_list.InsertBeforeAndInvalidateAllPointers<DebugBorderDrawQuad>(
539             quad_list.begin(), 1u);
540 
541     auto* debug_quad = static_cast<DebugBorderDrawQuad*>(*it);
542     gfx::Rect rect = gfx::ToEnclosingRect(overlay_rect);
543     rect.Inset(kDCLayerDebugBorderInsets);
544     debug_quad->SetNew(shared_quad_state, rect, rect, border_color,
545                        kDCLayerDebugBorderWidth);
546   }
547 
548   // Mark the entire output as damaged because the border quads might not be
549   // inside the current damage rect.  It's far simpler to mark the entire output
550   // as damaged instead of accounting for individual border quads which can
551   // change positions across frames.
552   damage_rect->Union(gfx::ToEnclosingRect(display_rect));
553 }
554 
IsPreviousFrameUnderlayRect(const gfx::Rect & quad_rectangle,size_t index)555 bool DCLayerOverlayProcessor::IsPreviousFrameUnderlayRect(
556     const gfx::Rect& quad_rectangle,
557     size_t index) {
558   if (use_overlay_damage_list_) {
559     if (index >= previous_frame_overlay_rects_.size()) {
560       return false;
561     } else {
562       // Although we can loop through the list to find out if there is an
563       // underlay with the same size from the previous frame, checking
564       // _rectx_[index] is the quickest way to do it. If we cannot find a match
565       // with the same index, there is probably a change in the number of
566       // overlays or layout. Then we won't be able to get a zero root damage
567       // rect in this case. Looping through the list won't give better power.
568       return (previous_frame_overlay_rects_[index].rect == quad_rectangle) &&
569              (previous_frame_overlay_rects_[index].is_overlay == false);
570     }
571   } else {
572     return (quad_rectangle == previous_frame_underlay_rect_) && (index == 0);
573   }
574 }
575 
Process(DisplayResourceProvider * resource_provider,const gfx::RectF & display_rect,const FilterOperationsMap & render_pass_filters,const FilterOperationsMap & render_pass_backdrop_filters,AggregatedRenderPassList * render_pass_list,gfx::Rect * damage_rect,SurfaceDamageRectList * surface_damage_rect_list,DCLayerOverlayList * dc_layer_overlays)576 void DCLayerOverlayProcessor::Process(
577     DisplayResourceProvider* resource_provider,
578     const gfx::RectF& display_rect,
579     const FilterOperationsMap& render_pass_filters,
580     const FilterOperationsMap& render_pass_backdrop_filters,
581     AggregatedRenderPassList* render_pass_list,
582     gfx::Rect* damage_rect,
583     SurfaceDamageRectList* surface_damage_rect_list,
584     DCLayerOverlayList* dc_layer_overlays) {
585   gfx::Rect this_frame_underlay_rect;
586   bool this_frame_has_occluding_damage_rect = false;
587   processed_yuv_overlay_count_ = 0;
588   surface_damage_rect_list_ = surface_damage_rect_list;
589 
590   // Output rects of child render passes that have backdrop filters in target
591   // space. These rects are used to determine if the overlay rect could be read
592   // by backdrop filters.
593   std::vector<gfx::Rect> backdrop_filter_rects;
594 
595   auto* root_render_pass = render_pass_list->back().get();
596   if (render_pass_list->back()->is_color_conversion_pass) {
597     DCHECK_GT(render_pass_list->size(), 1u);
598     root_render_pass = (*render_pass_list)[render_pass_list->size() - 2].get();
599   }
600 
601   // Used for generating the candidate index list.
602   QuadList* quad_list = &root_render_pass->quad_list;
603   std::vector<size_t> candidate_index_list;
604   size_t index = 0;
605 
606   // Used for looping through candidate_index_list to UpdateDCLayerOverlays()
607   size_t prev_index = 0;
608   auto prev_it = quad_list->begin();
609 
610   // Used for whether overlay should be skipped
611   int yuv_quads_in_quad_list = 0;
612   bool has_protected_video_or_texture_overlays = false;
613 
614   for (auto it = quad_list->begin(); it != quad_list->end(); ++it, ++index) {
615     if (it->material == DrawQuad::Material::kAggregatedRenderPass) {
616       const auto* rpdq = AggregatedRenderPassDrawQuad::MaterialCast(*it);
617       auto render_pass_it =
618           render_pass_backdrop_filters.find(rpdq->render_pass_id);
619       if (render_pass_it != render_pass_backdrop_filters.end()) {
620         backdrop_filter_rects.push_back(
621             gfx::ToEnclosingRect(ClippedQuadRectangle(rpdq)));
622       }
623       continue;
624     }
625 
626     DCLayerResult result;
627     switch (it->material) {
628       case DrawQuad::Material::kYuvVideoContent:
629         result = ValidateYUVQuad(
630             YUVVideoDrawQuad::MaterialCast(*it), backdrop_filter_rects,
631             has_overlay_support_, allowed_yuv_overlay_count_,
632             processed_yuv_overlay_count_, resource_provider);
633         yuv_quads_in_quad_list++;
634         if (result == DC_LAYER_SUCCESS)
635           processed_yuv_overlay_count_++;
636         break;
637       case DrawQuad::Material::kTextureContent:
638         result = ValidateTextureQuad(TextureDrawQuad::MaterialCast(*it),
639                                      backdrop_filter_rects, resource_provider);
640         break;
641       default:
642         result = DC_LAYER_FAILED_UNSUPPORTED_QUAD;
643     }
644 
645     if (result != DC_LAYER_SUCCESS) {
646       RecordDCLayerResult(result, it);
647       continue;
648     }
649 
650     const bool is_protected_video = IsProtectedVideo(it);
651     const bool is_texture_quad =
652         it->material == DrawQuad::Material::kTextureContent;
653     if (is_protected_video || is_texture_quad)
654       has_protected_video_or_texture_overlays = true;
655 
656     if (candidate_index_list.size() == 0) {
657       prev_index = index;
658       prev_it = it;
659     }
660 
661     candidate_index_list.push_back(index);
662   }
663   // A YUV quad might be rejected later due to not allowed as an underlay.
664   // Recount the YUV overlays when they are added to the overlay list
665   // successfully.
666   processed_yuv_overlay_count_ = 0;
667 
668   // TODO(magchen@): Revisit this code if allowed_yuv_overlay_count_ > 1.
669   // We might not save power if there are more than one videos and only one is
670   // promoted to overlay. Skip overlays for this frame unless there are
671   // protected video or texture overlays.
672   if (candidate_index_list.size() > 0 &&
673       yuv_quads_in_quad_list > allowed_yuv_overlay_count_ &&
674       !has_protected_video_or_texture_overlays) {
675     candidate_index_list.clear();
676     // In this case, there is only one candidate in the list.
677     RecordDCLayerResult(DC_LAYER_FAILED_TOO_MANY_OVERLAYS, prev_it);
678   }
679 
680   // Copy the overlay quad info to dc_layer_overlays and replace/delete overlay
681   // quads in quad_list.
682   for (auto index : candidate_index_list) {
683     auto it = std::next(prev_it, index - prev_index);
684     prev_it = it;
685     prev_index = index;
686 
687     gfx::Rect quad_rectangle_in_target_space =
688         gfx::ToEnclosingRect(ClippedQuadRectangle(*it));
689 
690     // Quad is considered an "overlay" if it has no occluders.
691     bool is_overlay =
692         !HasOccludingQuads(gfx::RectF(quad_rectangle_in_target_space),
693                            quad_list->begin(), it, render_pass_filters);
694 
695     // Protected video is always put in an overlay, but texture quads can be
696     // skipped if they're not underlay compatible.
697     const bool requires_overlay = IsProtectedVideo(it);
698 
699     // TODO(magchen@): Since we reject underlays here, the max number of YUV
700     // overlays we can promote might not be accurate. We should allow all YUV
701     // quads to be put into candidate_index_list, but only
702     // |allowed_yuv_overlay_count_| YUV quads should be promoted to
703     // overlays/underlays from that list.
704 
705     // Skip quad if it's an underlay and underlays are not allowed.
706     if (!is_overlay && !requires_overlay) {
707       DCLayerResult result = IsUnderlayAllowed(it);
708 
709       if (result != DC_LAYER_SUCCESS) {
710         RecordDCLayerResult(result, it);
711         continue;
712       }
713     }
714 
715     // Get the occluding damage rect for underlay.
716     gfx::Rect occluding_damage_rect;
717     if (!is_overlay) {
718       occluding_damage_rect = CalculateOccludingDamageRect(
719           it->shared_quad_state, surface_damage_rect_list,
720           quad_rectangle_in_target_space);
721 
722       // Used by a histogram.
723       if (!occluding_damage_rect.IsEmpty())
724         this_frame_has_occluding_damage_rect = true;
725     }
726 
727     UpdateDCLayerOverlays(
728         display_rect, root_render_pass, it, quad_rectangle_in_target_space,
729         occluding_damage_rect, is_overlay, &prev_it, &prev_index,
730         &this_frame_underlay_rect, damage_rect, dc_layer_overlays);
731   }
732 
733   // Update previous frame state after processing root pass. If there is no
734   // overlay in this frame, |previous_frame_overlay_rect_union_| will be added
735   // to the damage_rect here for GL composition because the overlay image from
736   // the previous frame is missing in the GL composition path. If any overlay is
737   // found in this frame, the previous overlay rects would have been handled
738   // above and |previous_frame_overlay_rect_union_| becomes empty.
739   UpdateRootDamageRect(display_rect, damage_rect);
740 
741   previous_frame_overlay_rect_union_ = current_frame_overlay_rect_union_;
742   current_frame_overlay_rect_union_ = gfx::Rect();
743   previous_frame_processed_overlay_count_ =
744       current_frame_processed_overlay_count_;
745   current_frame_processed_overlay_count_ = 0;
746   std::swap(previous_frame_overlay_rects_, current_frame_overlay_rects_);
747   current_frame_overlay_rects_.clear();
748   previous_display_rect_ = display_rect;
749   previous_frame_underlay_rect_ = this_frame_underlay_rect;
750 
751   if (!dc_layer_overlays->empty()) {
752     base::UmaHistogramExactLinear(
753         "GPU.DirectComposition.DCLayer.YUVOverlayCount",
754         /*sample=*/processed_yuv_overlay_count_,
755         /*value_max=*/10);
756 
757     RecordOverlayHistograms(dc_layer_overlays,
758                             this_frame_has_occluding_damage_rect, damage_rect);
759   }
760 
761   if (debug_settings_->show_dc_layer_debug_borders &&
762       dc_layer_overlays->size() > 0) {
763     InsertDebugBorderDrawQuad(dc_layer_overlays, root_render_pass, display_rect,
764                               damage_rect);
765   }
766 }
767 
UpdateDCLayerOverlays(const gfx::RectF & display_rect,AggregatedRenderPass * render_pass,const QuadList::Iterator & it,const gfx::Rect & quad_rectangle_in_target_space,const gfx::Rect & occluding_damage_rect,bool is_overlay,QuadList::Iterator * new_it,size_t * new_index,gfx::Rect * this_frame_underlay_rect,gfx::Rect * damage_rect,DCLayerOverlayList * dc_layer_overlays)768 void DCLayerOverlayProcessor::UpdateDCLayerOverlays(
769     const gfx::RectF& display_rect,
770     AggregatedRenderPass* render_pass,
771     const QuadList::Iterator& it,
772     const gfx::Rect& quad_rectangle_in_target_space,
773     const gfx::Rect& occluding_damage_rect,
774     bool is_overlay,
775     QuadList::Iterator* new_it,
776     size_t* new_index,
777     gfx::Rect* this_frame_underlay_rect,
778     gfx::Rect* damage_rect,
779     DCLayerOverlayList* dc_layer_overlays) {
780   // Record the result first before ProcessForOverlay().
781   RecordDCLayerResult(DC_LAYER_SUCCESS, it);
782 
783   DCLayerOverlay dc_layer;
784   switch (it->material) {
785     case DrawQuad::Material::kYuvVideoContent:
786       FromYUVQuad(YUVVideoDrawQuad::MaterialCast(*it),
787                   render_pass->transform_to_root_target, &dc_layer);
788       processed_yuv_overlay_count_++;
789       break;
790     case DrawQuad::Material::kTextureContent:
791       FromTextureQuad(TextureDrawQuad::MaterialCast(*it),
792                       render_pass->transform_to_root_target, &dc_layer);
793       break;
794     default:
795       NOTREACHED();
796   }
797 
798   // Underlays are less efficient, so attempt regular overlays first. Only
799   // check root render pass because we can only check for occlusion within a
800   // render pass.
801   if (is_overlay) {
802     *new_it =
803         ProcessForOverlay(display_rect, render_pass,
804                           quad_rectangle_in_target_space, it, damage_rect);
805     (*new_index)++;
806   } else {
807     ProcessForUnderlay(display_rect, render_pass,
808                        quad_rectangle_in_target_space, occluding_damage_rect,
809                        it, damage_rect, this_frame_underlay_rect, &dc_layer);
810   }
811 
812   gfx::Rect rect_in_root = cc::MathUtil::MapEnclosingClippedRect(
813       render_pass->transform_to_root_target, quad_rectangle_in_target_space);
814   current_frame_overlay_rect_union_.Union(rect_in_root);
815   current_frame_overlay_rects_.push_back({rect_in_root, is_overlay});
816 
817   dc_layer_overlays->push_back(dc_layer);
818 
819   // Recorded for each overlay.
820   UMA_HISTOGRAM_BOOLEAN("GPU.DirectComposition.IsUnderlay", !is_overlay);
821 
822   current_frame_processed_overlay_count_++;
823 }
824 
ProcessForOverlay(const gfx::RectF & display_rect,AggregatedRenderPass * render_pass,const gfx::Rect & quad_rectangle,const QuadList::Iterator & it,gfx::Rect * damage_rect)825 QuadList::Iterator DCLayerOverlayProcessor::ProcessForOverlay(
826     const gfx::RectF& display_rect,
827     AggregatedRenderPass* render_pass,
828     const gfx::Rect& quad_rectangle,
829     const QuadList::Iterator& it,
830     gfx::Rect* damage_rect) {
831   // The quad is on top, so promote it to an overlay and remove all damage
832   // underneath it.
833   const bool display_rect_changed = (display_rect != previous_display_rect_);
834   const bool is_axis_aligned = it->shared_quad_state->quad_to_target_transform
835                                    .Preserves2dAxisAlignment();
836   const bool needs_blending = it->ShouldDrawWithBlending();
837 
838   if (is_axis_aligned && !display_rect_changed && !needs_blending) {
839     RemoveOverlayDamageRect(it, quad_rectangle,
840                             /*occluding_damage_rect=*/gfx::Rect(), damage_rect);
841   }
842 
843   return render_pass->quad_list.EraseAndInvalidateAllPointers(it);
844 }
845 
ProcessForUnderlay(const gfx::RectF & display_rect,AggregatedRenderPass * render_pass,const gfx::Rect & quad_rectangle,const gfx::Rect & occluding_damage_rect,const QuadList::Iterator & it,gfx::Rect * damage_rect,gfx::Rect * this_frame_underlay_rect,DCLayerOverlay * dc_layer)846 void DCLayerOverlayProcessor::ProcessForUnderlay(
847     const gfx::RectF& display_rect,
848     AggregatedRenderPass* render_pass,
849     const gfx::Rect& quad_rectangle,
850     const gfx::Rect& occluding_damage_rect,
851     const QuadList::Iterator& it,
852     gfx::Rect* damage_rect,
853     gfx::Rect* this_frame_underlay_rect,
854     DCLayerOverlay* dc_layer) {
855   // Assign decreasing z-order so that underlays processed earlier, and hence
856   // which are above the subsequent underlays, are placed above in the direct
857   // composition visual tree.
858   dc_layer->z_order = -1 - current_frame_processed_overlay_count_;
859 
860   const SharedQuadState* shared_quad_state = it->shared_quad_state;
861   const gfx::Rect rect = it->visible_rect;
862   const bool needs_blending = it->needs_blending;
863 
864   // If the video is translucent and uses SrcOver blend mode, we can achieve the
865   // same result as compositing with video on top if we replace video quad with
866   // a solid color quad with DstOut blend mode, and rely on SrcOver blending
867   // of the root surface with video on bottom. Essentially,
868   //
869   // SrcOver_quad(V, B, V_alpha) = SrcOver_premul(DstOut(BLACK, B, V_alpha), V)
870   // where
871   //    V is the video quad
872   //    B is the background
873   //    SrcOver_quad uses opacity of source quad (V_alpha)
874   //    SrcOver_premul uses alpha channel and assumes premultipled alpha
875   bool is_opaque = false;
876 
877   if (it->ShouldDrawWithBlending() &&
878       shared_quad_state->blend_mode == SkBlendMode::kSrcOver) {
879     SharedQuadState* new_shared_quad_state =
880         render_pass->shared_quad_state_list.AllocateAndCopyFrom(
881             shared_quad_state);
882     new_shared_quad_state->blend_mode = SkBlendMode::kDstOut;
883 
884     auto* replacement =
885         render_pass->quad_list.ReplaceExistingElement<SolidColorDrawQuad>(it);
886     // Use needs_blending from original quad because blending might be because
887     // of this flag or opacity.
888     replacement->SetAll(new_shared_quad_state, rect, rect, needs_blending,
889                         SK_ColorBLACK, true /* force_anti_aliasing_off */);
890   } else {
891     // When the opacity == 1.0, drawing with transparent will be done without
892     // blending and will have the proper effect of completely clearing the
893     // layer.
894     render_pass->ReplaceExistingQuadWithOpaqueTransparentSolidColor(it);
895     is_opaque = true;
896   }
897 
898   bool display_rect_changed = (display_rect != previous_display_rect_);
899   bool underlay_rect_unchanged = IsPreviousFrameUnderlayRect(
900       quad_rectangle, current_frame_processed_overlay_count_);
901   bool is_axis_aligned =
902       shared_quad_state->quad_to_target_transform.Preserves2dAxisAlignment();
903 
904   if (is_axis_aligned && is_opaque && underlay_rect_unchanged &&
905       !display_rect_changed) {
906     // If this underlay rect is the same as for last frame, Remove its area
907     // from the damage of the main surface, as the cleared area was already
908     // cleared last frame.
909 
910     // If none of the quads on top give any damage, we can skip compositing
911     // these quads. The output root damage rect might be empty after we remove
912     // the damage from the video quad. We can save power if the root damage
913     // rect is empty.
914     RemoveOverlayDamageRect(it, quad_rectangle, occluding_damage_rect,
915                             damage_rect);
916   } else {
917     // Entire replacement quad must be redrawn.
918     damage_rect->Union(quad_rectangle);
919     surface_damage_rect_list_->push_back(quad_rectangle);
920   }
921 
922   // We only compare current frame's first underlay with the previous frame's
923   // first underlay. Non-opaque regions can have different alpha from one frame
924   // to another so this optimization doesn't work.
925   if (current_frame_processed_overlay_count_ == 0 && is_axis_aligned &&
926       is_opaque) {
927     *this_frame_underlay_rect = quad_rectangle;
928   }
929 }
930 
931 }  // namespace viz
932