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