1 // Copyright 2014 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/overlay_candidate.h"
6 
7 #include <algorithm>
8 #include <limits>
9 
10 #include "base/logging.h"
11 #include "build/build_config.h"
12 #include "cc/base/math_util.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/stream_video_draw_quad.h"
16 #include "components/viz/common/quads/texture_draw_quad.h"
17 #include "components/viz/common/quads/tile_draw_quad.h"
18 #include "components/viz/common/quads/video_hole_draw_quad.h"
19 #include "components/viz/common/quads/yuv_video_draw_quad.h"
20 #include "components/viz/service/display/display_resource_provider.h"
21 #include "ui/gfx/geometry/rect_conversions.h"
22 #include "ui/gfx/geometry/vector3d_f.h"
23 #include "ui/gfx/video_types.h"
24 
25 namespace viz {
26 
27 namespace {
28 // Tolerance for considering axis vector elements to be zero.
29 const SkScalar kEpsilon = std::numeric_limits<float>::epsilon();
30 
31 const gfx::BufferFormat kOverlayFormats[] = {
32     gfx::BufferFormat::RGBX_8888, gfx::BufferFormat::RGBA_8888,
33     gfx::BufferFormat::BGRX_8888, gfx::BufferFormat::BGRA_8888,
34     gfx::BufferFormat::BGR_565,   gfx::BufferFormat::YUV_420_BIPLANAR};
35 
36 enum Axis { NONE, AXIS_POS_X, AXIS_NEG_X, AXIS_POS_Y, AXIS_NEG_Y };
37 
VectorToAxis(const gfx::Vector3dF & vec)38 Axis VectorToAxis(const gfx::Vector3dF& vec) {
39   if (std::abs(vec.z()) > kEpsilon)
40     return NONE;
41   const bool x_zero = (std::abs(vec.x()) <= kEpsilon);
42   const bool y_zero = (std::abs(vec.y()) <= kEpsilon);
43   if (x_zero && !y_zero)
44     return (vec.y() > 0) ? AXIS_POS_Y : AXIS_NEG_Y;
45   else if (y_zero && !x_zero)
46     return (vec.x() > 0) ? AXIS_POS_X : AXIS_NEG_X;
47   else
48     return NONE;
49 }
50 
GetOverlayTransform(const gfx::Transform & quad_transform,bool y_flipped)51 gfx::OverlayTransform GetOverlayTransform(const gfx::Transform& quad_transform,
52                                           bool y_flipped) {
53   if (!quad_transform.Preserves2dAxisAlignment()) {
54     return gfx::OVERLAY_TRANSFORM_INVALID;
55   }
56 
57   gfx::Vector3dF x_axis = cc::MathUtil::GetXAxis(quad_transform);
58   gfx::Vector3dF y_axis = cc::MathUtil::GetYAxis(quad_transform);
59   if (y_flipped) {
60     y_axis.Scale(-1);
61   }
62 
63   Axis x_to = VectorToAxis(x_axis);
64   Axis y_to = VectorToAxis(y_axis);
65 
66   if (x_to == AXIS_POS_X && y_to == AXIS_POS_Y)
67     return gfx::OVERLAY_TRANSFORM_NONE;
68   else if (x_to == AXIS_NEG_X && y_to == AXIS_POS_Y)
69     return gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL;
70   else if (x_to == AXIS_POS_X && y_to == AXIS_NEG_Y)
71     return gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL;
72   else if (x_to == AXIS_NEG_Y && y_to == AXIS_POS_X)
73     return gfx::OVERLAY_TRANSFORM_ROTATE_270;
74   else if (x_to == AXIS_NEG_X && y_to == AXIS_NEG_Y)
75     return gfx::OVERLAY_TRANSFORM_ROTATE_180;
76   else if (x_to == AXIS_POS_Y && y_to == AXIS_NEG_X)
77     return gfx::OVERLAY_TRANSFORM_ROTATE_90;
78   else
79     return gfx::OVERLAY_TRANSFORM_INVALID;
80 }
81 
82 }  // namespace
83 
OverlayCandidate()84 OverlayCandidate::OverlayCandidate()
85     : transform(gfx::OVERLAY_TRANSFORM_NONE),
86       format(gfx::BufferFormat::RGBA_8888),
87       uv_rect(0.f, 0.f, 1.f, 1.f),
88       is_clipped(false),
89       is_opaque(false),
90       no_occluding_damage(false),
91       resource_id(0),
92 #if defined(OS_ANDROID)
93       is_backed_by_surface_texture(false),
94       is_promotable_hint(false),
95 #endif
96       plane_z_order(0),
97       is_unoccluded(false),
98       overlay_handled(false),
99       gpu_fence_id(0) {
100 }
101 
102 OverlayCandidate::OverlayCandidate(const OverlayCandidate& other) = default;
103 
104 OverlayCandidate::~OverlayCandidate() = default;
105 
106 // static
FromDrawQuad(DisplayResourceProvider * resource_provider,const SkMatrix44 & output_color_matrix,const DrawQuad * quad,OverlayCandidate * candidate)107 bool OverlayCandidate::FromDrawQuad(DisplayResourceProvider* resource_provider,
108                                     const SkMatrix44& output_color_matrix,
109                                     const DrawQuad* quad,
110                                     OverlayCandidate* candidate) {
111   // It is currently not possible to set a color conversion matrix on an HW
112   // overlay plane.
113   // TODO(https://crbug.com/792757): Remove this check once the bug is resolved.
114   if (!output_color_matrix.isIdentity())
115     return false;
116 
117   // We don't support an opacity value different than one for an overlay plane.
118   if (quad->shared_quad_state->opacity != 1.f)
119     return false;
120   // We can't support overlays with rounded corner clipping.
121   if (!quad->shared_quad_state->rounded_corner_bounds.IsEmpty())
122     return false;
123   // We support only kSrc (no blending) and kSrcOver (blending with premul).
124   if (!(quad->shared_quad_state->blend_mode == SkBlendMode::kSrc ||
125         quad->shared_quad_state->blend_mode == SkBlendMode::kSrcOver)) {
126     return false;
127   }
128 
129   switch (quad->material) {
130     case DrawQuad::Material::kTextureContent:
131       return FromTextureQuad(resource_provider,
132                              TextureDrawQuad::MaterialCast(quad), candidate);
133     case DrawQuad::Material::kVideoHole:
134       return FromVideoHoleQuad(
135           resource_provider, VideoHoleDrawQuad::MaterialCast(quad), candidate);
136     case DrawQuad::Material::kStreamVideoContent:
137       return FromStreamVideoQuad(resource_provider,
138                                  StreamVideoDrawQuad::MaterialCast(quad),
139                                  candidate);
140     default:
141       break;
142   }
143 
144   return false;
145 }
146 
147 // static
IsInvisibleQuad(const DrawQuad * quad)148 bool OverlayCandidate::IsInvisibleQuad(const DrawQuad* quad) {
149   float opacity = quad->shared_quad_state->opacity;
150   if (opacity < std::numeric_limits<float>::epsilon())
151     return true;
152   if (quad->material != DrawQuad::Material::kSolidColor)
153     return false;
154   const SkColor color = SolidColorDrawQuad::MaterialCast(quad)->color;
155   const float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity;
156   return quad->ShouldDrawWithBlending() &&
157          alpha < std::numeric_limits<float>::epsilon();
158 }
159 
160 // static
IsOccluded(const OverlayCandidate & candidate,QuadList::ConstIterator quad_list_begin,QuadList::ConstIterator quad_list_end)161 bool OverlayCandidate::IsOccluded(const OverlayCandidate& candidate,
162                                   QuadList::ConstIterator quad_list_begin,
163                                   QuadList::ConstIterator quad_list_end) {
164   // The rects are rounded as they're snapped by the compositor to pixel unless
165   // it is AA'ed, in which case, it won't be overlaid.
166   gfx::Rect display_rect = gfx::ToRoundedRect(candidate.display_rect);
167 
168   // Check that no visible quad overlaps the candidate.
169   for (auto overlap_iter = quad_list_begin; overlap_iter != quad_list_end;
170        ++overlap_iter) {
171     gfx::Rect overlap_rect = gfx::ToRoundedRect(cc::MathUtil::MapClippedRect(
172         overlap_iter->shared_quad_state->quad_to_target_transform,
173         gfx::RectF(overlap_iter->rect)));
174 
175     if (display_rect.Intersects(overlap_rect) &&
176         !OverlayCandidate::IsInvisibleQuad(*overlap_iter)) {
177       return true;
178     }
179   }
180   return false;
181 }
182 
183 // static
RequiresOverlay(const DrawQuad * quad)184 bool OverlayCandidate::RequiresOverlay(const DrawQuad* quad) {
185   switch (quad->material) {
186     case DrawQuad::Material::kTextureContent:
187       return TextureDrawQuad::MaterialCast(quad)->protected_video_type ==
188              gfx::ProtectedVideoType::kHardwareProtected;
189     case DrawQuad::Material::kVideoHole:
190       return true;
191     case DrawQuad::Material::kYuvVideoContent:
192       return YUVVideoDrawQuad::MaterialCast(quad)->protected_video_type ==
193              gfx::ProtectedVideoType::kHardwareProtected;
194     default:
195       return false;
196   }
197 }
198 
199 // static
IsOccludedByFilteredQuad(const OverlayCandidate & candidate,QuadList::ConstIterator quad_list_begin,QuadList::ConstIterator quad_list_end,const base::flat_map<RenderPassId,cc::FilterOperations * > & render_pass_backdrop_filters)200 bool OverlayCandidate::IsOccludedByFilteredQuad(
201     const OverlayCandidate& candidate,
202     QuadList::ConstIterator quad_list_begin,
203     QuadList::ConstIterator quad_list_end,
204     const base::flat_map<RenderPassId, cc::FilterOperations*>&
205         render_pass_backdrop_filters) {
206   for (auto overlap_iter = quad_list_begin; overlap_iter != quad_list_end;
207        ++overlap_iter) {
208     if (overlap_iter->material == DrawQuad::Material::kRenderPass) {
209       gfx::RectF overlap_rect = cc::MathUtil::MapClippedRect(
210           overlap_iter->shared_quad_state->quad_to_target_transform,
211           gfx::RectF(overlap_iter->rect));
212       const RenderPassDrawQuad* render_pass_draw_quad =
213           RenderPassDrawQuad::MaterialCast(*overlap_iter);
214       if (candidate.display_rect.Intersects(overlap_rect) &&
215           render_pass_backdrop_filters.count(
216               render_pass_draw_quad->render_pass_id)) {
217         return true;
218       }
219     }
220   }
221   return false;
222 }
223 
224 // static
FromDrawQuadResource(DisplayResourceProvider * resource_provider,const DrawQuad * quad,ResourceId resource_id,bool y_flipped,OverlayCandidate * candidate)225 bool OverlayCandidate::FromDrawQuadResource(
226     DisplayResourceProvider* resource_provider,
227     const DrawQuad* quad,
228     ResourceId resource_id,
229     bool y_flipped,
230     OverlayCandidate* candidate) {
231   if (!resource_provider->IsOverlayCandidate(resource_id))
232     return false;
233 
234   candidate->format = resource_provider->GetBufferFormat(resource_id);
235   candidate->color_space = resource_provider->GetColorSpace(resource_id);
236   if (!base::Contains(kOverlayFormats, candidate->format))
237     return false;
238 
239   gfx::OverlayTransform overlay_transform = GetOverlayTransform(
240       quad->shared_quad_state->quad_to_target_transform, y_flipped);
241   if (overlay_transform == gfx::OVERLAY_TRANSFORM_INVALID)
242     return false;
243 
244   auto& transform = quad->shared_quad_state->quad_to_target_transform;
245   candidate->display_rect = gfx::RectF(quad->rect);
246   transform.TransformRect(&candidate->display_rect);
247 
248   candidate->clip_rect = quad->shared_quad_state->clip_rect;
249   candidate->is_clipped = quad->shared_quad_state->is_clipped;
250   candidate->is_opaque = !quad->ShouldDrawWithBlending();
251   if (quad->shared_quad_state->occluding_damage_rect.has_value()) {
252     candidate->no_occluding_damage =
253         quad->shared_quad_state->occluding_damage_rect->IsEmpty();
254   }
255 
256   candidate->resource_id = resource_id;
257   candidate->transform = overlay_transform;
258   candidate->mailbox = resource_provider->GetMailbox(resource_id);
259 
260   return true;
261 }
262 
263 // static
264 // For VideoHoleDrawQuad, only calculate geometry information
265 // and put it in the |candidate|.
FromVideoHoleQuad(DisplayResourceProvider * resource_provider,const VideoHoleDrawQuad * quad,OverlayCandidate * candidate)266 bool OverlayCandidate::FromVideoHoleQuad(
267     DisplayResourceProvider* resource_provider,
268     const VideoHoleDrawQuad* quad,
269     OverlayCandidate* candidate) {
270   gfx::OverlayTransform overlay_transform = GetOverlayTransform(
271       quad->shared_quad_state->quad_to_target_transform, false);
272   if (overlay_transform == gfx::OVERLAY_TRANSFORM_INVALID)
273     return false;
274 
275   auto& transform = quad->shared_quad_state->quad_to_target_transform;
276   candidate->display_rect = gfx::RectF(quad->rect);
277   transform.TransformRect(&candidate->display_rect);
278   candidate->transform = overlay_transform;
279   if (quad->shared_quad_state->occluding_damage_rect.has_value()) {
280     candidate->no_occluding_damage =
281         quad->shared_quad_state->occluding_damage_rect->IsEmpty();
282   }
283 
284   return true;
285 }
286 
287 // static
FromTextureQuad(DisplayResourceProvider * resource_provider,const TextureDrawQuad * quad,OverlayCandidate * candidate)288 bool OverlayCandidate::FromTextureQuad(
289     DisplayResourceProvider* resource_provider,
290     const TextureDrawQuad* quad,
291     OverlayCandidate* candidate) {
292   if (quad->background_color != SK_ColorTRANSPARENT &&
293       (quad->background_color != SK_ColorBLACK ||
294        quad->ShouldDrawWithBlending()))
295     return false;
296   if (!FromDrawQuadResource(resource_provider, quad, quad->resource_id(),
297                             quad->y_flipped, candidate)) {
298     return false;
299   }
300   candidate->resource_size_in_pixels = quad->resource_size_in_pixels();
301   candidate->uv_rect = BoundingRect(quad->uv_top_left, quad->uv_bottom_right);
302   return true;
303 }
304 
305 // static
FromStreamVideoQuad(DisplayResourceProvider * resource_provider,const StreamVideoDrawQuad * quad,OverlayCandidate * candidate)306 bool OverlayCandidate::FromStreamVideoQuad(
307     DisplayResourceProvider* resource_provider,
308     const StreamVideoDrawQuad* quad,
309     OverlayCandidate* candidate) {
310   if (!FromDrawQuadResource(resource_provider, quad, quad->resource_id(), false,
311                             candidate)) {
312     return false;
313   }
314 
315   candidate->resource_id = quad->resource_id();
316   candidate->resource_size_in_pixels = quad->resource_size_in_pixels();
317   candidate->uv_rect = BoundingRect(quad->uv_top_left, quad->uv_bottom_right);
318 #if defined(OS_ANDROID)
319   candidate->is_backed_by_surface_texture =
320       resource_provider->IsBackedBySurfaceTexture(quad->resource_id());
321 #endif
322   return true;
323 }
324 }  // namespace viz
325