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