[M120 Migration] Skip flushAndSubmit sync call on gpu side for webgl
[platform/framework/web/chromium-efl.git] / components / viz / service / display / skia_renderer.cc
1 // Copyright 2012 The Chromium Authors
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/skia_renderer.h"
6
7 #include <limits>
8 #include <string>
9 #include <utility>
10
11 #include "base/auto_reset.h"
12 #include "base/command_line.h"
13 #include "base/debug/dump_without_crashing.h"
14 #include "base/feature_list.h"
15 #include "base/functional/bind.h"
16 #include "base/logging.h"
17 #include "base/memory/raw_ptr.h"
18 #include "base/memory/weak_ptr.h"
19 #include "base/metrics/histogram_macros.h"
20 #include "base/ranges/algorithm.h"
21 #include "base/task/bind_post_task.h"
22 #include "base/task/single_thread_task_runner.h"
23 #include "base/trace_event/trace_event.h"
24 #include "build/build_config.h"
25 #include "build/chromeos_buildflags.h"
26 #include "cc/base/math_util.h"
27 #include "cc/debug/debug_colors.h"
28 #include "cc/paint/render_surface_filters.h"
29 #include "components/viz/common/display/renderer_settings.h"
30 #include "components/viz/common/features.h"
31 #include "components/viz/common/frame_sinks/copy_output_request.h"
32 #include "components/viz/common/frame_sinks/copy_output_util.h"
33 #include "components/viz/common/quads/aggregated_render_pass.h"
34 #include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
35 #include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
36 #include "components/viz/common/quads/debug_border_draw_quad.h"
37 #include "components/viz/common/quads/picture_draw_quad.h"
38 #include "components/viz/common/quads/solid_color_draw_quad.h"
39 #include "components/viz/common/quads/texture_draw_quad.h"
40 #include "components/viz/common/quads/tile_draw_quad.h"
41 #include "components/viz/common/quads/yuv_video_draw_quad.h"
42 #include "components/viz/common/resources/platform_color.h"
43 #include "components/viz/common/resources/shared_image_format_utils.h"
44 #include "components/viz/common/skia_helper.h"
45 #include "components/viz/service/debugger/viz_debugger.h"
46 #include "components/viz/service/display/delegated_ink_handler.h"
47 #include "components/viz/service/display/delegated_ink_point_renderer_skia.h"
48 #include "components/viz/service/display/display_resource_provider.h"
49 #include "components/viz/service/display/display_resource_provider_skia.h"
50 #include "components/viz/service/display/output_surface.h"
51 #include "components/viz/service/display/output_surface_frame.h"
52 #include "components/viz/service/display/renderer_utils.h"
53 #include "components/viz/service/display/resource_fence.h"
54 #include "components/viz/service/display/skia_output_surface.h"
55 #include "gpu/command_buffer/client/shared_image_interface.h"
56 #include "gpu/command_buffer/common/shared_image_usage.h"
57 #include "gpu/command_buffer/common/swap_buffers_complete_params.h"
58 #include "gpu/command_buffer/common/sync_token.h"
59 #include "gpu/config/gpu_finch_features.h"
60 #include "skia/ext/opacity_filter_canvas.h"
61 #include "third_party/abseil-cpp/absl/types/optional.h"
62 #include "third_party/skia/include/core/SkCanvas.h"
63 #include "third_party/skia/include/core/SkColor.h"
64 #include "third_party/skia/include/core/SkColorFilter.h"
65 #include "third_party/skia/include/core/SkMatrix.h"
66 #include "third_party/skia/include/core/SkPath.h"
67 #include "third_party/skia/include/core/SkPixelRef.h"
68 #include "third_party/skia/include/core/SkShader.h"
69 #include "third_party/skia/include/core/SkString.h"
70 #include "third_party/skia/include/effects/SkColorMatrix.h"
71 #include "third_party/skia/include/effects/SkGradientShader.h"
72 #include "third_party/skia/include/effects/SkImageFilters.h"
73 #include "third_party/skia/include/effects/SkOverdrawColorFilter.h"
74 #include "third_party/skia/include/effects/SkRuntimeEffect.h"
75 #include "third_party/skia/include/effects/SkShaderMaskFilter.h"
76 #include "third_party/skia/include/gpu/GrBackendSurface.h"
77 #include "third_party/skia/include/gpu/GrDirectContext.h"
78 #include "third_party/skia/include/private/chromium/GrDeferredDisplayList.h"
79 #include "third_party/skia/modules/skcms/skcms.h"
80 #include "third_party/skia/src/core/SkCanvasPriv.h"
81 #include "ui/base/ui_base_features.h"
82 #include "ui/gfx/buffer_format_util.h"
83 #include "ui/gfx/color_transform.h"
84 #include "ui/gfx/geometry/angle_conversions.h"
85 #include "ui/gfx/geometry/axis_transform2d.h"
86 #include "ui/gfx/geometry/linear_gradient.h"
87 #include "ui/gfx/geometry/rect.h"
88 #include "ui/gfx/geometry/rect_conversions.h"
89 #include "ui/gfx/geometry/rect_f.h"
90 #include "ui/gfx/geometry/size.h"
91 #include "ui/gfx/geometry/size_conversions.h"
92 #include "ui/gfx/geometry/size_f.h"
93 #include "ui/gfx/geometry/skia_conversions.h"
94 #include "ui/gfx/geometry/transform.h"
95 #include "ui/gfx/geometry/transform_util.h"
96 #include "ui/gfx/gpu_fence_handle.h"
97
98 #if BUILDFLAG(IS_ANDROID)
99 #include "components/viz/service/display/overlay_processor_surface_control.h"
100 #endif
101
102 #if defined(TIZEN_VIDEO_HOLE)
103 #include "base/numerics/math_constants.h"
104 #endif
105
106 namespace viz {
107
108 namespace {
109
110 // Smallest unit that impacts anti-aliasing output. We use this to determine
111 // when an exterior edge (with AA) has been clipped (no AA). The specific value
112 // was chosen to match that used by gl_renderer.
113 static const float kAAEpsilon = 1.0f / 1024.0f;
114
115 // The gfx::QuadF draw_region passed to DoDrawQuad, converted to Skia types
116 struct SkDrawRegion {
117   SkDrawRegion() = default;
118   explicit SkDrawRegion(const gfx::QuadF& draw_region);
119
120   SkPoint points[4];
121 };
122
123 // Additional YUV information to skia renderer to draw 9- and 10- bits color.
124 struct YUVInput {
125   YUVInput() { memset(this, 0, sizeof(*this)); }
126   float offset;
127   float multiplier;
128 };
129
130 SkDrawRegion::SkDrawRegion(const gfx::QuadF& draw_region) {
131   points[0] = gfx::PointFToSkPoint(draw_region.p1());
132   points[1] = gfx::PointFToSkPoint(draw_region.p2());
133   points[2] = gfx::PointFToSkPoint(draw_region.p3());
134   points[3] = gfx::PointFToSkPoint(draw_region.p4());
135 }
136
137 bool IsTextureResource(DisplayResourceProviderSkia* resource_provider,
138                        ResourceId resource_id) {
139   return !resource_provider->IsResourceSoftwareBacked(resource_id);
140 }
141
142 unsigned GetCornerAAFlags(const DrawQuad* quad,
143                           const SkPoint& vertex,
144                           unsigned edge_mask) {
145   // Returns mask of SkCanvas::QuadAAFlags, with bits set for each edge of the
146   // shared quad state's quad_layer_rect that vertex is touching.
147
148   unsigned mask = SkCanvas::kNone_QuadAAFlags;
149   if (std::abs(vertex.x()) < kAAEpsilon)
150     mask |= SkCanvas::kLeft_QuadAAFlag;
151   if (std::abs(vertex.x() - quad->shared_quad_state->quad_layer_rect.width()) <
152       kAAEpsilon)
153     mask |= SkCanvas::kRight_QuadAAFlag;
154   if (std::abs(vertex.y()) < kAAEpsilon)
155     mask |= SkCanvas::kTop_QuadAAFlag;
156   if (std::abs(vertex.y() - quad->shared_quad_state->quad_layer_rect.height()) <
157       kAAEpsilon)
158     mask |= SkCanvas::kBottom_QuadAAFlag;
159   // & with the overall edge_mask to take into account edges that were clipped
160   // by the visible rect.
161   return mask & edge_mask;
162 }
163
164 // This is slightly different than Transform::IsPositiveScaleAndTranslation()
165 // in that it also allows zero scales. This is because in the common
166 // orthographic case the z scale is 0.
167 bool Is2dScaleTranslateTransform(const gfx::Transform& transform) {
168   return transform.IsScaleOrTranslation() && transform.rc(0, 0) >= 0.0f &&
169          transform.rc(1, 1) >= 0.0f && transform.rc(2, 2) >= 0.0f;
170 }
171
172 bool IsExteriorEdge(unsigned corner_mask1, unsigned corner_mask2) {
173   return (corner_mask1 & corner_mask2) != 0;
174 }
175
176 unsigned GetRectilinearEdgeFlags(const DrawQuad* quad) {
177   // In the normal case, turn on AA for edges that represent the outside of
178   // the layer, and that aren't clipped by the visible rect.
179   unsigned mask = SkCanvas::kNone_QuadAAFlags;
180   if (quad->IsLeftEdge() &&
181       std::abs(quad->rect.x() - quad->visible_rect.x()) < kAAEpsilon)
182     mask |= SkCanvas::kLeft_QuadAAFlag;
183   if (quad->IsTopEdge() &&
184       std::abs(quad->rect.y() - quad->visible_rect.y()) < kAAEpsilon)
185     mask |= SkCanvas::kTop_QuadAAFlag;
186   if (quad->IsRightEdge() &&
187       std::abs(quad->rect.right() - quad->visible_rect.right()) < kAAEpsilon)
188     mask |= SkCanvas::kRight_QuadAAFlag;
189   if (quad->IsBottomEdge() &&
190       std::abs(quad->rect.bottom() - quad->visible_rect.bottom()) < kAAEpsilon)
191     mask |= SkCanvas::kBottom_QuadAAFlag;
192
193   return mask;
194 }
195
196 // This also modifies draw_region to clean up any degeneracies
197 void GetClippedEdgeFlags(const DrawQuad* quad,
198                          unsigned* edge_mask,
199                          SkDrawRegion* draw_region) {
200   // Instead of trying to rotate vertices of draw_region to align with Skia's
201   // edge label conventions, turn on an edge's label if it is aligned to any
202   // exterior edge.
203   unsigned p0Mask = GetCornerAAFlags(quad, draw_region->points[0], *edge_mask);
204   unsigned p1Mask = GetCornerAAFlags(quad, draw_region->points[1], *edge_mask);
205   unsigned p2Mask = GetCornerAAFlags(quad, draw_region->points[2], *edge_mask);
206   unsigned p3Mask = GetCornerAAFlags(quad, draw_region->points[3], *edge_mask);
207
208   unsigned mask = SkCanvas::kNone_QuadAAFlags;
209   // The "top" is p0 to p1
210   if (IsExteriorEdge(p0Mask, p1Mask))
211     mask |= SkCanvas::kTop_QuadAAFlag;
212   // The "right" is p1 to p2
213   if (IsExteriorEdge(p1Mask, p2Mask))
214     mask |= SkCanvas::kRight_QuadAAFlag;
215   // The "bottom" is p2 to p3
216   if (IsExteriorEdge(p2Mask, p3Mask))
217     mask |= SkCanvas::kBottom_QuadAAFlag;
218   // The "left" is p3 to p0
219   if (IsExteriorEdge(p3Mask, p0Mask))
220     mask |= SkCanvas::kLeft_QuadAAFlag;
221
222   // If the clipped draw_region has adjacent non-AA edges that touch the
223   // exterior edge (which should be AA'ed), move the degenerate vertex to the
224   // appropriate index so that Skia knows to construct a coverage ramp at that
225   // corner. This is not an ideal solution, but is the best hint we can give,
226   // given our limited information post-BSP splitting.
227   if (draw_region->points[2] == draw_region->points[3]) {
228     // The BSP splitting always creates degenerate quads with the duplicate
229     // vertex in the last two indices.
230     if (p0Mask && !(mask & SkCanvas::kLeft_QuadAAFlag) &&
231         !(mask & SkCanvas::kTop_QuadAAFlag)) {
232       // Rewrite draw_region from p0,p1,p2,p2 to p0,p1,p2,p0; top edge stays off
233       // right edge is preserved, bottom edge turns off, left edge turns on
234       draw_region->points[3] = draw_region->points[0];
235       mask = SkCanvas::kLeft_QuadAAFlag | (mask & SkCanvas::kRight_QuadAAFlag);
236     } else if (p1Mask && !(mask & SkCanvas::kTop_QuadAAFlag) &&
237                !(mask & SkCanvas::kRight_QuadAAFlag)) {
238       // Rewrite draw_region to p0,p1,p1,p2; top edge stays off, right edge
239       // turns on, bottom edge turns off, left edge is preserved
240       draw_region->points[2] = draw_region->points[1];
241       mask = SkCanvas::kRight_QuadAAFlag | (mask & SkCanvas::kLeft_QuadAAFlag);
242     }
243     // p2 could follow the same process, but if its adjacent edges are AA
244     // (skipping the degenerate edge to p3), it's actually already in the
245     // desired vertex ordering; and since p3 is in the same location, it's
246     // equivalent to p2 so it doesn't need checking either.
247   }  // Else not degenerate, so can't to correct non-AA corners touching AA edge
248
249   *edge_mask = mask;
250 }
251
252 bool IsAAForcedOff(const DrawQuad* quad) {
253   switch (quad->material) {
254     case DrawQuad::Material::kPictureContent:
255       return PictureDrawQuad::MaterialCast(quad)->force_anti_aliasing_off;
256     case DrawQuad::Material::kCompositorRenderPass:
257       // We should not have compositor render passes here.
258       NOTREACHED();
259       return CompositorRenderPassDrawQuad::MaterialCast(quad)
260           ->force_anti_aliasing_off;
261     case DrawQuad::Material::kAggregatedRenderPass:
262       return AggregatedRenderPassDrawQuad::MaterialCast(quad)
263           ->force_anti_aliasing_off;
264     case DrawQuad::Material::kSolidColor:
265       return SolidColorDrawQuad::MaterialCast(quad)->force_anti_aliasing_off;
266     case DrawQuad::Material::kTiledContent:
267       return TileDrawQuad::MaterialCast(quad)->force_anti_aliasing_off;
268     case DrawQuad::Material::kYuvVideoContent:
269     case DrawQuad::Material::kTextureContent:
270       // This is done to match the behaviour of GLRenderer and we can revisit it
271       // later.
272       return true;
273     default:
274       return false;
275   }
276 }
277
278 bool UseNearestNeighborSampling(const DrawQuad* quad) {
279   switch (quad->material) {
280     case DrawQuad::Material::kPictureContent:
281       return PictureDrawQuad::MaterialCast(quad)->nearest_neighbor;
282     case DrawQuad::Material::kTextureContent:
283       return TextureDrawQuad::MaterialCast(quad)->nearest_neighbor;
284     case DrawQuad::Material::kTiledContent:
285       return TileDrawQuad::MaterialCast(quad)->nearest_neighbor;
286     default:
287       // Other quad types do not expose nearest_neighbor.
288       return false;
289   }
290 }
291
292 SkSamplingOptions GetSampling(const DrawQuad* quad) {
293   if (UseNearestNeighborSampling(quad))
294     return SkSamplingOptions(SkFilterMode::kNearest);
295
296   // Default to bilinear if the quad doesn't specify nearest_neighbor.
297   // TODO(penghuang): figure out how to set correct filter quality for YUV and
298   // video stream quads.
299   return SkSamplingOptions(SkFilterMode::kLinear);
300 }
301
302 // Returns kFast if sampling outside of vis_tex_coords due to AA or bilerp will
303 // not go outside of the content area, or if the content area is the full image
304 // (in which case hardware clamping handles it automatically). Different quad
305 // types have different rules for the content area within the image.
306 SkCanvas::SrcRectConstraint GetTextureConstraint(
307     const SkImage* image,
308     const gfx::RectF& vis_tex_coords,
309     const gfx::RectF& valid_texel_bounds) {
310   bool fills_left = valid_texel_bounds.x() <= 0.f;
311   bool fills_right = valid_texel_bounds.right() >= image->width();
312   bool fills_top = valid_texel_bounds.y() <= 0.f;
313   bool fills_bottom = valid_texel_bounds.bottom() >= image->height();
314   if (fills_left && fills_right && fills_top && fills_bottom) {
315     // The entire image is contained in the content area, so hardware clamping
316     // ensures only content texels are sampled
317     return SkCanvas::kFast_SrcRectConstraint;
318   }
319
320   gfx::RectF safe_texels = valid_texel_bounds;
321   safe_texels.Inset(0.5f);
322
323   // Check each axis independently; tile quads may only need clamping on one
324   // side (e.g. right or bottom) and this logic doesn't fully match a simple
325   // contains() check.
326   if ((!fills_left && vis_tex_coords.x() < safe_texels.x()) ||
327       (!fills_right && vis_tex_coords.right() > safe_texels.right())) {
328     return SkCanvas::kStrict_SrcRectConstraint;
329   }
330   if ((!fills_top && vis_tex_coords.y() < safe_texels.y()) ||
331       (!fills_bottom && vis_tex_coords.bottom() > safe_texels.bottom())) {
332     return SkCanvas::kStrict_SrcRectConstraint;
333   }
334
335   // The texture coordinates are far enough from the content area that even with
336   // bilerp and AA, it won't sample outside the content area
337   return SkCanvas::kFast_SrcRectConstraint;
338 }
339
340 // Return a color filter that multiplies the incoming color by the fixed alpha
341 sk_sp<SkColorFilter> MakeOpacityFilter(float alpha, sk_sp<SkColorFilter> in) {
342   SkColor4f alpha_as_color = {1.0, 1.0, 1.0, alpha};
343   // MakeModeFilter treats fixed color as src, and input color as dst.
344   // kDstIn is (srcAlpha * dstColor, srcAlpha * dstAlpha) so this makes the
345   // output color equal to input color * alpha.
346   // TODO(michaelludwig): Update to pass alpha_as_color as-is once
347   // skbug.com/13637 is resolved (adds Blend + SkColor4f variation).
348   sk_sp<SkColorFilter> opacity =
349       SkColorFilters::Blend(alpha_as_color.toSkColor(), SkBlendMode::kDstIn);
350   // Opaque (alpha = 1.0) and kDstIn returns nullptr to signal a no-op, so that
351   // case should just return 'in'.
352   if (opacity) {
353     return opacity->makeComposed(std::move(in));
354   } else {
355     return in;
356   }
357 }
358
359 // Porter-Duff blend mode utility functions, where the final color is
360 // represented as a weighted sum of the incoming src and existing dst color.
361 // See [https://skia.org/user/api/SkBlendMode_Reference]
362
363 bool IsPorterDuffBlendMode(SkBlendMode blendMode) {
364   return blendMode <= SkBlendMode::kLastCoeffMode;
365 }
366
367 // Returns true if drawing transparent black with |blendMode| would modify the
368 // destination buffer. If false is returned, the draw would have no discernible
369 // effect on the pixel color so the entire draw can be skipped.
370 bool TransparentBlackAffectsOutput(SkBlendMode blendMode) {
371   SkBlendModeCoeff src, dst;
372   if (!SkBlendMode_AsCoeff(blendMode, &src, &dst)) {
373     // An advanced blend mode that can't be represented as coefficients, so
374     // assume it modifies the output.
375     return true;
376   }
377   // True when the dst coefficient is not equal to 1 (when src = (0,0,0,0))
378   return dst != SkBlendModeCoeff::kOne && dst != SkBlendModeCoeff::kISA &&
379          dst != SkBlendModeCoeff::kISC;
380 }
381
382 // Returns true if src content drawn with |blendMode| into a RenderPass would
383 // produce the exact same image as the original src content.
384 bool RenderPassPreservesContent(SkBlendMode blendMode) {
385   SkBlendModeCoeff src, dst;
386   if (!SkBlendMode_AsCoeff(blendMode, &src, &dst)) {
387     return false;
388   }
389   // True when src coefficient is equal to 1 (when dst = (0,0,0,0))
390   return src == SkBlendModeCoeff::kOne || src == SkBlendModeCoeff::kIDA ||
391          src == SkBlendModeCoeff::kIDC;
392 }
393
394 // Returns true if src content draw with |blendMode| into an empty RenderPass
395 // would produce a transparent black image.
396 bool RenderPassRemainsTransparent(SkBlendMode blendMode) {
397   SkBlendModeCoeff src, dst;
398   if (!SkBlendMode_AsCoeff(blendMode, &src, &dst)) {
399     return false;
400   }
401
402   // True when src coefficient is equal to 0 (when dst = (0,0,0,0))
403   return src == SkBlendModeCoeff::kZero || src == SkBlendModeCoeff::kDA ||
404          src == SkBlendModeCoeff::kDC;
405 }
406
407 SkYUVAInfo::Subsampling SubsamplingFromTextureSizes(gfx::Size ya_size,
408                                                     gfx::Size uv_size) {
409   if (uv_size.height() == ya_size.height()) {
410     if (uv_size.width() == ya_size.width())
411       return SkYUVAInfo::Subsampling::k444;
412     if (uv_size.width() == (ya_size.width() + 1) / 2)
413       return SkYUVAInfo::Subsampling::k422;
414     if (uv_size.width() == (ya_size.width() + 3) / 4)
415       return SkYUVAInfo::Subsampling::k411;
416   } else if (uv_size.height() == (ya_size.height() + 1) / 2) {
417     if (uv_size.width() == ya_size.width())
418       return SkYUVAInfo::Subsampling::k440;
419     if (uv_size.width() == (ya_size.width() + 1) / 2)
420       return SkYUVAInfo::Subsampling::k420;
421     if (uv_size.width() == (ya_size.width() + 3) / 4)
422       return SkYUVAInfo::Subsampling::k410;
423   }
424   return SkYUVAInfo::Subsampling::kUnknown;
425 }
426
427 }  // namespace
428
429 // chrome style prevents this from going in skia_renderer.h, but since it
430 // uses absl::optional, the style also requires it to have a declared ctor
431 SkiaRenderer::BatchedQuadState::BatchedQuadState() = default;
432
433 // State calculated from a DrawQuad and current renderer state, that is common
434 // to all DrawQuad rendering.
435 struct SkiaRenderer::DrawQuadParams {
436   DrawQuadParams() = default;
437   DrawQuadParams(const gfx::Transform& cdt,
438                  const gfx::RectF& rect,
439                  const gfx::RectF& visible_rect,
440                  unsigned aa_flags,
441                  SkBlendMode blend_mode,
442                  float opacity,
443                  const SkSamplingOptions& sampling,
444                  const gfx::QuadF* draw_region);
445
446   // target_to_device_transform * quad_to_target_transform normally, or
447   // quad_to_target_transform if the remaining device transform is held in the
448   // DrawRPDQParams for a bypass quad.
449   gfx::Transform content_device_transform;
450   // The DrawQuad's rect.
451   gfx::RectF rect;
452   // The DrawQuad's visible_rect, possibly explicitly clipped by the scissor
453   gfx::RectF visible_rect;
454   // Initialized to the visible_rect, relevant quad types should updated based
455   // on their specialized properties.
456   gfx::RectF vis_tex_coords;
457   // SkCanvas::QuadAAFlags, already taking into account settings
458   // (but not certain quad type's force_antialias_off bit)
459   unsigned aa_flags;
460   // Final blend mode to use, respecting quad settings + opacity optimizations
461   SkBlendMode blend_mode;
462   // Final opacity of quad
463   float opacity;
464   // Resolved sampling from quad settings
465   SkSamplingOptions sampling;
466   // Optional restricted draw geometry, will point to a length 4 SkPoint array
467   // with its points in CW order matching Skia's vertex/edge expectations.
468   absl::optional<SkDrawRegion> draw_region;
469   // Optional mask filter info that may contain rounded corner clip and/or a
470   // gradient mask to apply. If present, rounded corner clip will have been
471   // transformed to device space and ShouldApplyRoundedCorner returns true. If
472   // present, gradient mask will have been transformed to device space and
473   // ShouldApplyGradientMask returns true.
474   absl::optional<gfx::MaskFilterInfo> mask_filter_info;
475   // Optional device space clip to apply. If present, it is equal to the current
476   // |scissor_rect_| of the renderer.
477   absl::optional<gfx::Rect> scissor_rect;
478
479   SkPaint paint(sk_sp<SkColorFilter> color_filter) const {
480     SkPaint p;
481     if (color_filter) {
482       p.setColorFilter(color_filter);
483     }
484     p.setBlendMode(blend_mode);
485     p.setAlphaf(opacity);
486     p.setAntiAlias(aa_flags != SkCanvas::kNone_QuadAAFlags);
487     return p;
488   }
489
490   SkPath draw_region_in_path() const {
491     if (draw_region) {
492       return SkPath::Polygon(draw_region->points,
493                              std::size(draw_region->points),
494                              /*isClosed=*/true);
495     }
496     return SkPath();
497   }
498
499   void ApplyScissor(const SkiaRenderer* renderer,
500                     const DrawQuad* quad,
501                     const absl::optional<gfx::Rect>& scissor_to_apply);
502 };
503
504 SkiaRenderer::DrawQuadParams::DrawQuadParams(const gfx::Transform& cdt,
505                                              const gfx::RectF& rect,
506                                              const gfx::RectF& visible_rect,
507                                              unsigned aa_flags,
508                                              SkBlendMode blend_mode,
509                                              float opacity,
510                                              const SkSamplingOptions& sampling,
511                                              const gfx::QuadF* draw_region)
512     : content_device_transform(cdt),
513       rect(rect),
514       visible_rect(visible_rect),
515       vis_tex_coords(visible_rect),
516       aa_flags(aa_flags),
517       blend_mode(blend_mode),
518       opacity(opacity),
519       sampling(sampling) {
520   if (draw_region) {
521     this->draw_region.emplace(*draw_region);
522   }
523 }
524
525 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
526 struct SkiaRenderer::RenderPassOverlayParams {
527   AggregatedRenderPassId render_pass_id;
528   RenderPassBacking render_pass_backing;
529   AggregatedRenderPassDrawQuad rpdq;
530   SharedQuadState shared_quad_state;
531   cc::FilterOperations filters;
532   cc::FilterOperations backdrop_filters;
533 };
534 #endif
535
536 enum class SkiaRenderer::BypassMode {
537   // The RenderPass's contents' blendmode would have made a transparent black
538   // image and the RenderPass's own blend mode does not effect transparent black
539   kSkip,
540   // The renderPass's contents' creates a transparent image, but the
541   // RenderPass's own blend mode must still process the transparent pixels (e.g.
542   // certain filters affect transparent black).
543   kDrawTransparentQuad,
544   // Can draw the bypass quad with the modified parameters
545   kDrawBypassQuad
546 };
547
548 // Scoped helper class for building SkImage from resource id.
549 class SkiaRenderer::ScopedSkImageBuilder {
550  public:
551   ScopedSkImageBuilder(SkiaRenderer* skia_renderer,
552                        ResourceId resource_id,
553                        bool maybe_concurrent_reads,
554                        SkAlphaType alpha_type = kPremul_SkAlphaType,
555                        GrSurfaceOrigin origin = kTopLeft_GrSurfaceOrigin,
556                        sk_sp<SkColorSpace> override_color_space = nullptr,
557                        bool raw_draw_if_possible = false);
558
559   ScopedSkImageBuilder(const ScopedSkImageBuilder&) = delete;
560   ScopedSkImageBuilder& operator=(const ScopedSkImageBuilder&) = delete;
561
562   ~ScopedSkImageBuilder() = default;
563
564   const SkImage* sk_image() const { return sk_image_.get(); }
565   const cc::PaintOpBuffer* paint_op_buffer() const { return paint_op_buffer_; }
566   const absl::optional<SkColor4f>& clear_color() const { return clear_color_; }
567
568  private:
569   sk_sp<SkImage> sk_image_;
570   raw_ptr<const cc::PaintOpBuffer> paint_op_buffer_ = nullptr;
571   absl::optional<SkColor4f> clear_color_;
572 };
573
574 SkiaRenderer::ScopedSkImageBuilder::ScopedSkImageBuilder(
575     SkiaRenderer* skia_renderer,
576     ResourceId resource_id,
577     bool maybe_concurrent_reads,
578     SkAlphaType alpha_type,
579     GrSurfaceOrigin origin,
580     sk_sp<SkColorSpace> override_color_space,
581     bool raw_draw_if_possible) {
582   if (!resource_id)
583     return;
584   auto* resource_provider = skia_renderer->resource_provider();
585   DCHECK(IsTextureResource(resource_provider, resource_id));
586
587   auto* image_context = skia_renderer->lock_set_for_external_use_->LockResource(
588       resource_id, maybe_concurrent_reads, raw_draw_if_possible);
589
590   // |ImageContext::image| provides thread safety: (a) this ImageContext is
591   // only accessed by GPU thread after |image| is set and (b) the fields of
592   // ImageContext that are accessed by both compositor and GPU thread are no
593   // longer modified after |image| is set.
594   if (!image_context->has_image()) {
595     image_context->set_alpha_type(alpha_type);
596     image_context->set_origin(origin);
597   }
598
599   // We need the original TransferableResource.color_space for YUV => RGB
600   // conversion.
601   skia_renderer->skia_output_surface_->MakePromiseSkImage(
602       image_context, resource_provider->GetOverlayColorSpace(resource_id));
603   paint_op_buffer_ = image_context->paint_op_buffer();
604   clear_color_ = image_context->clear_color();
605   sk_image_ = image_context->image();
606   LOG_IF(ERROR, !image_context->has_image() && !paint_op_buffer_)
607       << "Failed to create the promise sk image or get paint ops.";
608
609   if (sk_image_ && override_color_space) {
610     sk_image_ = sk_image_->reinterpretColorSpace(override_color_space);
611   }
612 }
613
614 class SkiaRenderer::ScopedYUVSkImageBuilder {
615  public:
616   ScopedYUVSkImageBuilder(SkiaRenderer* skia_renderer,
617                           const YUVVideoDrawQuad* quad,
618                           sk_sp<SkColorSpace> dst_color_space) {
619     DCHECK(IsTextureResource(skia_renderer->resource_provider(),
620                              quad->y_plane_resource_id()));
621     DCHECK(IsTextureResource(skia_renderer->resource_provider(),
622                              quad->u_plane_resource_id()));
623     DCHECK(IsTextureResource(skia_renderer->resource_provider(),
624                              quad->v_plane_resource_id()));
625     DCHECK(quad->a_plane_resource_id() == kInvalidResourceId ||
626            IsTextureResource(skia_renderer->resource_provider(),
627                              quad->a_plane_resource_id()));
628
629     // The image is always either NV12 or I420, possibly with a separate alpha
630     // plane.
631     SkYUVAInfo::PlaneConfig plane_config;
632     if (quad->a_plane_resource_id() == kInvalidResourceId) {
633       plane_config = quad->u_plane_resource_id() == quad->v_plane_resource_id()
634                          ? SkYUVAInfo::PlaneConfig::kY_UV
635                          : SkYUVAInfo::PlaneConfig::kY_U_V;
636     } else {
637       plane_config = quad->u_plane_resource_id() == quad->v_plane_resource_id()
638                          ? SkYUVAInfo::PlaneConfig::kY_UV_A
639                          : SkYUVAInfo::PlaneConfig::kY_U_V_A;
640     }
641     SkYUVAInfo::Subsampling subsampling =
642         SubsamplingFromTextureSizes(quad->ya_tex_size(), quad->uv_tex_size());
643     const int number_of_textures = SkYUVAInfo::NumPlanes(plane_config);
644     std::vector<ExternalUseClient::ImageContext*> contexts;
645     contexts.reserve(number_of_textures);
646     // Skia API ignores the color space information on the individual planes.
647     // Dropping them here avoids some LOG spam.
648     auto* y_context = skia_renderer->lock_set_for_external_use_->LockResource(
649         quad->y_plane_resource_id(), /*maybe_concurrent_reads=*/true);
650     contexts.push_back(std::move(y_context));
651     auto* u_context = skia_renderer->lock_set_for_external_use_->LockResource(
652         quad->u_plane_resource_id(), /*maybe_concurrent_reads=*/true);
653     contexts.push_back(std::move(u_context));
654     if (plane_config == SkYUVAInfo::PlaneConfig::kY_U_V ||
655         plane_config == SkYUVAInfo::PlaneConfig::kY_U_V_A) {
656       auto* v_context = skia_renderer->lock_set_for_external_use_->LockResource(
657           quad->v_plane_resource_id(), /*maybe_concurrent_reads=*/true);
658       contexts.push_back(std::move(v_context));
659     }
660
661     if (SkYUVAInfo::HasAlpha(plane_config)) {
662       auto* a_context = skia_renderer->lock_set_for_external_use_->LockResource(
663           quad->a_plane_resource_id(), /*maybe_concurrent_reads=*/true);
664       contexts.push_back(std::move(a_context));
665     }
666
667     // Note: YUV to RGB and color conversion is handled by a color filter.
668     sk_image_ = skia_renderer->skia_output_surface_->MakePromiseSkImageFromYUV(
669         std::move(contexts), dst_color_space, plane_config, subsampling);
670     LOG_IF(ERROR, !sk_image_) << "Failed to create the promise sk yuva image.";
671   }
672
673   ScopedYUVSkImageBuilder(const ScopedYUVSkImageBuilder&) = delete;
674   ScopedYUVSkImageBuilder& operator=(const ScopedYUVSkImageBuilder&) = delete;
675
676   ~ScopedYUVSkImageBuilder() = default;
677
678   const SkImage* sk_image() const { return sk_image_.get(); }
679
680  private:
681   sk_sp<SkImage> sk_image_;
682 };
683
684 // Parameters needed to draw a CompositorRenderPassDrawQuad.
685 struct SkiaRenderer::DrawRPDQParams {
686   struct BypassGeometry {
687     // The additional matrix to concatenate to the SkCanvas after image filters
688     // have been configured so that the DrawQuadParams geometry is properly
689     // mapped (i.e. when set, |visible_rect| and |draw_region| must be
690     // pre-transformed by this before |content_device_transform|).
691     SkMatrix transform;
692
693     // Clipping in bypassed render pass coordinate space. This can come from
694     // RenderPassDrawQuad::visible_rect and bypass quads clip_rect.
695     gfx::RectF clip_rect;
696   };
697
698   class MaskShader {
699    public:
700     MaskShader(ResourceId mask_resource_id, SkMatrix mask_to_quad_matrix)
701         : mask_resource_id_(mask_resource_id),
702           mask_to_quad_matrix_(mask_to_quad_matrix) {
703       CHECK(!mask_resource_id_.is_null());
704     }
705
706     // Get the resolved mask image and calculated transform matrix baked into an
707     // SkShader
708     sk_sp<SkShader> GetOrCreateSkShader(SkiaRenderer* renderer) const;
709
710    private:
711     ResourceId mask_resource_id_;
712
713     // Map mask rect to full quad rect so that mask coordinates don't change
714     // with clipping.
715     SkMatrix mask_to_quad_matrix_;
716
717     mutable sk_sp<SkShader> sk_shader_ = nullptr;
718   };
719
720   DrawRPDQParams() : filter_bounds(SkRect::MakeEmpty()) {}
721
722   explicit DrawRPDQParams(const gfx::RectF& visible_rect)
723       : filter_bounds(gfx::RectFToSkRect(visible_rect)) {}
724
725   // Root of the calculated image filter DAG to be applied to the render pass.
726   sk_sp<SkImageFilter> image_filter = nullptr;
727   // If |image_filter| can be represented as a single color filter, this will
728   // be that filter. |image_filter| will still be non-null.
729   sk_sp<SkColorFilter> color_filter = nullptr;
730   // Root of the calculated backdrop filter DAG to be applied to the render pass
731   // Backdrop filtered content must be clipped to |backdrop_filter_bounds| and
732   // the DrawQuad's rect (or draw_region if BSP-clipped).
733   sk_sp<SkImageFilter> backdrop_filter = nullptr;
734   // If present, the mask image, which can be applied using SkCanvas::clipShader
735   // in the RPDQ's coord space.
736   absl::optional<MaskShader> mask_shader;
737   // Backdrop border box for the render pass, to clip backdrop-filtered content
738   // (but not the rest of the RPDQ itself).
739   absl::optional<SkRRect> backdrop_filter_bounds;
740   // The content space bounds that includes any filtered extents. If empty,
741   // the draw can be skipped.It may represent fractional pixel coverage.
742   SkRect filter_bounds;
743
744   // Multiplier used for downscaling backdrop filter.
745   float backdrop_filter_quality = 1.0f;
746
747   // Geometry from the bypassed RenderPassDrawQuad.
748   absl::optional<BypassGeometry> bypass_geometry;
749
750   // True when there is an |image_filter| and it's not equivalent to
751   // |color_filter|.
752   bool has_complex_image_filter() const {
753     return image_filter && !color_filter;
754   }
755
756   // True if the RenderPass's output rect would clip the visible contents that
757   // are bypassing the renderpass' offscreen buffer.
758   bool needs_bypass_clip(const gfx::RectF& content_rect) const {
759     if (!bypass_geometry) {
760       return false;
761     }
762
763     SkRect content_bounds =
764         bypass_geometry->transform.mapRect(gfx::RectFToSkRect(content_rect));
765     return !bypass_geometry->clip_rect.Contains(
766         gfx::SkRectToRectF(content_bounds));
767   }
768
769   // Returns either |params->visible_rect| or |bypass_geometry->clip_rect|,
770   // which corresponds to the visible_rect of the originating RPDQ.
771   SkRect GetContentBounds(const DrawQuadParams* params) const {
772     return gfx::RectFToSkRect(bypass_geometry ? bypass_geometry->clip_rect
773                                               : params->visible_rect);
774   }
775
776   // Sets a clip on the canvas to restrict the size of the Skia layer that holds
777   // the backdrop filtered content to the size of the DrawQuad. When possible
778   // this is an exact clip to reduce operations performed within the backdrop
779   // layer; otherwise it's conservative to constrain size without impacting
780   // visuals.
781   void SetBackdropFilterClip(SkCanvas* canvas,
782                              const DrawQuadParams* params) const;
783
784   // Erases backdrop filtered content outside of the DrawQuad and backdrop
785   // filter bounds rrect within the backdrop layer. This is a no-op if exact
786   // clipping was used in SetBackdropFilterClip to achieve the same effect.
787   // Otherwise, this is necessary to limit the backdrop content without
788   // impacting the DrawQuad or regular filter output.
789   void ClearOutsideBackdropBounds(SkCanvas* canvas,
790                                   const DrawQuadParams* params) const;
791 };
792
793 sk_sp<SkShader> SkiaRenderer::DrawRPDQParams::MaskShader::GetOrCreateSkShader(
794     SkiaRenderer* renderer) const {
795   if (sk_shader_) {
796     return sk_shader_;
797   }
798
799   ScopedSkImageBuilder mask_image_builder(renderer, mask_resource_id_,
800                                           /*maybe_concurrent_reads=*/false);
801   const SkImage* mask_image = mask_image_builder.sk_image();
802   CHECK(mask_image);
803   sk_shader_ = mask_image->makeShader(
804       SkTileMode::kClamp, SkTileMode::kClamp,
805       SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone),
806       &mask_to_quad_matrix_);
807
808   return sk_shader_;
809 }
810
811 void SkiaRenderer::DrawRPDQParams::SetBackdropFilterClip(
812     SkCanvas* canvas,
813     const DrawQuadParams* params) const {
814   if (!backdrop_filter) {
815     return;  // No clipping necessary
816   }
817
818   const bool aa = params->aa_flags != SkCanvas::kNone_QuadAAFlags;
819   if (backdrop_filter_bounds) {
820     // The final backdrop content is complex, so excess filter values will be
821     // erased from within the layer. Only clip to the aggregate bounds.
822     canvas->clipRect(filter_bounds, aa);
823   } else {
824     // The backdrop content is restricted to the draw region and visible_rect
825     // (or bypass clip_rect, which corresponds to the visible_rect of the quad
826     // that had the filter on it).
827     canvas->clipRect(GetContentBounds(params), aa);
828   }
829   if (params->draw_region) {
830     SkPath clip_path = params->draw_region_in_path();
831     if (bypass_geometry) {
832       clip_path.transform(bypass_geometry->transform);
833     }
834     canvas->clipPath(clip_path, aa);
835   }
836 }
837
838 void SkiaRenderer::DrawRPDQParams::ClearOutsideBackdropBounds(
839     SkCanvas* canvas,
840     const DrawQuadParams* params) const {
841   if (!backdrop_filter || !backdrop_filter_bounds) {
842     return;  // Nothing to clear within the layer
843   }
844
845   // Must erase pixels not in the intersection of the backdrop_filter_bounds,
846   // visible_rect, and any draw_region. This is the union of the inverse fills
847   // of those shapes, which can be accomplished most efficiently by clipping
848   // the shape with the kDifference op and then clearing the canvas, per shape
849   const bool aa = params->aa_flags != SkCanvas::kNone_QuadAAFlags;
850
851   canvas->save();
852   canvas->clipRRect(*backdrop_filter_bounds, SkClipOp::kDifference, aa);
853   canvas->clear(SK_ColorTRANSPARENT);
854   canvas->restore();
855
856   if (params->draw_region) {
857     canvas->save();
858     canvas->concat(bypass_geometry->transform);
859     canvas->clipPath(params->draw_region_in_path(), SkClipOp::kDifference, aa);
860     canvas->clear(SK_ColorTRANSPARENT);
861     canvas->restore();
862   } else {
863     SkRect content = GetContentBounds(params);
864     if (!content.contains(backdrop_filter_bounds->rect())) {
865       // If the |draw_region| is defined, it's already a subset of |rect|, so
866       // we don't have to clear both. Similarly, if |backdrop_filter_bounds|
867       // is contained within the quad, the first clear was sufficient.
868       // Otherwise, have some excess backdrop content that must still be
869       // erased.
870       canvas->save();
871       canvas->clipRect(content, SkClipOp::kDifference, aa);
872       canvas->clear(SK_ColorTRANSPARENT);
873       canvas->restore();
874     }
875   }
876 }
877
878 // A read lock based fence that is signaled after gpu commands are completed
879 // meaning the resource has been read.
880 // TODO(fangzhoug): Move this out of this file s.t. it can be referenced in
881 // display_resource_provider_skia_unittest.cc.
882 class SkiaRenderer::FrameResourceGpuCommandsCompletedFence
883     : public ResourceFence {
884  public:
885   explicit FrameResourceGpuCommandsCompletedFence(
886       DisplayResourceProviderSkia* resource_provider)
887       : ResourceFence(resource_provider) {}
888   FrameResourceGpuCommandsCompletedFence() = delete;
889   FrameResourceGpuCommandsCompletedFence(
890       const FrameResourceGpuCommandsCompletedFence&) = delete;
891   FrameResourceGpuCommandsCompletedFence& operator=(
892       const FrameResourceGpuCommandsCompletedFence&) = delete;
893
894   // ResourceFence implementation.
895   bool HasPassed() override { return passed_; }
896   gfx::GpuFenceHandle GetGpuFenceHandle() override {
897     NOTREACHED();
898     return gfx::GpuFenceHandle();
899   }
900
901   void Signal() {
902     passed_ = true;
903     FencePassed();
904   }
905
906  private:
907   ~FrameResourceGpuCommandsCompletedFence() override = default;
908
909   bool passed_ = false;
910 };
911
912 // FrameResourceFence that gets a ReleaseFence which is later set to returned
913 // resources.
914 // TODO(fangzhoug): Move this out of this file s.t. it can be referenced in
915 // display_resource_provider_skia_unittest.cc.
916 class SkiaRenderer::FrameResourceReleaseFence : public ResourceFence {
917  public:
918   explicit FrameResourceReleaseFence(
919       DisplayResourceProviderSkia* resource_provider)
920       : ResourceFence(resource_provider) {}
921   FrameResourceReleaseFence() = delete;
922   FrameResourceReleaseFence(const FrameResourceReleaseFence&) = delete;
923   FrameResourceReleaseFence& operator=(const FrameResourceReleaseFence&) =
924       delete;
925
926   // ResourceFence implementation:
927   bool HasPassed() override { return release_fence_.has_value(); }
928   gfx::GpuFenceHandle GetGpuFenceHandle() override {
929     return HasPassed() ? release_fence_.value().Clone() : gfx::GpuFenceHandle();
930   }
931
932   void SetReleaseFenceCallback(gfx::GpuFenceHandle release_fence) {
933     release_fence_ = std::move(release_fence);
934     FencePassed();
935   }
936
937  private:
938   ~FrameResourceReleaseFence() override = default;
939
940   // This is made optional so that the value is set after
941   // SetReleaseFenceCallback is called. Otherwise, there is no way to know if
942   // the fence has been set and a null handle is a "valid" handle.
943   absl::optional<gfx::GpuFenceHandle> release_fence_;
944 };
945
946 SkiaRenderer::SkiaRenderer(const RendererSettings* settings,
947                            const DebugRendererSettings* debug_settings,
948                            OutputSurface* output_surface,
949                            DisplayResourceProviderSkia* resource_provider,
950                            OverlayProcessorInterface* overlay_processor,
951                            SkiaOutputSurface* skia_output_surface)
952     : DirectRenderer(settings,
953                      debug_settings,
954                      output_surface,
955                      resource_provider,
956                      overlay_processor),
957       skia_output_surface_(skia_output_surface),
958 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
959       can_skip_render_pass_overlay_(
960           base::FeatureList::IsEnabled(features::kCanSkipRenderPassOverlay)),
961 #endif
962       is_using_raw_draw_(features::IsUsingRawDraw()) {
963   DCHECK(skia_output_surface_);
964   lock_set_for_external_use_.emplace(resource_provider, skia_output_surface_);
965
966   // There can be different synchronization types requested for different
967   // resources. Some of them may require SyncToken, others - ReadLockFence, and
968   // others may need ReleaseFence. SyncTokens are set when the output surface
969   // is flushed and external resources are released. However, other resources
970   // require additional setup, which helps to handle that.
971   current_gpu_commands_completed_fence_ =
972       base::MakeRefCounted<FrameResourceGpuCommandsCompletedFence>(
973           resource_provider);
974   current_release_fence_ =
975       base::MakeRefCounted<FrameResourceReleaseFence>(resource_provider);
976   this->resource_provider()->SetGpuCommandsCompletedFence(
977       current_gpu_commands_completed_fence_.get());
978   this->resource_provider()->SetReleaseFence(current_release_fence_.get());
979
980 #if BUILDFLAG(IS_WIN)
981   // Windows does not use buffer queue because a swap chain and DComp surface
982   // internally manage buffers and cross-frame damage. It instead lets the
983   // renderer allocate the root surface like a normal render pass backing.
984 #else
985   if (output_surface->capabilities().renderer_allocates_images) {
986     // When using dynamic frame buffer allocation we'll start with 0 buffers and
987     // let EnsureMinNumberOfBuffers() increase it later.
988     size_t number_of_buffers =
989         output_surface->capabilities().supports_dynamic_frame_buffer_allocation
990             ? 0
991             : output_surface->capabilities().number_of_buffers;
992     buffer_queue_ = std::make_unique<BufferQueue>(
993         skia_output_surface_, skia_output_surface_->GetSurfaceHandle(),
994         number_of_buffers);
995   }
996 #endif
997 }
998
999 SkiaRenderer::~SkiaRenderer() = default;
1000
1001 bool SkiaRenderer::CanPartialSwap() {
1002   return output_surface_->capabilities().supports_post_sub_buffer;
1003 }
1004
1005 void SkiaRenderer::BeginDrawingFrame() {
1006   TRACE_EVENT0("viz", "SkiaRenderer::BeginDrawingFrame");
1007
1008   DCHECK(!current_gpu_commands_completed_fence_->was_set());
1009   DCHECK(!current_release_fence_->was_set());
1010 }
1011
1012 void SkiaRenderer::FinishDrawingFrame() {
1013   TRACE_EVENT0("viz", "SkiaRenderer::FinishDrawingFrame");
1014   current_canvas_ = nullptr;
1015
1016   swap_buffer_rect_ = current_frame()->root_damage_rect;
1017
1018 #if BUILDFLAG(IS_OZONE)
1019   MaybeScheduleBackgroundImage(current_frame()->overlay_list);
1020 #endif  // BUILDFLAG(IS_OZONE)
1021
1022   // TODO(weiliangc): Remove this once OverlayProcessor schedules overlays.
1023   if (current_frame()->output_surface_plane) {
1024     auto& surface_plane = current_frame()->output_surface_plane.value();
1025
1026     if (!output_surface_->capabilities().renderer_allocates_images) {
1027       skia_output_surface_->ScheduleOutputSurfaceAsOverlay(surface_plane);
1028     } else {
1029       auto root_pass_backing =
1030           render_pass_backings_.find(current_frame()->root_render_pass->id);
1031       // The root pass backing should always exist.
1032       DCHECK(root_pass_backing != render_pass_backings_.end());
1033
1034       OverlayCandidate surface_candidate;
1035       surface_candidate.mailbox = root_pass_backing->second.mailbox;
1036       surface_candidate.is_root_render_pass = true;
1037 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN)
1038       surface_candidate.transform = gfx::Transform();
1039 #else
1040       surface_candidate.transform = surface_plane.transform;
1041 #endif
1042       surface_candidate.display_rect = surface_plane.display_rect;
1043       surface_candidate.uv_rect = surface_plane.uv_rect;
1044       surface_candidate.resource_size_in_pixels = surface_plane.resource_size;
1045       surface_candidate.format = surface_plane.format;
1046       surface_candidate.color_space = surface_plane.color_space;
1047       if (current_frame()->root_render_pass->content_color_usage ==
1048           gfx::ContentColorUsage::kHDR) {
1049         surface_candidate.hdr_metadata.extended_range.emplace();
1050         // TODO(https://crbug.com/1430768): Track the actual brightness of the
1051         // content. For now, assume that all HDR content is 1,000 nits.
1052         surface_candidate.hdr_metadata.extended_range->desired_headroom =
1053             gfx::HdrMetadataExtendedRange::kDefaultHdrHeadroom;
1054       }
1055       surface_candidate.is_opaque = !surface_plane.enable_blending;
1056       surface_candidate.opacity = surface_plane.opacity;
1057       surface_candidate.priority_hint = surface_plane.priority_hint;
1058       surface_candidate.rounded_corners = surface_plane.rounded_corners;
1059       surface_candidate.damage_rect =
1060           use_partial_swap_ ? gfx::RectF(swap_buffer_rect_)
1061                             : gfx::RectF(surface_plane.resource_size);
1062       current_frame()->overlay_list.insert(
1063           current_frame()->overlay_list.begin(), surface_candidate);
1064     }
1065   } else {
1066 #if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_APPLE)
1067     // If there's no primary plane on these platforms it mean's we're delegating
1068     // to the system compositor, and don't need the buffers anymore. If those
1069     // buffers are managed by buffer_queue_, we can tell it to destroy them.
1070     // They'll be recreated when we need them again when GetCurrentBuffer() is
1071     // called.
1072     if (buffer_queue_) {
1073       buffer_queue_->DestroyBuffers();
1074     }
1075 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_APPLE)
1076   }
1077
1078   ScheduleOverlays();
1079   debug_tint_modulate_count_++;
1080 }
1081
1082 void SkiaRenderer::SwapBuffers(SwapFrameData swap_frame_data) {
1083   DCHECK(visible_);
1084   DCHECK(output_surface_->capabilities().supports_viewporter ||
1085          viewport_size_for_swap_buffers() == surface_size_for_swap_buffers());
1086   TRACE_EVENT0("viz,benchmark", "SkiaRenderer::SwapBuffers");
1087
1088   OutputSurfaceFrame output_frame;
1089   output_frame.latency_info = std::move(swap_frame_data.latency_info);
1090   output_frame.top_controls_visible_height_changed =
1091       swap_frame_data.top_controls_visible_height_changed;
1092 #if BUILDFLAG(IS_EFL)
1093   output_frame.can_skip_flush = swap_frame_data.can_skip_flush;
1094 #endif
1095   output_frame.choreographer_vsync_id = swap_frame_data.choreographer_vsync_id;
1096   output_frame.size = viewport_size_for_swap_buffers();
1097   output_frame.data.seq = swap_frame_data.seq;
1098   output_frame.data.swap_trace_id = swap_frame_data.swap_trace_id;
1099   if (use_partial_swap_) {
1100     swap_buffer_rect_.Intersect(gfx::Rect(surface_size_for_swap_buffers()));
1101     output_frame.sub_buffer_rect = swap_buffer_rect_;
1102   }
1103   if (delegated_ink_handler_ && !UsingSkiaForDelegatedInk()) {
1104     output_frame.delegated_ink_metadata =
1105         delegated_ink_handler_->TakeMetadata();
1106   }
1107 #if BUILDFLAG(IS_APPLE)
1108   output_frame.ca_layer_error_code = swap_frame_data.ca_layer_error_code;
1109 #endif
1110
1111   if (buffer_queue_) {
1112     gfx::Rect damage_rect = output_frame.sub_buffer_rect.value_or(
1113         gfx::Rect(surface_size_for_swap_buffers()));
1114     buffer_queue_->SwapBuffers(damage_rect);
1115   }
1116
1117   skia_output_surface_->SwapBuffers(std::move(output_frame));
1118   swap_buffer_rect_ = gfx::Rect();
1119
1120   FlushOutputSurface();
1121
1122 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
1123   // Delete render pass overlay backings from the previous frame that will not
1124   // be used again.
1125   for (auto& overlay : available_render_pass_overlay_backings_) {
1126     skia_output_surface_->DestroySharedImage(
1127         overlay.render_pass_backing.mailbox);
1128   }
1129   available_render_pass_overlay_backings_.clear();
1130 #endif  // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
1131
1132 #if BUILDFLAG(IS_OZONE)
1133   // Clear cached solid color buffers that weren't reused.
1134   base::EraseIf(solid_color_buffers_, [this](auto entry) {
1135     SolidColorBuffer& color_buffer = entry.second;
1136     if (!color_buffer.use_count) {
1137       skia_output_surface_->DestroySharedImage(color_buffer.mailbox);
1138       return true;
1139     }
1140     return false;
1141   });
1142 #endif  // BUILDFLAG(IS_OZONE)
1143 }
1144
1145 void SkiaRenderer::SwapBuffersSkipped() {
1146   gfx::Rect root_pass_damage_rect = gfx::Rect(surface_size_for_swap_buffers());
1147   if (use_partial_swap_)
1148     root_pass_damage_rect.Intersect(swap_buffer_rect_);
1149
1150 #if BUILDFLAG(IS_OZONE)
1151   MaybeDecrementSolidColorBuffers(pending_overlay_locks_.back());
1152 #endif  // BUILDFLAG(IS_OZONE)
1153
1154   pending_overlay_locks_.pop_back();
1155   skia_output_surface_->SwapBuffersSkipped(root_pass_damage_rect);
1156   if (buffer_queue_) {
1157     buffer_queue_->SwapBuffersSkipped(root_pass_damage_rect);
1158   }
1159   swap_buffer_rect_ = gfx::Rect();
1160
1161   FlushOutputSurface();
1162 }
1163
1164 void SkiaRenderer::SwapBuffersComplete(
1165     const gpu::SwapBuffersCompleteParams& params,
1166     gfx::GpuFenceHandle release_fence) {
1167   auto& read_lock_release_fence_overlay_locks =
1168       read_lock_release_fence_overlay_locks_.emplace_back();
1169   auto read_fence_lock_iter = committed_overlay_locks_.end();
1170
1171   if (buffer_queue_) {
1172     if (params.swap_response.result ==
1173         gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS) {
1174       buffer_queue_->RecreateBuffers();
1175     }
1176     buffer_queue_->SwapBuffersComplete();
1177   }
1178   if (!release_fence.is_null()) {
1179     // Set release fences to return overlay resources for last frame.
1180     for (auto& lock : committed_overlay_locks_) {
1181       lock.SetReleaseFence(release_fence.Clone());
1182     }
1183     // Find all locks that have a read-lock fence associated with them and move
1184     // them to the back of locks. If we have a release fence, it's not safe to
1185     // release them here. Release them later in BuffersPresented().
1186     read_fence_lock_iter = std::partition(
1187         committed_overlay_locks_.begin(), committed_overlay_locks_.end(),
1188         [](auto& lock) { return !lock.HasReadLockFence(); });
1189     read_lock_release_fence_overlay_locks.insert(
1190         read_lock_release_fence_overlay_locks.end(),
1191         std::make_move_iterator(read_fence_lock_iter),
1192         std::make_move_iterator(committed_overlay_locks_.end()));
1193   }
1194
1195 #if BUILDFLAG(IS_OZONE)
1196   MaybeDecrementSolidColorBuffers(committed_overlay_locks_);
1197 #endif  // BUILDFLAG(IS_OZONE)
1198
1199   // Right now, only macOS and Ozone need to return mailboxes of released
1200   // overlays, so we should not release |committed_overlay_locks_| here. The
1201   // resources in it will be released in DidReceiveReleasedOverlays() later.
1202 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
1203   for (auto lock_iter = committed_overlay_locks_.begin();
1204        lock_iter != read_fence_lock_iter; ++lock_iter) {
1205     awaiting_release_overlay_locks_.insert(std::move(*lock_iter));
1206   }
1207 #endif  // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
1208
1209   // Current pending locks should have been committed by the next time
1210   // SwapBuffers() is completed.
1211   committed_overlay_locks_.clear();
1212   std::swap(committed_overlay_locks_, pending_overlay_locks_.front());
1213   pending_overlay_locks_.pop_front();
1214 }
1215
1216 void SkiaRenderer::BuffersPresented() {
1217   if (read_lock_release_fence_overlay_locks_.empty()) {
1218     // This shouldn't be needed, but could not figure out the cause in
1219     // crbug.com/1357789 and crbug.com/1372602
1220     return;
1221   }
1222   read_lock_release_fence_overlay_locks_.pop_front();
1223 }
1224
1225 void SkiaRenderer::DidReceiveReleasedOverlays(
1226     const std::vector<gpu::Mailbox>& released_overlays) {
1227   DisplayResourceProvider::ScopedBatchReturnResources returner(
1228       resource_provider_.get(), /*allow_access_to_gpu_thread=*/true);
1229
1230   // This method is only called on macOS and Ozone right now.
1231 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
1232   for (const auto& mailbox : released_overlays) {
1233     // If this mailbox is for render pass overlay, mark the released render pass
1234     // overlay backing as available to be re-used.
1235     auto it =
1236         base::ranges::find(in_flight_render_pass_overlay_backings_, mailbox,
1237                            [](const RenderPassOverlayParams& overlay) {
1238                              return overlay.render_pass_backing.mailbox;
1239                            });
1240     if (it != in_flight_render_pass_overlay_backings_.end()) {
1241       available_render_pass_overlay_backings_.push_back(*it);
1242       in_flight_render_pass_overlay_backings_.erase(it);
1243     }
1244
1245     auto iter = base::ranges::find(awaiting_release_overlay_locks_, mailbox,
1246                                    &OverlayLock::mailbox);
1247     if (iter == awaiting_release_overlay_locks_.end()) {
1248 // TODO(crbug.com/1299794): Re-enable this DCHECK on Ozone.
1249 #if !BUILDFLAG(IS_OZONE)
1250       // The released overlay should always be found as awaiting to be released.
1251       DLOG(FATAL) << "Got an unexpected mailbox";
1252 #endif  // !BUILDFLAG(IS_OZONE)
1253       continue;
1254     }
1255     awaiting_release_overlay_locks_.erase(iter);
1256   }
1257 #else
1258   NOTREACHED();
1259 #endif  // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
1260 }
1261
1262 bool SkiaRenderer::FlippedFramebuffer() const {
1263   // TODO(weiliangc): Make sure flipped correctly for Windows.
1264   // (crbug.com/644851)
1265   return false;
1266 }
1267
1268 void SkiaRenderer::EnsureScissorTestDisabled() {
1269   scissor_rect_.reset();
1270 }
1271
1272 void SkiaRenderer::BindFramebufferToOutputSurface() {
1273   current_canvas_ = skia_output_surface_->BeginPaintCurrentFrame();
1274   if (debug_settings_->show_overdraw_feedback) {
1275     current_canvas_ = skia_output_surface_->RecordOverdrawForCurrentPaint();
1276   }
1277 }
1278
1279 void SkiaRenderer::BindFramebufferToTexture(
1280     const AggregatedRenderPassId render_pass_id) {
1281   auto iter = render_pass_backings_.find(render_pass_id);
1282   DCHECK(render_pass_backings_.end() != iter);
1283
1284   bool is_root = render_pass_id == current_frame()->root_render_pass->id;
1285   // This function is called after AllocateRenderPassResourceIfNeeded, so there
1286   // should be backing ready.
1287   RenderPassBacking& backing = iter->second;
1288   current_canvas_ = skia_output_surface_->BeginPaintRenderPass(
1289       render_pass_id, backing.size, backing.format, backing.alpha_type,
1290       backing.generate_mipmap, backing.scanout_dcomp_surface,
1291       RenderPassBackingSkColorSpace(backing),
1292       /*is_overlay=*/is_root, backing.mailbox);
1293
1294   if (is_root && debug_settings_->show_overdraw_feedback) {
1295     DCHECK(output_surface_->capabilities().renderer_allocates_images);
1296     current_canvas_ = skia_output_surface_->RecordOverdrawForCurrentPaint();
1297   }
1298 }
1299
1300 void SkiaRenderer::SetScissorTestRect(const gfx::Rect& scissor_rect) {
1301   scissor_rect_ = absl::optional<gfx::Rect>(scissor_rect);
1302 }
1303
1304 void SkiaRenderer::ClearCanvas(SkColor4f color) {
1305   if (!current_canvas_)
1306     return;
1307
1308   if (scissor_rect_.has_value()) {
1309     // Limit the clear with the scissor rect.
1310     SkAutoCanvasRestore autoRestore(current_canvas_, true /* do_save */);
1311     current_canvas_->clipRect(gfx::RectToSkRect(scissor_rect_.value()));
1312     current_canvas_->clear(color);
1313   } else {
1314     current_canvas_->clear(color);
1315   }
1316 }
1317
1318 void SkiaRenderer::ClearFramebuffer() {
1319   if (current_frame()->current_render_pass->has_transparent_background) {
1320     ClearCanvas(SkColors::kTransparent);
1321   } else {
1322 #if DCHECK_IS_ON() && !BUILDFLAG(IS_LINUX)
1323     // On DEBUG builds, opaque render passes are cleared to blue
1324     // to easily see regions that were not drawn on the screen.
1325     // ClearCavas() call causes slight pixel difference, so linux-ref and
1326     // linux-blink-ref bots cannot share the same baseline for webtest.
1327     // So remove this ClearCanvas() call for dcheck on build for now.
1328     // TODO(crbug.com/1330278): add it back.
1329     ClearCanvas(SkColors::kBlue);
1330 #endif
1331   }
1332 }
1333
1334 void SkiaRenderer::BeginDrawingRenderPass(
1335     bool needs_clear,
1336     const gfx::Rect& render_pass_update_rect) {
1337   TRACE_EVENT0("viz", "SkiaRenderer::BeginDrawingRenderPass");
1338
1339   if (render_pass_update_rect == current_viewport_rect_) {
1340     EnsureScissorTestDisabled();
1341   } else {
1342     SetScissorTestRect(render_pass_update_rect);
1343   }
1344
1345   if (needs_clear) {
1346     ClearFramebuffer();
1347   }
1348
1349   current_render_pass_update_rect_ = render_pass_update_rect;
1350 }
1351
1352 void SkiaRenderer::DoDrawQuad(const DrawQuad* quad,
1353                               const gfx::QuadF* draw_region) {
1354   if (!current_canvas_)
1355     return;
1356   TRACE_EVENT0("viz", "SkiaRenderer::DoDrawQuad");
1357   DrawQuadParams params =
1358       CalculateDrawQuadParams(current_frame()->target_to_device_transform,
1359                               scissor_rect_, quad, draw_region);
1360   // The outer DrawQuad will never have RPDQ params
1361   DrawQuadInternal(quad, /* rpdq */ nullptr, &params);
1362 }
1363
1364 void SkiaRenderer::DrawQuadInternal(const DrawQuad* quad,
1365                                     const DrawRPDQParams* rpdq_params,
1366                                     DrawQuadParams* params) {
1367   if (MustFlushBatchedQuads(quad, rpdq_params, *params)) {
1368     FlushBatchedQuads();
1369   }
1370
1371   if (OverlayCandidate::RequiresOverlay(quad)) {
1372     // We cannot composite this quad properly, replace it with a fallback
1373     // solid color quad.
1374     if (!batched_quads_.empty()) {
1375       FlushBatchedQuads();
1376     }
1377 #if DCHECK_IS_ON()
1378     DrawColoredQuad(SkColors::kMagenta, rpdq_params, params);
1379 #else
1380     DrawColoredQuad(SkColors::kBlack, rpdq_params, params);
1381 #endif
1382     return;
1383   }
1384
1385   switch (quad->material) {
1386     case DrawQuad::Material::kAggregatedRenderPass:
1387       DrawRenderPassQuad(AggregatedRenderPassDrawQuad::MaterialCast(quad),
1388                          rpdq_params, params);
1389       break;
1390     case DrawQuad::Material::kDebugBorder:
1391       // DebugBorders draw directly into the device space, so are not compatible
1392       // with image filters, so should never have been promoted as a bypass quad
1393       DCHECK(rpdq_params == nullptr);
1394       DrawDebugBorderQuad(DebugBorderDrawQuad::MaterialCast(quad), params);
1395       break;
1396     case DrawQuad::Material::kPictureContent:
1397       // PictureQuads represent a collection of drawn elements that are
1398       // dynamically rasterized by Skia; bypassing a RenderPass to redraw the
1399       // N elements doesn't make much sense.
1400       DCHECK(rpdq_params == nullptr);
1401       DrawPictureQuad(PictureDrawQuad::MaterialCast(quad), params);
1402       break;
1403     case DrawQuad::Material::kCompositorRenderPass:
1404       // RenderPassDrawQuads should be converted to
1405       // AggregatedRenderPassDrawQuads at this point.
1406       DrawUnsupportedQuad(quad, rpdq_params, params);
1407       NOTREACHED();
1408       break;
1409     case DrawQuad::Material::kSolidColor:
1410       DrawSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad), rpdq_params,
1411                          params);
1412       break;
1413     case DrawQuad::Material::kTextureContent:
1414       DrawTextureQuad(TextureDrawQuad::MaterialCast(quad), rpdq_params, params);
1415       break;
1416     case DrawQuad::Material::kTiledContent:
1417       DrawTileDrawQuad(TileDrawQuad::MaterialCast(quad), rpdq_params, params);
1418       break;
1419     case DrawQuad::Material::kSharedElement:
1420       DrawUnsupportedQuad(quad, rpdq_params, params);
1421       NOTREACHED();
1422       break;
1423     case DrawQuad::Material::kYuvVideoContent:
1424       DrawYUVVideoQuad(YUVVideoDrawQuad::MaterialCast(quad), rpdq_params,
1425                        params);
1426       break;
1427     case DrawQuad::Material::kInvalid:
1428       DrawUnsupportedQuad(quad, rpdq_params, params);
1429       NOTREACHED();
1430       break;
1431     case DrawQuad::Material::kVideoHole:
1432       // VideoHoleDrawQuad should only be used by Cast, and should
1433       // have been replaced by cast-specific OverlayProcessor before
1434       // reach here. In non-cast build, an untrusted render could send such
1435       // Quad and the quad would then reach here unexpectedly. Therefore
1436       // we should skip NOTREACHED() so an untrusted render is not capable
1437       // of causing a crash.
1438       DrawUnsupportedQuad(quad, rpdq_params, params);
1439       break;
1440     default:
1441       // If we've reached here, it's a new quad type that needs a
1442       // dedicated implementation
1443       DrawUnsupportedQuad(quad, rpdq_params, params);
1444       NOTREACHED();
1445       break;
1446   }
1447 }
1448
1449 void SkiaRenderer::PrepareCanvas(
1450     const absl::optional<gfx::Rect>& scissor_rect,
1451     const absl::optional<gfx::MaskFilterInfo>& mask_filter_info,
1452     const gfx::Transform* cdt) {
1453   // Scissor is applied in the device space (CTM == I) and since no changes
1454   // to the canvas persist, CTM should already be the identity
1455   DCHECK(current_canvas_->getTotalMatrix() == SkMatrix::I());
1456
1457   if (scissor_rect.has_value()) {
1458     current_canvas_->clipRect(gfx::RectToSkRect(*scissor_rect));
1459   }
1460
1461   if (mask_filter_info.has_value()) {
1462     current_canvas_->clipRRect(
1463         static_cast<SkRRect>(mask_filter_info->rounded_corner_bounds()),
1464         /*doAntiAlias=*/true);
1465
1466     if (mask_filter_info->HasGradientMask())
1467       PrepareGradient(mask_filter_info);
1468   }
1469
1470   if (cdt) {
1471     SkMatrix m = gfx::TransformToFlattenedSkMatrix(*cdt);
1472     current_canvas_->concat(m);
1473   }
1474 }
1475
1476 #define MaskColor(a) SkColorSetARGB(a, a, a, a);
1477
1478 void SkiaRenderer::PrepareGradient(
1479     const absl::optional<gfx::MaskFilterInfo>& mask_filter_info) {
1480   if (!mask_filter_info || !mask_filter_info->HasGradientMask())
1481     return;
1482
1483   const gfx::RectF rect = mask_filter_info->bounds();
1484   const absl::optional<gfx::LinearGradient>& gradient_mask =
1485       mask_filter_info->gradient_mask();
1486
1487   int16_t angle = gradient_mask->angle() % 360;
1488   if (angle < 0) angle += 360;
1489
1490   SkPoint start_end[2];
1491
1492   float rad_angle = gfx::DegToRad(static_cast<float>(angle));
1493   float s = std::sin(rad_angle);
1494   float c = std::cos(rad_angle);
1495
1496   if (angle % 180 > 90) {
1497     float start_x = rect.width() * c * c;
1498     float start_y = rect.height() - (rect.width() * s * c);
1499     float end_x = rect.height() * s * c;
1500     float end_y = rect.height() * c * c;
1501
1502     if (angle < 180) {
1503       start_end[0] = {start_x, start_y};
1504       start_end[1] = {end_x, end_y};
1505     } else {
1506       start_end[0] = {end_x, end_y};
1507       start_end[1] = {start_x, start_y};
1508     }
1509
1510   } else {
1511     float start_x = -rect.height() * s * c;
1512     float start_y = rect.height() * s * s;
1513     float end_x = rect.width() * c * c;
1514     float end_y = -rect.width() * s * c;
1515
1516     if (angle < 180) {
1517       start_end[0] = {start_x, start_y};
1518       start_end[1] = {end_x, end_y};
1519     } else {
1520       start_end[0] = {end_x, end_y};
1521       start_end[1] = {start_x, start_y};
1522     }
1523   }
1524
1525   SkScalar positions[gfx::LinearGradient::kMaxStepSize];
1526   SkColor gradient_colors[gfx::LinearGradient::kMaxStepSize];
1527
1528   size_t i = 0;
1529   for (; i < gradient_mask->step_count(); ++i) {
1530     positions[i] = gradient_mask->steps()[i].fraction;
1531     gradient_colors[i] = MaskColor(gradient_mask->steps()[i].alpha);
1532   }
1533
1534   SkPoint::Offset(start_end, /*count=*/2, rect.x(), rect.y());
1535   sk_sp<SkShader> gradient = SkGradientShader::MakeLinear(
1536       start_end, gradient_colors, positions, /*count=*/i, SkTileMode::kClamp);
1537   current_canvas_->clipShader(std::move(gradient));
1538 }
1539
1540 void SkiaRenderer::PrepareCanvasForRPDQ(const DrawRPDQParams& rpdq_params,
1541                                         DrawQuadParams* params) {
1542   // Clip before the saveLayer() so that Skia only filters the backdrop that is
1543   // necessary for the |backdrop_filter_bounds| (otherwise it will fill the
1544   // quad's SharedQuadState's |clip_rect|).
1545   rpdq_params.SetBackdropFilterClip(current_canvas_, params);
1546
1547   SkPaint layer_paint = params->paint(nullptr /* color_filter */);
1548   // The layer always consumes the opacity, but its blend mode depends on if
1549   // it was initialized with backdrop content or not.
1550   params->opacity = 1.f;
1551   if (rpdq_params.backdrop_filter) {
1552     layer_paint.setBlendMode(SkBlendMode::kSrcOver);
1553   } else {
1554     params->blend_mode = SkBlendMode::kSrcOver;
1555   }
1556
1557   if (rpdq_params.color_filter) {
1558     layer_paint.setColorFilter(rpdq_params.color_filter);
1559   } else if (rpdq_params.image_filter) {
1560     layer_paint.setImageFilter(rpdq_params.image_filter);
1561   }
1562
1563   SkRect bounds = rpdq_params.GetContentBounds(params);
1564   current_canvas_->saveLayer(SkCanvasPriv::ScaledBackdropLayer(
1565       &bounds, &layer_paint, rpdq_params.backdrop_filter.get(),
1566       rpdq_params.backdrop_filter_quality, 0));
1567
1568   // If we have backdrop filtered content (and not transparent black like with
1569   // regular render passes), we have to clear out the parts of the layer that
1570   // shouldn't show the backdrop.
1571   rpdq_params.ClearOutsideBackdropBounds(current_canvas_, params);
1572 }
1573
1574 void SkiaRenderer::PreparePaintOrCanvasForRPDQ(
1575     const DrawRPDQParams& rpdq_params,
1576     DrawQuadParams* params,
1577     SkPaint* paint) {
1578   // When the draw call accepts an SkPaint, some of the rpdq effects are more
1579   // efficient to store on the paint instead of making an explicit layer in
1580   // the canvas. But there are several requirements in order for the order of
1581   // operations to be consistent with what RenderPasses require:
1582   // 1. Backdrop filtering always requires a layer.
1583   // 2. The content bypassing the renderpass needs to be clipped before the
1584   //    image filter is evaluated.
1585   bool needs_bypass_clip = rpdq_params.needs_bypass_clip(params->visible_rect);
1586   bool needs_save_layer = false;
1587   if (rpdq_params.backdrop_filter)
1588     needs_save_layer = true;
1589   else if (rpdq_params.has_complex_image_filter())
1590     needs_save_layer = needs_bypass_clip;
1591
1592   if (rpdq_params.mask_shader) {
1593     // Apply the mask image using clipShader(), this works the same regardless
1594     // of if we need a saveLayer for image filtering since the clip is applied
1595     // at the end automatically.
1596     current_canvas_->clipShader(
1597         rpdq_params.mask_shader->GetOrCreateSkShader(this));
1598   }
1599
1600   if (needs_save_layer) {
1601     PrepareCanvasForRPDQ(rpdq_params, params);
1602     // Sync the content's paint with the updated |params|
1603     paint->setAlphaf(params->opacity);
1604     paint->setBlendMode(params->blend_mode);
1605   } else {
1606     // At this point, the image filter and/or color filter are on the paint.
1607     DCHECK(!rpdq_params.backdrop_filter);
1608     if (rpdq_params.color_filter) {
1609       // Use the color filter directly, instead of the image filter.
1610       if (paint->getColorFilter()) {
1611         paint->setColorFilter(
1612             rpdq_params.color_filter->makeComposed(paint->refColorFilter()));
1613       } else {
1614         paint->setColorFilter(rpdq_params.color_filter);
1615       }
1616       DCHECK(paint->getColorFilter());
1617     } else if (rpdq_params.image_filter) {
1618       // Store the image filter on the paint.
1619       if (params->opacity != 1.f) {
1620         // Apply opacity as the last step of image filter so it is uniform
1621         // across any overlapping content produced by the image filters.
1622         paint->setImageFilter(SkImageFilters::ColorFilter(
1623             MakeOpacityFilter(params->opacity, nullptr),
1624             rpdq_params.image_filter));
1625         paint->setAlphaf(1.f);
1626         params->opacity = 1.f;
1627       } else {
1628         paint->setImageFilter(rpdq_params.image_filter);
1629       }
1630     }
1631   }
1632
1633   // Whether or not we saved a layer, clip the bypassed RenderPass's content
1634   if (needs_bypass_clip) {
1635     current_canvas_->clipRect(
1636         gfx::RectFToSkRect(rpdq_params.bypass_geometry->clip_rect),
1637         params->aa_flags != SkCanvas::kNone_QuadAAFlags);
1638   }
1639 }
1640
1641 void SkiaRenderer::PrepareColorOrCanvasForRPDQ(
1642     const DrawRPDQParams& rpdq_params,
1643     DrawQuadParams* params,
1644     SkColor4f* content_color) {
1645   // When the draw call only takes a color and not an SkPaint, rpdq params
1646   // with just a color filter can be handled directly. Otherwise, the rpdq
1647   // params must use a layer on the canvas.
1648   bool needs_save_layer =
1649       rpdq_params.has_complex_image_filter() || rpdq_params.backdrop_filter;
1650   if (rpdq_params.mask_shader) {
1651     current_canvas_->clipShader(
1652         rpdq_params.mask_shader->GetOrCreateSkShader(this));
1653   }
1654
1655   if (needs_save_layer) {
1656     PrepareCanvasForRPDQ(rpdq_params, params);
1657   } else if (rpdq_params.color_filter) {
1658     // At this point, the RPDQ effect is at most a color filter, so it can
1659     // modify |content_color| directly.
1660     SkColorSpace* cs = nullptr;
1661     *content_color =
1662         rpdq_params.color_filter->filterColor4f(*content_color, cs, cs);
1663   }
1664
1665   // Even if the color filter image filter was applied to the content color
1666   // directly (so no explicit save layer), the draw may need to be clipped to
1667   // the output rect of the renderpass it is bypassing.
1668   if (rpdq_params.needs_bypass_clip(params->visible_rect)) {
1669     current_canvas_->clipRect(
1670         gfx::RectFToSkRect(rpdq_params.bypass_geometry->clip_rect),
1671         params->aa_flags != SkCanvas::kNone_QuadAAFlags);
1672   }
1673 }
1674
1675 SkiaRenderer::DrawQuadParams SkiaRenderer::CalculateDrawQuadParams(
1676     const gfx::AxisTransform2d& target_to_device,
1677     const absl::optional<gfx::Rect>& scissor_rect,
1678     const DrawQuad* quad,
1679     const gfx::QuadF* draw_region) const {
1680   DrawQuadParams params(
1681       quad->shared_quad_state->quad_to_target_transform, gfx::RectF(quad->rect),
1682       gfx::RectF(quad->visible_rect), SkCanvas::kNone_QuadAAFlags,
1683       quad->shared_quad_state->blend_mode, quad->shared_quad_state->opacity,
1684       GetSampling(quad), draw_region);
1685
1686   params.content_device_transform.PostConcat(target_to_device);
1687   params.content_device_transform.Flatten();
1688
1689   // Respect per-quad setting overrides as highest priority setting
1690   if (!IsAAForcedOff(quad)) {
1691     if (settings_->force_antialiasing) {
1692       // This setting makes the entire draw AA, so don't bother checking edges
1693       params.aa_flags = SkCanvas::kAll_QuadAAFlags;
1694     } else if (settings_->allow_antialiasing) {
1695       params.aa_flags = GetRectilinearEdgeFlags(quad);
1696       if (draw_region && params.aa_flags != SkCanvas::kNone_QuadAAFlags) {
1697         // Turn off interior edges' AA from the BSP splitting
1698         GetClippedEdgeFlags(quad, &params.aa_flags, &*params.draw_region);
1699       }
1700     }
1701   }
1702
1703   if (!quad->ShouldDrawWithBlending()) {
1704     // The quad layer is src-over with 1.0 opacity and its needs_blending flag
1705     // has been set to false. However, even if the layer's opacity is 1.0, the
1706     // contents may not be (e.g. png or a color with alpha).
1707     if (quad->shared_quad_state->are_contents_opaque) {
1708       // Visually, this is the same as kSrc but Skia is faster with SrcOver
1709       params.blend_mode = SkBlendMode::kSrcOver;
1710     } else {
1711       // Replaces dst contents with the new color (e.g. no blending); this is
1712       // just as fast as srcover when there's no AA, but is slow when coverage
1713       // must be taken into account.
1714       params.blend_mode = SkBlendMode::kSrc;
1715     }
1716     params.opacity = 1.f;
1717   }
1718
1719   params.ApplyScissor(this, quad, scissor_rect);
1720
1721   // Determine final rounded rect clip geometry. We transform it from target
1722   // space to window space to make batching and canvas preparation easier
1723   // (otherwise we'd have to separate those two matrices in the CDT).
1724   if (ShouldApplyRoundedCorner(quad) || ShouldApplyGradientMask(quad)) {
1725     params.mask_filter_info.emplace(quad->shared_quad_state->mask_filter_info);
1726     // Transform by the window and projection matrix to go from target to
1727     // device space, which should always be a scale+translate.
1728     params.mask_filter_info->ApplyTransform(target_to_device);
1729   }
1730
1731   return params;
1732 }
1733
1734 void SkiaRenderer::DrawQuadParams::ApplyScissor(
1735     const SkiaRenderer* renderer,
1736     const DrawQuad* quad,
1737     const absl::optional<gfx::Rect>& scissor_to_apply) {
1738   // No scissor should have been set before calling ApplyScissor
1739   DCHECK(!scissor_rect.has_value());
1740
1741   if (!scissor_to_apply) {
1742     // No scissor at all, which matches the DCHECK'ed state above
1743     return;
1744   }
1745
1746   // Assume at start that the scissor will be applied through the canvas clip,
1747   // so that this can simply return when it detects the scissor cannot be
1748   // applied explicitly to |visible_rect|.
1749   scissor_rect = *scissor_to_apply;
1750
1751   // PICTURE_CONTENT is not like the others, since it is executing a list of
1752   // draw calls into the canvas.
1753   if (quad->material == DrawQuad::Material::kPictureContent)
1754     return;
1755
1756   // DebugBorderDrawQuads draw a path so they must be explicitly clipped.
1757   if (quad->material == DrawQuad::Material::kDebugBorder)
1758     return;
1759
1760   // Intersection with scissor and a quadrilateral is not necessarily a quad,
1761   // so don't complicate things
1762   if (draw_region.has_value())
1763     return;
1764
1765   if (!Is2dScaleTranslateTransform(content_device_transform)) {
1766     return;
1767   }
1768
1769   // State check: should not have a CompositorRenderPassDrawQuad if we got here.
1770   DCHECK_NE(quad->material, DrawQuad::Material::kCompositorRenderPass);
1771   if (const auto* quad_pass =
1772           quad->DynamicCast<AggregatedRenderPassDrawQuad>()) {
1773     // If the renderpass has filters, the filters may modify the effective
1774     // geometry beyond the quad's visible_rect, so it's not safe to pre-clip.
1775     auto pass_id = quad_pass->render_pass_id;
1776     if (renderer->FiltersForPass(pass_id) ||
1777         renderer->BackdropFiltersForPass(pass_id))
1778       return;
1779   }
1780
1781   // If the intersection of the scissor and the quad's visible_rect results in
1782   // subpixel device-space geometry, do not drop the scissor. Otherwise Skia
1783   // sees an unclipped anti-aliased hairline and uses different AA methods that
1784   // would cause the rasterized result to extend beyond the scissor.
1785   gfx::RectF device_bounds = content_device_transform.MapRect(visible_rect);
1786   device_bounds.Intersect(gfx::RectF(*scissor_rect));
1787   if (device_bounds.width() < 1.0f || device_bounds.height() < 1.0f) {
1788     return;
1789   }
1790
1791   // The explicit scissor is applied in the quad's local space. If the transform
1792   // does not leave sufficient precision to round-trip the scissor rect to-from
1793   // device->local->device space, the explicitly "clipped" geometry does not
1794   // necessarily respect the original scissor.
1795   absl::optional<gfx::RectF> local_scissor =
1796       content_device_transform.InverseMapRect(gfx::RectF(*scissor_rect));
1797   if (!local_scissor) {
1798     return;
1799   }
1800   gfx::RectF remapped_scissor =
1801       content_device_transform.MapRect(*local_scissor);
1802   if (gfx::ToRoundedRect(remapped_scissor) != *scissor_rect) {
1803     return;
1804   }
1805
1806   // At this point, we've determined that we can transform the scissor rect into
1807   // the quad's local space and adjust |vis_rect|, such that when it's mapped to
1808   // device space, it will be contained in in the original scissor.
1809   // Applying the scissor explicitly means avoiding a clipRect() call and
1810   // allows more quads to be batched together in a DrawEdgeAAImageSet call
1811   float x_epsilon = kAAEpsilon / content_device_transform.rc(0, 0);
1812   float y_epsilon = kAAEpsilon / content_device_transform.rc(1, 1);
1813
1814   // The scissor is a non-AA clip, so unset the bit flag for clipped edges.
1815   if (local_scissor->x() - visible_rect.x() >= x_epsilon)
1816     aa_flags &= ~SkCanvas::kLeft_QuadAAFlag;
1817   if (local_scissor->y() - visible_rect.y() >= y_epsilon)
1818     aa_flags &= ~SkCanvas::kTop_QuadAAFlag;
1819   if (visible_rect.right() - local_scissor->right() >= x_epsilon)
1820     aa_flags &= ~SkCanvas::kRight_QuadAAFlag;
1821   if (visible_rect.bottom() - local_scissor->bottom() >= y_epsilon)
1822     aa_flags &= ~SkCanvas::kBottom_QuadAAFlag;
1823
1824   visible_rect.Intersect(*local_scissor);
1825   vis_tex_coords = visible_rect;
1826   scissor_rect.reset();
1827 }
1828
1829 const DrawQuad* SkiaRenderer::CanPassBeDrawnDirectly(
1830     const AggregatedRenderPass* pass) {
1831   bool is_directly_drawable_with_single_rpdq = false;
1832   const auto* draw_quad = CanPassBeDrawnDirectlyInternal(
1833       pass, &is_directly_drawable_with_single_rpdq);
1834   UMA_HISTOGRAM_BOOLEAN(
1835       "Compositing.SkiaRenderer.DirectlyDrawableRenderPassWithRPDQ",
1836       is_directly_drawable_with_single_rpdq);
1837   return draw_quad;
1838 }
1839
1840 const DrawQuad* SkiaRenderer::CanPassBeDrawnDirectlyInternal(
1841     const AggregatedRenderPass* pass,
1842     bool* is_directly_drawable_with_single_rpdq) {
1843   // If render pass bypassing is disabled for testing
1844   if (settings_->disable_render_pass_bypassing)
1845     return nullptr;
1846
1847   // Only supports bypassing render passes with a single child quad and simple
1848   // content.
1849   if (pass->quad_list.size() != 1) {
1850     return nullptr;
1851   }
1852
1853   // If it there are supposed to be mipmaps, the renderpass must exist
1854   if (pass->generate_mipmap)
1855     return nullptr;
1856
1857   const DrawQuad* quad = *pass->quad_list.BackToFrontBegin();
1858   // For simplicity in debug border and picture quad draw implementations, don't
1859   // bypass a render pass containing those. Their draw functions do not take a
1860   // DrawRPDQParams.
1861   if (quad->material == DrawQuad::Material::kDebugBorder ||
1862       quad->material == DrawQuad::Material::kPictureContent)
1863     return nullptr;
1864
1865   // TODO(penghuang): support composite TileDrawQuad in a sub render pass for
1866   // raw draw directly.
1867   if (is_using_raw_draw_ && quad->material == DrawQuad::Material::kTiledContent)
1868     return nullptr;
1869
1870   // If the quad specifies nearest-neighbor scaling then there could be two
1871   // scaling operations at different quality levels. This requires drawing to an
1872   // intermediate render pass. See https://crbug.com/1155338.
1873   if (UseNearestNeighborSampling(quad))
1874     return nullptr;
1875
1876   if (const auto* tex = quad->DynamicCast<TextureDrawQuad>()) {
1877     // Per-vertex opacities complicate bypassing the RP and alpha blending the
1878     // texture with image filters, so punt under that rare circumstance.
1879     if (tex->vertex_opacity[0] < 1.f || tex->vertex_opacity[1] < 1.f ||
1880         tex->vertex_opacity[2] < 1.f || tex->vertex_opacity[3] < 1.f) {
1881       return nullptr;
1882     }
1883   }
1884
1885   // In order to concatenate the bypass'ed quads transform with RP itself, it
1886   // needs to be invertible.
1887   // TODO(michaelludwig) - See crbug.com/1175981 and crbug.com/1186657;
1888   // We can't use gfx::Transform.IsInvertible() since that checks the 4x4 matrix
1889   // and the rest of skia_renderer->Skia flattens to a 3x3 matrix, which can
1890   // change invertibility.
1891   SkMatrix flattened = gfx::TransformToFlattenedSkMatrix(
1892       quad->shared_quad_state->quad_to_target_transform);
1893   if (!flattened.invert(nullptr))
1894     return nullptr;
1895
1896   // A renderpass normally draws its content into a transparent destination,
1897   // using the quad's blend mode, then that result is later drawn into the
1898   // real dst with the RP's blend mode. In order to bypass the RP and draw
1899   // correctly, CalculateBypassParams must be able to reason about the quad's
1900   // blend mode.
1901   if (!IsPorterDuffBlendMode(quad->shared_quad_state->blend_mode))
1902     return nullptr;
1903   // All Porter-Duff blending with transparent black should fall into one of
1904   // these two categories:
1905   DCHECK(RenderPassPreservesContent(quad->shared_quad_state->blend_mode) ||
1906          RenderPassRemainsTransparent(quad->shared_quad_state->blend_mode));
1907
1908   // The content must not have any rrect clipping, since skia_renderer applies
1909   // the rrect in device space, and in this case, the bypass quad's device space
1910   // is the RP's buffer.
1911   // TODO(michaelludwig) - If this becomes a bottleneck, we can track the
1912   // bypass rrect separately and update PrepareCanvasForRDQP to apply the
1913   // additional clip.
1914   if (ShouldApplyRoundedCorner(quad))
1915     return nullptr;
1916
1917   if (ShouldApplyGradientMask(quad))
1918     return nullptr;
1919
1920   if (const auto* render_pass_quad =
1921           quad->DynamicCast<AggregatedRenderPassDrawQuad>()) {
1922     if (render_pass_quad->mask_resource_id()) {
1923       return nullptr;
1924     }
1925
1926     // Only allow merging render passes containing RenderPassDrawQuads if they
1927     // have 2D scale/translate transform. This allows merging clip rects into
1928     // a single intermediate coordinate space.
1929     if (!Is2dScaleTranslateTransform(
1930             render_pass_quad->shared_quad_state->quad_to_target_transform)) {
1931       return nullptr;
1932     }
1933
1934     const auto nested_render_pass_id = render_pass_quad->render_pass_id;
1935     auto it = base::ranges::find_if(
1936         *current_frame()->render_passes_in_draw_order,
1937         [&nested_render_pass_id](const auto& render_pass) {
1938           return render_pass->id == nested_render_pass_id;
1939         });
1940
1941     DCHECK(it != current_frame()->render_passes_in_draw_order->end());
1942     const auto& nested_render_pass = *it;
1943     if (!nested_render_pass->filters.IsEmpty() ||
1944         !nested_render_pass->backdrop_filters.IsEmpty()) {
1945       return nullptr;
1946     }
1947
1948     *is_directly_drawable_with_single_rpdq = true;
1949
1950     if (!base::FeatureList::IsEnabled(features::kAllowBypassRenderPassQuads)) {
1951       return nullptr;
1952     }
1953   }
1954
1955   // The quad type knows how to apply RPDQ filters, and the quad settings can
1956   // be merged into the RPDQs settings in CalculateBypassParams.
1957   return quad;
1958 }
1959
1960 SkiaRenderer::BypassMode SkiaRenderer::CalculateBypassParams(
1961     const DrawQuad* bypass_quad,
1962     DrawRPDQParams* rpdq_params,
1963     DrawQuadParams* params) const {
1964   // Depending on bypass_quad's blend mode, its content may be irrelevant
1965   if (RenderPassRemainsTransparent(
1966           bypass_quad->shared_quad_state->blend_mode)) {
1967     // NOTE: this uses the pass's blend mode since this refers to the final draw
1968     // of the render pass itself
1969     if (TransparentBlackAffectsOutput(params->blend_mode)) {
1970       return BypassMode::kDrawTransparentQuad;
1971     } else {
1972       return BypassMode::kSkip;
1973     }
1974   }
1975   // If we made it here, the bypass blend mode would have just preserved the
1976   // bypass quad's content, so we can draw it directly using the render pass's
1977   // blend mode instead.
1978   DCHECK(
1979       RenderPassPreservesContent(bypass_quad->shared_quad_state->blend_mode));
1980
1981   // The bypass quad will be drawn directly, so update |params| and
1982   // |rpdq_params| to reflect the change of coordinate system and merge settings
1983   // between the inner and outer quads.
1984   SkMatrix bypass_to_rpdq = gfx::TransformToFlattenedSkMatrix(
1985       bypass_quad->shared_quad_state->quad_to_target_transform);
1986
1987   if (params->draw_region) {
1988     SkMatrix rpdq_to_bypass;
1989     bool inverted = bypass_to_rpdq.invert(&rpdq_to_bypass);
1990     // Invertibility was a requirement for being bypassable.
1991     DCHECK(inverted);
1992
1993     // The draw region was determined by the RPDQ's geometry, so map the
1994     // quadrilateral to the bypass'ed quad's coordinate space so that BSP
1995     // splitting is still respected.
1996     rpdq_to_bypass.mapPoints(params->draw_region->points,
1997                              std::size(params->draw_region->points));
1998   }
1999
2000   absl::optional<gfx::RectF> bypassed_quad_clip_rect;
2001   if (rpdq_params->bypass_geometry) {
2002     // If BypassGeometry is already populated then this is part of a chain of
2003     // bypassed render passes. This merges any additional transform and clip
2004     // into the first bypassed render pass coordinate space. This is always
2005     // possible as RenderPassDrawQuads are only bypassed if they have 2D
2006     // scale/translation transform.
2007     auto& bypass_geometry = rpdq_params->bypass_geometry.value();
2008     gfx::Transform bypass_transform =
2009         gfx::SkMatrixToTransform(bypass_geometry.transform);
2010     DCHECK(Is2dScaleTranslateTransform(bypass_transform));
2011
2012     // `bypass_geometry.clip_rect` is in the first bypassed render pass
2013     // coordinate space. The last CalculateBypassParams() updated
2014     // `params->visible_rect` so it is in the current bypassed render pass
2015     // coordinate space. Transform that into the first bypassed render pass
2016     // coordinate space and intersect with existing clip there. That way there
2017     // is a single intermediate clip entirely in the original bypassed render
2018     // pass coordinate space.
2019     gfx::RectF bypass_visible_rect =
2020         bypass_transform.MapRect(params->visible_rect);
2021     bypass_geometry.clip_rect.Intersect(bypass_visible_rect);
2022
2023     if (bypass_quad->shared_quad_state->clip_rect) {
2024       // The bypass_quad clip_rect needs to be transformed from current bypassed
2025       // render pass coordinate space into the first bypassed render pass
2026       // coordinate space.
2027       bypassed_quad_clip_rect = bypass_transform.MapRect(
2028           gfx::RectF(*bypass_quad->shared_quad_state->clip_rect));
2029     }
2030
2031     // Update transform so it maps from `bypass_quad` to the first bypassed
2032     // render pass coordinate space.
2033     bypass_geometry.transform.preConcat(bypass_to_rpdq);
2034   } else {
2035     // BypassGeometry holds the RenderPassDrawQuad visible_rect, which is in the
2036     // bypassed render pass coordinate space, along with the transform from
2037     // `bypass_quad` to bypassed render pass coordinate space.
2038     rpdq_params->bypass_geometry =
2039         DrawRPDQParams::BypassGeometry{bypass_to_rpdq, params->visible_rect};
2040
2041     if (bypass_quad->shared_quad_state->clip_rect) {
2042       // The bypass_quad clip_rect is in the same coordinate space as the RPDQ +
2043       // bypassed render pass aka the same as bypass_geometry.
2044       bypassed_quad_clip_rect =
2045           gfx::RectF(*bypass_quad->shared_quad_state->clip_rect);
2046     }
2047   }
2048
2049   if (bypassed_quad_clip_rect) {
2050     // If bypassed_quad clip_rect isn't empty then normally it would be added
2051     // to scissor_rect in SetScissorStateForQuad(). That never happens when the
2052     // render pass is bypassed so add it here.
2053     rpdq_params->bypass_geometry->clip_rect.Intersect(*bypassed_quad_clip_rect);
2054   }
2055
2056   // Compute draw params for `bypass_quad` to update some of the original draw
2057   // params. Both transform and scissor_rect are already accounted for in
2058   // BypassGeometry so pass identify and empty for those.
2059   DrawQuadParams bypass_quad_params = CalculateDrawQuadParams(
2060       /*target_to_device=*/gfx::AxisTransform2d(),
2061       /*scissor_rect=*/absl::nullopt, bypass_quad,
2062       /*draw_region=*/nullptr);
2063
2064   // NOTE: params |content_device_transform| remains that of the RPDQ to prepare
2065   // the canvas' CTM to match what any image filters require. The above
2066   // BypassGeometry::transform is then applied when drawing so that these
2067   // updated coordinates are correctly transformed to device space.
2068   params->visible_rect = bypass_quad_params.visible_rect;
2069   params->vis_tex_coords = bypass_quad_params.vis_tex_coords;
2070
2071   // Combine anti-aliasing policy (use AND so that any draw_region clipping
2072   // is preserved).
2073   params->aa_flags &= bypass_quad_params.aa_flags;
2074
2075   // Blending will use the top-level RPDQ blend mode, but factor in the
2076   // content's opacity as well, since that would have normally been baked into
2077   // the RP's buffer.
2078   params->opacity *= bypass_quad_params.opacity;
2079
2080   // Take the highest quality filter, since this single draw will reflect the
2081   // filtering decisions made both when drawing into the RP and when drawing the
2082   // RP results itself. The ord() lambda simulates this notion of "highest" when
2083   // we used to use FilterQuality.
2084   auto ord = [](const SkSamplingOptions& sampling) {
2085     if (sampling.useCubic) {
2086       return 3;
2087     } else if (sampling.mipmap != SkMipmapMode::kNone) {
2088       return 2;
2089     }
2090     return sampling.filter == SkFilterMode::kLinear ? 1 : 0;
2091   };
2092
2093   if (ord(bypass_quad_params.sampling) > ord(params->sampling)) {
2094     params->sampling = bypass_quad_params.sampling;
2095   }
2096
2097   // Rounded corner bounds are in device space, which gets tricky when bypassing
2098   // the device that the RP would have represented
2099   DCHECK(!bypass_quad_params.mask_filter_info.has_value());
2100
2101   return BypassMode::kDrawBypassQuad;
2102 }
2103
2104 SkCanvas::ImageSetEntry SkiaRenderer::MakeEntry(
2105     const SkImage* image,
2106     int matrix_index,
2107     const DrawQuadParams& params) const {
2108   return SkCanvas::ImageSetEntry(
2109       {sk_ref_sp(image), gfx::RectFToSkRect(params.vis_tex_coords),
2110        gfx::RectFToSkRect(params.visible_rect), matrix_index, params.opacity,
2111        params.aa_flags, params.draw_region.has_value()});
2112 }
2113
2114 SkCanvas::SrcRectConstraint SkiaRenderer::ResolveTextureConstraints(
2115     const SkImage* image,
2116     const gfx::RectF& valid_texel_bounds,
2117     DrawQuadParams* params) const {
2118   if (params->aa_flags == SkCanvas::kNone_QuadAAFlags &&
2119       params->sampling == SkSamplingOptions()) {
2120     // Non-AA and no bilinear filtering so rendering won't filter outside the
2121     // provided texture coordinates.
2122     return SkCanvas::kFast_SrcRectConstraint;
2123   }
2124
2125   // Resolve texture coordinates against the valid content area of the image
2126   SkCanvas::SrcRectConstraint constraint =
2127       GetTextureConstraint(image, params->vis_tex_coords, valid_texel_bounds);
2128
2129   // Skia clamps to the provided texture coordinates, not the content_area. If
2130   // there is a mismatch, have to update the draw params to account for the new
2131   // constraint
2132   if (constraint == SkCanvas::kFast_SrcRectConstraint ||
2133       valid_texel_bounds == params->vis_tex_coords) {
2134     return constraint;
2135   }
2136
2137   // To get |valid_texel_bounds| as the constraint, it must be sent as the tex
2138   // coords. To draw the right shape, store |visible_rect| as the |draw_region|
2139   // and change the visible rect so that the mapping from |visible_rect| to
2140   // |valid_texel_bounds| causes |draw_region| to map to original
2141   // |vis_tex_coords|
2142   if (!params->draw_region) {
2143     params->draw_region.emplace(gfx::QuadF(params->visible_rect));
2144   }
2145
2146   // Preserve the src-to-dst transformation for the padded texture coords
2147   SkMatrix src_to_dst =
2148       SkMatrix::RectToRect(gfx::RectFToSkRect(params->vis_tex_coords),
2149                            gfx::RectFToSkRect(params->visible_rect));
2150   params->visible_rect = gfx::SkRectToRectF(
2151       src_to_dst.mapRect(gfx::RectFToSkRect(valid_texel_bounds)));
2152   params->vis_tex_coords = valid_texel_bounds;
2153
2154   return SkCanvas::kStrict_SrcRectConstraint;
2155 }
2156
2157 bool SkiaRenderer::MustFlushBatchedQuads(const DrawQuad* new_quad,
2158                                          const DrawRPDQParams* rpdq_params,
2159                                          const DrawQuadParams& params) const {
2160   if (batched_quads_.empty())
2161     return false;
2162
2163   // If |new_quad| is the bypass quad for a renderpass with filters, it must be
2164   // drawn by itself, regardless of if it could otherwise would've been batched.
2165   if (rpdq_params)
2166     return true;
2167
2168   DCHECK_NE(new_quad->material, DrawQuad::Material::kCompositorRenderPass);
2169   if (new_quad->material != DrawQuad::Material::kAggregatedRenderPass &&
2170       new_quad->material != DrawQuad::Material::kTextureContent &&
2171       new_quad->material != DrawQuad::Material::kTiledContent)
2172     return true;
2173
2174   if (batched_quad_state_.blend_mode != params.blend_mode ||
2175       batched_quad_state_.sampling != params.sampling)
2176     return true;
2177
2178   if (batched_quad_state_.scissor_rect != params.scissor_rect) {
2179     return true;
2180   }
2181
2182   if (batched_quad_state_.mask_filter_info != params.mask_filter_info) {
2183     return true;
2184   }
2185
2186   return false;
2187 }
2188
2189 void SkiaRenderer::AddQuadToBatch(const SkImage* image,
2190                                   const gfx::RectF& valid_texel_bounds,
2191                                   DrawQuadParams* params) {
2192   SkCanvas::SrcRectConstraint constraint =
2193       ResolveTextureConstraints(image, valid_texel_bounds, params);
2194   // Last check for flushing the batch, since constraint can't be known until
2195   // the last minute.
2196   if (!batched_quads_.empty() && batched_quad_state_.constraint != constraint) {
2197     FlushBatchedQuads();
2198   }
2199
2200   // Configure batch state if it's the first
2201   if (batched_quads_.empty()) {
2202     batched_quad_state_.scissor_rect = params->scissor_rect;
2203     batched_quad_state_.mask_filter_info = params->mask_filter_info;
2204     batched_quad_state_.blend_mode = params->blend_mode;
2205     batched_quad_state_.sampling = params->sampling;
2206     batched_quad_state_.constraint = constraint;
2207   }
2208   DCHECK(batched_quad_state_.constraint == constraint);
2209
2210   // Add entry, with optional clip quad and shared transform
2211   if (params->draw_region) {
2212     for (const auto& point : params->draw_region->points) {
2213       batched_draw_regions_.push_back(point);
2214     }
2215   }
2216
2217   SkMatrix m =
2218       gfx::TransformToFlattenedSkMatrix(params->content_device_transform);
2219
2220   if (batched_cdt_matrices_.empty() || batched_cdt_matrices_.back() != m) {
2221     batched_cdt_matrices_.push_back(m);
2222   }
2223   int matrix_index = batched_cdt_matrices_.size() - 1;
2224
2225   batched_quads_.push_back(MakeEntry(image, matrix_index, *params));
2226 }
2227
2228 void SkiaRenderer::FlushBatchedQuads() {
2229   TRACE_EVENT0("viz", "SkiaRenderer::FlushBatchedQuads");
2230
2231   SkAutoCanvasRestore acr(current_canvas_, true /* do_save */);
2232   PrepareCanvas(batched_quad_state_.scissor_rect,
2233                 batched_quad_state_.mask_filter_info, nullptr);
2234
2235   SkPaint paint;
2236   sk_sp<SkColorFilter> color_filter = GetContentColorFilter();
2237   if (color_filter)
2238     paint.setColorFilter(color_filter);
2239   paint.setBlendMode(batched_quad_state_.blend_mode);
2240
2241   current_canvas_->experimental_DrawEdgeAAImageSet(
2242       &batched_quads_.front(), batched_quads_.size(),
2243       batched_draw_regions_.data(), &batched_cdt_matrices_.front(),
2244       batched_quad_state_.sampling, &paint, batched_quad_state_.constraint);
2245
2246   batched_quads_.clear();
2247   batched_draw_regions_.clear();
2248   batched_cdt_matrices_.clear();
2249 }
2250
2251 void SkiaRenderer::DrawColoredQuad(SkColor4f color,
2252                                    const DrawRPDQParams* rpdq_params,
2253                                    DrawQuadParams* params) {
2254   TRACE_EVENT0("viz", "SkiaRenderer::DrawColoredQuad");
2255   DCHECK(batched_quads_.empty());
2256
2257   SkAutoCanvasRestore acr(current_canvas_, true /* do_save */);
2258   PrepareCanvas(params->scissor_rect, params->mask_filter_info,
2259                 &params->content_device_transform);
2260
2261   if (rpdq_params) {
2262     // This will modify the provided content color as needed for the RP effects,
2263     // or it will make an explicit save layer on the current canvas
2264     PrepareColorOrCanvasForRPDQ(*rpdq_params, params, &color);
2265     if (rpdq_params->bypass_geometry) {
2266       // Concatenate the bypass'ed quad's transform after all the RPDQ state
2267       // has been pushed to the canvas.
2268       current_canvas_->concat(rpdq_params->bypass_geometry->transform);
2269     }
2270   }
2271
2272   sk_sp<SkColorFilter> content_color_filter = GetContentColorFilter();
2273   if (content_color_filter) {
2274     SkColorSpace* color_space = current_canvas_->imageInfo().colorSpace();
2275     color = content_color_filter->filterColor4f(
2276         color, SkColorSpace::MakeSRGB().get(), color_space);
2277     // DrawEdgeAAQuad lacks color filter support via SkPaint, so we apply the
2278     // color filter to the quad color directly. When applying a color filter
2279     // via drawRect or drawImage, Skia will first transform the src into the
2280     // dst color space before applying the color filter. Thus, here we need to
2281     // apply the color filter in the dst color space and then convert back to
2282     // the src color space. More formally:
2283     //    (C * Xfrm * CF * Xfrm_inv)[in Viz] * Xfrm[in Skia] = C * Xfrm * CF
2284     if (color_space && !color_space->isSRGB()) {
2285       SkPaint paint;
2286       paint.setColor(color, color_space);
2287       color = paint.getColor4f();
2288     }
2289   }
2290   // PrepareCanvasForRPDQ will have updated params->opacity and blend_mode to
2291   // account for the layer applying those effects. We need to truncate to an
2292   // integral value of [0, 255] to match the explicit floor workaround in
2293   // blink::ConversionContext::StartEffect.
2294 #if defined(TIZEN_VIDEO_HOLE)
2295   SkColor4f dst_color = color;
2296   SkBlendMode blend_mode = params->blend_mode;
2297   bool is_video_hole =
2298       color == SkColors::kTransparent && (current_frame()->has_video_hole);
2299
2300   // kSrc blend mode means disable blend
2301   if (is_video_hole) {
2302     blend_mode = SkBlendMode::kSrc;
2303     if (std::fabs(1.f - params->opacity) >
2304         std::numeric_limits<float>::epsilon()) {
2305       dst_color = SkColors::kBlack;
2306     }
2307   }
2308
2309   dst_color.fA = floor(params->opacity * dst_color.fA * 255.f) / 255.f;
2310 #else
2311   color.fA = floor(params->opacity * color.fA * 255.f) / 255.f;
2312 #endif
2313
2314   const SkPoint* draw_region =
2315       params->draw_region ? params->draw_region->points : nullptr;
2316
2317   current_canvas_->experimental_DrawEdgeAAQuad(
2318       gfx::RectFToSkRect(params->visible_rect), draw_region,
2319       static_cast<SkCanvas::QuadAAFlags>(params->aa_flags),
2320 #if defined(TIZEN_VIDEO_HOLE)
2321       dst_color, blend_mode
2322 #else
2323       color, params->blend_mode
2324 #endif
2325   );
2326 }
2327
2328 void SkiaRenderer::DrawSingleImage(const SkImage* image,
2329                                    const gfx::RectF& valid_texel_bounds,
2330                                    const DrawRPDQParams* rpdq_params,
2331                                    SkPaint* paint,
2332                                    DrawQuadParams* params) {
2333   DCHECK(batched_quads_.empty());
2334   TRACE_EVENT0("viz", "SkiaRenderer::DrawSingleImage");
2335
2336   SkAutoCanvasRestore acr(current_canvas_, true /* do_save */);
2337
2338   PrepareCanvas(params->scissor_rect, params->mask_filter_info,
2339                 &params->content_device_transform);
2340
2341   int matrix_index = -1;
2342   const SkMatrix* bypass_transform = nullptr;
2343   if (rpdq_params) {
2344     // This will modify the provided content paint as needed for the RP effects,
2345     // or it will make an explicit save layer on the current canvas
2346     PreparePaintOrCanvasForRPDQ(*rpdq_params, params, paint);
2347     if (rpdq_params->bypass_geometry) {
2348       // Incorporate the bypass transform, but unlike for solid color quads, do
2349       // not modify the SkCanvas's CTM. This is because the RPDQ's filters may
2350       // have been optimally placed on the SkPaint of the draw, which means the
2351       // canvas' transform must match that of the RenderPass. The pre-CTM matrix
2352       // of the image set entry can be used instead to modify the drawn geometry
2353       // without impacting the filter's coordinate space.
2354       bypass_transform = &rpdq_params->bypass_geometry->transform;
2355       matrix_index = 0;
2356     }
2357   }
2358
2359   // At this point, the params' opacity should be handled by |paint| (either
2360   // as its alpha or in a color filter), or in an image filter from the RPDQ,
2361   // or in the saved layer for the RPDQ. Set opacity to 1 to ensure the image
2362   // set entry does not doubly-apply the opacity then.
2363   params->opacity = 1.f;
2364
2365   SkCanvas::SrcRectConstraint constraint =
2366       ResolveTextureConstraints(image, valid_texel_bounds, params);
2367
2368   // Use -1 for matrix index since the cdt is set on the canvas.
2369   SkCanvas::ImageSetEntry entry = MakeEntry(image, matrix_index, *params);
2370   const SkPoint* draw_region =
2371       params->draw_region ? params->draw_region->points : nullptr;
2372   current_canvas_->experimental_DrawEdgeAAImageSet(
2373       &entry, 1, draw_region, bypass_transform, params->sampling, paint,
2374       constraint);
2375 }
2376
2377 void SkiaRenderer::DrawPaintOpBuffer(
2378     const cc::PaintOpBuffer* buffer,
2379     const absl::optional<SkColor4f>& clear_color,
2380     const TileDrawQuad* quad,
2381     const DrawQuadParams* params) {
2382   TRACE_EVENT0("viz", "SkiaRenderer::DrawPaintOpBuffer");
2383   if (!batched_quads_.empty())
2384     FlushBatchedQuads();
2385
2386   SkAutoCanvasRestore auto_canvas_restore(current_canvas_, true /* do_save */);
2387   PrepareCanvas(params->scissor_rect, params->mask_filter_info,
2388                 &params->content_device_transform);
2389
2390   auto visible_rect = gfx::RectFToSkRect(params->visible_rect);
2391   current_canvas_->clipRect(visible_rect);
2392
2393   if (params->draw_region) {
2394     bool aa = params->aa_flags != SkCanvas::kNone_QuadAAFlags;
2395     current_canvas_->clipPath(params->draw_region_in_path(), aa);
2396   }
2397
2398   if (quad->ShouldDrawWithBlending()) {
2399     auto paint = params->paint(nullptr);
2400     // TODO(penghuang): saveLayer() is expensive, try to avoid it as much as
2401     // possible.
2402     current_canvas_->saveLayer(&visible_rect, &paint);
2403   }
2404
2405   if (clear_color)
2406     current_canvas_->drawColor(*clear_color);
2407
2408   float scale_x = params->rect.width() / quad->tex_coord_rect.width();
2409   float scale_y = params->rect.height() / quad->tex_coord_rect.height();
2410
2411   float offset_x =
2412       params->visible_rect.x() - params->vis_tex_coords.x() * scale_x;
2413   float offset_y =
2414       params->visible_rect.y() - params->vis_tex_coords.y() * scale_y;
2415
2416   current_canvas_->translate(offset_x, offset_y);
2417   current_canvas_->scale(scale_x, scale_y);
2418
2419   cc::PlaybackParams playback_params(nullptr, SkM44());
2420   buffer->Playback(current_canvas_, playback_params);
2421 }
2422
2423 void SkiaRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad,
2424                                        DrawQuadParams* params) {
2425   TRACE_EVENT0("viz", "SkiaRenderer::DrawDebugBorderQuad");
2426   DCHECK(batched_quads_.empty());
2427
2428   SkAutoCanvasRestore acr(current_canvas_, true /* do_save */);
2429   // We need to apply the matrix manually to have pixel-sized stroke width.
2430   PrepareCanvas(params->scissor_rect, params->mask_filter_info, nullptr);
2431   SkMatrix cdt =
2432       gfx::TransformToFlattenedSkMatrix(params->content_device_transform);
2433
2434   SkPath path = params->draw_region
2435                     ? params->draw_region_in_path()
2436                     : SkPath::Rect(gfx::RectFToSkRect(params->visible_rect));
2437   path.transform(cdt);
2438
2439   SkPaint paint = params->paint(nullptr /* color_filter */);
2440   paint.setColor(quad->color);  // Must correct alpha afterwards
2441   paint.setAlphaf(params->opacity * paint.getAlphaf());
2442   paint.setStyle(SkPaint::kStroke_Style);
2443   paint.setStrokeJoin(SkPaint::kMiter_Join);
2444   paint.setStrokeWidth(quad->width);
2445
2446   current_canvas_->drawPath(path, paint);
2447 }
2448
2449 void SkiaRenderer::DrawPictureQuad(const PictureDrawQuad* quad,
2450                                    DrawQuadParams* params) {
2451   DCHECK(batched_quads_.empty());
2452   TRACE_EVENT0("viz", "SkiaRenderer::DrawPictureQuad");
2453
2454   // If the layer is transparent or needs a non-SrcOver blend mode, saveLayer
2455   // must be used so that the display list is drawn into a transient image and
2456   // then blended as a single layer at the end.
2457   const bool needs_transparency =
2458       params->opacity < 1.f || params->blend_mode != SkBlendMode::kSrcOver;
2459   const bool disable_image_filtering = disable_picture_quad_image_filtering_ ||
2460                                        params->sampling == SkSamplingOptions();
2461
2462   SkAutoCanvasRestore acr(current_canvas_, true /* do_save */);
2463   PrepareCanvas(params->scissor_rect,
2464                 params->mask_filter_info,
2465                 &params->content_device_transform);
2466
2467   // Unlike other quads which draw visible_rect or draw_region as their geometry
2468   // these represent the valid windows of content to show for the display list,
2469   // so they need to be used as a clip in Skia.
2470   SkRect visible_rect = gfx::RectFToSkRect(params->visible_rect);
2471   SkPaint paint = params->paint(GetContentColorFilter());
2472
2473   if (params->draw_region) {
2474     current_canvas_->clipPath(params->draw_region_in_path(),
2475                               paint.isAntiAlias());
2476   } else {
2477     current_canvas_->clipRect(visible_rect, paint.isAntiAlias());
2478   }
2479
2480   if (needs_transparency) {
2481     // Use the DrawQuadParams' paint for the layer, since that will affect the
2482     // final draw of the backing image.
2483     current_canvas_->saveLayer(&visible_rect, &paint);
2484   }
2485
2486   SkCanvas* raster_canvas = current_canvas_;
2487   absl::optional<skia::OpacityFilterCanvas> opacity_canvas;
2488   if (disable_image_filtering) {
2489     // TODO(vmpstr): Fold this canvas into playback and have raster source
2490     // accept a set of settings on playback that will determine which canvas to
2491     // apply. (http://crbug.com/594679)
2492     // saveLayer applies the opacity, this filter is only used for quality
2493     // overriding in the display list, hence the fixed 1.f for alpha.
2494     opacity_canvas.emplace(raster_canvas, 1.f, disable_image_filtering);
2495     raster_canvas = &*opacity_canvas;
2496   }
2497
2498   // Treat all subnormal values as zero for performance.
2499   cc::ScopedSubnormalFloatDisabler disabler;
2500
2501   raster_canvas->concat(SkMatrix::RectToRect(
2502       gfx::RectFToSkRect(quad->tex_coord_rect), gfx::RectToSkRect(quad->rect)));
2503
2504   raster_canvas->translate(-quad->content_rect.x(), -quad->content_rect.y());
2505   raster_canvas->clipRect(gfx::RectToSkRect(quad->content_rect));
2506   raster_canvas->scale(quad->contents_scale, quad->contents_scale);
2507   quad->display_item_list->Raster(raster_canvas);
2508 }
2509
2510 void SkiaRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad,
2511                                       const DrawRPDQParams* rpdq_params,
2512                                       DrawQuadParams* params) {
2513   DrawColoredQuad(quad->color, rpdq_params, params);
2514 }
2515
2516 void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad,
2517                                    const DrawRPDQParams* rpdq_params,
2518                                    DrawQuadParams* params) {
2519   TRACE_EVENT0("viz", "SkiaRenderer::DrawTextureQuad");
2520   const gfx::ColorSpace& src_color_space =
2521       resource_provider()->GetSamplerColorSpace(quad->resource_id());
2522   const bool needs_color_conversion_filter =
2523       ((quad->is_video_frame && src_color_space.IsHDR()) ||
2524        src_color_space.IsToneMappedByDefault()) &&
2525       // Don't do color conversions for stream video.
2526       !quad->is_stream_video;
2527
2528   sk_sp<SkColorSpace> override_color_space;
2529   if (needs_color_conversion_filter) {
2530     override_color_space = CurrentRenderPassSkColorSpace();
2531   }
2532
2533     // TODO(b/221643955): Some Chrome OS tests rely on the old GLRenderer
2534     // behavior of skipping color space conversions if the quad's color space is
2535     // invalid. Once these tests are migrated, we can remove the override here
2536     // and revert to Skia's default behavior of assuming sRGB on invalid.
2537 #if BUILDFLAG(IS_CHROMEOS_ASH)
2538   if (!src_color_space.IsValid())
2539     override_color_space = CurrentRenderPassSkColorSpace();
2540 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
2541
2542 #if BUILDFLAG(IS_ANDROID)
2543   if (quad->is_stream_video) {
2544     // If overlay processor would override color space, override it here to to
2545     // avoid color changes during promotion.
2546     if (auto overlay_color_space =
2547             OverlayProcessorSurfaceControl::GetOverrideColorSpace()) {
2548       override_color_space = overlay_color_space->ToSkColorSpace();
2549     }
2550   }
2551 #else
2552   // Only on android stream video can be composited.
2553   CHECK(!quad->is_stream_video);
2554 #endif
2555
2556   ScopedSkImageBuilder builder(
2557       this, quad->resource_id(), /*maybe_concurrent_reads=*/true,
2558       quad->premultiplied_alpha ? kPremul_SkAlphaType : kUnpremul_SkAlphaType,
2559       quad->y_flipped ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin,
2560       override_color_space);
2561   const SkImage* image = builder.sk_image();
2562   if (!image)
2563     return;
2564   gfx::RectF uv_rect = gfx::ScaleRect(
2565       gfx::BoundingRect(quad->uv_top_left, quad->uv_bottom_right),
2566       image->width(), image->height());
2567   params->vis_tex_coords = cc::MathUtil::ScaleRectProportional(
2568       uv_rect, gfx::RectF(quad->rect), params->visible_rect);
2569
2570   // Use provided resource size if not empty, otherwise use the full image size
2571   // as the content area
2572   gfx::RectF valid_texel_bounds =
2573       quad->resource_size_in_pixels().IsEmpty()
2574           ? gfx::RectF(image->width(), image->height())
2575           : gfx::RectF(gfx::SizeF(quad->resource_size_in_pixels()));
2576
2577   // There are three scenarios where a texture quad cannot be put into a batch:
2578   // 1. It needs to be blended with a constant background color.
2579   // 2. The vertex opacities are not all 1s.
2580   // 3. The quad contains video which might need special white level adjustment.
2581   const bool blend_background =
2582       quad->background_color != SkColors::kTransparent && !image->isOpaque();
2583   const bool vertex_alpha =
2584       quad->vertex_opacity[0] < 1.f || quad->vertex_opacity[1] < 1.f ||
2585       quad->vertex_opacity[2] < 1.f || quad->vertex_opacity[3] < 1.f;
2586
2587   if (!blend_background && !vertex_alpha && !needs_color_conversion_filter &&
2588       !rpdq_params) {
2589     // This is a simple texture draw and can go into the batching system
2590     DCHECK(!MustFlushBatchedQuads(quad, rpdq_params, *params));
2591     AddQuadToBatch(image, valid_texel_bounds, params);
2592     return;
2593   }
2594
2595   // This needs a color filter for background blending and/or a mask filter
2596   // to simulate the vertex opacity, which requires configuring a full SkPaint
2597   // and is incompatible with anything batched, but since MustFlushBatchedQuads
2598   // was optimistic for TextureQuad's, we're responsible for flushing now.
2599   if (!batched_quads_.empty())
2600     FlushBatchedQuads();
2601
2602   SkPaint paint = params->paint(GetContentColorFilter());
2603
2604   float quad_alpha;
2605   if (rpdq_params) {
2606     // The added color filters for background blending will not apply the
2607     // layer's opacity, but make sure there's no vertex_alpha since
2608     // CanPassBeDrawnDirectly() should have caught that case.
2609     DCHECK(!vertex_alpha);
2610     quad_alpha = 1.f;
2611   } else {
2612     // We will entirely handle the quad's opacity with the mask or color filter
2613     quad_alpha = params->opacity;
2614     params->opacity = 1.f;
2615   }
2616
2617   if (vertex_alpha) {
2618     // If they are all the same value, combine it with the overall opacity,
2619     // otherwise use a mask filter to emulate vertex opacity interpolation
2620     if (quad->vertex_opacity[0] == quad->vertex_opacity[1] &&
2621         quad->vertex_opacity[0] == quad->vertex_opacity[2] &&
2622         quad->vertex_opacity[0] == quad->vertex_opacity[3]) {
2623       quad_alpha *= quad->vertex_opacity[0];
2624     } else {
2625       // The only occurrences of non-constant vertex opacities come from unit
2626       // tests and src/chrome/browser/android/compositor/decoration_title.cc,
2627       // but they always produce the effect of a linear alpha gradient.
2628       // All signs indicate point order is [BL, TL, TR, BR]
2629       SkPoint gradient_pts[2];
2630       if (quad->vertex_opacity[0] == quad->vertex_opacity[1] &&
2631           quad->vertex_opacity[2] == quad->vertex_opacity[3]) {
2632         // Left to right gradient
2633         float y =
2634             params->visible_rect.y() + 0.5f * params->visible_rect.height();
2635         gradient_pts[0] = {params->visible_rect.x(), y};
2636         gradient_pts[1] = {params->visible_rect.right(), y};
2637       } else if (quad->vertex_opacity[0] == quad->vertex_opacity[3] &&
2638                  quad->vertex_opacity[1] == quad->vertex_opacity[2]) {
2639         // Top to bottom gradient
2640         float x =
2641             params->visible_rect.x() + 0.5f * params->visible_rect.width();
2642         gradient_pts[0] = {x, params->visible_rect.y()};
2643         gradient_pts[1] = {x, params->visible_rect.bottom()};
2644       } else {
2645         // Not sure how to emulate
2646         NOTIMPLEMENTED();
2647         return;
2648       }
2649
2650       float a1 = quad->vertex_opacity[0] * quad_alpha;
2651       float a2 = quad->vertex_opacity[2] * quad_alpha;
2652       SkColor4f gradient_colors[2] = {SkColor4f({a1, a1, a1, a1}),
2653                                       SkColor4f({a2, a2, a2, a2})};
2654       sk_sp<SkShader> gradient = SkGradientShader::MakeLinear(
2655           gradient_pts, gradient_colors, nullptr /*sk_sp<SkColorSpace>*/,
2656           nullptr, 2, SkTileMode::kClamp);
2657       paint.setMaskFilter(SkShaderMaskFilter::Make(std::move(gradient)));
2658       // shared quad opacity was folded into the gradient, so this will shorten
2659       // any color filter chain needed for background blending
2660       quad_alpha = 1.f;
2661     }
2662   }
2663
2664   if (needs_color_conversion_filter) {
2665     // Skia won't perform color conversion.
2666     const gfx::ColorSpace dst_color_space = CurrentRenderPassColorSpace();
2667     DCHECK(SkColorSpace::Equals(image->colorSpace(),
2668                                 CurrentRenderPassSkColorSpace().get()));
2669     sk_sp<SkColorFilter> color_filter = GetColorSpaceConversionFilter(
2670         src_color_space, absl::nullopt, quad->hdr_metadata, dst_color_space);
2671     paint.setColorFilter(color_filter->makeComposed(paint.refColorFilter()));
2672   }
2673
2674   // From gl_renderer, the final src color will be
2675   // vertexAlpha * (textureColor + backgroundColor * (1 - textureAlpha)), where
2676   // vertexAlpha is the quad's alpha * interpolated per-vertex alpha
2677   if (blend_background) {
2678     // Add a color filter that does DstOver blending between texture and the
2679     // background color. Then, modulate by quad's opacity *after* blending.
2680     // TODO(crbug/1308932) remove toSkColor and make all SkColor4f
2681     sk_sp<SkColorFilter> cf = SkColorFilters::Blend(
2682         quad->background_color.toSkColor(), SkBlendMode::kDstOver);
2683     if (quad_alpha < 1.f) {
2684       cf = MakeOpacityFilter(quad_alpha, std::move(cf));
2685       quad_alpha = 1.f;
2686       DCHECK(cf);
2687     }
2688     // |cf| could be null if alpha in |quad->background_color| is 0.
2689     if (cf)
2690       paint.setColorFilter(cf->makeComposed(paint.refColorFilter()));
2691   }
2692
2693   if (!rpdq_params && !quad->is_stream_video) {
2694     // Reset the paint's alpha, since it started as params.opacity and that
2695     // is now applied outside of the paint's alpha.
2696     paint.setAlphaf(quad_alpha);
2697   }
2698
2699   DrawSingleImage(image, valid_texel_bounds, rpdq_params, &paint, params);
2700 }
2701
2702 void SkiaRenderer::DrawTileDrawQuad(const TileDrawQuad* quad,
2703                                     const DrawRPDQParams* rpdq_params,
2704                                     DrawQuadParams* params) {
2705   TRACE_EVENT0("viz", "SkiaRenderer::DrawTileDrawQuad");
2706   DCHECK(!MustFlushBatchedQuads(quad, rpdq_params, *params));
2707   // |resource_provider()| can be NULL in resourceless software draws, which
2708   // should never produce tile quads in the first place.
2709   DCHECK(resource_provider());
2710
2711   // If quad->ShouldDrawWithBlending() is true, we need to raster tile paint ops
2712   // to an offscreen texture first, and then blend it with content behind the
2713   // tile. Since a tile could be used cross frames, so it would better to not
2714   // use raw draw.
2715   bool raw_draw_if_possible =
2716       is_using_raw_draw_ && !quad->ShouldDrawWithBlending();
2717   ScopedSkImageBuilder builder(
2718       this, quad->resource_id(), /*maybe_concurrent_reads=*/false,
2719       quad->is_premultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType,
2720       /*origin=*/kTopLeft_GrSurfaceOrigin,
2721       /*override_color_space=*/nullptr, raw_draw_if_possible);
2722
2723   params->vis_tex_coords = cc::MathUtil::ScaleRectProportional(
2724       quad->tex_coord_rect, gfx::RectF(quad->rect), params->visible_rect);
2725
2726   bool using_raw_draw = builder.paint_op_buffer();
2727   if (is_using_raw_draw_) {
2728     UMA_HISTOGRAM_BOOLEAN(
2729         "Compositing.SkiaRenderer.DrawTileDrawQuad.UsingRawDraw",
2730         using_raw_draw);
2731   }
2732   if (using_raw_draw) {
2733     DCHECK(!rpdq_params);
2734     DrawPaintOpBuffer(builder.paint_op_buffer(), builder.clear_color(), quad,
2735                       params);
2736     return;
2737   }
2738
2739   const SkImage* image = builder.sk_image();
2740   if (!image)
2741     return;
2742
2743   // When a tile is at the right or bottom edge of the entire tiled area, its
2744   // images won't be fully filled so use the unclipped texture coords. On
2745   // interior tiles or left/top tiles, the image has been filled with
2746   // overlapping content so the entire image is valid for sampling.
2747   gfx::RectF valid_texel_bounds(gfx::SizeF(quad->texture_size));
2748   if (quad->IsRightEdge()) {
2749     // Restrict the width to match far side of texture coords
2750     valid_texel_bounds.set_width(quad->tex_coord_rect.right());
2751   }
2752   if (quad->IsBottomEdge()) {
2753     // Restrict the height to match far side of texture coords
2754     valid_texel_bounds.set_height(quad->tex_coord_rect.bottom());
2755   }
2756
2757   if (rpdq_params) {
2758     SkPaint paint = params->paint(GetContentColorFilter());
2759     DrawSingleImage(image, valid_texel_bounds, rpdq_params, &paint, params);
2760   } else {
2761     AddQuadToBatch(image, valid_texel_bounds, params);
2762   }
2763 }
2764
2765 void SkiaRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad,
2766                                     const DrawRPDQParams* rpdq_params,
2767                                     DrawQuadParams* params) {
2768   TRACE_EVENT0("viz", "SkiaRenderer::DrawYUVVideoQuad");
2769   // Since YUV quads always use a color filter, they require a complex skPaint
2770   // that precludes batching. If this changes, we could add YUV quads that don't
2771   // require a filter to the batch instead of drawing one at a time.
2772   DCHECK(batched_quads_.empty());
2773
2774   gfx::ColorSpace src_color_space = quad->video_color_space;
2775   // Invalid or unspecified color spaces should be treated as REC709.
2776   if (!src_color_space.IsValid())
2777     src_color_space = gfx::ColorSpace::CreateREC709();
2778
2779   // We might modify |dst_color_space| to be something other than the
2780   // destination color space for the frame. The generated SkColorFilter does the
2781   // real color space adjustment. To avoid having skia also try to adjust the
2782   // color space we lie and say the SkImage destination color space is always
2783   // the same as the rest of the frame. Otherwise the two color space
2784   // adjustments combined will produce the wrong result.
2785   gfx::ColorSpace dst_color_space = CurrentRenderPassColorSpace();
2786
2787 #if BUILDFLAG(IS_WIN)
2788   // Force sRGB output on Windows for overlay candidate video quads to match
2789   // DirectComposition behavior in case these switch between overlays and
2790   // compositing. See https://crbug.com/811118 for details.
2791   // Currently if HDR is supported, OverlayProcessor doesn't promote HDR video
2792   // frame as overlay candidate. So it's unnecessary to worry about the
2793   // compositing-overlay switch here. In addition drawing a HDR video using sRGB
2794   // can cancel the advantages of HDR.
2795   const bool supports_dc_layers =
2796       output_surface_->capabilities().supports_dc_layers;
2797   if (supports_dc_layers && !src_color_space.IsHDR() &&
2798       resource_provider()->IsOverlayCandidate(quad->y_plane_resource_id())) {
2799     DCHECK(
2800         resource_provider()->IsOverlayCandidate(quad->u_plane_resource_id()));
2801     dst_color_space = gfx::ColorSpace::CreateSRGB();
2802   }
2803 #endif
2804
2805   DCHECK(resource_provider());
2806   // Pass in |CurrentRenderPassSkColorSpace()| here instead of |dst_color_space|
2807   // so the color space transform going from SkImage to SkSurface is identity.
2808   // The SkColorFilter already handles color space conversion so this avoids
2809   // applying the conversion twice.
2810   ScopedYUVSkImageBuilder builder(this, quad, CurrentRenderPassSkColorSpace());
2811   const SkImage* image = builder.sk_image();
2812   if (!image)
2813     return;
2814
2815   params->vis_tex_coords = cc::MathUtil::ScaleRectProportional(
2816       quad->ya_tex_coord_rect(), gfx::RectF(quad->rect), params->visible_rect);
2817
2818   sk_sp<SkColorFilter> color_filter = GetColorSpaceConversionFilter(
2819       src_color_space, quad->bits_per_channel, quad->hdr_metadata,
2820       dst_color_space, quad->resource_offset, quad->resource_multiplier);
2821
2822   auto content_color_filter = GetContentColorFilter();
2823   if (content_color_filter)
2824     color_filter = content_color_filter->makeComposed(color_filter);
2825
2826   // Use provided, unclipped texture coordinates as the content area, which will
2827   // force coord clamping unless the geometry was clipped, or they span the
2828   // entire YUV image.
2829   SkPaint paint = params->paint(color_filter);
2830
2831   DrawSingleImage(image, quad->ya_tex_coord_rect(), rpdq_params, &paint,
2832                   params);
2833 }
2834
2835 void SkiaRenderer::DrawUnsupportedQuad(const DrawQuad* quad,
2836                                        const DrawRPDQParams* rpdq_params,
2837                                        DrawQuadParams* params) {
2838 #ifdef NDEBUG
2839   DrawColoredQuad(SkColors::kWhite, rpdq_params, params);
2840 #else
2841   DrawColoredQuad(SkColors::kMagenta, rpdq_params, params);
2842 #endif
2843 }
2844
2845 void SkiaRenderer::ScheduleOverlays() {
2846   DCHECK(!current_gpu_commands_completed_fence_->was_set());
2847   DCHECK(!current_release_fence_->was_set());
2848
2849   // Always add an empty set of locks to be used in either SwapBuffersSkipped()
2850   // or SwapBuffersComplete().
2851   pending_overlay_locks_.emplace_back();
2852   [[maybe_unused]] auto& locks = pending_overlay_locks_.back();
2853
2854   if (current_frame()->overlay_list.empty())
2855     return;
2856
2857   std::vector<gpu::SyncToken> sync_tokens;
2858
2859 #if !BUILDFLAG(IS_WIN)
2860   DCHECK(output_surface_->capabilities().supports_surfaceless);
2861 #endif
2862
2863   for (auto& overlay : current_frame()->overlay_list) {
2864     if (overlay.is_root_render_pass) {
2865       continue;
2866     }
2867
2868 #if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_APPLE)
2869     if (overlay.rpdq) {
2870       PrepareRenderPassOverlay(&overlay);
2871       locks.emplace_back(overlay.mailbox);
2872       continue;
2873     }
2874 #else
2875     DCHECK(!overlay.rpdq);
2876 #endif
2877
2878     if (overlay.is_solid_color) {
2879       DCHECK(overlay.color);
2880       DCHECK(!overlay.resource_id);
2881
2882 #if BUILDFLAG(IS_OZONE)
2883       // If non-backed solid color overlays aren't supported (e.g. Lacros on
2884       // Linux) then we need to create buffers to send over Wayland.
2885       if (!output_surface_->capabilities()
2886                .supports_non_backed_solid_color_overlays &&
2887           !output_surface_->capabilities().supports_single_pixel_buffer) {
2888         overlay.mailbox = GetImageMailboxForColor(*overlay.color);
2889         overlay.resource_size_in_pixels = gfx::Size(1, 1);
2890         // This can now be treated as a regular overlay with a mailbox backing.
2891         overlay.is_solid_color = false;
2892         locks.emplace_back(overlay.mailbox);
2893       }
2894 #else
2895       // All other platforms that support solid color overlays don't need fake
2896       // buffer.
2897       DCHECK(output_surface_->capabilities()
2898                  .supports_non_backed_solid_color_overlays);
2899 #endif
2900       continue;
2901     }
2902
2903     // Resources will be unlocked after the next SwapBuffers() is completed.
2904     locks.emplace_back(resource_provider(), overlay.resource_id);
2905     auto& lock = locks.back();
2906
2907     // Sync tokens ensure the texture to be overlaid is available before
2908     // scheduling it for display.
2909     if (lock.sync_token().HasData())
2910       sync_tokens.push_back(lock.sync_token());
2911
2912     overlay.mailbox = lock.mailbox();
2913     DCHECK(!overlay.mailbox.IsZero());
2914   }
2915
2916   DCHECK(!current_gpu_commands_completed_fence_->was_set());
2917   DCHECK(!current_release_fence_->was_set());
2918
2919   skia_output_surface_->ScheduleOverlays(
2920       std::move(current_frame()->overlay_list), std::move(sync_tokens));
2921 }
2922
2923 sk_sp<SkColorFilter> SkiaRenderer::GetColorSpaceConversionFilter(
2924     const gfx::ColorSpace& src,
2925     absl::optional<uint32_t> src_bit_depth,
2926     absl::optional<gfx::HDRMetadata> src_hdr_metadata,
2927     const gfx::ColorSpace& dst,
2928     float resource_offset,
2929     float resource_multiplier) {
2930   return color_filter_cache_.Get(
2931       src, dst, resource_offset, resource_multiplier, src_bit_depth,
2932       src_hdr_metadata,
2933       current_frame()->display_color_spaces.GetSDRMaxLuminanceNits(),
2934       current_frame()->display_color_spaces.GetHDRMaxLuminanceRelative());
2935 }
2936
2937 namespace {
2938 SkColorMatrix ToColorMatrix(const SkM44& mat) {
2939   std::array<float, 20> values;
2940   values.fill(0.0f);
2941   for (uint32_t r = 0; r < 4; r++) {
2942     for (uint32_t c = 0; c < 4; c++) {
2943       values[r * 5 + c] = mat.rc(r, c);
2944     }
2945   }
2946   SkColorMatrix mat_out;
2947   mat_out.setRowMajor(values.data());
2948   return mat_out;
2949 }
2950 }  // namespace
2951
2952 sk_sp<SkColorFilter> SkiaRenderer::GetContentColorFilter() {
2953   sk_sp<SkColorFilter> color_transform = nullptr;
2954   bool is_root =
2955       current_frame()->current_render_pass == current_frame()->root_render_pass;
2956
2957   if (is_root && output_surface_->color_matrix() != SkM44()) {
2958     color_transform =
2959         SkColorFilters::Matrix(ToColorMatrix(output_surface_->color_matrix()));
2960   }
2961
2962   sk_sp<SkColorFilter> tint_transform = nullptr;
2963   if (is_root && debug_settings_->tint_composited_content) {
2964     if (debug_settings_->tint_composited_content_modulate) {
2965       // Integer counter causes modulation through rgb dimming variations.
2966       std::array<float, 3> rgb;
2967       uint32_t ci = debug_tint_modulate_count_ % 7u;
2968       for (int rc = 0; rc < 3; rc++) {
2969         rgb[rc] = (ci & (1u << rc)) ? 0.7f : 1.0f;
2970       }
2971       SkColorMatrix color_mat;
2972       color_mat.setScale(rgb[0], rgb[1], rgb[2]);
2973       tint_transform = SkColorFilters::Matrix(color_mat);
2974     } else {
2975       SkM44 mat44 = SkM44::ColMajor(
2976           cc::DebugColors::TintCompositedContentColorTransformMatrix().data());
2977       tint_transform = SkColorFilters::Matrix(ToColorMatrix(mat44));
2978     }
2979   }
2980
2981   if (color_transform) {
2982     return tint_transform ? color_transform->makeComposed(tint_transform)
2983                           : color_transform;
2984   } else {
2985     return tint_transform;
2986   }
2987 }
2988
2989 SkiaRenderer::DrawRPDQParams SkiaRenderer::CalculateRPDQParams(
2990     const AggregatedRenderPassDrawQuad* quad,
2991     const DrawQuadParams* params) {
2992   DrawRPDQParams rpdq_params(params->visible_rect);
2993
2994   if (!quad->mask_resource_id().is_null()) {
2995     // Scale normalized uv rect into absolute texel coordinates.
2996     SkRect mask_rect = gfx::RectFToSkRect(
2997         gfx::ScaleRect(quad->mask_uv_rect, quad->mask_texture_size.width(),
2998                        quad->mask_texture_size.height()));
2999     SkMatrix mask_to_quad_matrix =
3000         SkMatrix::RectToRect(mask_rect, gfx::RectToSkRect(quad->rect));
3001     rpdq_params.mask_shader.emplace(quad->mask_resource_id(),
3002                                     mask_to_quad_matrix);
3003   }
3004
3005   const cc::FilterOperations* filters = FiltersForPass(quad->render_pass_id);
3006   const cc::FilterOperations* backdrop_filters =
3007       BackdropFiltersForPass(quad->render_pass_id);
3008   // Early out if there are no filters to convert to SkImageFilters
3009   if (!filters && !backdrop_filters) {
3010     return rpdq_params;
3011   }
3012
3013   // Calculate local matrix that's shared by filters and backdrop_filters. This
3014   // local matrix represents the UI display scale that's already been applied to
3015   // the DrawQuads but not any geometric properties of the filters.
3016   SkMatrix local_matrix;
3017   local_matrix.setTranslate(quad->filters_origin.x(), quad->filters_origin.y());
3018   local_matrix.postScale(quad->filters_scale.x(), quad->filters_scale.y());
3019
3020   // Calculate local clip bounds. Note that doing this here is redundant with
3021   // letting SkCanvas reject clipped-out draws, but detecting it early means
3022   // SkiaRenderer does not have to prepare renderpass backings in those cases.
3023   absl::optional<SkRect> local_clip_rect;
3024   if (!params->content_device_transform.HasPerspective() &&
3025       params->content_device_transform.IsInvertible()) {
3026     // The |clip_rect| and |current_draw_rect_| are in target space, so map
3027     // to device space, then inverse-map to quad space.
3028     const auto& target_to_device = current_frame()->target_to_device_transform;
3029     gfx::RectF clip_rect = target_to_device.MapRect(gfx::RectF(
3030         quad->shared_quad_state->clip_rect.value_or(current_draw_rect_)));
3031     local_clip_rect = gfx::RectFToSkRect(
3032         cc::MathUtil::InverseMapQuadToLocalSpace(
3033             params->content_device_transform, gfx::QuadF(clip_rect))
3034             .BoundingBox());
3035   }
3036
3037   auto to_sk_image_filter =
3038       [](sk_sp<cc::PaintFilter> paint_filter,
3039          const SkMatrix& local_matrix) -> sk_sp<SkImageFilter> {
3040     if (paint_filter && paint_filter->cached_sk_filter_) {
3041       return paint_filter->cached_sk_filter_->makeWithLocalMatrix(local_matrix);
3042     } else {
3043       return nullptr;
3044     }
3045   };
3046
3047   // Convert CC image filters into a SkImageFilter root node
3048   if (filters) {
3049     DCHECK(!filters->IsEmpty());
3050     auto paint_filter = cc::RenderSurfaceFilters::BuildImageFilter(*filters);
3051     rpdq_params.image_filter =
3052         to_sk_image_filter(std::move(paint_filter), local_matrix);
3053
3054     if (rpdq_params.image_filter) {
3055       // Update the filter bounds to account for how the image filters
3056       // grow or move the area touched by the base quad.
3057       rpdq_params.filter_bounds = rpdq_params.image_filter->computeFastBounds(
3058           rpdq_params.filter_bounds);
3059
3060       // If after applying the filter we would be clipped out, skip the draw.
3061       if (local_clip_rect) {
3062         if (!rpdq_params.filter_bounds.intersect(*local_clip_rect)) {
3063           return {};
3064         }
3065       }
3066
3067       // Attempt to simplify the image filter to a color filter, which enables
3068       // the RPDQ effects to be applied more efficiently.
3069       SkColorFilter* color_filter_ptr = nullptr;
3070       if (rpdq_params.image_filter->asAColorFilter(&color_filter_ptr)) {
3071         // asAColorFilter already ref'ed the filter when true is returned,
3072         // reset() does not add a ref itself, so everything is okay.
3073         rpdq_params.color_filter.reset(color_filter_ptr);
3074       }
3075     }
3076   }
3077
3078   // Convert CC image filters for the backdrop into a SkImageFilter root node
3079   if (backdrop_filters) {
3080     DCHECK(!backdrop_filters->IsEmpty());
3081     rpdq_params.backdrop_filter_quality = quad->backdrop_filter_quality;
3082
3083     // quad->rect represents the layer's bounds *after* any display scale has
3084     // been applied to it. The ZOOM FilterOperation uses the layer's bounds as
3085     // its "lens" bounds. All image filters operate with a local matrix to
3086     // match the display scale. We must undo the local matrix's effect on
3087     // quad->rect to get the input bounds for ZOOM. Otherwise its lens would be
3088     // doubly-scaled while none of the other filter operations would align.
3089     SkMatrix inv_local_matrix;
3090     if (local_matrix.invert(&inv_local_matrix)) {
3091       SkIRect filter_rect =
3092           inv_local_matrix.mapRect(gfx::RectToSkRect(quad->rect)).roundOut();
3093       auto bg_paint_filter = cc::RenderSurfaceFilters::BuildImageFilter(
3094           *backdrop_filters, gfx::SkIRectToRect(filter_rect));
3095
3096       rpdq_params.backdrop_filter =
3097           to_sk_image_filter(std::move(bg_paint_filter), local_matrix);
3098     }
3099   }
3100
3101   // Determine the clipping to apply to the backdrop filter. Skia normally
3102   // fills layers with the backdrop content, whereas viz wants the backdrop
3103   // content restricted to the intersection of the DrawQuad and any defined
3104   // |backdrop_filter_bounds|.
3105   if (rpdq_params.backdrop_filter) {
3106     SkRect backdrop_rect = gfx::RectFToSkRect(params->visible_rect);
3107     // Pass bounds do not match the display scale; they will be scaled and
3108     // converted into an SkRRect in |backdrop_filter_bounds| if defined.
3109     absl::optional<gfx::RRectF> pass_bounds =
3110         BackdropFilterBoundsForPass(quad->render_pass_id);
3111     absl::optional<SkRRect> backdrop_filter_bounds;
3112     if (pass_bounds) {
3113       // Scale by the filter's scale, but don't apply filter origin
3114       SkRRect result;
3115       if (!SkRRect(*pass_bounds).transform(local_matrix, &result) ||
3116           !backdrop_rect.intersect(result.rect())) {
3117         // No visible backdrop filter
3118         rpdq_params.backdrop_filter = nullptr;
3119         return rpdq_params;
3120       } else {
3121         backdrop_filter_bounds = result;
3122       }
3123
3124       if (backdrop_filter_bounds->contains(rpdq_params.filter_bounds)) {
3125         // The backdrop filter bounds are a no-op since the quad rect or region
3126         // fully limits the backdrop filter.
3127         backdrop_filter_bounds.reset();
3128       } else {
3129         // The backdrop filter bounds might have an effect, but a simple case to
3130         // check for is if the backdrop rounded corners are identical to the
3131         // quad's rounded corner mask info. In that case, the prior contains()
3132         // check would be false, but we can still discard these bounds since the
3133         // final mask clip will achieve the same visual effect.
3134         if (params->mask_filter_info) {
3135           SkMatrix m = gfx::TransformToFlattenedSkMatrix(
3136               params->content_device_transform);
3137           if (backdrop_filter_bounds->transform(m, &result) &&
3138               SkRRect(params->mask_filter_info->rounded_corner_bounds()) ==
3139                   result) {
3140             backdrop_filter_bounds.reset();
3141           }
3142         }
3143       }
3144     }
3145
3146     if (local_clip_rect && !backdrop_rect.intersect(*local_clip_rect)) {
3147       rpdq_params.backdrop_filter = nullptr;
3148       return rpdq_params;
3149     }
3150
3151     // Besides ensuring the output of the backdrop filter doesn't go beyond its
3152     // bounds, it should not read pixels outside of its bounds to prevent color
3153     // bleeding. If it's a pixel-moving filter, we compose a kClamp-tiling Crop
3154     // image filter to enforce this requirement.
3155     // TODO(crbug.com/978031): Revisit the tilemode, perhaps kMirror is better
3156     SkIRect sk_crop_rect = backdrop_rect.roundOut();
3157     SkIRect sk_src_rect = rpdq_params.backdrop_filter->filterBounds(
3158         sk_crop_rect, SkMatrix::I(), SkImageFilter::kReverse_MapDirection,
3159         /*inputRect=*/nullptr);
3160     if (!sk_crop_rect.contains(sk_src_rect)) {
3161       rpdq_params.backdrop_filter = SkImageFilters::Compose(
3162           /*outer=*/std::move(rpdq_params.backdrop_filter),
3163           /*inner=*/SkImageFilters::Crop(backdrop_rect, SkTileMode::kClamp,
3164                                          nullptr));
3165     }
3166
3167     // Update |filter_bounds| to include content produced by the backdrop. Under
3168     // most circumstances this will be a no-op since content is restricted to
3169     // underneath the RPDQ's draw region, but if a backdrop filter is combined
3170     // with some pixel-moving filters, that may not remain the case and this
3171     // ensures |filter_bounds| will contain all possible output.
3172     rpdq_params.filter_bounds.join(backdrop_rect);
3173     rpdq_params.backdrop_filter_bounds = backdrop_filter_bounds;
3174   }
3175
3176   return rpdq_params;
3177 }
3178
3179 void SkiaRenderer::DrawRenderPassQuad(
3180     const AggregatedRenderPassDrawQuad* quad,
3181     const DrawRPDQParams* bypassed_rpdq_params,
3182     DrawQuadParams* params) {
3183   TRACE_EVENT0("viz", "SkiaRenderer::DrawRenderPassQuad");
3184   DrawRPDQParams rpdq_params = bypassed_rpdq_params
3185                                    ? *bypassed_rpdq_params
3186                                    : CalculateRPDQParams(quad, params);
3187
3188   // |filter_bounds| is the content space bounds that includes any filtered
3189   // extents. If empty, the draw can be skipped.
3190   if (rpdq_params.filter_bounds.isEmpty()) {
3191     return;
3192   }
3193
3194   auto bypass = render_pass_bypass_quads_.find(quad->render_pass_id);
3195   // When Render Pass has a single quad inside we would draw that directly.
3196   if (bypass != render_pass_bypass_quads_.end()) {
3197     BypassMode mode =
3198         CalculateBypassParams(bypass->second, &rpdq_params, params);
3199     if (mode == BypassMode::kDrawTransparentQuad) {
3200       // The RPDQ is masquerading as a solid color quad, which do not support
3201       // batching.
3202       if (!batched_quads_.empty())
3203         FlushBatchedQuads();
3204       DrawColoredQuad(SkColors::kTransparent, &rpdq_params, params);
3205     } else if (mode == BypassMode::kDrawBypassQuad) {
3206       DrawQuadInternal(bypass->second, &rpdq_params, params);
3207     }  // else mode == kSkip
3208     return;
3209   }
3210
3211   // A real render pass that was turned into an image
3212   auto iter = render_pass_backings_.find(quad->render_pass_id);
3213   DCHECK(render_pass_backings_.end() != iter)
3214       << "Could not find render pass id # " << quad->render_pass_id
3215       << " in the render pass overlay backings";
3216   // This function is called after AllocateRenderPassResourceIfNeeded, so
3217   // there should be backing ready.
3218   RenderPassBacking& backing = iter->second;
3219
3220   sk_sp<SkImage> content_image =
3221       skia_output_surface_->MakePromiseSkImageFromRenderPass(
3222           quad->render_pass_id, backing.size, backing.format,
3223           backing.generate_mipmap, RenderPassBackingSkColorSpace(backing),
3224           backing.mailbox);
3225   DLOG_IF(ERROR, !content_image)
3226       << "MakePromiseSkImageFromRenderPass() failed for render pass";
3227
3228   if (!content_image)
3229     return;
3230
3231   if (backing.generate_mipmap)
3232     params->sampling =
3233         SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
3234
3235   params->vis_tex_coords = cc::MathUtil::ScaleRectProportional(
3236       quad->tex_coord_rect, gfx::RectF(quad->rect), params->visible_rect);
3237   gfx::RectF valid_texel_bounds(content_image->width(),
3238                                 content_image->height());
3239
3240   // When the RPDQ was needed because of a copy request, it may not require any
3241   // advanced filtering/effects at which point it's basically a tiled quad.
3242   if (!rpdq_params.image_filter && !rpdq_params.backdrop_filter &&
3243       !rpdq_params.mask_shader && !rpdq_params.bypass_geometry) {
3244     DCHECK(!MustFlushBatchedQuads(quad, nullptr, *params));
3245     AddQuadToBatch(content_image.get(), valid_texel_bounds, params);
3246     return;
3247   }
3248
3249   // The paint is complex enough that it has to be drawn on its own, and since
3250   // MustFlushBatchedQuads() was optimistic, we manage the flush here.
3251   if (!batched_quads_.empty())
3252     FlushBatchedQuads();
3253
3254   SkPaint paint = params->paint(GetContentColorFilter());
3255
3256   DrawSingleImage(content_image.get(), valid_texel_bounds, &rpdq_params, &paint,
3257                   params);
3258 }
3259
3260 void SkiaRenderer::CopyDrawnRenderPass(
3261     const copy_output::RenderPassGeometry& geometry,
3262     std::unique_ptr<CopyOutputRequest> request) {
3263   // TODO(weiliangc): Make copy request work. (crbug.com/644851)
3264   TRACE_EVENT0("viz", "SkiaRenderer::CopyDrawnRenderPass");
3265
3266   // Root framebuffer uses a zero-mailbox in SkiaOutputSurface.
3267   gpu::Mailbox mailbox;
3268   const auto* const render_pass = current_frame()->current_render_pass;
3269   AggregatedRenderPassId render_pass_id = render_pass->id;
3270   auto it = render_pass_backings_.find(render_pass_id);
3271   if (it != render_pass_backings_.end()) {
3272     mailbox = it->second.mailbox;
3273   }
3274
3275   skia_output_surface_->CopyOutput(geometry, CurrentRenderPassColorSpace(),
3276                                    std::move(request), mailbox);
3277 }
3278
3279 void SkiaRenderer::DidChangeVisibility() {
3280   if (visible_)
3281     output_surface_->EnsureBackbuffer();
3282   else
3283     output_surface_->DiscardBackbuffer();
3284 }
3285
3286 void SkiaRenderer::FinishDrawingRenderPass() {
3287   TRACE_EVENT0("viz", "SkiaRenderer::FinishDrawingRenderPass");
3288   if (!current_canvas_)
3289     return;
3290
3291   if (!batched_quads_.empty())
3292     FlushBatchedQuads();
3293
3294   bool is_root_render_pass =
3295       current_frame()->current_render_pass == current_frame()->root_render_pass;
3296
3297   // Drawing the delegated ink trail must happen after the final
3298   // FlushBatchedQuads() call so that the trail can always be on top of
3299   // everything else that has already been drawn on the page. For the same
3300   // reason, it should only happen on the root render pass.
3301   if (is_root_render_pass && UsingSkiaForDelegatedInk())
3302     DrawDelegatedInkTrail();
3303
3304   current_canvas_ = nullptr;
3305   // Non-root render passes that are scheduled as overlays will be painted in
3306   // PrepareRenderPassOverlay().
3307   bool is_overlay =
3308       skia_output_surface_->capabilities().renderer_allocates_images &&
3309       is_root_render_pass;
3310   EndPaint(current_render_pass_update_rect_, /*failed=*/false, is_overlay);
3311
3312   // Defer flushing drawing task for root render pass, to avoid extra
3313   // MakeCurrent() call. It is expensive on GL.
3314   // TODO(https://crbug.com/1141008): Consider deferring drawing tasks for
3315   // all render passes.
3316   if (is_root_render_pass)
3317     return;
3318
3319   FlushOutputSurface();
3320 }
3321
3322 void SkiaRenderer::GenerateMipmap() {
3323   // This is a no-op since setting FilterQuality to high during drawing of
3324   // CompositorRenderPassDrawQuad is what actually generates generate_mipmap.
3325 }
3326
3327 void SkiaRenderer::UpdateRenderPassTextures(
3328     const AggregatedRenderPassList& render_passes_in_draw_order,
3329     const base::flat_map<AggregatedRenderPassId, RenderPassRequirements>&
3330         render_passes_in_frame) {
3331   const auto& root_pass_id = render_passes_in_draw_order.back()->id;
3332   std::vector<AggregatedRenderPassId> passes_to_delete;
3333   for (const auto& [backing_id, backing] : render_pass_backings_) {
3334     // Buffer queue's root manages the root pass backing and its bookkeeping
3335     // separately from other render pass backings.
3336     if (buffer_queue_) {
3337       // If a root backing exists but its id does not match the current root
3338       // render pass id, then it must be an old backing that should be deleted.
3339       // Otherwise we should not delete a root backing in case it is scheduled
3340       // this frame but not drawn (e.g. in the case of overlay-only damage).
3341       if (backing.is_root) {
3342         if (backing_id != root_pass_id) {
3343           passes_to_delete.push_back(backing_id);
3344         }
3345         continue;
3346       }
3347     }
3348
3349     auto render_pass_it = render_passes_in_frame.find(backing_id);
3350     if (render_pass_it == render_passes_in_frame.end()) {
3351       passes_to_delete.push_back(backing_id);
3352       continue;
3353     }
3354
3355     const RenderPassRequirements& requirements = render_pass_it->second;
3356     const bool size_is_exact_match = backing.size == requirements.size;
3357     const bool size_is_sufficient =
3358         backing.size.width() >= requirements.size.width() &&
3359         backing.size.height() >= requirements.size.height();
3360     bool size_appropriate =
3361         backing.is_root ? size_is_exact_match : size_is_sufficient;
3362     bool mipmap_appropriate =
3363         !requirements.generate_mipmap || backing.generate_mipmap;
3364     bool no_change_in_format = requirements.format == backing.format;
3365     bool no_change_in_alpha_type =
3366         requirements.alpha_type == backing.alpha_type;
3367     bool no_change_in_color_space =
3368         requirements.color_space == backing.color_space;
3369     bool scanout_appropriate =
3370         requirements.is_scanout == backing.is_scanout &&
3371         requirements.scanout_dcomp_surface == backing.scanout_dcomp_surface;
3372
3373     if (!size_appropriate || !mipmap_appropriate || !no_change_in_format ||
3374         !no_change_in_alpha_type || !no_change_in_color_space ||
3375         !scanout_appropriate) {
3376       passes_to_delete.push_back(backing_id);
3377     }
3378   }
3379
3380   // Delete RenderPass backings from the previous frame that will not be used
3381   // again.
3382   for (size_t i = 0; i < passes_to_delete.size(); ++i) {
3383     auto it = render_pass_backings_.find(passes_to_delete[i]);
3384     auto& backing = it->second;
3385     // Root render pass backings managed by |buffer_queue_| are not managed by
3386     // DisplayResourceProvider, so we should not destroy them here. This
3387     // reallocation is done in Reshape before drawing the frame
3388     if (!(buffer_queue_ && backing.is_root)) {
3389       skia_output_surface_->DestroySharedImage(backing.mailbox);
3390     }
3391     render_pass_backings_.erase(it);
3392   }
3393
3394   if (!passes_to_delete.empty()) {
3395     skia_output_surface_->RemoveRenderPassResource(std::move(passes_to_delete));
3396   }
3397 }
3398
3399 void SkiaRenderer::AllocateRenderPassResourceIfNeeded(
3400     const AggregatedRenderPassId& render_pass_id,
3401     const RenderPassRequirements& requirements) {
3402   const bool is_root = render_pass_id == current_frame()->root_render_pass->id;
3403
3404   // Root render pass backings managed by |buffer_queue_| are not managed by
3405   // DisplayResourceProvider, so we should not allocate them here.
3406   if (buffer_queue_ && is_root) {
3407     auto& root_pass_backing = render_pass_backings_[render_pass_id];
3408     root_pass_backing.is_root = true;
3409     root_pass_backing.mailbox = buffer_queue_->GetCurrentBuffer();
3410     root_pass_backing.generate_mipmap = requirements.generate_mipmap;
3411     root_pass_backing.size = requirements.size;
3412     root_pass_backing.format = requirements.format;
3413     root_pass_backing.alpha_type = requirements.alpha_type;
3414     root_pass_backing.color_space = requirements.color_space;
3415     root_pass_backing.is_scanout = true;
3416     root_pass_backing.scanout_dcomp_surface = false;
3417     return;
3418   }
3419
3420   auto it = render_pass_backings_.find(render_pass_id);
3421   if (it != render_pass_backings_.end()) {
3422     DCHECK(gfx::Rect(it->second.size).Contains(gfx::Rect(requirements.size)));
3423     // A root backing should not be used for other render passes. If the root
3424     // pass id has changed, then it's old backing should have been deleted
3425     // already in UpdateRenderPassTextures().
3426     DCHECK(!(buffer_queue_ && it->second.is_root));
3427     return;
3428   }
3429
3430   uint32_t usage = gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
3431                    gpu::SHARED_IMAGE_USAGE_DISPLAY_WRITE;
3432   if (requirements.generate_mipmap) {
3433     DCHECK(!requirements.is_scanout);
3434     usage |= gpu::SHARED_IMAGE_USAGE_MIPMAP;
3435   }
3436   if (requirements.is_scanout) {
3437     usage |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
3438
3439 #if BUILDFLAG(IS_WIN)
3440     // DComp surfaces do not support RGB10A2 so we must fall back to swap
3441     // chains. If this happens with video overlays, this can result in the video
3442     // overlay and its parent surface having unsynchronized updates.
3443     //
3444     // TODO(tangm): We should clean this up by either avoiding HDR or using
3445     //              RGBAF16 surfaces in this case.
3446     const bool dcomp_surface_unsupported_format =
3447         requirements.format == SinglePlaneFormat::kRGBA_1010102;
3448
3449     if (requirements.scanout_dcomp_surface &&
3450         !dcomp_surface_unsupported_format) {
3451       usage |= gpu::SHARED_IMAGE_USAGE_SCANOUT_DCOMP_SURFACE;
3452
3453       // DComp surfaces are write-only, viz cannot sample them.
3454       usage &= ~gpu::SHARED_IMAGE_USAGE_DISPLAY_READ;
3455     }
3456 #else
3457     DCHECK(!requirements.scanout_dcomp_surface);
3458 #endif
3459   } else {
3460     DCHECK(!requirements.scanout_dcomp_surface);
3461   }
3462
3463   auto mailbox = skia_output_surface_->CreateSharedImage(
3464       requirements.format, requirements.size, requirements.color_space,
3465       requirements.alpha_type, usage, "RenderPassBacking",
3466       gpu::kNullSurfaceHandle);
3467   render_pass_backings_.emplace(
3468       render_pass_id,
3469       RenderPassBacking({requirements.size, requirements.generate_mipmap,
3470                          requirements.color_space, requirements.alpha_type,
3471                          requirements.format, mailbox, is_root,
3472                          requirements.is_scanout,
3473                          requirements.scanout_dcomp_surface}));
3474 }
3475
3476 void SkiaRenderer::FlushOutputSurface() {
3477   auto sync_token = skia_output_surface_->Flush();
3478   lock_set_for_external_use_->UnlockResources(sync_token);
3479 }
3480
3481 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
3482 bool SkiaRenderer::CanSkipRenderPassOverlay(
3483     AggregatedRenderPassId render_pass_id,
3484     const AggregatedRenderPassDrawQuad* rpdq,
3485     RenderPassOverlayParams** output_render_pass_overlay) {
3486   // The render pass draw quad can be skipped if (1) the render pass has no
3487   // damage and is skipped in DirectRender and (2) the parameters of drawing the
3488   // render pass has not changed.
3489
3490   if (!can_skip_render_pass_overlay_)
3491     return false;
3492
3493   // Check if the render pass has been re-drawn.
3494   if (skipped_render_pass_ids_.count(render_pass_id) == 0)
3495     return false;
3496
3497   // Every time a new render_pass_overlay is allocated, it's added to the back
3498   // of the list. In order to get the render_pass_overlay of the previous frame,
3499   // loop through the list in a reverse order since there might be multiple
3500   // render pass overlays with the same render pass id.
3501   RenderPassOverlayParams* overlay_found = nullptr;
3502   bool found_in_available_backings = false;
3503   for (auto rit = in_flight_render_pass_overlay_backings_.rbegin();
3504        rit != in_flight_render_pass_overlay_backings_.rend(); ++rit) {
3505     if (rit->render_pass_id == render_pass_id) {
3506       overlay_found = &*rit;
3507       break;
3508     }
3509   }
3510
3511   // The backing of the previous frame might be complete and moved to
3512   // available_render_pass_overlay_backings_ when this frame starts.
3513   std::vector<RenderPassOverlayParams>::iterator it_to_delete;
3514   if (!overlay_found) {
3515     int index = 0;
3516     for (auto rit = available_render_pass_overlay_backings_.rbegin();
3517          rit != available_render_pass_overlay_backings_.rend();
3518          ++rit, ++index) {
3519       if (rit->render_pass_id == render_pass_id) {
3520         found_in_available_backings = true;
3521         // Cannot use reverse_iterator. Convert it to const_iterator.
3522         it_to_delete =
3523             available_render_pass_overlay_backings_.begin() +
3524             (available_render_pass_overlay_backings_.size() - index - 1);
3525         overlay_found = &*rit;
3526         break;
3527       }
3528     }
3529   }
3530
3531   if (!overlay_found) {
3532     return false;
3533   }
3534
3535   // Compare RenderPassDrawQuads of the previous frame and the current frame.
3536   const cc::FilterOperations* filters = FiltersForPass(render_pass_id);
3537   const cc::FilterOperations* backdrop_filters =
3538       BackdropFiltersForPass(render_pass_id);
3539   overlay_found->rpdq.shared_quad_state = &(overlay_found->shared_quad_state);
3540
3541   bool no_change_in_rpdq = overlay_found->rpdq.Equals(*rpdq);
3542   bool no_change_in_filters =
3543       filters ? (overlay_found->filters == *filters)
3544               : (overlay_found->filters == cc::FilterOperations());
3545   bool no_change_in_backdrop_filters =
3546       backdrop_filters
3547           ? (overlay_found->backdrop_filters == *backdrop_filters)
3548           : (overlay_found->backdrop_filters == cc::FilterOperations());
3549
3550   if (no_change_in_rpdq && no_change_in_filters &&
3551       no_change_in_backdrop_filters) {
3552     if (found_in_available_backings) {
3553       in_flight_render_pass_overlay_backings_.push_back(*overlay_found);
3554       available_render_pass_overlay_backings_.erase(it_to_delete);
3555       *output_render_pass_overlay =
3556           &in_flight_render_pass_overlay_backings_.back();
3557     } else {
3558       *output_render_pass_overlay = overlay_found;
3559     }
3560     return true;
3561   } else {
3562     return false;
3563   }
3564 }
3565
3566 SkiaRenderer::RenderPassOverlayParams*
3567 SkiaRenderer::GetOrCreateRenderPassOverlayBacking(
3568     AggregatedRenderPassId render_pass_id,
3569     const AggregatedRenderPassDrawQuad* rpdq,
3570     SharedImageFormat buffer_format,
3571     gfx::ColorSpace color_space,
3572     const gfx::Size& buffer_size) {
3573   RenderPassOverlayParams overlay_params;
3574   auto it = base::ranges::find_if(available_render_pass_overlay_backings_,
3575                                   [&buffer_format, &buffer_size, &color_space](
3576                                       const RenderPassOverlayParams& overlay) {
3577                                     auto& backing = overlay.render_pass_backing;
3578                                     return backing.format == buffer_format &&
3579                                            backing.size == buffer_size &&
3580                                            backing.color_space == color_space;
3581                                   });
3582   if (it == available_render_pass_overlay_backings_.end()) {
3583     // Allocate the image for render pass overlay if there is no existing
3584     // available one.
3585     constexpr auto kOverlayUsage =
3586         gpu::SHARED_IMAGE_USAGE_SCANOUT | gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
3587         gpu::SHARED_IMAGE_USAGE_DISPLAY_WRITE | gpu::SHARED_IMAGE_USAGE_RASTER;
3588     auto mailbox = skia_output_surface_->CreateSharedImage(
3589         buffer_format, buffer_size, color_space, RenderPassAlphaType::kPremul,
3590         kOverlayUsage, "RenderPassOverlay", gpu::kNullSurfaceHandle);
3591     overlay_params.render_pass_backing = {buffer_size,
3592                                           /*generate_mipmap=*/false,
3593                                           color_space,
3594                                           RenderPassAlphaType::kPremul,
3595                                           buffer_format,
3596                                           mailbox,
3597                                           /*is_root=*/false,
3598                                           /*is_scanout=*/true,
3599                                           /*scanout_dcomp_surface=*/false};
3600   } else {
3601     overlay_params = *it;
3602     available_render_pass_overlay_backings_.erase(it);
3603   }
3604
3605   // Add current rpdq to RenderPassOverlayParams.
3606   overlay_params.render_pass_id = render_pass_id;
3607   overlay_params.shared_quad_state.SetAll(*rpdq->shared_quad_state);
3608   overlay_params.rpdq.SetAll(*rpdq);
3609
3610   if (const cc::FilterOperations* filters = FiltersForPass(render_pass_id);
3611       filters) {
3612     overlay_params.filters = *filters;
3613   }
3614   if (const cc::FilterOperations* backdrop_filters =
3615           BackdropFiltersForPass(render_pass_id);
3616       backdrop_filters) {
3617     overlay_params.backdrop_filters = *backdrop_filters;
3618   }
3619
3620   in_flight_render_pass_overlay_backings_.push_back(overlay_params);
3621
3622   return &in_flight_render_pass_overlay_backings_.back();
3623 }
3624
3625 void SkiaRenderer::PrepareRenderPassOverlay(
3626     OverlayProcessorInterface::PlatformOverlayCandidate* overlay) {
3627   DCHECK(!current_canvas_);
3628   DCHECK(batched_quads_.empty());
3629   DCHECK(overlay->rpdq);
3630
3631   auto* const quad = overlay->rpdq;
3632
3633   // The overlay will be sent to GPU the thread, so set rpdq to nullptr to avoid
3634   // being accessed on the GPU thread.
3635   overlay->rpdq = nullptr;
3636
3637   // The |current_render_pass| could be used for calculating destination
3638   // color space or clipping rect for backdrop filters. However
3639   // the |current_render_pass| is nullptr during ScheduleOverlays(), since all
3640   // overlay quads should be in the |root_render_pass|, before they are promoted
3641   // to overlays, so set the |root_render_pass| to the |current_render_pass|.
3642   base::AutoReset<const AggregatedRenderPass*> auto_reset_current_render_pass(
3643       &current_frame()->current_render_pass, current_frame()->root_render_pass);
3644
3645   auto* shared_quad_state =
3646       const_cast<SharedQuadState*>(quad->shared_quad_state);
3647
3648   absl::optional<gfx::Transform> quad_to_target_transform_inverse;
3649   // We cannot handle rotation with clip rect or mask filter.
3650   if ((shared_quad_state->clip_rect ||
3651        !shared_quad_state->mask_filter_info.IsEmpty()) &&
3652       shared_quad_state->quad_to_target_transform.Preserves2dAxisAlignment()) {
3653     quad_to_target_transform_inverse.emplace();
3654     // Flatten before inverting, since we're interested in how points
3655     // with z=0 in local space map to the clip rect, not in how the clip
3656     // rect at z=0 in device space maps to some other z in local space.
3657     gfx::Transform flat_quad_to_target_transform(
3658         shared_quad_state->quad_to_target_transform);
3659     flat_quad_to_target_transform.Flatten();
3660     quad_to_target_transform_inverse =
3661         flat_quad_to_target_transform.GetCheckedInverse();
3662   }
3663
3664   // The |clip_rect| is in the device coordinate and with all transforms
3665   // (translation, scaling, rotation, etc), so remove them.
3666   absl::optional<base::AutoReset<absl::optional<gfx::Rect>>>
3667       auto_reset_clip_rect;
3668   if (shared_quad_state->clip_rect) {
3669     gfx::RectF clip_rect(*shared_quad_state->clip_rect);
3670     if (quad_to_target_transform_inverse) {
3671       clip_rect = quad_to_target_transform_inverse->MapRect(clip_rect);
3672       auto_reset_clip_rect.emplace(&shared_quad_state->clip_rect,
3673                                    gfx::ToEnclosedRect(clip_rect));
3674     } else {
3675       // If we can't position the clip rect into render pass space, we shouldn't
3676       // use it when rendering.
3677       auto_reset_clip_rect.emplace(&shared_quad_state->clip_rect,
3678                                    absl::nullopt);
3679     }
3680   }
3681
3682   // The |mask_filter_info| is in the device coordinate and with all transforms
3683   // (translation, scaling, rotation, etc), so remove them.
3684   if (!shared_quad_state->mask_filter_info.IsEmpty() &&
3685       shared_quad_state->quad_to_target_transform.Preserves2dAxisAlignment()) {
3686     shared_quad_state->mask_filter_info.ApplyTransform(
3687         *quad_to_target_transform_inverse);
3688   }
3689
3690   const auto& viewport_size = current_frame()->device_viewport_size;
3691   gfx::AxisTransform2d target_to_device = gfx::OrthoProjectionTransform(
3692       /*left=*/0, /*right=*/viewport_size.width(), /*bottom=*/0,
3693       /*top=*/viewport_size.height());
3694   target_to_device.PostConcat(
3695       gfx::WindowTransform(/*x=*/0, /*y=*/0, /*width=*/viewport_size.width(),
3696                            /*height=*/viewport_size.height()));
3697
3698   DrawQuadParams params;
3699   DrawRPDQParams rpdq_params{gfx::RectF()};
3700   {
3701     // Reset |quad_to_target_transform|, so the quad will be rendered at the
3702     // origin (0,0) without all transforms (translation, scaling, rotation, etc)
3703     // and then we will use OS compositor to do those transforms.
3704     base::AutoReset<gfx::Transform> auto_reset_transform(
3705         &shared_quad_state->quad_to_target_transform, gfx::Transform());
3706     // Use nullptr scissor, so we can always render the whole render pass in an
3707     // overlay backing.
3708     // TODO(penghuang): reusing overlay backing from previous frame to avoid
3709     // reproducing the overlay backing if the render pass content quad
3710     // properties and content are not changed.
3711     params = CalculateDrawQuadParams(target_to_device,
3712                                      /*scissor_rect=*/absl::nullopt, quad,
3713                                      /*draw_region=*/nullptr);
3714     rpdq_params = CalculateRPDQParams(quad, &params);
3715   }
3716
3717   const gfx::Rect filter_bounds =
3718       gfx::SkIRectToRect(rpdq_params.filter_bounds.roundOut());
3719
3720   // |filter_bounds| is the content space bounds that includes any filtered
3721   // extents. If empty, the draw can be skipped.
3722   if (filter_bounds.IsEmpty()) {
3723     return;
3724   }
3725
3726   SharedImageFormat buffer_format;
3727   gfx::ColorSpace color_space;
3728
3729   RenderPassBacking* src_quad_backing = nullptr;
3730   auto bypass = render_pass_bypass_quads_.find(quad->render_pass_id);
3731   BypassMode bypass_mode = BypassMode::kSkip;
3732   // When Render Pass has a single quad inside we would draw that directly.
3733   if (bypass != render_pass_bypass_quads_.end()) {
3734     bypass_mode = CalculateBypassParams(bypass->second, &rpdq_params, &params);
3735     if (bypass_mode == BypassMode::kSkip) {
3736       return;
3737     }
3738
3739     // For bypassed render pass, we use the same format and color space for the
3740     // framebuffer.
3741     buffer_format = GetSinglePlaneSharedImageFormat(reshape_buffer_format());
3742     color_space = reshape_color_space();
3743   } else {
3744     // A real render pass that was turned into an image
3745     auto it = render_pass_backings_.find(quad->render_pass_id);
3746     DCHECK(render_pass_backings_.end() != it);
3747     // This function is called after AllocateRenderPassResourceIfNeeded, so
3748     // there should be backing ready.
3749     src_quad_backing = &it->second;
3750     buffer_format = src_quad_backing->format;
3751     color_space = src_quad_backing->color_space;
3752   }
3753
3754   // Adjust the overlay |buffer_size| to reduce memory fragmentation. It also
3755   // increases buffer reusing possibilities.
3756   constexpr int kBufferMultiple = 64;
3757   gfx::Size buffer_size(
3758       cc::MathUtil::CheckedRoundUp(filter_bounds.width(), kBufferMultiple),
3759       cc::MathUtil::CheckedRoundUp(filter_bounds.height(), kBufferMultiple));
3760
3761   RenderPassOverlayParams* overlay_params = nullptr;
3762   bool can_skip_render_pass =
3763       CanSkipRenderPassOverlay(quad->render_pass_id, quad, &overlay_params);
3764   if (!can_skip_render_pass) {
3765     overlay_params = GetOrCreateRenderPassOverlayBacking(
3766         quad->render_pass_id, quad, buffer_format, color_space, buffer_size);
3767   }
3768   DCHECK(overlay_params);
3769   UMA_HISTOGRAM_BOOLEAN(
3770       "Compositing.SkiaRenderer.SkipOverlayRenderPassDrawQuad",
3771       can_skip_render_pass);
3772
3773   const RenderPassBacking& dst_overlay_backing =
3774       overlay_params->render_pass_backing;
3775   overlay->mailbox = dst_overlay_backing.mailbox;
3776   overlay->resource_size_in_pixels = dst_overlay_backing.size;
3777
3778   if (can_skip_render_pass) {
3779     int pixel_size = quad->rect.width() * quad->rect.height();
3780     UMA_HISTOGRAM_COUNTS_10M(
3781         "Compositing.SkiaRenderer.SkippedOverlayRenderPassDrawQuadSize",
3782         pixel_size);
3783   } else {
3784     current_canvas_ = skia_output_surface_->BeginPaintRenderPass(
3785         quad->render_pass_id, dst_overlay_backing.size,
3786         dst_overlay_backing.format, dst_overlay_backing.alpha_type,
3787         /*mipmap=*/false, /*scanout_dcomp_surface=*/false,
3788         RenderPassBackingSkColorSpace(dst_overlay_backing),
3789         /*is_overlay=*/true, overlay->mailbox);
3790     if (!current_canvas_) {
3791       DLOG(ERROR)
3792           << "BeginPaintRenderPass() in PrepareRenderPassOverlay() failed.";
3793       return;
3794     }
3795
3796     // Clear the backing to ARGB(0,0,0,0).
3797     current_canvas_->clear(SkColorSetARGB(0, 0, 0, 0));
3798
3799     // Adjust the |content_device_transform| to make sure filter extends are
3800     // drawn inside of the buffer.
3801     params.content_device_transform.Translate(-filter_bounds.x(),
3802                                               -filter_bounds.y());
3803
3804     // Also adjust the |rounded_corner_bounds| to the new location.
3805     if (params.mask_filter_info) {
3806       params.mask_filter_info->ApplyTransform(params.content_device_transform);
3807     }
3808
3809     // When Render Pass has a single quad inside we would draw that directly.
3810     if (bypass != render_pass_bypass_quads_.end()) {
3811       if (bypass_mode == BypassMode::kDrawTransparentQuad) {
3812         DrawColoredQuad(SkColors::kTransparent, &rpdq_params, &params);
3813       } else if (bypass_mode == BypassMode::kDrawBypassQuad) {
3814         DrawQuadInternal(bypass->second, &rpdq_params, &params);
3815       } else {
3816         NOTREACHED();
3817       }
3818     } else {
3819       DCHECK(src_quad_backing);
3820       auto content_image =
3821           skia_output_surface_->MakePromiseSkImageFromRenderPass(
3822               quad->render_pass_id, src_quad_backing->size,
3823               src_quad_backing->format, src_quad_backing->generate_mipmap,
3824               RenderPassBackingSkColorSpace(*src_quad_backing),
3825               src_quad_backing->mailbox);
3826       if (!content_image) {
3827         DLOG(ERROR) << "MakePromiseSkImageFromRenderPass() in "
3828                        "PrepareRenderPassOverlay() failed.";
3829         EndPaint(gfx::Rect(dst_overlay_backing.size), /*failed=*/true,
3830                  /*is_overlay=*/true);
3831         return;
3832       }
3833
3834       if (src_quad_backing->generate_mipmap) {
3835         params.sampling =
3836             SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
3837       }
3838
3839       params.vis_tex_coords = cc::MathUtil::ScaleRectProportional(
3840           quad->tex_coord_rect, gfx::RectF(quad->rect), params.visible_rect);
3841
3842       gfx::RectF valid_texel_bounds(content_image->width(),
3843                                     content_image->height());
3844
3845       SkPaint paint = params.paint(GetContentColorFilter());
3846
3847       DrawSingleImage(content_image.get(), valid_texel_bounds, &rpdq_params,
3848                       &paint, &params);
3849     }
3850
3851     current_canvas_ = nullptr;
3852     EndPaint(gfx::Rect(dst_overlay_backing.size), /*failed=*/false,
3853              /*is_overlay=*/true);
3854   }
3855
3856 #if BUILDFLAG(IS_APPLE)
3857   // Adjust |bounds_rect| to contain the whole buffer and at the right location.
3858   overlay->display_rect.set_origin(gfx::PointF(filter_bounds.origin()));
3859   overlay->display_rect.set_size(gfx::SizeF(buffer_size));
3860 #else   // BUILDFLAG(IS_OZONE)
3861   // TODO(fangzhoug): Merge Ozone and Apple code paths of delegated compositing.
3862
3863   // Set |uv_rect| to reflect rounding up from |filter_bounds| to |buffer_size|.
3864   overlay->uv_rect = gfx::RectF(filter_bounds.size());
3865   overlay->uv_rect.InvScale(buffer_size.width(), buffer_size.height());
3866
3867   if (absl::holds_alternative<gfx::OverlayTransform>(overlay->transform)) {
3868     // When using an OverlayTransform, the transform should be baked into the
3869     // display_rect.
3870     overlay->display_rect =
3871         quad->shared_quad_state->quad_to_target_transform.MapRect(
3872             gfx::RectF(filter_bounds));
3873     // Apply all clipping because we can't always delegate quads that extend
3874     // beyond window bounds in Lacros.
3875     gfx::Rect apply_clip = gfx::Rect(current_frame()->device_viewport_size);
3876     if (overlay->clip_rect.has_value()) {
3877       apply_clip.Intersect(overlay->clip_rect.value());
3878     }
3879     OverlayCandidate::ApplyClip(*overlay, gfx::RectF(apply_clip));
3880     overlay->clip_rect = absl::nullopt;
3881   }
3882
3883   // Fill in |format| and |color_space| information based on selected backing.
3884   overlay->color_space = color_space;
3885   overlay->format = SinglePlaneSharedImageFormatToBufferFormat(buffer_format);
3886 #endif  // BUILDFLAG(IS_APPLE)
3887 }
3888 #endif  // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
3889
3890 void SkiaRenderer::EndPaint(const gfx::Rect& update_rect,
3891                             bool failed,
3892                             bool is_overlay) {
3893   base::OnceClosure on_finished_callback;
3894   base::OnceCallback<void(gfx::GpuFenceHandle)> on_return_release_fence_cb;
3895   // If SkiaRenderer has not failed, prepare callbacks and pass them to
3896   // SkiaOutputSurface.
3897   if (!failed) {
3898     // Signal |current_frame_resource_fence_| when the root render pass is
3899     // finished.
3900     if (current_gpu_commands_completed_fence_->was_set()) {
3901       on_finished_callback = base::BindPostTask(
3902           base::SingleThreadTaskRunner::GetCurrentDefault(),
3903           base::BindOnce(&FrameResourceGpuCommandsCompletedFence::Signal,
3904                          std::move(current_gpu_commands_completed_fence_)));
3905       current_gpu_commands_completed_fence_ =
3906           base::MakeRefCounted<FrameResourceGpuCommandsCompletedFence>(
3907               resource_provider());
3908       resource_provider()->SetGpuCommandsCompletedFence(
3909           current_gpu_commands_completed_fence_.get());
3910     }
3911
3912     // Return a release fence to the |current_release_fence_|
3913     // when the root render pass is finished.
3914     if (current_release_fence_->was_set()) {
3915       on_return_release_fence_cb = base::BindPostTask(
3916           base::SingleThreadTaskRunner::GetCurrentDefault(),
3917           base::BindOnce(&FrameResourceReleaseFence::SetReleaseFenceCallback,
3918                          std::move(current_release_fence_)));
3919       current_release_fence_ =
3920           base::MakeRefCounted<FrameResourceReleaseFence>(resource_provider());
3921       resource_provider()->SetReleaseFence(current_release_fence_.get());
3922     }
3923   }
3924   skia_output_surface_->EndPaint(std::move(on_finished_callback),
3925                                  std::move(on_return_release_fence_cb),
3926                                  update_rect, is_overlay);
3927 }
3928
3929 bool SkiaRenderer::IsRenderPassResourceAllocated(
3930     const AggregatedRenderPassId& render_pass_id) const {
3931   auto it = render_pass_backings_.find(render_pass_id);
3932   return it != render_pass_backings_.end();
3933 }
3934
3935 gfx::Size SkiaRenderer::GetRenderPassBackingPixelSize(
3936     const AggregatedRenderPassId& render_pass_id) {
3937   auto it = render_pass_backings_.find(render_pass_id);
3938   DCHECK(it != render_pass_backings_.end());
3939   return it->second.size;
3940 }
3941
3942 void SkiaRenderer::SetDelegatedInkPointRendererSkiaForTest(
3943     std::unique_ptr<DelegatedInkPointRendererSkia> renderer) {
3944   DCHECK(!delegated_ink_handler_);
3945   delegated_ink_handler_ = std::make_unique<DelegatedInkHandler>(
3946       output_surface_->capabilities().supports_delegated_ink);
3947   delegated_ink_handler_->SetDelegatedInkPointRendererForTest(
3948       std::move(renderer));
3949 }
3950
3951 void SkiaRenderer::DrawDelegatedInkTrail() {
3952   if (!delegated_ink_handler_ || !delegated_ink_handler_->GetInkRenderer())
3953     return;
3954
3955   delegated_ink_handler_->GetInkRenderer()->DrawDelegatedInkTrail(
3956       current_canvas_);
3957 }
3958
3959 DelegatedInkPointRendererBase* SkiaRenderer::GetDelegatedInkPointRenderer(
3960     bool create_if_necessary) {
3961   if (!delegated_ink_handler_ && !create_if_necessary)
3962     return nullptr;
3963
3964   if (!delegated_ink_handler_) {
3965     delegated_ink_handler_ = std::make_unique<DelegatedInkHandler>(
3966         output_surface_->capabilities().supports_delegated_ink);
3967   }
3968
3969   return delegated_ink_handler_->GetInkRenderer();
3970 }
3971
3972 bool SkiaRenderer::SupportsBGRA() const {
3973   return skia_output_surface_->SupportsBGRA();
3974 }
3975
3976 void SkiaRenderer::SetDelegatedInkMetadata(
3977     std::unique_ptr<gfx::DelegatedInkMetadata> metadata) {
3978   if (!delegated_ink_handler_) {
3979     delegated_ink_handler_ = std::make_unique<DelegatedInkHandler>(
3980         output_surface_->capabilities().supports_delegated_ink);
3981   }
3982   delegated_ink_handler_->SetDelegatedInkMetadata(std::move(metadata));
3983 }
3984
3985 bool SkiaRenderer::UsingSkiaForDelegatedInk() const {
3986   return delegated_ink_handler_ && delegated_ink_handler_->GetInkRenderer();
3987 }
3988
3989 gfx::Rect SkiaRenderer::GetCurrentFramebufferDamage() const {
3990   if (buffer_queue_) {
3991     return buffer_queue_->CurrentBufferDamage();
3992   } else {
3993     return skia_output_surface_->GetCurrentFramebufferDamage();
3994   }
3995 }
3996
3997 void SkiaRenderer::Reshape(const OutputSurface::ReshapeParams& reshape_params) {
3998   if (buffer_queue_) {
3999     buffer_queue_->Reshape(reshape_params.size, reshape_params.color_space,
4000                            reshape_params.format);
4001   }
4002   // Even if we have our own BufferQueue, we still need to forward the Reshape()
4003   // call down to the OutputPresenter.
4004   skia_output_surface_->Reshape(reshape_params);
4005 }
4006
4007 void SkiaRenderer::EnsureMinNumberOfBuffers(int n) {
4008   if (buffer_queue_) {
4009     buffer_queue_->EnsureMinNumberOfBuffers(n);
4010   } else if (skia_output_surface_->EnsureMinNumberOfBuffers(n)) {
4011     ReallocatedFrameBuffers();
4012   }
4013 }
4014
4015 gpu::Mailbox SkiaRenderer::GetPrimaryPlaneOverlayTestingMailbox() {
4016   // For the purpose of testing the overlay configuration, the mailbox for ANY
4017   // buffer from BufferQueue is good enough because they're all created with
4018   // identical properties.
4019   // At the time we're testing overlays we don't yet know which mailbox will be
4020   // presented this frame so we'll just use the last swapped buffer. (We might
4021   // present a new frame's mailbox, or if we empty-swap we'll present the
4022   // previous frame's mailbox.)
4023   if (buffer_queue_) {
4024     return buffer_queue_->GetLastSwappedBuffer();
4025   } else {
4026     // OutputSurface::GetOverlayMailbox() returns the mailbox for the last
4027     // swapped buffer.
4028     return skia_output_surface_->GetOverlayMailbox();
4029   }
4030 }
4031
4032 #if BUILDFLAG(IS_OZONE)
4033 const gpu::Mailbox SkiaRenderer::GetImageMailboxForColor(
4034     const SkColor4f& color) {
4035   // Currently the Wayland protocol does not have protocol to support solid
4036   // color quads natively as surfaces. Here we create tiny 1x1 image buffers
4037   // in the color space of the frame buffer and fill them with the quad's solid
4038   // color. These freshly created buffers are then treated like any other
4039   // overlay via the mailbox interface.
4040   gpu::Mailbox solid_color_mailbox;
4041   // First try for an existing same color image.
4042   auto it = solid_color_buffers_.find(color.toSkColor());
4043   if (it != solid_color_buffers_.end()) {
4044     solid_color_mailbox = it->second.mailbox;
4045     it->second.use_count++;
4046   } else {
4047     solid_color_mailbox = skia_output_surface_->CreateSolidColorSharedImage(
4048         color, reshape_color_space());
4049
4050     solid_color_buffers_.insert({color.toSkColor(), {solid_color_mailbox, 1}});
4051   }
4052   return solid_color_mailbox;
4053 }
4054
4055 void SkiaRenderer::MaybeScheduleBackgroundImage(
4056     OverlayProcessorInterface::CandidateList& overlay_list) {
4057   if (!output_surface_->capabilities().needs_background_image) {
4058     return;
4059   }
4060
4061   OverlayCandidate background_candidate;
4062   background_candidate.color_space = reshape_color_space();
4063   background_candidate.display_rect =
4064       gfx::RectF(gfx::SizeF(viewport_size_for_swap_buffers()));
4065   background_candidate.color = SkColors::kTransparent;
4066   background_candidate.plane_z_order = INT32_MIN;
4067   // ScheduleOverlays() will convert this to a buffer-backed solid color overlay
4068   // if necessary.
4069   background_candidate.is_solid_color = true;
4070   if (overlay_processor_) {
4071     background_candidate.damage_rect =
4072         overlay_processor_->GetUnassignedDamage();
4073     DBG_DRAW_RECT("damage_not_assigned", background_candidate.damage_rect);
4074   }
4075
4076   overlay_list.push_back(background_candidate);
4077 }
4078
4079 void SkiaRenderer::MaybeDecrementSolidColorBuffers(
4080     std::vector<OverlayLock>& finished_locks) {
4081   if (output_surface_->capabilities()
4082           .supports_non_backed_solid_color_overlays ||
4083       output_surface_->capabilities().supports_single_pixel_buffer) {
4084     return;
4085   }
4086   for (auto& lock : finished_locks) {
4087     for (auto& entry : solid_color_buffers_) {
4088       if (entry.second.mailbox == lock.mailbox()) {
4089         entry.second.use_count--;
4090         break;
4091       }
4092     }
4093   }
4094 }
4095 #endif  // BUILDFLAG(IS_OZONE)
4096
4097 SkiaRenderer::OverlayLock::OverlayLock(
4098     DisplayResourceProvider* resource_provider,
4099     ResourceId resource_id) {
4100   resource_lock.emplace(resource_provider, resource_id);
4101 }
4102
4103 SkiaRenderer::OverlayLock::~OverlayLock() = default;
4104
4105 SkiaRenderer::OverlayLock::OverlayLock(SkiaRenderer::OverlayLock&& other) {
4106   resource_lock = std::move(other.resource_lock);
4107
4108 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
4109   render_pass_lock = std::move(other.render_pass_lock);
4110 #endif  // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
4111 }
4112
4113 SkiaRenderer::OverlayLock& SkiaRenderer::OverlayLock::OverlayLock::operator=(
4114     SkiaRenderer::OverlayLock&& other) {
4115   resource_lock = std::move(other.resource_lock);
4116
4117 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
4118   render_pass_lock = std::move(other.render_pass_lock);
4119 #endif  // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
4120
4121   return *this;
4122 }
4123
4124 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
4125 SkiaRenderer::OverlayLock::OverlayLock(gpu::Mailbox mailbox) {
4126   render_pass_lock.emplace(mailbox);
4127 }
4128
4129 bool SkiaRenderer::OverlayLockComparator::operator()(
4130     const OverlayLock& lhs,
4131     const OverlayLock& rhs) const {
4132   return lhs.mailbox() < rhs.mailbox();
4133 }
4134 #endif  // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
4135
4136 }  // namespace viz