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