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.
5 #include "components/viz/service/display/skia_renderer.h"
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"
98 #if BUILDFLAG(IS_ANDROID)
99 #include "components/viz/service/display/overlay_processor_surface_control.h"
102 #if defined(TIZEN_VIDEO_HOLE)
103 #include "base/numerics/math_constants.h"
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;
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);
123 // Additional YUV information to skia renderer to draw 9- and 10- bits color.
125 YUVInput() { memset(this, 0, sizeof(*this)); }
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());
137 bool IsTextureResource(DisplayResourceProviderSkia* resource_provider,
138 ResourceId resource_id) {
139 return !resource_provider->IsResourceSoftwareBacked(resource_id);
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.
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()) <
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()) <
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;
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;
172 bool IsExteriorEdge(unsigned corner_mask1, unsigned corner_mask2) {
173 return (corner_mask1 & corner_mask2) != 0;
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;
196 // This also modifies draw_region to clean up any degeneracies
197 void GetClippedEdgeFlags(const DrawQuad* quad,
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
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);
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;
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);
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
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.
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
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;
287 // Other quad types do not expose nearest_neighbor.
292 SkSamplingOptions GetSampling(const DrawQuad* quad) {
293 if (UseNearestNeighborSampling(quad))
294 return SkSamplingOptions(SkFilterMode::kNearest);
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);
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;
320 gfx::RectF safe_texels = valid_texel_bounds;
321 safe_texels.Inset(0.5f);
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
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;
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;
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;
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'.
353 return opacity->makeComposed(std::move(in));
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]
363 bool IsPorterDuffBlendMode(SkBlendMode blendMode) {
364 return blendMode <= SkBlendMode::kLastCoeffMode;
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.
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;
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)) {
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;
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)) {
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;
407 SkYUVAInfo::Subsampling SubsamplingFromTextureSizes(gfx::Size ya_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;
424 return SkYUVAInfo::Subsampling::kUnknown;
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;
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,
441 SkBlendMode blend_mode,
443 const SkSamplingOptions& sampling,
444 const gfx::QuadF* draw_region);
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.
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)
460 // Final blend mode to use, respecting quad settings + opacity optimizations
461 SkBlendMode blend_mode;
462 // Final opacity of quad
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;
479 SkPaint paint(sk_sp<SkColorFilter> color_filter) const {
482 p.setColorFilter(color_filter);
484 p.setBlendMode(blend_mode);
485 p.setAlphaf(opacity);
486 p.setAntiAlias(aa_flags != SkCanvas::kNone_QuadAAFlags);
490 SkPath draw_region_in_path() const {
492 return SkPath::Polygon(draw_region->points,
493 std::size(draw_region->points),
499 void ApplyScissor(const SkiaRenderer* renderer,
500 const DrawQuad* quad,
501 const absl::optional<gfx::Rect>& scissor_to_apply);
504 SkiaRenderer::DrawQuadParams::DrawQuadParams(const gfx::Transform& cdt,
505 const gfx::RectF& rect,
506 const gfx::RectF& visible_rect,
508 SkBlendMode blend_mode,
510 const SkSamplingOptions& sampling,
511 const gfx::QuadF* draw_region)
512 : content_device_transform(cdt),
514 visible_rect(visible_rect),
515 vis_tex_coords(visible_rect),
517 blend_mode(blend_mode),
521 this->draw_region.emplace(*draw_region);
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;
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
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
548 // Scoped helper class for building SkImage from resource id.
549 class SkiaRenderer::ScopedSkImageBuilder {
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);
559 ScopedSkImageBuilder(const ScopedSkImageBuilder&) = delete;
560 ScopedSkImageBuilder& operator=(const ScopedSkImageBuilder&) = delete;
562 ~ScopedSkImageBuilder() = default;
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_; }
569 sk_sp<SkImage> sk_image_;
570 raw_ptr<const cc::PaintOpBuffer> paint_op_buffer_ = nullptr;
571 absl::optional<SkColor4f> clear_color_;
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) {
584 auto* resource_provider = skia_renderer->resource_provider();
585 DCHECK(IsTextureResource(resource_provider, resource_id));
587 auto* image_context = skia_renderer->lock_set_for_external_use_->LockResource(
588 resource_id, maybe_concurrent_reads, raw_draw_if_possible);
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);
599 // We need the original TransferableResource.color_space for YUV => RGB
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.";
609 if (sk_image_ && override_color_space) {
610 sk_image_ = sk_image_->reinterpretColorSpace(override_color_space);
614 class SkiaRenderer::ScopedYUVSkImageBuilder {
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()));
629 // The image is always either NV12 or I420, possibly with a separate alpha
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;
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;
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));
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));
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.";
673 ScopedYUVSkImageBuilder(const ScopedYUVSkImageBuilder&) = delete;
674 ScopedYUVSkImageBuilder& operator=(const ScopedYUVSkImageBuilder&) = delete;
676 ~ScopedYUVSkImageBuilder() = default;
678 const SkImage* sk_image() const { return sk_image_.get(); }
681 sk_sp<SkImage> sk_image_;
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|).
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;
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());
706 // Get the resolved mask image and calculated transform matrix baked into an
708 sk_sp<SkShader> GetOrCreateSkShader(SkiaRenderer* renderer) const;
711 ResourceId mask_resource_id_;
713 // Map mask rect to full quad rect so that mask coordinates don't change
715 SkMatrix mask_to_quad_matrix_;
717 mutable sk_sp<SkShader> sk_shader_ = nullptr;
720 DrawRPDQParams() : filter_bounds(SkRect::MakeEmpty()) {}
722 explicit DrawRPDQParams(const gfx::RectF& visible_rect)
723 : filter_bounds(gfx::RectFToSkRect(visible_rect)) {}
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;
744 // Multiplier used for downscaling backdrop filter.
745 float backdrop_filter_quality = 1.0f;
747 // Geometry from the bypassed RenderPassDrawQuad.
748 absl::optional<BypassGeometry> bypass_geometry;
750 // True when there is an |image_filter| and it's not equivalent to
752 bool has_complex_image_filter() const {
753 return image_filter && !color_filter;
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) {
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));
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);
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
781 void SetBackdropFilterClip(SkCanvas* canvas,
782 const DrawQuadParams* params) const;
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;
793 sk_sp<SkShader> SkiaRenderer::DrawRPDQParams::MaskShader::GetOrCreateSkShader(
794 SkiaRenderer* renderer) const {
799 ScopedSkImageBuilder mask_image_builder(renderer, mask_resource_id_,
800 /*maybe_concurrent_reads=*/false);
801 const SkImage* mask_image = mask_image_builder.sk_image();
803 sk_shader_ = mask_image->makeShader(
804 SkTileMode::kClamp, SkTileMode::kClamp,
805 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone),
806 &mask_to_quad_matrix_);
811 void SkiaRenderer::DrawRPDQParams::SetBackdropFilterClip(
813 const DrawQuadParams* params) const {
814 if (!backdrop_filter) {
815 return; // No clipping necessary
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);
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);
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);
834 canvas->clipPath(clip_path, aa);
838 void SkiaRenderer::DrawRPDQParams::ClearOutsideBackdropBounds(
840 const DrawQuadParams* params) const {
841 if (!backdrop_filter || !backdrop_filter_bounds) {
842 return; // Nothing to clear within the layer
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;
852 canvas->clipRRect(*backdrop_filter_bounds, SkClipOp::kDifference, aa);
853 canvas->clear(SK_ColorTRANSPARENT);
856 if (params->draw_region) {
858 canvas->concat(bypass_geometry->transform);
859 canvas->clipPath(params->draw_region_in_path(), SkClipOp::kDifference, aa);
860 canvas->clear(SK_ColorTRANSPARENT);
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
871 canvas->clipRect(content, SkClipOp::kDifference, aa);
872 canvas->clear(SK_ColorTRANSPARENT);
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 {
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;
894 // ResourceFence implementation.
895 bool HasPassed() override { return passed_; }
896 gfx::GpuFenceHandle GetGpuFenceHandle() override {
898 return gfx::GpuFenceHandle();
907 ~FrameResourceGpuCommandsCompletedFence() override = default;
909 bool passed_ = false;
912 // FrameResourceFence that gets a ReleaseFence which is later set to returned
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 {
918 explicit FrameResourceReleaseFence(
919 DisplayResourceProviderSkia* resource_provider)
920 : ResourceFence(resource_provider) {}
921 FrameResourceReleaseFence() = delete;
922 FrameResourceReleaseFence(const FrameResourceReleaseFence&) = delete;
923 FrameResourceReleaseFence& operator=(const FrameResourceReleaseFence&) =
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();
932 void SetReleaseFenceCallback(gfx::GpuFenceHandle release_fence) {
933 release_fence_ = std::move(release_fence);
938 ~FrameResourceReleaseFence() override = default;
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_;
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,
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)),
962 is_using_raw_draw_(features::IsUsingRawDraw()) {
963 DCHECK(skia_output_surface_);
964 lock_set_for_external_use_.emplace(resource_provider, skia_output_surface_);
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>(
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());
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.
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
991 : output_surface->capabilities().number_of_buffers;
992 buffer_queue_ = std::make_unique<BufferQueue>(
993 skia_output_surface_, skia_output_surface_->GetSurfaceHandle(),
999 SkiaRenderer::~SkiaRenderer() = default;
1001 bool SkiaRenderer::CanPartialSwap() {
1002 return output_surface_->capabilities().supports_post_sub_buffer;
1005 void SkiaRenderer::BeginDrawingFrame() {
1006 TRACE_EVENT0("viz", "SkiaRenderer::BeginDrawingFrame");
1008 DCHECK(!current_gpu_commands_completed_fence_->was_set());
1009 DCHECK(!current_release_fence_->was_set());
1012 void SkiaRenderer::FinishDrawingFrame() {
1013 TRACE_EVENT0("viz", "SkiaRenderer::FinishDrawingFrame");
1014 current_canvas_ = nullptr;
1016 swap_buffer_rect_ = current_frame()->root_damage_rect;
1018 #if BUILDFLAG(IS_OZONE)
1019 MaybeScheduleBackgroundImage(current_frame()->overlay_list);
1020 #endif // BUILDFLAG(IS_OZONE)
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();
1026 if (!output_surface_->capabilities().renderer_allocates_images) {
1027 skia_output_surface_->ScheduleOutputSurfaceAsOverlay(surface_plane);
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());
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();
1040 surface_candidate.transform = surface_plane.transform;
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;
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);
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
1072 if (buffer_queue_) {
1073 buffer_queue_->DestroyBuffers();
1075 #endif // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_APPLE)
1079 debug_tint_modulate_count_++;
1082 void SkiaRenderer::SwapBuffers(SwapFrameData swap_frame_data) {
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");
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;
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_;
1103 if (delegated_ink_handler_ && !UsingSkiaForDelegatedInk()) {
1104 output_frame.delegated_ink_metadata =
1105 delegated_ink_handler_->TakeMetadata();
1107 #if BUILDFLAG(IS_APPLE)
1108 output_frame.ca_layer_error_code = swap_frame_data.ca_layer_error_code;
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);
1117 skia_output_surface_->SwapBuffers(std::move(output_frame));
1118 swap_buffer_rect_ = gfx::Rect();
1120 FlushOutputSurface();
1122 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
1123 // Delete render pass overlay backings from the previous frame that will not
1125 for (auto& overlay : available_render_pass_overlay_backings_) {
1126 skia_output_surface_->DestroySharedImage(
1127 overlay.render_pass_backing.mailbox);
1129 available_render_pass_overlay_backings_.clear();
1130 #endif // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
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);
1142 #endif // BUILDFLAG(IS_OZONE)
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_);
1150 #if BUILDFLAG(IS_OZONE)
1151 MaybeDecrementSolidColorBuffers(pending_overlay_locks_.back());
1152 #endif // BUILDFLAG(IS_OZONE)
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);
1159 swap_buffer_rect_ = gfx::Rect();
1161 FlushOutputSurface();
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();
1171 if (buffer_queue_) {
1172 if (params.swap_response.result ==
1173 gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS) {
1174 buffer_queue_->RecreateBuffers();
1176 buffer_queue_->SwapBuffersComplete();
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());
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()));
1195 #if BUILDFLAG(IS_OZONE)
1196 MaybeDecrementSolidColorBuffers(committed_overlay_locks_);
1197 #endif // BUILDFLAG(IS_OZONE)
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));
1207 #endif // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
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();
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
1222 read_lock_release_fence_overlay_locks_.pop_front();
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);
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.
1236 base::ranges::find(in_flight_render_pass_overlay_backings_, mailbox,
1237 [](const RenderPassOverlayParams& overlay) {
1238 return overlay.render_pass_backing.mailbox;
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);
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)
1255 awaiting_release_overlay_locks_.erase(iter);
1259 #endif // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
1262 bool SkiaRenderer::FlippedFramebuffer() const {
1263 // TODO(weiliangc): Make sure flipped correctly for Windows.
1264 // (crbug.com/644851)
1268 void SkiaRenderer::EnsureScissorTestDisabled() {
1269 scissor_rect_.reset();
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();
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);
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);
1294 if (is_root && debug_settings_->show_overdraw_feedback) {
1295 DCHECK(output_surface_->capabilities().renderer_allocates_images);
1296 current_canvas_ = skia_output_surface_->RecordOverdrawForCurrentPaint();
1300 void SkiaRenderer::SetScissorTestRect(const gfx::Rect& scissor_rect) {
1301 scissor_rect_ = absl::optional<gfx::Rect>(scissor_rect);
1304 void SkiaRenderer::ClearCanvas(SkColor4f color) {
1305 if (!current_canvas_)
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);
1314 current_canvas_->clear(color);
1318 void SkiaRenderer::ClearFramebuffer() {
1319 if (current_frame()->current_render_pass->has_transparent_background) {
1320 ClearCanvas(SkColors::kTransparent);
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);
1334 void SkiaRenderer::BeginDrawingRenderPass(
1336 const gfx::Rect& render_pass_update_rect) {
1337 TRACE_EVENT0("viz", "SkiaRenderer::BeginDrawingRenderPass");
1339 if (render_pass_update_rect == current_viewport_rect_) {
1340 EnsureScissorTestDisabled();
1342 SetScissorTestRect(render_pass_update_rect);
1349 current_render_pass_update_rect_ = render_pass_update_rect;
1352 void SkiaRenderer::DoDrawQuad(const DrawQuad* quad,
1353 const gfx::QuadF* draw_region) {
1354 if (!current_canvas_)
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, ¶ms);
1364 void SkiaRenderer::DrawQuadInternal(const DrawQuad* quad,
1365 const DrawRPDQParams* rpdq_params,
1366 DrawQuadParams* params) {
1367 if (MustFlushBatchedQuads(quad, rpdq_params, *params)) {
1368 FlushBatchedQuads();
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();
1378 DrawColoredQuad(SkColors::kMagenta, rpdq_params, params);
1380 DrawColoredQuad(SkColors::kBlack, rpdq_params, params);
1385 switch (quad->material) {
1386 case DrawQuad::Material::kAggregatedRenderPass:
1387 DrawRenderPassQuad(AggregatedRenderPassDrawQuad::MaterialCast(quad),
1388 rpdq_params, params);
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);
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);
1403 case DrawQuad::Material::kCompositorRenderPass:
1404 // RenderPassDrawQuads should be converted to
1405 // AggregatedRenderPassDrawQuads at this point.
1406 DrawUnsupportedQuad(quad, rpdq_params, params);
1409 case DrawQuad::Material::kSolidColor:
1410 DrawSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad), rpdq_params,
1413 case DrawQuad::Material::kTextureContent:
1414 DrawTextureQuad(TextureDrawQuad::MaterialCast(quad), rpdq_params, params);
1416 case DrawQuad::Material::kTiledContent:
1417 DrawTileDrawQuad(TileDrawQuad::MaterialCast(quad), rpdq_params, params);
1419 case DrawQuad::Material::kSharedElement:
1420 DrawUnsupportedQuad(quad, rpdq_params, params);
1423 case DrawQuad::Material::kYuvVideoContent:
1424 DrawYUVVideoQuad(YUVVideoDrawQuad::MaterialCast(quad), rpdq_params,
1427 case DrawQuad::Material::kInvalid:
1428 DrawUnsupportedQuad(quad, rpdq_params, params);
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);
1441 // If we've reached here, it's a new quad type that needs a
1442 // dedicated implementation
1443 DrawUnsupportedQuad(quad, rpdq_params, params);
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());
1457 if (scissor_rect.has_value()) {
1458 current_canvas_->clipRect(gfx::RectToSkRect(*scissor_rect));
1461 if (mask_filter_info.has_value()) {
1462 current_canvas_->clipRRect(
1463 static_cast<SkRRect>(mask_filter_info->rounded_corner_bounds()),
1464 /*doAntiAlias=*/true);
1466 if (mask_filter_info->HasGradientMask())
1467 PrepareGradient(mask_filter_info);
1471 SkMatrix m = gfx::TransformToFlattenedSkMatrix(*cdt);
1472 current_canvas_->concat(m);
1476 #define MaskColor(a) SkColorSetARGB(a, a, a, a);
1478 void SkiaRenderer::PrepareGradient(
1479 const absl::optional<gfx::MaskFilterInfo>& mask_filter_info) {
1480 if (!mask_filter_info || !mask_filter_info->HasGradientMask())
1483 const gfx::RectF rect = mask_filter_info->bounds();
1484 const absl::optional<gfx::LinearGradient>& gradient_mask =
1485 mask_filter_info->gradient_mask();
1487 int16_t angle = gradient_mask->angle() % 360;
1488 if (angle < 0) angle += 360;
1490 SkPoint start_end[2];
1492 float rad_angle = gfx::DegToRad(static_cast<float>(angle));
1493 float s = std::sin(rad_angle);
1494 float c = std::cos(rad_angle);
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;
1503 start_end[0] = {start_x, start_y};
1504 start_end[1] = {end_x, end_y};
1506 start_end[0] = {end_x, end_y};
1507 start_end[1] = {start_x, start_y};
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;
1517 start_end[0] = {start_x, start_y};
1518 start_end[1] = {end_x, end_y};
1520 start_end[0] = {end_x, end_y};
1521 start_end[1] = {start_x, start_y};
1525 SkScalar positions[gfx::LinearGradient::kMaxStepSize];
1526 SkColor gradient_colors[gfx::LinearGradient::kMaxStepSize];
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);
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));
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);
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);
1554 params->blend_mode = SkBlendMode::kSrcOver;
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);
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));
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);
1574 void SkiaRenderer::PreparePaintOrCanvasForRPDQ(
1575 const DrawRPDQParams& rpdq_params,
1576 DrawQuadParams* params,
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;
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));
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);
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()));
1614 paint->setColorFilter(rpdq_params.color_filter);
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;
1628 paint->setImageFilter(rpdq_params.image_filter);
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);
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));
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;
1662 rpdq_params.color_filter->filterColor4f(*content_color, cs, cs);
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);
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);
1686 params.content_device_transform.PostConcat(target_to_device);
1687 params.content_device_transform.Flatten();
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, ¶ms.aa_flags, &*params.draw_region);
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;
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;
1716 params.opacity = 1.f;
1719 params.ApplyScissor(this, quad, scissor_rect);
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);
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());
1741 if (!scissor_to_apply) {
1742 // No scissor at all, which matches the DCHECK'ed state above
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;
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)
1756 // DebugBorderDrawQuads draw a path so they must be explicitly clipped.
1757 if (quad->material == DrawQuad::Material::kDebugBorder)
1760 // Intersection with scissor and a quadrilateral is not necessarily a quad,
1761 // so don't complicate things
1762 if (draw_region.has_value())
1765 if (!Is2dScaleTranslateTransform(content_device_transform)) {
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))
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) {
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) {
1800 gfx::RectF remapped_scissor =
1801 content_device_transform.MapRect(*local_scissor);
1802 if (gfx::ToRoundedRect(remapped_scissor) != *scissor_rect) {
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);
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;
1824 visible_rect.Intersect(*local_scissor);
1825 vis_tex_coords = visible_rect;
1826 scissor_rect.reset();
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);
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)
1847 // Only supports bypassing render passes with a single child quad and simple
1849 if (pass->quad_list.size() != 1) {
1853 // If it there are supposed to be mipmaps, the renderpass must exist
1854 if (pass->generate_mipmap)
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
1861 if (quad->material == DrawQuad::Material::kDebugBorder ||
1862 quad->material == DrawQuad::Material::kPictureContent)
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)
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))
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) {
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))
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
1901 if (!IsPorterDuffBlendMode(quad->shared_quad_state->blend_mode))
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));
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
1914 if (ShouldApplyRoundedCorner(quad))
1917 if (ShouldApplyGradientMask(quad))
1920 if (const auto* render_pass_quad =
1921 quad->DynamicCast<AggregatedRenderPassDrawQuad>()) {
1922 if (render_pass_quad->mask_resource_id()) {
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)) {
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;
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()) {
1948 *is_directly_drawable_with_single_rpdq = true;
1950 if (!base::FeatureList::IsEnabled(features::kAllowBypassRenderPassQuads)) {
1955 // The quad type knows how to apply RPDQ filters, and the quad settings can
1956 // be merged into the RPDQs settings in CalculateBypassParams.
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;
1972 return BypassMode::kSkip;
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.
1979 RenderPassPreservesContent(bypass_quad->shared_quad_state->blend_mode));
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);
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.
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));
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));
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);
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));
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);
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};
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);
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);
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);
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;
2071 // Combine anti-aliasing policy (use AND so that any draw_region clipping
2073 params->aa_flags &= bypass_quad_params.aa_flags;
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
2078 params->opacity *= bypass_quad_params.opacity;
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) {
2087 } else if (sampling.mipmap != SkMipmapMode::kNone) {
2090 return sampling.filter == SkFilterMode::kLinear ? 1 : 0;
2093 if (ord(bypass_quad_params.sampling) > ord(params->sampling)) {
2094 params->sampling = bypass_quad_params.sampling;
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());
2101 return BypassMode::kDrawBypassQuad;
2104 SkCanvas::ImageSetEntry SkiaRenderer::MakeEntry(
2105 const SkImage* image,
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()});
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;
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);
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
2132 if (constraint == SkCanvas::kFast_SrcRectConstraint ||
2133 valid_texel_bounds == params->vis_tex_coords) {
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
2142 if (!params->draw_region) {
2143 params->draw_region.emplace(gfx::QuadF(params->visible_rect));
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;
2154 return SkCanvas::kStrict_SrcRectConstraint;
2157 bool SkiaRenderer::MustFlushBatchedQuads(const DrawQuad* new_quad,
2158 const DrawRPDQParams* rpdq_params,
2159 const DrawQuadParams& params) const {
2160 if (batched_quads_.empty())
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.
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)
2174 if (batched_quad_state_.blend_mode != params.blend_mode ||
2175 batched_quad_state_.sampling != params.sampling)
2178 if (batched_quad_state_.scissor_rect != params.scissor_rect) {
2182 if (batched_quad_state_.mask_filter_info != params.mask_filter_info) {
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
2196 if (!batched_quads_.empty() && batched_quad_state_.constraint != constraint) {
2197 FlushBatchedQuads();
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;
2208 DCHECK(batched_quad_state_.constraint == constraint);
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);
2218 gfx::TransformToFlattenedSkMatrix(params->content_device_transform);
2220 if (batched_cdt_matrices_.empty() || batched_cdt_matrices_.back() != m) {
2221 batched_cdt_matrices_.push_back(m);
2223 int matrix_index = batched_cdt_matrices_.size() - 1;
2225 batched_quads_.push_back(MakeEntry(image, matrix_index, *params));
2228 void SkiaRenderer::FlushBatchedQuads() {
2229 TRACE_EVENT0("viz", "SkiaRenderer::FlushBatchedQuads");
2231 SkAutoCanvasRestore acr(current_canvas_, true /* do_save */);
2232 PrepareCanvas(batched_quad_state_.scissor_rect,
2233 batched_quad_state_.mask_filter_info, nullptr);
2236 sk_sp<SkColorFilter> color_filter = GetContentColorFilter();
2238 paint.setColorFilter(color_filter);
2239 paint.setBlendMode(batched_quad_state_.blend_mode);
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);
2246 batched_quads_.clear();
2247 batched_draw_regions_.clear();
2248 batched_cdt_matrices_.clear();
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());
2257 SkAutoCanvasRestore acr(current_canvas_, true /* do_save */);
2258 PrepareCanvas(params->scissor_rect, params->mask_filter_info,
2259 ¶ms->content_device_transform);
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);
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()) {
2286 paint.setColor(color, color_space);
2287 color = paint.getColor4f();
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);
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;
2309 dst_color.fA = floor(params->opacity * dst_color.fA * 255.f) / 255.f;
2311 color.fA = floor(params->opacity * color.fA * 255.f) / 255.f;
2314 const SkPoint* draw_region =
2315 params->draw_region ? params->draw_region->points : nullptr;
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
2323 color, params->blend_mode
2328 void SkiaRenderer::DrawSingleImage(const SkImage* image,
2329 const gfx::RectF& valid_texel_bounds,
2330 const DrawRPDQParams* rpdq_params,
2332 DrawQuadParams* params) {
2333 DCHECK(batched_quads_.empty());
2334 TRACE_EVENT0("viz", "SkiaRenderer::DrawSingleImage");
2336 SkAutoCanvasRestore acr(current_canvas_, true /* do_save */);
2338 PrepareCanvas(params->scissor_rect, params->mask_filter_info,
2339 ¶ms->content_device_transform);
2341 int matrix_index = -1;
2342 const SkMatrix* bypass_transform = nullptr;
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;
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;
2365 SkCanvas::SrcRectConstraint constraint =
2366 ResolveTextureConstraints(image, valid_texel_bounds, params);
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,
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();
2386 SkAutoCanvasRestore auto_canvas_restore(current_canvas_, true /* do_save */);
2387 PrepareCanvas(params->scissor_rect, params->mask_filter_info,
2388 ¶ms->content_device_transform);
2390 auto visible_rect = gfx::RectFToSkRect(params->visible_rect);
2391 current_canvas_->clipRect(visible_rect);
2393 if (params->draw_region) {
2394 bool aa = params->aa_flags != SkCanvas::kNone_QuadAAFlags;
2395 current_canvas_->clipPath(params->draw_region_in_path(), aa);
2398 if (quad->ShouldDrawWithBlending()) {
2399 auto paint = params->paint(nullptr);
2400 // TODO(penghuang): saveLayer() is expensive, try to avoid it as much as
2402 current_canvas_->saveLayer(&visible_rect, &paint);
2406 current_canvas_->drawColor(*clear_color);
2408 float scale_x = params->rect.width() / quad->tex_coord_rect.width();
2409 float scale_y = params->rect.height() / quad->tex_coord_rect.height();
2412 params->visible_rect.x() - params->vis_tex_coords.x() * scale_x;
2414 params->visible_rect.y() - params->vis_tex_coords.y() * scale_y;
2416 current_canvas_->translate(offset_x, offset_y);
2417 current_canvas_->scale(scale_x, scale_y);
2419 cc::PlaybackParams playback_params(nullptr, SkM44());
2420 buffer->Playback(current_canvas_, playback_params);
2423 void SkiaRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad,
2424 DrawQuadParams* params) {
2425 TRACE_EVENT0("viz", "SkiaRenderer::DrawDebugBorderQuad");
2426 DCHECK(batched_quads_.empty());
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);
2432 gfx::TransformToFlattenedSkMatrix(params->content_device_transform);
2434 SkPath path = params->draw_region
2435 ? params->draw_region_in_path()
2436 : SkPath::Rect(gfx::RectFToSkRect(params->visible_rect));
2437 path.transform(cdt);
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);
2446 current_canvas_->drawPath(path, paint);
2449 void SkiaRenderer::DrawPictureQuad(const PictureDrawQuad* quad,
2450 DrawQuadParams* params) {
2451 DCHECK(batched_quads_.empty());
2452 TRACE_EVENT0("viz", "SkiaRenderer::DrawPictureQuad");
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();
2462 SkAutoCanvasRestore acr(current_canvas_, true /* do_save */);
2463 PrepareCanvas(params->scissor_rect,
2464 params->mask_filter_info,
2465 ¶ms->content_device_transform);
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());
2473 if (params->draw_region) {
2474 current_canvas_->clipPath(params->draw_region_in_path(),
2475 paint.isAntiAlias());
2477 current_canvas_->clipRect(visible_rect, paint.isAntiAlias());
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);
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;
2498 // Treat all subnormal values as zero for performance.
2499 cc::ScopedSubnormalFloatDisabler disabler;
2501 raster_canvas->concat(SkMatrix::RectToRect(
2502 gfx::RectFToSkRect(quad->tex_coord_rect), gfx::RectToSkRect(quad->rect)));
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);
2510 void SkiaRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad,
2511 const DrawRPDQParams* rpdq_params,
2512 DrawQuadParams* params) {
2513 DrawColoredQuad(quad->color, rpdq_params, params);
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;
2528 sk_sp<SkColorSpace> override_color_space;
2529 if (needs_color_conversion_filter) {
2530 override_color_space = CurrentRenderPassSkColorSpace();
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)
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();
2552 // Only on android stream video can be composited.
2553 CHECK(!quad->is_stream_video);
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();
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);
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()));
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;
2587 if (!blend_background && !vertex_alpha && !needs_color_conversion_filter &&
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);
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();
2602 SkPaint paint = params->paint(GetContentColorFilter());
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);
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;
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];
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
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
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()};
2645 // Not sure how to emulate
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
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()));
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));
2688 // |cf| could be null if alpha in |quad->background_color| is 0.
2690 paint.setColorFilter(cf->makeComposed(paint.refColorFilter()));
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);
2699 DrawSingleImage(image, valid_texel_bounds, rpdq_params, &paint, params);
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());
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
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);
2723 params->vis_tex_coords = cc::MathUtil::ScaleRectProportional(
2724 quad->tex_coord_rect, gfx::RectF(quad->rect), params->visible_rect);
2726 bool using_raw_draw = builder.paint_op_buffer();
2727 if (is_using_raw_draw_) {
2728 UMA_HISTOGRAM_BOOLEAN(
2729 "Compositing.SkiaRenderer.DrawTileDrawQuad.UsingRawDraw",
2732 if (using_raw_draw) {
2733 DCHECK(!rpdq_params);
2734 DrawPaintOpBuffer(builder.paint_op_buffer(), builder.clear_color(), quad,
2739 const SkImage* image = builder.sk_image();
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());
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());
2758 SkPaint paint = params->paint(GetContentColorFilter());
2759 DrawSingleImage(image, valid_texel_bounds, rpdq_params, &paint, params);
2761 AddQuadToBatch(image, valid_texel_bounds, params);
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());
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();
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();
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())) {
2800 resource_provider()->IsOverlayCandidate(quad->u_plane_resource_id()));
2801 dst_color_space = gfx::ColorSpace::CreateSRGB();
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();
2815 params->vis_tex_coords = cc::MathUtil::ScaleRectProportional(
2816 quad->ya_tex_coord_rect(), gfx::RectF(quad->rect), params->visible_rect);
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);
2822 auto content_color_filter = GetContentColorFilter();
2823 if (content_color_filter)
2824 color_filter = content_color_filter->makeComposed(color_filter);
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);
2831 DrawSingleImage(image, quad->ya_tex_coord_rect(), rpdq_params, &paint,
2835 void SkiaRenderer::DrawUnsupportedQuad(const DrawQuad* quad,
2836 const DrawRPDQParams* rpdq_params,
2837 DrawQuadParams* params) {
2839 DrawColoredQuad(SkColors::kWhite, rpdq_params, params);
2841 DrawColoredQuad(SkColors::kMagenta, rpdq_params, params);
2845 void SkiaRenderer::ScheduleOverlays() {
2846 DCHECK(!current_gpu_commands_completed_fence_->was_set());
2847 DCHECK(!current_release_fence_->was_set());
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();
2854 if (current_frame()->overlay_list.empty())
2857 std::vector<gpu::SyncToken> sync_tokens;
2859 #if !BUILDFLAG(IS_WIN)
2860 DCHECK(output_surface_->capabilities().supports_surfaceless);
2863 for (auto& overlay : current_frame()->overlay_list) {
2864 if (overlay.is_root_render_pass) {
2868 #if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_APPLE)
2870 PrepareRenderPassOverlay(&overlay);
2871 locks.emplace_back(overlay.mailbox);
2875 DCHECK(!overlay.rpdq);
2878 if (overlay.is_solid_color) {
2879 DCHECK(overlay.color);
2880 DCHECK(!overlay.resource_id);
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);
2895 // All other platforms that support solid color overlays don't need fake
2897 DCHECK(output_surface_->capabilities()
2898 .supports_non_backed_solid_color_overlays);
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();
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());
2912 overlay.mailbox = lock.mailbox();
2913 DCHECK(!overlay.mailbox.IsZero());
2916 DCHECK(!current_gpu_commands_completed_fence_->was_set());
2917 DCHECK(!current_release_fence_->was_set());
2919 skia_output_surface_->ScheduleOverlays(
2920 std::move(current_frame()->overlay_list), std::move(sync_tokens));
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,
2933 current_frame()->display_color_spaces.GetSDRMaxLuminanceNits(),
2934 current_frame()->display_color_spaces.GetHDRMaxLuminanceRelative());
2938 SkColorMatrix ToColorMatrix(const SkM44& mat) {
2939 std::array<float, 20> values;
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);
2946 SkColorMatrix mat_out;
2947 mat_out.setRowMajor(values.data());
2952 sk_sp<SkColorFilter> SkiaRenderer::GetContentColorFilter() {
2953 sk_sp<SkColorFilter> color_transform = nullptr;
2955 current_frame()->current_render_pass == current_frame()->root_render_pass;
2957 if (is_root && output_surface_->color_matrix() != SkM44()) {
2959 SkColorFilters::Matrix(ToColorMatrix(output_surface_->color_matrix()));
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;
2971 SkColorMatrix color_mat;
2972 color_mat.setScale(rgb[0], rgb[1], rgb[2]);
2973 tint_transform = SkColorFilters::Matrix(color_mat);
2975 SkM44 mat44 = SkM44::ColMajor(
2976 cc::DebugColors::TintCompositedContentColorTransformMatrix().data());
2977 tint_transform = SkColorFilters::Matrix(ToColorMatrix(mat44));
2981 if (color_transform) {
2982 return tint_transform ? color_transform->makeComposed(tint_transform)
2985 return tint_transform;
2989 SkiaRenderer::DrawRPDQParams SkiaRenderer::CalculateRPDQParams(
2990 const AggregatedRenderPassDrawQuad* quad,
2991 const DrawQuadParams* params) {
2992 DrawRPDQParams rpdq_params(params->visible_rect);
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);
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) {
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());
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))
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);
3047 // Convert CC image filters into a SkImageFilter root node
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);
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);
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)) {
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);
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;
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));
3096 rpdq_params.backdrop_filter =
3097 to_sk_image_filter(std::move(bg_paint_filter), local_matrix);
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;
3113 // Scale by the filter's scale, but don't apply filter origin
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;
3121 backdrop_filter_bounds = result;
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();
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()) ==
3140 backdrop_filter_bounds.reset();
3146 if (local_clip_rect && !backdrop_rect.intersect(*local_clip_rect)) {
3147 rpdq_params.backdrop_filter = nullptr;
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,
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;
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);
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()) {
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()) {
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
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
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;
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),
3225 DLOG_IF(ERROR, !content_image)
3226 << "MakePromiseSkImageFromRenderPass() failed for render pass";
3231 if (backing.generate_mipmap)
3233 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
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());
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);
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();
3254 SkPaint paint = params->paint(GetContentColorFilter());
3256 DrawSingleImage(content_image.get(), valid_texel_bounds, &rpdq_params, &paint,
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");
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;
3275 skia_output_surface_->CopyOutput(geometry, CurrentRenderPassColorSpace(),
3276 std::move(request), mailbox);
3279 void SkiaRenderer::DidChangeVisibility() {
3281 output_surface_->EnsureBackbuffer();
3283 output_surface_->DiscardBackbuffer();
3286 void SkiaRenderer::FinishDrawingRenderPass() {
3287 TRACE_EVENT0("viz", "SkiaRenderer::FinishDrawingRenderPass");
3288 if (!current_canvas_)
3291 if (!batched_quads_.empty())
3292 FlushBatchedQuads();
3294 bool is_root_render_pass =
3295 current_frame()->current_render_pass == current_frame()->root_render_pass;
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();
3304 current_canvas_ = nullptr;
3305 // Non-root render passes that are scheduled as overlays will be painted in
3306 // PrepareRenderPassOverlay().
3308 skia_output_surface_->capabilities().renderer_allocates_images &&
3309 is_root_render_pass;
3310 EndPaint(current_render_pass_update_rect_, /*failed=*/false, is_overlay);
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)
3319 FlushOutputSurface();
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.
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);
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);
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;
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);
3380 // Delete RenderPass backings from the previous frame that will not be used
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);
3391 render_pass_backings_.erase(it);
3394 if (!passes_to_delete.empty()) {
3395 skia_output_surface_->RemoveRenderPassResource(std::move(passes_to_delete));
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;
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;
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));
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;
3436 if (requirements.is_scanout) {
3437 usage |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
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.
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;
3449 if (requirements.scanout_dcomp_surface &&
3450 !dcomp_surface_unsupported_format) {
3451 usage |= gpu::SHARED_IMAGE_USAGE_SCANOUT_DCOMP_SURFACE;
3453 // DComp surfaces are write-only, viz cannot sample them.
3454 usage &= ~gpu::SHARED_IMAGE_USAGE_DISPLAY_READ;
3457 DCHECK(!requirements.scanout_dcomp_surface);
3460 DCHECK(!requirements.scanout_dcomp_surface);
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(
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}));
3476 void SkiaRenderer::FlushOutputSurface() {
3477 auto sync_token = skia_output_surface_->Flush();
3478 lock_set_for_external_use_->UnlockResources(sync_token);
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.
3490 if (!can_skip_render_pass_overlay_)
3493 // Check if the render pass has been re-drawn.
3494 if (skipped_render_pass_ids_.count(render_pass_id) == 0)
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;
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) {
3516 for (auto rit = available_render_pass_overlay_backings_.rbegin();
3517 rit != available_render_pass_overlay_backings_.rend();
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.
3523 available_render_pass_overlay_backings_.begin() +
3524 (available_render_pass_overlay_backings_.size() - index - 1);
3525 overlay_found = &*rit;
3531 if (!overlay_found) {
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);
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 =
3547 ? (overlay_found->backdrop_filters == *backdrop_filters)
3548 : (overlay_found->backdrop_filters == cc::FilterOperations());
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();
3558 *output_render_pass_overlay = overlay_found;
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;
3582 if (it == available_render_pass_overlay_backings_.end()) {
3583 // Allocate the image for render pass overlay if there is no existing
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,
3594 RenderPassAlphaType::kPremul,
3598 /*is_scanout=*/true,
3599 /*scanout_dcomp_surface=*/false};
3601 overlay_params = *it;
3602 available_render_pass_overlay_backings_.erase(it);
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);
3610 if (const cc::FilterOperations* filters = FiltersForPass(render_pass_id);
3612 overlay_params.filters = *filters;
3614 if (const cc::FilterOperations* backdrop_filters =
3615 BackdropFiltersForPass(render_pass_id);
3617 overlay_params.backdrop_filters = *backdrop_filters;
3620 in_flight_render_pass_overlay_backings_.push_back(overlay_params);
3622 return &in_flight_render_pass_overlay_backings_.back();
3625 void SkiaRenderer::PrepareRenderPassOverlay(
3626 OverlayProcessorInterface::PlatformOverlayCandidate* overlay) {
3627 DCHECK(!current_canvas_);
3628 DCHECK(batched_quads_.empty());
3629 DCHECK(overlay->rpdq);
3631 auto* const quad = overlay->rpdq;
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;
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 ¤t_frame()->current_render_pass, current_frame()->root_render_pass);
3645 auto* shared_quad_state =
3646 const_cast<SharedQuadState*>(quad->shared_quad_state);
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();
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));
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,
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);
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()));
3698 DrawQuadParams params;
3699 DrawRPDQParams rpdq_params{gfx::RectF()};
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
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, ¶ms);
3717 const gfx::Rect filter_bounds =
3718 gfx::SkIRectToRect(rpdq_params.filter_bounds.roundOut());
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()) {
3726 SharedImageFormat buffer_format;
3727 gfx::ColorSpace color_space;
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, ¶ms);
3735 if (bypass_mode == BypassMode::kSkip) {
3739 // For bypassed render pass, we use the same format and color space for the
3741 buffer_format = GetSinglePlaneSharedImageFormat(reshape_buffer_format());
3742 color_space = reshape_color_space();
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;
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));
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);
3768 DCHECK(overlay_params);
3769 UMA_HISTOGRAM_BOOLEAN(
3770 "Compositing.SkiaRenderer.SkipOverlayRenderPassDrawQuad",
3771 can_skip_render_pass);
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;
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",
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_) {
3792 << "BeginPaintRenderPass() in PrepareRenderPassOverlay() failed.";
3796 // Clear the backing to ARGB(0,0,0,0).
3797 current_canvas_->clear(SkColorSetARGB(0, 0, 0, 0));
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());
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);
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, ¶ms);
3813 } else if (bypass_mode == BypassMode::kDrawBypassQuad) {
3814 DrawQuadInternal(bypass->second, &rpdq_params, ¶ms);
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);
3834 if (src_quad_backing->generate_mipmap) {
3836 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
3839 params.vis_tex_coords = cc::MathUtil::ScaleRectProportional(
3840 quad->tex_coord_rect, gfx::RectF(quad->rect), params.visible_rect);
3842 gfx::RectF valid_texel_bounds(content_image->width(),
3843 content_image->height());
3845 SkPaint paint = params.paint(GetContentColorFilter());
3847 DrawSingleImage(content_image.get(), valid_texel_bounds, &rpdq_params,
3851 current_canvas_ = nullptr;
3852 EndPaint(gfx::Rect(dst_overlay_backing.size), /*failed=*/false,
3853 /*is_overlay=*/true);
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.
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());
3867 if (absl::holds_alternative<gfx::OverlayTransform>(overlay->transform)) {
3868 // When using an OverlayTransform, the transform should be baked into the
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());
3879 OverlayCandidate::ApplyClip(*overlay, gfx::RectF(apply_clip));
3880 overlay->clip_rect = absl::nullopt;
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)
3888 #endif // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
3890 void SkiaRenderer::EndPaint(const gfx::Rect& update_rect,
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.
3898 // Signal |current_frame_resource_fence_| when the root render pass is
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());
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());
3924 skia_output_surface_->EndPaint(std::move(on_finished_callback),
3925 std::move(on_return_release_fence_cb),
3926 update_rect, is_overlay);
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();
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;
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));
3951 void SkiaRenderer::DrawDelegatedInkTrail() {
3952 if (!delegated_ink_handler_ || !delegated_ink_handler_->GetInkRenderer())
3955 delegated_ink_handler_->GetInkRenderer()->DrawDelegatedInkTrail(
3959 DelegatedInkPointRendererBase* SkiaRenderer::GetDelegatedInkPointRenderer(
3960 bool create_if_necessary) {
3961 if (!delegated_ink_handler_ && !create_if_necessary)
3964 if (!delegated_ink_handler_) {
3965 delegated_ink_handler_ = std::make_unique<DelegatedInkHandler>(
3966 output_surface_->capabilities().supports_delegated_ink);
3969 return delegated_ink_handler_->GetInkRenderer();
3972 bool SkiaRenderer::SupportsBGRA() const {
3973 return skia_output_surface_->SupportsBGRA();
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);
3982 delegated_ink_handler_->SetDelegatedInkMetadata(std::move(metadata));
3985 bool SkiaRenderer::UsingSkiaForDelegatedInk() const {
3986 return delegated_ink_handler_ && delegated_ink_handler_->GetInkRenderer();
3989 gfx::Rect SkiaRenderer::GetCurrentFramebufferDamage() const {
3990 if (buffer_queue_) {
3991 return buffer_queue_->CurrentBufferDamage();
3993 return skia_output_surface_->GetCurrentFramebufferDamage();
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);
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);
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();
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();
4026 // OutputSurface::GetOverlayMailbox() returns the mailbox for the last
4028 return skia_output_surface_->GetOverlayMailbox();
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++;
4047 solid_color_mailbox = skia_output_surface_->CreateSolidColorSharedImage(
4048 color, reshape_color_space());
4050 solid_color_buffers_.insert({color.toSkColor(), {solid_color_mailbox, 1}});
4052 return solid_color_mailbox;
4055 void SkiaRenderer::MaybeScheduleBackgroundImage(
4056 OverlayProcessorInterface::CandidateList& overlay_list) {
4057 if (!output_surface_->capabilities().needs_background_image) {
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
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);
4076 overlay_list.push_back(background_candidate);
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) {
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--;
4095 #endif // BUILDFLAG(IS_OZONE)
4097 SkiaRenderer::OverlayLock::OverlayLock(
4098 DisplayResourceProvider* resource_provider,
4099 ResourceId resource_id) {
4100 resource_lock.emplace(resource_provider, resource_id);
4103 SkiaRenderer::OverlayLock::~OverlayLock() = default;
4105 SkiaRenderer::OverlayLock::OverlayLock(SkiaRenderer::OverlayLock&& other) {
4106 resource_lock = std::move(other.resource_lock);
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)
4113 SkiaRenderer::OverlayLock& SkiaRenderer::OverlayLock::OverlayLock::operator=(
4114 SkiaRenderer::OverlayLock&& other) {
4115 resource_lock = std::move(other.resource_lock);
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)
4124 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)
4125 SkiaRenderer::OverlayLock::OverlayLock(gpu::Mailbox mailbox) {
4126 render_pass_lock.emplace(mailbox);
4129 bool SkiaRenderer::OverlayLockComparator::operator()(
4130 const OverlayLock& lhs,
4131 const OverlayLock& rhs) const {
4132 return lhs.mailbox() < rhs.mailbox();
4134 #endif // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_OZONE)