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 "base/metrics/histogram_macros.h"
8 #include "base/threading/thread_task_runner_handle.h"
9 #include "build/build_config.h"
10 #include "cc/base/math_util.h"
11 #include "components/viz/common/display/renderer_settings.h"
12 #include "components/viz/common/quads/debug_border_draw_quad.h"
13 #include "components/viz/common/quads/render_pass_draw_quad.h"
14 #include "components/viz/common/quads/solid_color_draw_quad.h"
15 #include "components/viz/common/quads/texture_draw_quad.h"
16 #include "components/viz/common/quads/yuv_video_draw_quad.h"
17 #include "components/viz/service/display/display_resource_provider.h"
18 #include "components/viz/service/display/overlay_processor_interface.h"
19 #include "gpu/GLES2/gl2extchromium.h"
20 #include "gpu/config/gpu_finch_features.h"
21 #include "ui/gfx/geometry/insets.h"
22 #include "ui/gfx/geometry/rect.h"
23 #include "ui/gfx/geometry/rect_conversions.h"
24 #include "ui/gl/gl_switches.h"
25 #include "ui/gl/gl_utils.h"
26 #include "ui/gl/gpu_switching_manager.h"
27 
28 namespace viz {
29 
30 namespace {
31 
32 constexpr int kDCLayerDebugBorderWidth = 4;
33 constexpr gfx::Insets kDCLayerDebugBorderInsets = gfx::Insets(-2);
34 
35 // This is used for a histogram to determine why overlays are or aren't used,
36 // so don't remove entries and make sure to update enums.xml if it changes.
37 enum DCLayerResult {
38   DC_LAYER_SUCCESS,
39   DC_LAYER_FAILED_UNSUPPORTED_QUAD,  // not recorded
40   DC_LAYER_FAILED_QUAD_BLEND_MODE,
41   DC_LAYER_FAILED_TEXTURE_NOT_CANDIDATE,
42   DC_LAYER_FAILED_OCCLUDED,
43   DC_LAYER_FAILED_COMPLEX_TRANSFORM,
44   DC_LAYER_FAILED_TRANSPARENT,
45   DC_LAYER_FAILED_NON_ROOT,
46   DC_LAYER_FAILED_TOO_MANY_OVERLAYS,
47   DC_LAYER_FAILED_NO_HW_OVERLAY_SUPPORT,  // deprecated
48   DC_LAYER_FAILED_ROUNDED_CORNERS,
49   DC_LAYER_FAILED_BACKDROP_FILTERS,
50   kMaxValue = DC_LAYER_FAILED_BACKDROP_FILTERS,
51 };
52 
53 enum : size_t {
54   kTextureResourceIndex = 0,
55   kYPlaneResourceIndex = 0,
56   kUVPlaneResourceIndex = 1,
57 };
58 
59 // This returns the smallest rectangle in target space that contains the quad.
ClippedQuadRectangle(const DrawQuad * quad)60 gfx::RectF ClippedQuadRectangle(const DrawQuad* quad) {
61   gfx::RectF quad_rect = cc::MathUtil::MapClippedRect(
62       quad->shared_quad_state->quad_to_target_transform,
63       gfx::RectF(quad->rect));
64   if (quad->shared_quad_state->is_clipped)
65     quad_rect.Intersect(gfx::RectF(quad->shared_quad_state->clip_rect));
66   return quad_rect;
67 }
68 
FromYUVQuad(const YUVVideoDrawQuad * quad,const gfx::Transform & transform_to_root_target,const std::vector<gfx::Rect> & backdrop_filter_rects,bool has_hw_overlay_support,int current_frame_processed_overlay_count,DisplayResourceProvider * resource_provider,DCLayerOverlay * dc_layer)69 DCLayerResult FromYUVQuad(const YUVVideoDrawQuad* quad,
70                           const gfx::Transform& transform_to_root_target,
71                           const std::vector<gfx::Rect>& backdrop_filter_rects,
72                           bool has_hw_overlay_support,
73                           int current_frame_processed_overlay_count,
74                           DisplayResourceProvider* resource_provider,
75                           DCLayerOverlay* dc_layer) {
76   // To support software protected video on machines without hardware overlay
77   // capability. Don't do dc layer overlay if no hardware support.
78   gfx::ProtectedVideoType protected_video_type = quad->protected_video_type;
79   bool allow_video_overlay =
80       has_hw_overlay_support ||
81       (protected_video_type == gfx::ProtectedVideoType::kSoftwareProtected &&
82        base::FeatureList::IsEnabled(
83            features::kUseDCOverlaysForSoftwareProtectedVideo));
84   if (!allow_video_overlay)
85     return DC_LAYER_FAILED_UNSUPPORTED_QUAD;
86 
87   // Check that resources are overlay compatible first so that subsequent
88   // assumptions are valid.
89   for (const auto& resource : quad->resources) {
90     if (!resource_provider->IsOverlayCandidate(resource))
91       return DC_LAYER_FAILED_TEXTURE_NOT_CANDIDATE;
92   }
93 
94   // Hardware protected video must use Direct Composition Overlay
95   if (protected_video_type != gfx::ProtectedVideoType::kHardwareProtected) {
96     if (quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver)
97       return DC_LAYER_FAILED_QUAD_BLEND_MODE;
98 
99     bool is_axis_aligned = quad->shared_quad_state->quad_to_target_transform
100                                .Preserves2dAxisAlignment();
101     if (!is_axis_aligned && !base::FeatureList::IsEnabled(
102                                 features::kDirectCompositionComplexOverlays)) {
103       return DC_LAYER_FAILED_COMPLEX_TRANSFORM;
104     }
105 
106     if (current_frame_processed_overlay_count > 0)
107       return DC_LAYER_FAILED_TOO_MANY_OVERLAYS;
108 
109     // Rounded corner on overlays are not supported.
110     if (!quad->shared_quad_state->rounded_corner_bounds.IsEmpty())
111       return DC_LAYER_FAILED_ROUNDED_CORNERS;
112 
113     auto quad_target_rect = gfx::ToEnclosingRect(ClippedQuadRectangle(quad));
114     for (const auto& filter_target_rect : backdrop_filter_rects) {
115       if (filter_target_rect.Intersects(quad_target_rect))
116         return DC_LAYER_FAILED_BACKDROP_FILTERS;
117     }
118   }
119 
120   // Direct composition path only supports single NV12 buffer, or two buffers
121   // one each for Y and UV planes.
122   DCHECK(quad->y_plane_resource_id() && quad->u_plane_resource_id());
123   DCHECK_EQ(quad->u_plane_resource_id(), quad->v_plane_resource_id());
124   dc_layer->resources[kYPlaneResourceIndex] = quad->y_plane_resource_id();
125   dc_layer->resources[kUVPlaneResourceIndex] = quad->u_plane_resource_id();
126 
127   dc_layer->z_order = 1;
128   dc_layer->content_rect = gfx::ToNearestRect(quad->ya_tex_coord_rect);
129   dc_layer->quad_rect = quad->rect;
130   // Quad rect is in quad content space so both quad to target, and target to
131   // root transforms must be applied to it.
132   gfx::Transform quad_to_root_transform(
133       quad->shared_quad_state->quad_to_target_transform);
134   quad_to_root_transform.ConcatTransform(transform_to_root_target);
135   // Flatten transform to 2D since DirectComposition doesn't support 3D
136   // transforms.  This only applies when non axis aligned overlays are enabled.
137   quad_to_root_transform.FlattenTo2d();
138   dc_layer->transform = quad_to_root_transform;
139 
140   dc_layer->is_clipped = quad->shared_quad_state->is_clipped;
141   if (dc_layer->is_clipped) {
142     // Clip rect is in quad target space, and must be transformed to root target
143     // space.
144     gfx::RectF clip_rect = gfx::RectF(quad->shared_quad_state->clip_rect);
145     transform_to_root_target.TransformRect(&clip_rect);
146     dc_layer->clip_rect = gfx::ToEnclosingRect(clip_rect);
147   }
148   dc_layer->color_space = quad->video_color_space;
149   dc_layer->protected_video_type = quad->protected_video_type;
150 
151   return DC_LAYER_SUCCESS;
152 }
153 
FromTextureQuad(const TextureDrawQuad * quad,const gfx::Transform & transform_to_root_target,const std::vector<gfx::Rect> & backdrop_filter_rects,DisplayResourceProvider * resource_provider,DCLayerOverlay * dc_layer)154 DCLayerResult FromTextureQuad(
155     const TextureDrawQuad* quad,
156     const gfx::Transform& transform_to_root_target,
157     const std::vector<gfx::Rect>& backdrop_filter_rects,
158     DisplayResourceProvider* resource_provider,
159     DCLayerOverlay* dc_layer) {
160   // Check that resources are overlay compatible first so that subsequent
161   // assumptions are valid.
162   for (const auto& resource : quad->resources) {
163     if (!resource_provider->IsOverlayCandidate(resource))
164       return DC_LAYER_FAILED_TEXTURE_NOT_CANDIDATE;
165   }
166 
167   if (quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver)
168     return DC_LAYER_FAILED_QUAD_BLEND_MODE;
169 
170   if (!quad->shared_quad_state->quad_to_target_transform
171            .Preserves2dAxisAlignment()) {
172     return DC_LAYER_FAILED_COMPLEX_TRANSFORM;
173   }
174 
175   // Rounded corner on overlays are not supported.
176   if (!quad->shared_quad_state->rounded_corner_bounds.IsEmpty())
177     return DC_LAYER_FAILED_ROUNDED_CORNERS;
178 
179   auto quad_target_rect = gfx::ToEnclosingRect(ClippedQuadRectangle(quad));
180   for (const auto& filter_target_rect : backdrop_filter_rects) {
181     if (filter_target_rect.Intersects(quad_target_rect))
182       return DC_LAYER_FAILED_BACKDROP_FILTERS;
183   }
184 
185   dc_layer->resources[kTextureResourceIndex] = quad->resource_id();
186   dc_layer->z_order = 1;
187   dc_layer->content_rect = gfx::Rect(quad->resource_size_in_pixels());
188   dc_layer->quad_rect = quad->rect;
189   // Quad rect is in quad content space so both quad to target, and target to
190   // root transforms must be applied to it.
191   gfx::Transform quad_to_root_transform;
192   if (quad->y_flipped) {
193     quad_to_root_transform.Scale(1.0, -1.0);
194     quad_to_root_transform.PostTranslate(0.0, dc_layer->content_rect.height());
195   }
196   quad_to_root_transform.ConcatTransform(
197       quad->shared_quad_state->quad_to_target_transform);
198   quad_to_root_transform.ConcatTransform(transform_to_root_target);
199   // Flatten transform to 2D since DirectComposition doesn't support 3D
200   // transforms.  This only applies when non axis aligned overlays are enabled.
201   quad_to_root_transform.FlattenTo2d();
202   dc_layer->transform = quad_to_root_transform;
203 
204   dc_layer->is_clipped = quad->shared_quad_state->is_clipped;
205   if (dc_layer->is_clipped) {
206     // Clip rect is in quad target space, and must be transformed to root target
207     // space.
208     gfx::RectF clip_rect = gfx::RectF(quad->shared_quad_state->clip_rect);
209     transform_to_root_target.TransformRect(&clip_rect);
210     dc_layer->clip_rect = gfx::ToEnclosingRect(clip_rect);
211   }
212   dc_layer->color_space = gfx::ColorSpace::CreateSRGB();
213 
214   return DC_LAYER_SUCCESS;
215 }
216 
IsUnderlayAllowed(const QuadList::Iterator & it,bool is_root,const DCLayerOverlay & dc_layer)217 DCLayerResult IsUnderlayAllowed(const QuadList::Iterator& it,
218                                 bool is_root,
219                                 const DCLayerOverlay& dc_layer) {
220   if (!dc_layer.RequiresOverlay()) {
221     if (!base::FeatureList::IsEnabled(features::kDirectCompositionUnderlays)) {
222       return DC_LAYER_FAILED_OCCLUDED;
223     }
224     if (!is_root && !base::FeatureList::IsEnabled(
225                         features::kDirectCompositionNonrootOverlays)) {
226       return DC_LAYER_FAILED_NON_ROOT;
227     }
228     if (it->shared_quad_state->opacity < 1.0f) {
229       return DC_LAYER_FAILED_TRANSPARENT;
230     }
231   }
232   return DC_LAYER_SUCCESS;
233 }
234 
235 // 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)236 bool HasOccludingQuads(const gfx::RectF& target_quad,
237                        QuadList::ConstIterator quad_list_begin,
238                        QuadList::ConstIterator quad_list_end) {
239   for (auto overlap_iter = quad_list_begin; overlap_iter != quad_list_end;
240        ++overlap_iter) {
241     float opacity = overlap_iter->shared_quad_state->opacity;
242     if (opacity < std::numeric_limits<float>::epsilon())
243       continue;
244     const DrawQuad* quad = *overlap_iter;
245     gfx::RectF overlap_rect = ClippedQuadRectangle(quad);
246     if (quad->material == DrawQuad::Material::kSolidColor) {
247       SkColor color = SolidColorDrawQuad::MaterialCast(quad)->color;
248       float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity;
249       if (quad->ShouldDrawWithBlending() &&
250           alpha < std::numeric_limits<float>::epsilon())
251         continue;
252     }
253     if (overlap_rect.Intersects(target_quad))
254       return true;
255   }
256   return false;
257 }
258 
RecordVideoDCLayerResult(DCLayerResult result,gfx::ProtectedVideoType protected_video_type)259 void RecordVideoDCLayerResult(DCLayerResult result,
260                               gfx::ProtectedVideoType protected_video_type) {
261   switch (protected_video_type) {
262     case gfx::ProtectedVideoType::kClear:
263       UMA_HISTOGRAM_ENUMERATION(
264           "GPU.DirectComposition.DCLayerResult.Video.Clear", result);
265       break;
266     case gfx::ProtectedVideoType::kSoftwareProtected:
267       UMA_HISTOGRAM_ENUMERATION(
268           "GPU.DirectComposition.DCLayerResult.Video.SoftwareProtected",
269           result);
270       break;
271     case gfx::ProtectedVideoType::kHardwareProtected:
272       UMA_HISTOGRAM_ENUMERATION(
273           "GPU.DirectComposition.DCLayerResult.Video.HardwareProtected",
274           result);
275       break;
276   }
277 }
278 
RecordDCLayerResult(DCLayerResult result,QuadList::Iterator it)279 void RecordDCLayerResult(DCLayerResult result, QuadList::Iterator it) {
280   // Skip recording unsupported quads since that'd dwarf the data we care about.
281   if (result == DC_LAYER_FAILED_UNSUPPORTED_QUAD)
282     return;
283 
284   switch (it->material) {
285     case DrawQuad::Material::kYuvVideoContent:
286       RecordVideoDCLayerResult(
287           result, YUVVideoDrawQuad::MaterialCast(*it)->protected_video_type);
288       break;
289     case DrawQuad::Material::kTextureContent:
290       UMA_HISTOGRAM_ENUMERATION("GPU.DirectComposition.DCLayerResult.Texture",
291                                 result);
292       break;
293     default:
294       break;
295   }
296 }
297 
RecordOverlayHistograms(bool is_overlay,const gfx::Rect & occluding_damage_rect,gfx::Rect * damage_rect)298 void RecordOverlayHistograms(bool is_overlay,
299                              const gfx::Rect& occluding_damage_rect,
300                              gfx::Rect* damage_rect) {
301   UMA_HISTOGRAM_BOOLEAN("GPU.DirectComposition.IsUnderlay", !is_overlay);
302 
303   bool has_occluding_surface_damage = !occluding_damage_rect.IsEmpty();
304   bool occluding_damage_equal_to_damage_rect =
305       occluding_damage_rect == *damage_rect;
306   OverlayProcessorInterface::RecordOverlayDamageRectHistograms(
307       is_overlay, has_occluding_surface_damage, damage_rect->IsEmpty(),
308       occluding_damage_equal_to_damage_rect);
309 }
310 }  // namespace
311 
312 DCLayerOverlay::DCLayerOverlay() = default;
313 DCLayerOverlay::DCLayerOverlay(const DCLayerOverlay& other) = default;
314 DCLayerOverlay& DCLayerOverlay::operator=(const DCLayerOverlay& other) =
315     default;
316 DCLayerOverlay::~DCLayerOverlay() = default;
317 
318 DCLayerOverlayProcessor::RenderPassData::RenderPassData() = default;
319 DCLayerOverlayProcessor::RenderPassData::RenderPassData(
320     const RenderPassData& other) = default;
321 DCLayerOverlayProcessor::RenderPassData::~RenderPassData() = default;
322 
DCLayerOverlayProcessor(const RendererSettings & settings)323 DCLayerOverlayProcessor::DCLayerOverlayProcessor(
324     const RendererSettings& settings)
325     : show_debug_borders_(settings.show_dc_layer_debug_borders),
326       viz_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
327   UpdateHasHwOverlaySupport();
328   ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
329 }
330 
DCLayerOverlayProcessor()331 DCLayerOverlayProcessor::DCLayerOverlayProcessor()
332     : has_hw_overlay_support_(true), show_debug_borders_(false) {}
333 
~DCLayerOverlayProcessor()334 DCLayerOverlayProcessor::~DCLayerOverlayProcessor() {
335   ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this);
336 }
337 
338 // Called on the Viz Compositor thread
UpdateHasHwOverlaySupport()339 void DCLayerOverlayProcessor::UpdateHasHwOverlaySupport() {
340   DCHECK(viz_task_runner_->BelongsToCurrentThread());
341   has_hw_overlay_support_ = gl::AreOverlaysSupportedWin();
342 }
343 
344 // Not on the Viz Compositor thread
OnDisplayAdded()345 void DCLayerOverlayProcessor::OnDisplayAdded() {
346   viz_task_runner_->PostTask(
347       FROM_HERE,
348       base::BindOnce(&DCLayerOverlayProcessor::UpdateHasHwOverlaySupport,
349                      base::Unretained(this)));
350 }
351 
352 // Not on the Viz Compositor thread
OnDisplayRemoved()353 void DCLayerOverlayProcessor::OnDisplayRemoved() {
354   viz_task_runner_->PostTask(
355       FROM_HERE,
356       base::BindOnce(&DCLayerOverlayProcessor::UpdateHasHwOverlaySupport,
357                      base::Unretained(this)));
358 }
359 
Process(DisplayResourceProvider * resource_provider,const gfx::RectF & display_rect,RenderPassList * render_passes,gfx::Rect * damage_rect,DCLayerOverlayList * dc_layer_overlays)360 void DCLayerOverlayProcessor::Process(
361     DisplayResourceProvider* resource_provider,
362     const gfx::RectF& display_rect,
363     RenderPassList* render_passes,
364     gfx::Rect* damage_rect,
365     DCLayerOverlayList* dc_layer_overlays) {
366   render_pass_data_.clear();
367   for (auto& pass : *render_passes) {
368     bool is_root = (pass == render_passes->back());
369     ProcessRenderPass(resource_provider, display_rect, pass.get(), is_root,
370                       is_root ? damage_rect : &pass->damage_rect,
371                       dc_layer_overlays);
372   }
373 }
374 
ClearOverlayState()375 void DCLayerOverlayProcessor::ClearOverlayState() {
376   previous_frame_underlay_rect_ = gfx::Rect();
377   previous_frame_overlay_rect_union_ = gfx::Rect();
378   previous_frame_processed_overlay_count_ = 0;
379 }
380 
ProcessRenderPassDrawQuad(RenderPass * render_pass,gfx::Rect * damage_rect,QuadList::Iterator it)381 QuadList::Iterator DCLayerOverlayProcessor::ProcessRenderPassDrawQuad(
382     RenderPass* render_pass,
383     gfx::Rect* damage_rect,
384     QuadList::Iterator it) {
385   DCHECK_EQ(DrawQuad::Material::kRenderPass, it->material);
386   const RenderPassDrawQuad* rpdq = RenderPassDrawQuad::MaterialCast(*it);
387 
388   ++it;
389   // Check if this quad is broken to avoid corrupting |render_pass_data_|.
390   if (rpdq->render_pass_id == render_pass->id)
391     return it;
392 
393   // This will be filled in for all render passes even non-root overlays are
394   // disabled.
395   const auto& render_pass_data = render_pass_data_[rpdq->render_pass_id];
396   if (render_pass_data.has_backdrop_filters) {
397     render_pass_data_[render_pass->id].backdrop_filter_rects.push_back(
398         gfx::ToEnclosingRect(ClippedQuadRectangle(rpdq)));
399   }
400 
401   // |punch_through_rects| will be empty unless non-root overlays are enabled.
402   const auto& punch_through_rects = render_pass_data.punch_through_rects;
403   if (punch_through_rects.empty())
404     return it;
405 
406   // Punch holes through for all child video quads that will be displayed in
407   // underlays. This doesn't work perfectly in all cases - it breaks with
408   // complex overlap or filters - but it's needed to be able to display these
409   // videos at all. The EME spec allows that some HTML rendering capabilities
410   // may be unavailable for EME videos.
411   //
412   // For opaque video we punch a transparent hole behind the RPDQ so that
413   // translucent elements in front of the video do not blend with elements
414   // behind the video.
415   //
416   // For translucent video we can achieve the same result as SrcOver blending of
417   // video in multiple stacked render passes if the root render pass got the
418   // color contribution from the render passes sans video, and the alpha was set
419   // to 1 - video's accumulated alpha (product of video and render pass draw
420   // quad opacities). To achieve this we can put a transparent solid color quad
421   // with SrcOver blending in place of video. This quad's pixels rendered
422   // finally on the root render pass will give the color contribution of all
423   // content below the video with the intermediate opacities taken into account.
424   // Finally we need to set the corresponding area in the root render pass to
425   // the correct alpha. This can be achieved with a DstOut black quad above the
426   // video with the accumulated alpha and color mask set to write only alpha
427   // channel. Essentially,
428   //
429   // SrcOver_quad(SrcOver_quad(V, RP1, V_a), RP2, RPDQ1_a) = SrcOver_premul(
430   //    DstOut_mask(
431   //        BLACK,
432   //        SrcOver_quad(SrcOver_quad(TRANSPARENT, RP1, V_a), RP2, RPDQ1_a),
433   //        acc_a),
434   //    V)
435   //
436   // where V is the video
437   //       RP1 and RP2 are the inner and outer render passes
438   //       acc_a is the accumulated alpha
439   //       SrcOver_quad uses opacity of the source quad (V_a and RPDQ1_a)
440   //       SrcOver_premul assumes premultiplied alpha channel
441   //
442   // TODO(sunnyps): Implement the above. This requires support for setting
443   // color mask in solid color draw quad which we don't have today. Another
444   // difficulty is undoing the SrcOver blending in child render passes if any
445   // render pass above has a non-supported blend mode.
446   const SharedQuadState* original_shared_quad_state = rpdq->shared_quad_state;
447 
448   // Copy shared state from RPDQ to get the same clip rect.
449   SharedQuadState* new_shared_quad_state =
450       render_pass->shared_quad_state_list.AllocateAndCopyFrom(
451           original_shared_quad_state);
452   // Set opacity to 1 since we're not blending.
453   new_shared_quad_state->opacity = 1.f;
454 
455   // The iterator was advanced above so InsertBefore inserts after the RPDQ.
456   it = render_pass->quad_list
457            .InsertBeforeAndInvalidateAllPointers<SolidColorDrawQuad>(
458                it, punch_through_rects.size());
459   rpdq = nullptr;
460   for (const gfx::Rect& punch_through_rect : punch_through_rects) {
461     auto* solid_quad = static_cast<SolidColorDrawQuad*>(*it++);
462     solid_quad->SetAll(new_shared_quad_state, punch_through_rect,
463                        punch_through_rect, false, SK_ColorTRANSPARENT, true);
464 
465     gfx::Rect clipped_quad_rect =
466         gfx::ToEnclosingRect(ClippedQuadRectangle(solid_quad));
467     // Propagate punch through rect as damage up the stack of render passes.
468     // TODO(sunnyps): We should avoid this extra damage if we knew that the
469     // video (in child render surface) was the only thing damaging this
470     // render surface.
471     damage_rect->Union(clipped_quad_rect);
472 
473     // Add transformed info to list in case this renderpass is included in
474     // another pass.
475     render_pass_data_[render_pass->id].punch_through_rects.push_back(
476         clipped_quad_rect);
477   }
478   return it;
479 }
480 
InsertDebugBorderDrawQuads(const gfx::RectF & display_rect,const gfx::Rect & overlay_rect,RenderPass * root_render_pass,gfx::Rect * damage_rect)481 void DCLayerOverlayProcessor::InsertDebugBorderDrawQuads(
482     const gfx::RectF& display_rect,
483     const gfx::Rect& overlay_rect,
484     RenderPass* root_render_pass,
485     gfx::Rect* damage_rect) {
486   auto* shared_quad_state = root_render_pass->CreateAndAppendSharedQuadState();
487   auto& quad_list = root_render_pass->quad_list;
488 
489   if (!overlay_rect.IsEmpty()) {
490     auto it =
491         quad_list.InsertBeforeAndInvalidateAllPointers<DebugBorderDrawQuad>(
492             quad_list.begin(), 1u);
493     auto* debug_quad = static_cast<DebugBorderDrawQuad*>(*it);
494     gfx::Rect rect = overlay_rect;
495     rect.Inset(kDCLayerDebugBorderInsets);
496     debug_quad->SetNew(shared_quad_state, rect, rect, SK_ColorRED,
497                        kDCLayerDebugBorderWidth);
498   }
499 
500   const auto& punch_through_rects =
501       render_pass_data_[root_render_pass->id].punch_through_rects;
502 
503   auto it = quad_list.InsertBeforeAndInvalidateAllPointers<DebugBorderDrawQuad>(
504       quad_list.begin(), punch_through_rects.size());
505 
506   for (const gfx::Rect& punch_through_rect : punch_through_rects) {
507     auto* debug_quad = static_cast<DebugBorderDrawQuad*>(*it++);
508     gfx::Rect rect = punch_through_rect;
509     rect.Inset(kDCLayerDebugBorderInsets);
510     debug_quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLUE,
511                        kDCLayerDebugBorderWidth);
512   }
513 
514   // Mark the entire output as damaged because the border quads might not be
515   // inside the current damage rect.  It's far simpler to mark the entire output
516   // as damaged instead of accounting for individual border quads which can
517   // change positions across frames.
518   damage_rect->Union(gfx::ToEnclosingRect(display_rect));
519 }
520 
ProcessRenderPass(DisplayResourceProvider * resource_provider,const gfx::RectF & display_rect,RenderPass * render_pass,bool is_root,gfx::Rect * damage_rect,DCLayerOverlayList * dc_layer_overlays)521 void DCLayerOverlayProcessor::ProcessRenderPass(
522     DisplayResourceProvider* resource_provider,
523     const gfx::RectF& display_rect,
524     RenderPass* render_pass,
525     bool is_root,
526     gfx::Rect* damage_rect,
527     DCLayerOverlayList* dc_layer_overlays) {
528   gfx::Rect this_frame_overlay_rect;
529   gfx::Rect this_frame_underlay_rect;
530 
531   // Always fill in |has_backdrop_filters| even if non-root overlays are
532   // disabled because it's needed to reject overlays that are read by backdrop
533   // filters.  Note that the backdrop filter rejection doesn't work properly for
534   // overlays that are in non-root render passes since we can't determine if
535   // there's an indirect ancestor render pass which has child RPDQs that could
536   // read this quad's output.
537   render_pass_data_[render_pass->id].has_backdrop_filters =
538       !render_pass->backdrop_filters.IsEmpty();
539 
540   QuadList* quad_list = &render_pass->quad_list;
541   auto next_it = quad_list->begin();
542   for (auto it = quad_list->begin(); it != quad_list->end(); it = next_it) {
543     // next_it may be modified inside the loop if methods modify the quad list
544     // and invalidate iterators to it.
545     next_it = it;
546     ++next_it;
547 
548     if (it->material == DrawQuad::Material::kRenderPass) {
549       next_it = ProcessRenderPassDrawQuad(render_pass, damage_rect, it);
550       continue;
551     }
552 
553     DCLayerOverlay dc_layer;
554     DCLayerResult result;
555     switch (it->material) {
556       case DrawQuad::Material::kYuvVideoContent:
557         result = FromYUVQuad(
558             YUVVideoDrawQuad::MaterialCast(*it),
559             render_pass->transform_to_root_target,
560             render_pass_data_[render_pass->id].backdrop_filter_rects,
561             has_hw_overlay_support_, current_frame_processed_overlay_count_,
562             resource_provider, &dc_layer);
563         break;
564       case DrawQuad::Material::kTextureContent:
565         result = FromTextureQuad(
566             TextureDrawQuad::MaterialCast(*it),
567             render_pass->transform_to_root_target,
568             render_pass_data_[render_pass->id].backdrop_filter_rects,
569             resource_provider, &dc_layer);
570         break;
571       default:
572         result = DC_LAYER_FAILED_UNSUPPORTED_QUAD;
573     }
574 
575     if (result != DC_LAYER_SUCCESS) {
576       RecordDCLayerResult(result, it);
577       continue;
578     }
579 
580     gfx::Rect quad_rectangle_in_target_space =
581         gfx::ToEnclosingRect(ClippedQuadRectangle(*it));
582     gfx::Rect occluding_damage_rect =
583         it->shared_quad_state->occluding_damage_rect.has_value()
584             ? it->shared_quad_state->occluding_damage_rect.value()
585             : quad_rectangle_in_target_space;
586     // Non-root video is always treated as underlay.
587     bool is_overlay = is_root && !HasOccludingQuads(
588                                      gfx::RectF(quad_rectangle_in_target_space),
589                                      quad_list->begin(), it);
590 
591     // Skip quad if it's an underlay and underlays are not allowed
592     if (!is_overlay) {
593       result = IsUnderlayAllowed(it, is_root, dc_layer);
594       if (result != DC_LAYER_SUCCESS) {
595         RecordDCLayerResult(result, it);
596         continue;
597       }
598     }
599     RecordDCLayerResult(DC_LAYER_SUCCESS, it);
600 
601     // Quad is always promoted to either an underlay or an overlay after this
602     // point. It should not fail.
603 
604     // If the current overlay has changed in size/position from the previous
605     // frame, we have to add the overlay quads from the previous frame to the
606     // damage rect for GL compositor. It's hard to optimize multiple overlays or
607     // an overlay in non-root render pass. So always add the overlay rects back
608     // in these two cases. This is only done once at the first overlay/underlay.
609     if (current_frame_processed_overlay_count_ == 0 && is_root &&
610         !previous_frame_overlay_rect_union_.IsEmpty()) {
611       if (quad_rectangle_in_target_space !=
612               previous_frame_overlay_rect_union_ ||
613           previous_frame_processed_overlay_count_ > 1)
614         damage_rect->Union(previous_frame_overlay_rect_union_);
615       previous_frame_overlay_rect_union_ = gfx::Rect();
616     }
617 
618     // Underlays are less efficient, so attempt regular overlays first. Only
619     // check root render pass because we can only check for occlusion within a
620     // render pass. Only check if an overlay hasn't been processed already since
621     // our damage calculations will be wrong otherwise.
622     // TODO(sunnyps): Is the above comment correct?  We seem to allow multiple
623     // overlays for protected video, but don't calculate damage differently.
624     // TODO(magchen): Collect all overlay candidates, and filter the list at the
625     // end to find the best candidates (largest size?).
626     if (is_overlay) {
627       next_it =
628           ProcessForOverlay(display_rect, render_pass,
629                             quad_rectangle_in_target_space, it, damage_rect);
630       this_frame_overlay_rect = quad_rectangle_in_target_space;
631     } else {
632       ProcessForUnderlay(display_rect, render_pass,
633                          quad_rectangle_in_target_space, it, is_root,
634                          damage_rect, &this_frame_underlay_rect, &dc_layer);
635     }
636 
637     gfx::Rect rect_in_root = cc::MathUtil::MapEnclosingClippedRect(
638         render_pass->transform_to_root_target, quad_rectangle_in_target_space);
639     current_frame_overlay_rect_union_.Union(rect_in_root);
640 
641     RecordOverlayHistograms(is_overlay, occluding_damage_rect, damage_rect);
642 
643     dc_layer_overlays->push_back(dc_layer);
644 
645     // Only allow one overlay unless it's hardware protected video.
646     // TODO(magchen): We want to produce all overlay candidates, and then
647     // choose the best one.
648     current_frame_processed_overlay_count_++;
649   }
650 
651   // Update previous frame state after processing root pass
652   if (is_root) {
653     // If there is no overlay in this frame, previous_frame_overlay_rect_union_
654     // will be added to the damage_rect here for GL composition because the
655     // overlay image from the previous frame is missing in the GL composition
656     // path. If any overlay is found in this frame, the previous overlay rects
657     // would have been handled above and previous_frame_overlay_rect_union_
658     // becomes empty.
659     damage_rect->Union(previous_frame_overlay_rect_union_);
660     previous_frame_overlay_rect_union_ = current_frame_overlay_rect_union_;
661     current_frame_overlay_rect_union_ = gfx::Rect();
662     previous_frame_processed_overlay_count_ =
663         current_frame_processed_overlay_count_;
664     current_frame_processed_overlay_count_ = 0;
665 
666     damage_rect->Intersect(gfx::ToEnclosingRect(display_rect));
667     previous_display_rect_ = display_rect;
668     previous_frame_underlay_rect_ = this_frame_underlay_rect;
669 
670     if (show_debug_borders_) {
671       InsertDebugBorderDrawQuads(display_rect, this_frame_overlay_rect,
672                                  render_pass, damage_rect);
673     }
674   }
675 }
676 
ProcessForOverlay(const gfx::RectF & display_rect,RenderPass * render_pass,const gfx::Rect & quad_rectangle,const QuadList::Iterator & it,gfx::Rect * damage_rect)677 QuadList::Iterator DCLayerOverlayProcessor::ProcessForOverlay(
678     const gfx::RectF& display_rect,
679     RenderPass* render_pass,
680     const gfx::Rect& quad_rectangle,
681     const QuadList::Iterator& it,
682     gfx::Rect* damage_rect) {
683   // The quad is on top, so promote it to an overlay and remove all damage
684   // underneath it.
685   const bool display_rect_changed = (display_rect != previous_display_rect_);
686   const bool is_axis_aligned = it->shared_quad_state->quad_to_target_transform
687                                    .Preserves2dAxisAlignment();
688   const bool needs_blending = it->ShouldDrawWithBlending();
689 
690   if (is_axis_aligned && !display_rect_changed && !needs_blending)
691     damage_rect->Subtract(quad_rectangle);
692 
693   return render_pass->quad_list.EraseAndInvalidateAllPointers(it);
694 }
695 
ProcessForUnderlay(const gfx::RectF & display_rect,RenderPass * render_pass,const gfx::Rect & quad_rectangle,const QuadList::Iterator & it,bool is_root,gfx::Rect * damage_rect,gfx::Rect * this_frame_underlay_rect,DCLayerOverlay * dc_layer)696 void DCLayerOverlayProcessor::ProcessForUnderlay(
697     const gfx::RectF& display_rect,
698     RenderPass* render_pass,
699     const gfx::Rect& quad_rectangle,
700     const QuadList::Iterator& it,
701     bool is_root,
702     gfx::Rect* damage_rect,
703     gfx::Rect* this_frame_underlay_rect,
704     DCLayerOverlay* dc_layer) {
705   // Assign decreasing z-order so that underlays processed earlier, and hence
706   // which are above the subsequent underlays, are placed above in the direct
707   // composition visual tree.
708   dc_layer->z_order = -1 - current_frame_processed_overlay_count_;
709 
710   const SharedQuadState* shared_quad_state = it->shared_quad_state;
711   const gfx::Rect rect = it->visible_rect;
712   const bool needs_blending = it->needs_blending;
713 
714   // If the video is translucent and uses SrcOver blend mode, we can achieve the
715   // same result as compositing with video on top if we replace video quad with
716   // a solid color quad with DstOut blend mode, and rely on SrcOver blending
717   // of the root surface with video on bottom. Essentially,
718   //
719   // SrcOver_quad(V, B, V_alpha) = SrcOver_premul(DstOut(BLACK, B, V_alpha), V)
720   // where
721   //    V is the video quad
722   //    B is the background
723   //    SrcOver_quad uses opacity of source quad (V_alpha)
724   //    SrcOver_premul uses alpha channel and assumes premultipled alpha
725   bool is_opaque = false;
726   SharedQuadState* new_shared_quad_state =
727       render_pass->shared_quad_state_list.AllocateAndCopyFrom(
728           shared_quad_state);
729 
730   if (it->ShouldDrawWithBlending() &&
731       shared_quad_state->blend_mode == SkBlendMode::kSrcOver) {
732     new_shared_quad_state->blend_mode = SkBlendMode::kDstOut;
733 
734     auto* replacement =
735         render_pass->quad_list.ReplaceExistingElement<SolidColorDrawQuad>(it);
736     // Use needs_blending from original quad because blending might be because
737     // of this flag or opacity.
738     replacement->SetAll(new_shared_quad_state, rect, rect, needs_blending,
739                         SK_ColorBLACK, true /* force_anti_aliasing_off */);
740   } else {
741     // Set |are_contents_opaque| true so SkiaRenderer draws the replacement quad
742     // with SkBlendMode::kSrc.
743     new_shared_quad_state->are_contents_opaque = false;
744     it->shared_quad_state = new_shared_quad_state;
745 
746     // When the opacity == 1.0, drawing with transparent will be done without
747     // blending and will have the proper effect of completely clearing the
748     // layer.
749     render_pass->quad_list.ReplaceExistingQuadWithOpaqueTransparentSolidColor(
750         it);
751     is_opaque = true;
752   }
753 
754   bool display_rect_changed = (display_rect != previous_display_rect_);
755   bool underlay_rect_changed =
756       (quad_rectangle != previous_frame_underlay_rect_);
757   bool is_axis_aligned =
758       shared_quad_state->quad_to_target_transform.Preserves2dAxisAlignment();
759 
760   if (is_root && current_frame_processed_overlay_count_ == 0 &&
761       is_axis_aligned && is_opaque && !underlay_rect_changed &&
762       !display_rect_changed &&
763       shared_quad_state->occluding_damage_rect.has_value()) {
764     // If this underlay rect is the same as for last frame, subtract its area
765     // from the damage of the main surface, as the cleared area was already
766     // cleared last frame. Add back the damage from the occluded area for this
767     // frame.
768     damage_rect->Subtract(quad_rectangle);
769 
770     // If none of the quads on top give any damage, we can skip compositing
771     // these quads when the incoming damage rect is smaller or equal to the
772     // video quad. After subtraction, the resulting output damage rect for GL
773     // compositor will be empty. If the incoming damage rect is bigger than the
774     // video quad, we don't have an oppertunity for power optimization even if
775     // no damage on top. The output damage rect will not be empty in this case.
776     damage_rect->Union(shared_quad_state->occluding_damage_rect.value());
777   } else {
778     // Entire replacement quad must be redrawn.
779     damage_rect->Union(quad_rectangle);
780   }
781 
782   // We only compare current frame's first root pass underlay with the previous
783   // frame's first root pass underlay. Non-opaque regions can have different
784   // alpha from one frame to another so this optimization doesn't work.
785   if (is_root && current_frame_processed_overlay_count_ == 0 &&
786       is_axis_aligned && is_opaque) {
787     *this_frame_underlay_rect = quad_rectangle;
788   }
789 
790   // Propagate the punched holes up the chain of render passes. Punch through
791   // rects are in quad target (child render pass) space, and are transformed to
792   // RPDQ target (parent render pass) in ProcessRenderPassDrawQuad().
793   render_pass_data_[render_pass->id].punch_through_rects.push_back(
794       gfx::ToEnclosingRect(ClippedQuadRectangle(*it)));
795 }
796 
797 }  // namespace viz
798