1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/viz/service/display/software_renderer.h"
6
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
11 #include "base/process/memory.h"
12 #include "base/trace_event/trace_event.h"
13 #include "cc/base/math_util.h"
14 #include "cc/paint/image_provider.h"
15 #include "cc/paint/render_surface_filters.h"
16 #include "components/viz/common/display/renderer_settings.h"
17 #include "components/viz/common/frame_sinks/copy_output_request.h"
18 #include "components/viz/common/frame_sinks/copy_output_util.h"
19 #include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
20 #include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
21 #include "components/viz/common/quads/debug_border_draw_quad.h"
22 #include "components/viz/common/quads/picture_draw_quad.h"
23 #include "components/viz/common/quads/solid_color_draw_quad.h"
24 #include "components/viz/common/quads/texture_draw_quad.h"
25 #include "components/viz/common/quads/tile_draw_quad.h"
26 #include "components/viz/common/skia_helper.h"
27 #include "components/viz/common/viz_utils.h"
28 #include "components/viz/service/display/output_surface.h"
29 #include "components/viz/service/display/output_surface_frame.h"
30 #include "components/viz/service/display/renderer_utils.h"
31 #include "components/viz/service/display/software_output_device.h"
32 #include "skia/ext/image_operations.h"
33 #include "skia/ext/legacy_display_globals.h"
34 #include "skia/ext/opacity_filter_canvas.h"
35 #include "third_party/skia/include/core/SkCanvas.h"
36 #include "third_party/skia/include/core/SkColor.h"
37 #include "third_party/skia/include/core/SkImageFilter.h"
38 #include "third_party/skia/include/core/SkMatrix.h"
39 #include "third_party/skia/include/core/SkPath.h"
40 #include "third_party/skia/include/core/SkPoint.h"
41 #include "third_party/skia/include/core/SkShader.h"
42 #include "third_party/skia/include/effects/SkShaderMaskFilter.h"
43 #include "ui/gfx/geometry/axis_transform2d.h"
44 #include "ui/gfx/geometry/rect_conversions.h"
45 #include "ui/gfx/skia_util.h"
46 #include "ui/gfx/transform.h"
47
48 namespace viz {
49 namespace {
50 class AnimatedImagesProvider : public cc::ImageProvider {
51 public:
AnimatedImagesProvider(const PictureDrawQuad::ImageAnimationMap * image_animation_map)52 AnimatedImagesProvider(
53 const PictureDrawQuad::ImageAnimationMap* image_animation_map)
54 : image_animation_map_(image_animation_map) {}
55 ~AnimatedImagesProvider() override = default;
56
GetRasterContent(const cc::DrawImage & draw_image)57 ImageProvider::ScopedResult GetRasterContent(
58 const cc::DrawImage& draw_image) override {
59 // TODO(xidachen): Ensure this function works for paint worklet generated
60 // images.
61 const auto& paint_image = draw_image.paint_image();
62 auto it = image_animation_map_->find(paint_image.stable_id());
63 size_t frame_index = it == image_animation_map_->end()
64 ? cc::PaintImage::kDefaultFrameIndex
65 : it->second;
66 return ScopedResult(cc::DecodedDrawImage(
67 paint_image.GetSkImageForFrame(
68 frame_index, cc::PaintImage::kDefaultGeneratorClientId),
69 nullptr, SkSize::Make(0, 0), SkSize::Make(1.f, 1.f),
70 draw_image.filter_quality()));
71 }
72
73 private:
74 const PictureDrawQuad::ImageAnimationMap* image_animation_map_;
75 };
76
77 } // namespace
78
SoftwareRenderer(const RendererSettings * settings,const DebugRendererSettings * debug_settings,OutputSurface * output_surface,DisplayResourceProvider * resource_provider,OverlayProcessorInterface * overlay_processor)79 SoftwareRenderer::SoftwareRenderer(const RendererSettings* settings,
80 const DebugRendererSettings* debug_settings,
81 OutputSurface* output_surface,
82 DisplayResourceProvider* resource_provider,
83 OverlayProcessorInterface* overlay_processor)
84 : DirectRenderer(settings,
85 debug_settings,
86 output_surface,
87 resource_provider,
88 overlay_processor),
89 output_device_(output_surface->software_device()) {}
90
~SoftwareRenderer()91 SoftwareRenderer::~SoftwareRenderer() {}
92
CanPartialSwap()93 bool SoftwareRenderer::CanPartialSwap() {
94 return true;
95 }
96
BeginDrawingFrame()97 void SoftwareRenderer::BeginDrawingFrame() {
98 TRACE_EVENT0("viz", "SoftwareRenderer::BeginDrawingFrame");
99 }
100
FinishDrawingFrame()101 void SoftwareRenderer::FinishDrawingFrame() {
102 TRACE_EVENT0("viz", "SoftwareRenderer::FinishDrawingFrame");
103 current_framebuffer_canvas_.reset();
104 current_canvas_ = nullptr;
105
106 if (root_canvas_)
107 output_device_->EndPaint();
108 root_canvas_ = nullptr;
109 }
110
SwapBuffers(SwapFrameData swap_frame_data)111 void SoftwareRenderer::SwapBuffers(SwapFrameData swap_frame_data) {
112 DCHECK(visible_);
113 TRACE_EVENT0("viz", "SoftwareRenderer::SwapBuffers");
114 OutputSurfaceFrame output_frame;
115 output_frame.latency_info = std::move(swap_frame_data.latency_info);
116 output_frame.top_controls_visible_height_changed =
117 swap_frame_data.top_controls_visible_height_changed;
118 output_surface_->SwapBuffers(std::move(output_frame));
119 }
120
FlippedFramebuffer() const121 bool SoftwareRenderer::FlippedFramebuffer() const {
122 return false;
123 }
124
EnsureScissorTestEnabled()125 void SoftwareRenderer::EnsureScissorTestEnabled() {
126 is_scissor_enabled_ = true;
127 }
128
EnsureScissorTestDisabled()129 void SoftwareRenderer::EnsureScissorTestDisabled() {
130 is_scissor_enabled_ = false;
131 }
132
BindFramebufferToOutputSurface()133 void SoftwareRenderer::BindFramebufferToOutputSurface() {
134 DCHECK(!output_surface_->HasExternalStencilTest());
135 DCHECK(!root_canvas_);
136
137 current_framebuffer_canvas_.reset();
138 root_canvas_ = output_device_->BeginPaint(current_frame()->root_damage_rect);
139 if (!root_canvas_)
140 output_device_->EndPaint();
141 current_canvas_ = root_canvas_;
142 }
143
BindFramebufferToTexture(const AggregatedRenderPassId render_pass_id)144 void SoftwareRenderer::BindFramebufferToTexture(
145 const AggregatedRenderPassId render_pass_id) {
146 auto it = render_pass_bitmaps_.find(render_pass_id);
147 DCHECK(it != render_pass_bitmaps_.end());
148 SkBitmap& bitmap = it->second;
149
150 current_framebuffer_canvas_ = std::make_unique<SkCanvas>(
151 bitmap, skia::LegacyDisplayGlobals::GetSkSurfaceProps());
152 current_canvas_ = current_framebuffer_canvas_.get();
153 }
154
SetScissorTestRect(const gfx::Rect & scissor_rect)155 void SoftwareRenderer::SetScissorTestRect(const gfx::Rect& scissor_rect) {
156 is_scissor_enabled_ = true;
157 scissor_rect_ = scissor_rect;
158 }
159
SetClipRect(const gfx::Rect & rect)160 void SoftwareRenderer::SetClipRect(const gfx::Rect& rect) {
161 if (!current_canvas_)
162 return;
163 // Skia applies the current matrix to clip rects so we reset it temporarily.
164 SkMatrix current_matrix = current_canvas_->getTotalMatrix();
165 current_canvas_->resetMatrix();
166
167 // Checks below are incompatible with WebView as the canvas size and clip
168 // provided by Android or embedder app. And Chrome doesn't use
169 // SoftwareRenderer on Android.
170 #if !defined(OS_ANDROID)
171 // SetClipRect is assumed to be applied temporarily, on an
172 // otherwise-unclipped canvas.
173 DCHECK_EQ(current_canvas_->getDeviceClipBounds().width(),
174 current_canvas_->imageInfo().width());
175 DCHECK_EQ(current_canvas_->getDeviceClipBounds().height(),
176 current_canvas_->imageInfo().height());
177 #endif
178 current_canvas_->clipRect(gfx::RectToSkRect(rect));
179 current_canvas_->setMatrix(current_matrix);
180 }
181
SetClipRRect(const gfx::RRectF & rrect)182 void SoftwareRenderer::SetClipRRect(const gfx::RRectF& rrect) {
183 if (!current_canvas_)
184 return;
185
186 gfx::Transform screen_transform =
187 current_frame()->window_matrix * current_frame()->projection_matrix;
188 SkRRect result;
189 if (SkRRect(rrect).transform(SkMatrix(screen_transform.matrix()), &result)) {
190 // Skia applies the current matrix to clip rects so we reset it temporarily.
191 SkMatrix current_matrix = current_canvas_->getTotalMatrix();
192 current_canvas_->resetMatrix();
193 current_canvas_->clipRRect(result, true);
194 current_canvas_->setMatrix(current_matrix);
195 }
196 }
197
ClearCanvas(SkColor color)198 void SoftwareRenderer::ClearCanvas(SkColor color) {
199 if (!current_canvas_)
200 return;
201
202 if (is_scissor_enabled_) {
203 // The same paint used by SkCanvas::clear, but applied to the scissor rect.
204 SkPaint clear_paint;
205 clear_paint.setColor(color);
206 clear_paint.setBlendMode(SkBlendMode::kSrc);
207 current_canvas_->drawRect(gfx::RectToSkRect(scissor_rect_), clear_paint);
208 } else {
209 current_canvas_->clear(color);
210 }
211 }
212
ClearFramebuffer()213 void SoftwareRenderer::ClearFramebuffer() {
214 if (current_frame()->current_render_pass->has_transparent_background) {
215 ClearCanvas(SkColorSetARGB(0, 0, 0, 0));
216 } else {
217 #ifndef NDEBUG
218 // On DEBUG builds, opaque render passes are cleared to blue
219 // to easily see regions that were not drawn on the screen.
220 ClearCanvas(SkColorSetARGB(255, 0, 0, 255));
221 #endif
222 }
223 }
224
PrepareSurfaceForPass(SurfaceInitializationMode initialization_mode,const gfx::Rect & render_pass_scissor)225 void SoftwareRenderer::PrepareSurfaceForPass(
226 SurfaceInitializationMode initialization_mode,
227 const gfx::Rect& render_pass_scissor) {
228 switch (initialization_mode) {
229 case SURFACE_INITIALIZATION_MODE_PRESERVE:
230 EnsureScissorTestDisabled();
231 return;
232 case SURFACE_INITIALIZATION_MODE_FULL_SURFACE_CLEAR:
233 EnsureScissorTestDisabled();
234 ClearFramebuffer();
235 break;
236 case SURFACE_INITIALIZATION_MODE_SCISSORED_CLEAR:
237 SetScissorTestRect(render_pass_scissor);
238 ClearFramebuffer();
239 break;
240 }
241 }
242
IsSoftwareResource(ResourceId resource_id) const243 bool SoftwareRenderer::IsSoftwareResource(ResourceId resource_id) const {
244 return resource_provider_->IsResourceSoftwareBacked(resource_id);
245 }
246
DoDrawQuad(const DrawQuad * quad,const gfx::QuadF * draw_region)247 void SoftwareRenderer::DoDrawQuad(const DrawQuad* quad,
248 const gfx::QuadF* draw_region) {
249 if (!current_canvas_)
250 return;
251
252 TRACE_EVENT0("viz", "SoftwareRenderer::DoDrawQuad");
253 const bool should_apply_rounded_corner = ShouldApplyRoundedCorner(quad);
254 bool do_save =
255 draw_region || is_scissor_enabled_ || should_apply_rounded_corner;
256 SkAutoCanvasRestore canvas_restore(current_canvas_, do_save);
257 if (is_scissor_enabled_) {
258 SetClipRect(scissor_rect_);
259 }
260
261 if (should_apply_rounded_corner)
262 SetClipRRect(
263 quad->shared_quad_state->mask_filter_info.rounded_corner_bounds());
264
265 gfx::Transform quad_rect_matrix;
266 QuadRectTransform(&quad_rect_matrix,
267 quad->shared_quad_state->quad_to_target_transform,
268 gfx::RectF(quad->rect));
269 gfx::Transform contents_device_transform =
270 current_frame()->window_matrix * current_frame()->projection_matrix *
271 quad_rect_matrix;
272 contents_device_transform.FlattenTo2d();
273 SkMatrix sk_device_matrix;
274 gfx::TransformToFlattenedSkMatrix(contents_device_transform,
275 &sk_device_matrix);
276 current_canvas_->setMatrix(sk_device_matrix);
277
278 current_paint_.reset();
279 if (settings_->force_antialiasing ||
280 !IsScaleAndIntegerTranslate(sk_device_matrix)) {
281 // TODO(danakj): Until we can enable AA only on exterior edges of the
282 // layer, disable AA if any interior edges are present. crbug.com/248175
283 bool all_four_edges_are_exterior =
284 quad->IsTopEdge() && quad->IsLeftEdge() && quad->IsBottomEdge() &&
285 quad->IsRightEdge();
286 if (settings_->allow_antialiasing &&
287 (settings_->force_antialiasing || all_four_edges_are_exterior))
288 current_paint_.setAntiAlias(true);
289 current_paint_.setFilterQuality(kLow_SkFilterQuality);
290 }
291
292 if (quad->ShouldDrawWithBlending() ||
293 quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver) {
294 current_paint_.setAlpha(quad->shared_quad_state->opacity * 255);
295 current_paint_.setBlendMode(quad->shared_quad_state->blend_mode);
296 } else {
297 current_paint_.setBlendMode(SkBlendMode::kSrc);
298 }
299
300 if (draw_region) {
301 gfx::QuadF local_draw_region(*draw_region);
302 SkPath draw_region_clip_path;
303 local_draw_region -=
304 gfx::Vector2dF(quad->visible_rect.x(), quad->visible_rect.y());
305 local_draw_region.Scale(1.0f / quad->visible_rect.width(),
306 1.0f / quad->visible_rect.height());
307 local_draw_region -= gfx::Vector2dF(0.5f, 0.5f);
308
309 SkPoint clip_points[4];
310 QuadFToSkPoints(local_draw_region, clip_points);
311 draw_region_clip_path.addPoly(clip_points, 4, true);
312
313 current_canvas_->clipPath(draw_region_clip_path);
314 }
315
316 switch (quad->material) {
317 case DrawQuad::Material::kAggregatedRenderPass:
318 DrawRenderPassQuad(AggregatedRenderPassDrawQuad::MaterialCast(quad));
319 break;
320 case DrawQuad::Material::kDebugBorder:
321 DrawDebugBorderQuad(DebugBorderDrawQuad::MaterialCast(quad));
322 break;
323 case DrawQuad::Material::kPictureContent:
324 DrawPictureQuad(PictureDrawQuad::MaterialCast(quad));
325 break;
326 case DrawQuad::Material::kCompositorRenderPass:
327 // At this point, all RenderPassDrawQuads should be converted to
328 // AggregatedRenderPassDrawQuads.
329 NOTREACHED();
330 break;
331 case DrawQuad::Material::kSolidColor:
332 DrawSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad));
333 break;
334 case DrawQuad::Material::kTextureContent:
335 DrawTextureQuad(TextureDrawQuad::MaterialCast(quad));
336 break;
337 case DrawQuad::Material::kTiledContent:
338 DrawTileQuad(TileDrawQuad::MaterialCast(quad));
339 break;
340 case DrawQuad::Material::kSurfaceContent:
341 // Surface content should be fully resolved to other quad types before
342 // reaching a direct renderer.
343 NOTREACHED();
344 break;
345 case DrawQuad::Material::kInvalid:
346 case DrawQuad::Material::kYuvVideoContent:
347 case DrawQuad::Material::kStreamVideoContent:
348 DrawUnsupportedQuad(quad);
349 NOTREACHED();
350 break;
351 case DrawQuad::Material::kVideoHole:
352 // VideoHoleDrawQuad should only be used by Cast, and should
353 // have been replaced by cast-specific OverlayProcessor before
354 // reach here. In non-cast build, an untrusted render could send such
355 // Quad and the quad would then reach here unexpectedly. Therefore
356 // we should skip NOTREACHED() so an untrusted render is not capable
357 // of causing a crash.
358 DrawUnsupportedQuad(quad);
359 break;
360 }
361
362 current_canvas_->resetMatrix();
363 }
364
DrawDebugBorderQuad(const DebugBorderDrawQuad * quad)365 void SoftwareRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) {
366 // We need to apply the matrix manually to have pixel-sized stroke width.
367 SkPoint vertices[4];
368 gfx::RectFToSkRect(QuadVertexRect()).toQuad(vertices);
369 SkPoint transformed_vertices[4];
370 current_canvas_->getTotalMatrix().mapPoints(transformed_vertices, vertices,
371 4);
372 current_canvas_->resetMatrix();
373
374 current_paint_.setColor(quad->color);
375 current_paint_.setAlpha(quad->shared_quad_state->opacity *
376 SkColorGetA(quad->color));
377 current_paint_.setStyle(SkPaint::kStroke_Style);
378 current_paint_.setStrokeWidth(quad->width);
379 current_canvas_->drawPoints(SkCanvas::kPolygon_PointMode, 4,
380 transformed_vertices, current_paint_);
381 }
382
DrawPictureQuad(const PictureDrawQuad * quad)383 void SoftwareRenderer::DrawPictureQuad(const PictureDrawQuad* quad) {
384 SkMatrix content_matrix;
385 content_matrix.setRectToRect(gfx::RectFToSkRect(quad->tex_coord_rect),
386 gfx::RectFToSkRect(QuadVertexRect()),
387 SkMatrix::kFill_ScaleToFit);
388 current_canvas_->concat(content_matrix);
389
390 const bool needs_transparency =
391 SkScalarRoundToInt(quad->shared_quad_state->opacity * 255) < 255;
392 const bool disable_image_filtering =
393 disable_picture_quad_image_filtering_ || quad->nearest_neighbor;
394
395 TRACE_EVENT0("viz", "SoftwareRenderer::DrawPictureQuad");
396
397 SkCanvas* raster_canvas = current_canvas_;
398
399 base::Optional<skia::OpacityFilterCanvas> opacity_canvas;
400 if (needs_transparency || disable_image_filtering) {
401 // TODO(aelias): This isn't correct in all cases. We should detect these
402 // cases and fall back to a persistent bitmap backing
403 // (http://crbug.com/280374).
404 // TODO(vmpstr): Fold this canvas into playback and have raster source
405 // accept a set of settings on playback that will determine which canvas to
406 // apply. (http://crbug.com/594679)
407 opacity_canvas.emplace(raster_canvas, quad->shared_quad_state->opacity,
408 disable_image_filtering);
409 raster_canvas = &*opacity_canvas;
410 }
411
412 // Treat all subnormal values as zero for performance.
413 cc::ScopedSubnormalFloatDisabler disabler;
414
415 // Use an image provider to select the correct frame for animated images.
416 AnimatedImagesProvider image_provider(&quad->image_animation_map);
417
418 raster_canvas->save();
419 raster_canvas->translate(-quad->content_rect.x(), -quad->content_rect.y());
420 raster_canvas->clipRect(gfx::RectToSkRect(quad->content_rect));
421 raster_canvas->scale(quad->contents_scale, quad->contents_scale);
422 quad->display_item_list->Raster(raster_canvas, &image_provider);
423 raster_canvas->restore();
424 }
425
DrawSolidColorQuad(const SolidColorDrawQuad * quad)426 void SoftwareRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad) {
427 gfx::RectF visible_quad_vertex_rect = cc::MathUtil::ScaleRectProportional(
428 QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
429 current_paint_.setColor(quad->color);
430 current_paint_.setAlpha(quad->shared_quad_state->opacity *
431 SkColorGetA(quad->color));
432 current_canvas_->drawRect(gfx::RectFToSkRect(visible_quad_vertex_rect),
433 current_paint_);
434 }
435
DrawTextureQuad(const TextureDrawQuad * quad)436 void SoftwareRenderer::DrawTextureQuad(const TextureDrawQuad* quad) {
437 if (!IsSoftwareResource(quad->resource_id())) {
438 DrawUnsupportedQuad(quad);
439 return;
440 }
441
442 // TODO(skaslev): Add support for non-premultiplied alpha.
443 DisplayResourceProvider::ScopedReadLockSkImage lock(resource_provider_,
444 quad->resource_id());
445 if (!lock.valid())
446 return;
447 const SkImage* image = lock.sk_image();
448 gfx::RectF uv_rect = gfx::ScaleRect(
449 gfx::BoundingRect(quad->uv_top_left, quad->uv_bottom_right),
450 image->width(), image->height());
451 gfx::RectF visible_uv_rect = cc::MathUtil::ScaleRectProportional(
452 uv_rect, gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
453 SkRect sk_uv_rect = gfx::RectFToSkRect(visible_uv_rect);
454 gfx::RectF visible_quad_vertex_rect = cc::MathUtil::ScaleRectProportional(
455 QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
456 SkRect quad_rect = gfx::RectFToSkRect(visible_quad_vertex_rect);
457
458 if (quad->y_flipped)
459 current_canvas_->scale(1, -1);
460
461 bool blend_background =
462 quad->background_color != SK_ColorTRANSPARENT && !image->isOpaque();
463 bool needs_layer = blend_background && (current_paint_.getAlpha() != 0xFF);
464 if (needs_layer) {
465 current_canvas_->saveLayerAlpha(&quad_rect, current_paint_.getAlpha());
466 current_paint_.setAlpha(0xFF);
467 }
468 if (blend_background) {
469 SkPaint background_paint;
470 background_paint.setColor(quad->background_color);
471 current_canvas_->drawRect(quad_rect, background_paint);
472 }
473 current_paint_.setFilterQuality(
474 quad->nearest_neighbor ? kNone_SkFilterQuality : kLow_SkFilterQuality);
475 current_canvas_->drawImageRect(image, sk_uv_rect, quad_rect, ¤t_paint_);
476 if (needs_layer)
477 current_canvas_->restore();
478 }
479
DrawTileQuad(const TileDrawQuad * quad)480 void SoftwareRenderer::DrawTileQuad(const TileDrawQuad* quad) {
481 // |resource_provider_| can be NULL in resourceless software draws, which
482 // should never produce tile quads in the first place.
483 DCHECK(resource_provider_);
484 DCHECK(IsSoftwareResource(quad->resource_id()));
485
486 DisplayResourceProvider::ScopedReadLockSkImage lock(resource_provider_,
487 quad->resource_id());
488 if (!lock.valid())
489 return;
490
491 gfx::RectF visible_tex_coord_rect = cc::MathUtil::ScaleRectProportional(
492 quad->tex_coord_rect, gfx::RectF(quad->rect),
493 gfx::RectF(quad->visible_rect));
494 gfx::RectF visible_quad_vertex_rect = cc::MathUtil::ScaleRectProportional(
495 QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
496
497 SkRect uv_rect = gfx::RectFToSkRect(visible_tex_coord_rect);
498 current_paint_.setFilterQuality(
499 quad->nearest_neighbor ? kNone_SkFilterQuality : kLow_SkFilterQuality);
500 current_canvas_->drawImageRect(lock.sk_image(), uv_rect,
501 gfx::RectFToSkRect(visible_quad_vertex_rect),
502 ¤t_paint_);
503 }
504
DrawRenderPassQuad(const AggregatedRenderPassDrawQuad * quad)505 void SoftwareRenderer::DrawRenderPassQuad(
506 const AggregatedRenderPassDrawQuad* quad) {
507 auto it = render_pass_bitmaps_.find(quad->render_pass_id);
508 if (it == render_pass_bitmaps_.end())
509 return;
510 SkBitmap& source_bitmap = it->second;
511
512 SkRect dest_rect = gfx::RectFToSkRect(QuadVertexRect());
513 SkRect dest_visible_rect =
514 gfx::RectFToSkRect(cc::MathUtil::ScaleRectProportional(
515 QuadVertexRect(), gfx::RectF(quad->rect),
516 gfx::RectF(quad->visible_rect)));
517 SkRect content_rect = RectFToSkRect(quad->tex_coord_rect);
518
519 sk_sp<SkImage> filter_image;
520 const cc::FilterOperations* filters = FiltersForPass(quad->render_pass_id);
521 if (filters) {
522 DCHECK(!filters->IsEmpty());
523 auto paint_filter = cc::RenderSurfaceFilters::BuildImageFilter(
524 *filters, gfx::SizeF(source_bitmap.width(), source_bitmap.height()));
525 auto image_filter =
526 paint_filter ? paint_filter->cached_sk_filter_ : nullptr;
527 if (image_filter) {
528 SkIRect result_rect;
529 // TODO(ajuma): Apply the filter in the same pass as the content where
530 // possible (e.g. when there's no origin offset). See crbug.com/308201.
531 filter_image =
532 ApplyImageFilter(image_filter.get(), quad, source_bitmap,
533 /* offset_expanded_bounds = */ true, &result_rect);
534 if (result_rect.isEmpty()) {
535 return;
536 }
537 if (filter_image) {
538 gfx::RectF rect = gfx::SkRectToRectF(SkRect::Make(result_rect));
539 dest_rect = dest_visible_rect =
540 gfx::RectFToSkRect(cc::MathUtil::ScaleRectProportional(
541 QuadVertexRect(), gfx::RectF(quad->rect), rect));
542 content_rect =
543 SkRect::MakeWH(result_rect.width(), result_rect.height());
544 }
545 }
546 }
547
548 SkMatrix content_mat;
549 content_mat.setRectToRect(content_rect, dest_rect,
550 SkMatrix::kFill_ScaleToFit);
551
552 sk_sp<SkShader> shader;
553 if (!filter_image) {
554 shader = source_bitmap.makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
555 &content_mat);
556 } else {
557 shader = filter_image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
558 &content_mat);
559 }
560
561 if (quad->mask_resource_id()) {
562 DisplayResourceProvider::ScopedReadLockSkImage mask_lock(
563 resource_provider_, quad->mask_resource_id());
564 if (!mask_lock.valid())
565 return;
566
567 // Scale normalized uv rect into absolute texel coordinates.
568 SkRect mask_rect = gfx::RectFToSkRect(
569 gfx::ScaleRect(quad->mask_uv_rect, quad->mask_texture_size.width(),
570 quad->mask_texture_size.height()));
571
572 SkMatrix mask_mat;
573 mask_mat.setRectToRect(mask_rect, dest_rect, SkMatrix::kFill_ScaleToFit);
574
575 current_paint_.setMaskFilter(
576 SkShaderMaskFilter::Make(mask_lock.sk_image()->makeShader(
577 SkTileMode::kClamp, SkTileMode::kClamp, &mask_mat)));
578 }
579
580 // If we have a backdrop filter shader, render its results first.
581 sk_sp<SkShader> backdrop_filter_shader =
582 GetBackdropFilterShader(quad, SkTileMode::kClamp);
583 if (backdrop_filter_shader) {
584 SkPaint paint;
585 paint.setShader(std::move(backdrop_filter_shader));
586 paint.setMaskFilter(current_paint_.refMaskFilter());
587 current_canvas_->drawRect(dest_visible_rect, paint);
588 }
589 current_paint_.setShader(std::move(shader));
590 current_canvas_->drawRect(dest_visible_rect, current_paint_);
591 }
592
DrawUnsupportedQuad(const DrawQuad * quad)593 void SoftwareRenderer::DrawUnsupportedQuad(const DrawQuad* quad) {
594 #ifdef NDEBUG
595 current_paint_.setColor(SK_ColorWHITE);
596 #else
597 current_paint_.setColor(SK_ColorMAGENTA);
598 #endif
599 current_paint_.setAlpha(quad->shared_quad_state->opacity * 255);
600 current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
601 current_paint_);
602 }
603
CopyDrawnRenderPass(const copy_output::RenderPassGeometry & geometry,std::unique_ptr<CopyOutputRequest> request)604 void SoftwareRenderer::CopyDrawnRenderPass(
605 const copy_output::RenderPassGeometry& geometry,
606 std::unique_ptr<CopyOutputRequest> request) {
607 sk_sp<SkColorSpace> color_space =
608 CurrentRenderPassColorSpace().ToSkColorSpace();
609 DCHECK(color_space);
610
611 SkBitmap bitmap;
612 if (request->is_scaled()) {
613 // Resolve the source for the scaling input: Initialize a SkPixmap that
614 // selects the current RenderPass's output rect within the current canvas
615 // and provides access to its pixels.
616 SkPixmap render_pass_output;
617 if (!current_canvas_->peekPixels(&render_pass_output))
618 return;
619 {
620 render_pass_output =
621 SkPixmap(render_pass_output.info()
622 .makeWH(geometry.sampling_bounds.width(),
623 geometry.sampling_bounds.height())
624 .makeColorSpace(std::move(color_space)),
625 render_pass_output.addr(geometry.sampling_bounds.x(),
626 geometry.sampling_bounds.y()),
627 render_pass_output.rowBytes());
628 }
629
630 // Execute the scaling: For downscaling, use the RESIZE_BETTER strategy
631 // (appropriate for thumbnailing); and, for upscaling, use the RESIZE_BEST
632 // strategy. Note that processing is only done on the subset of the
633 // RenderPass output that contributes to the result.
634 using skia::ImageOperations;
635 const bool is_downscale_in_both_dimensions =
636 request->scale_to().x() < request->scale_from().x() &&
637 request->scale_to().y() < request->scale_from().y();
638 const ImageOperations::ResizeMethod method =
639 is_downscale_in_both_dimensions ? ImageOperations::RESIZE_BETTER
640 : ImageOperations::RESIZE_BEST;
641 bitmap = ImageOperations::Resize(
642 render_pass_output, method, geometry.result_bounds.width(),
643 geometry.result_bounds.height(),
644 SkIRect{geometry.result_selection.x(), geometry.result_selection.y(),
645 geometry.result_selection.right(),
646 geometry.result_selection.bottom()});
647 } else /* if (!request->is_scaled()) */ {
648 SkImageInfo info = SkImageInfo::MakeN32Premul(
649 geometry.result_selection.width(), geometry.result_selection.height(),
650 std::move(color_space));
651 if (!bitmap.tryAllocPixels(info))
652 return;
653
654 if (!current_canvas_->readPixels(bitmap, geometry.readback_offset.x(),
655 geometry.readback_offset.y()))
656 return;
657 }
658
659 // Deliver the result. SoftwareRenderer supports RGBA_BITMAP and I420_PLANES
660 // only. For legacy reasons, if a RGBA_TEXTURE request is being made, clients
661 // are prepared to accept RGBA_BITMAP results.
662 //
663 // TODO(crbug/754872): Get rid of the legacy behavior and send empty results
664 // for RGBA_TEXTURE requests once tab capture is moved into VIZ.
665 const CopyOutputResult::Format result_format =
666 (request->result_format() == CopyOutputResult::Format::RGBA_TEXTURE)
667 ? CopyOutputResult::Format::RGBA_BITMAP
668 : request->result_format();
669 // Note: The CopyOutputSkBitmapResult automatically provides I420 format
670 // conversion, if needed.
671 request->SendResult(std::make_unique<CopyOutputSkBitmapResult>(
672 result_format, geometry.result_selection, bitmap));
673 }
674
DidChangeVisibility()675 void SoftwareRenderer::DidChangeVisibility() {
676 if (visible_)
677 output_surface_->EnsureBackbuffer();
678 else
679 output_surface_->DiscardBackbuffer();
680 }
681
GenerateMipmap()682 void SoftwareRenderer::GenerateMipmap() {
683 NOTIMPLEMENTED();
684 }
685
ShouldApplyBackdropFilters(const cc::FilterOperations * backdrop_filters,const AggregatedRenderPassDrawQuad * quad) const686 bool SoftwareRenderer::ShouldApplyBackdropFilters(
687 const cc::FilterOperations* backdrop_filters,
688 const AggregatedRenderPassDrawQuad* quad) const {
689 if (!backdrop_filters)
690 return false;
691 if (quad->shared_quad_state->opacity == 0.f)
692 return false;
693 DCHECK(!backdrop_filters->IsEmpty());
694 return true;
695 }
696
697 // Applies |filter| to |to_filter| bitmap. |result_rect| will be filled with the
698 // automatically-computed destination bounds. If |offset_expanded_bounds| is
699 // true, the bitmap will be offset for any pixel-moving filters. This function
700 // is called for both filters and backdrop_filters. The difference between those
701 // two paths is that the filter path wants to offset to the expanded bounds
702 // (including border for pixel moving filters) when drawing the bitmap into the
703 // canvas, while the backdrop filter path needs to keep the origin unmoved (at
704 // quad->rect origin) so that it gets put in the right spot relative to the
705 // underlying backdrop.
ApplyImageFilter(SkImageFilter * filter,const AggregatedRenderPassDrawQuad * quad,const SkBitmap & to_filter,bool offset_expanded_bounds,SkIRect * result_rect) const706 sk_sp<SkImage> SoftwareRenderer::ApplyImageFilter(
707 SkImageFilter* filter,
708 const AggregatedRenderPassDrawQuad* quad,
709 const SkBitmap& to_filter,
710 bool offset_expanded_bounds,
711 SkIRect* result_rect) const {
712 DCHECK(result_rect);
713 if (!filter)
714 return nullptr;
715
716 SkMatrix local_matrix;
717 local_matrix.setTranslate(quad->filters_origin.x(), quad->filters_origin.y());
718 local_matrix.postScale(quad->filters_scale.x(), quad->filters_scale.y());
719 *result_rect =
720 filter->filterBounds(gfx::RectToSkIRect(quad->rect), local_matrix,
721 SkImageFilter::kForward_MapDirection);
722 gfx::Point canvas_offset =
723 offset_expanded_bounds ? gfx::Point(result_rect->x(), result_rect->y())
724 : quad->rect.origin();
725 SkImageInfo dst_info =
726 SkImageInfo::MakeN32Premul(result_rect->width(), result_rect->height());
727 sk_sp<SkSurface> surface = SkSurface::MakeRaster(dst_info);
728 if (!surface)
729 return nullptr;
730
731 SkPaint paint;
732 // Treat subnormal float values as zero for performance.
733 cc::ScopedSubnormalFloatDisabler disabler;
734 paint.setImageFilter(filter->makeWithLocalMatrix(local_matrix));
735 surface->getCanvas()->translate(-canvas_offset.x(), -canvas_offset.y());
736 surface->getCanvas()->drawBitmap(to_filter, quad->rect.x(), quad->rect.y(),
737 &paint);
738 return surface->makeImageSnapshot();
739 }
740
GetBackdropBitmap(const gfx::Rect & bounding_rect) const741 SkBitmap SoftwareRenderer::GetBackdropBitmap(
742 const gfx::Rect& bounding_rect) const {
743 SkImageInfo info =
744 SkImageInfo::MakeN32Premul(bounding_rect.width(), bounding_rect.height());
745 SkBitmap bitmap;
746 if (!bitmap.tryAllocPixels(info))
747 base::TerminateBecauseOutOfMemory(info.computeMinByteSize());
748
749 if (!current_canvas_->readPixels(bitmap, bounding_rect.x(),
750 bounding_rect.y()))
751 bitmap.reset();
752 return bitmap;
753 }
754
GetBackdropBoundingBoxForRenderPassQuad(const AggregatedRenderPassDrawQuad * quad,const cc::FilterOperations * backdrop_filters,base::Optional<gfx::RRectF> backdrop_filter_bounds_input,gfx::Transform contents_device_transform,gfx::Transform * backdrop_filter_bounds_transform,base::Optional<gfx::RRectF> * backdrop_filter_bounds,gfx::Rect * unclipped_rect) const755 gfx::Rect SoftwareRenderer::GetBackdropBoundingBoxForRenderPassQuad(
756 const AggregatedRenderPassDrawQuad* quad,
757 const cc::FilterOperations* backdrop_filters,
758 base::Optional<gfx::RRectF> backdrop_filter_bounds_input,
759 gfx::Transform contents_device_transform,
760 gfx::Transform* backdrop_filter_bounds_transform,
761 base::Optional<gfx::RRectF>* backdrop_filter_bounds,
762 gfx::Rect* unclipped_rect) const {
763 DCHECK(backdrop_filter_bounds_transform);
764 DCHECK(backdrop_filter_bounds);
765 DCHECK(unclipped_rect);
766
767 // |backdrop_filter_bounds| is a rounded rect in [-0.5,0.5] space that
768 // represents |backdrop_filter_bounds_input| as a fraction of the space
769 // defined by |quad->rect|, not including its offset.
770 *backdrop_filter_bounds = gfx::RRectF();
771 if (!backdrop_filter_bounds_input ||
772 !GetScaledRRectF(quad->rect, backdrop_filter_bounds_input.value(),
773 &backdrop_filter_bounds->value())) {
774 backdrop_filter_bounds->reset();
775 }
776
777 // |backdrop_rect| is now the bounding box of clip_region, in window pixel
778 // coordinates, and with flip applied.
779 gfx::Rect backdrop_rect = gfx::ToEnclosingRect(cc::MathUtil::MapClippedRect(
780 contents_device_transform, QuadVertexRect()));
781
782 *unclipped_rect = backdrop_rect;
783 backdrop_rect.Intersect(MoveFromDrawToWindowSpace(
784 current_frame()->current_render_pass->output_rect));
785
786 // Shift to the space of the captured backdrop image.
787 *backdrop_filter_bounds_transform = contents_device_transform;
788 backdrop_filter_bounds_transform->PostTranslate(-backdrop_rect.x(),
789 -backdrop_rect.y());
790
791 return backdrop_rect;
792 }
793
GetBackdropFilterShader(const AggregatedRenderPassDrawQuad * quad,SkTileMode content_tile_mode) const794 sk_sp<SkShader> SoftwareRenderer::GetBackdropFilterShader(
795 const AggregatedRenderPassDrawQuad* quad,
796 SkTileMode content_tile_mode) const {
797 const cc::FilterOperations* backdrop_filters =
798 BackdropFiltersForPass(quad->render_pass_id);
799 if (!ShouldApplyBackdropFilters(backdrop_filters, quad))
800 return nullptr;
801 base::Optional<gfx::RRectF> backdrop_filter_bounds_input =
802 BackdropFilterBoundsForPass(quad->render_pass_id);
803 DCHECK(!FiltersForPass(quad->render_pass_id))
804 << "Filters should always be in a separate Effect node";
805 if (backdrop_filter_bounds_input.has_value()) {
806 backdrop_filter_bounds_input->Scale(quad->filters_scale.x(),
807 quad->filters_scale.y());
808 }
809
810 gfx::Transform quad_rect_matrix;
811 QuadRectTransform(&quad_rect_matrix,
812 quad->shared_quad_state->quad_to_target_transform,
813 gfx::RectF(quad->rect));
814 gfx::Transform contents_device_transform =
815 current_frame()->window_matrix * current_frame()->projection_matrix *
816 quad_rect_matrix;
817 contents_device_transform.FlattenTo2d();
818
819 base::Optional<gfx::RRectF> backdrop_filter_bounds;
820 gfx::Transform backdrop_filter_bounds_transform;
821 gfx::Rect unclipped_rect;
822 gfx::Rect backdrop_rect = GetBackdropBoundingBoxForRenderPassQuad(
823 quad, backdrop_filters, backdrop_filter_bounds_input,
824 contents_device_transform, &backdrop_filter_bounds_transform,
825 &backdrop_filter_bounds, &unclipped_rect);
826
827 // Figure out the transformations to move it back to pixel space.
828 gfx::Transform contents_device_transform_inverse;
829 if (!contents_device_transform.GetInverse(&contents_device_transform_inverse))
830 return nullptr;
831
832 SkMatrix filter_backdrop_transform =
833 SkMatrix(contents_device_transform_inverse.matrix());
834 filter_backdrop_transform.preTranslate(backdrop_rect.x(), backdrop_rect.y());
835
836 SkBitmap backdrop_bitmap = GetBackdropBitmap(backdrop_rect);
837 gfx::Point image_offset = gfx::Point(0, 0);
838 if (backdrop_filter_bounds.has_value()) {
839 gfx::Rect filter_clip = gfx::ToEnclosingRect(cc::MathUtil::MapClippedRect(
840 backdrop_filter_bounds_transform, backdrop_filter_bounds->rect()));
841 filter_clip.Intersect(
842 gfx::Rect(backdrop_bitmap.width(), backdrop_bitmap.height()));
843 if (filter_clip.IsEmpty())
844 return nullptr;
845 // Crop the source image to the backdrop_filter_bounds.
846 sk_sp<SkImage> cropped_image = SkImage::MakeFromBitmap(backdrop_bitmap);
847 cropped_image =
848 cropped_image->makeSubset(RectToSkIRect(filter_clip), nullptr);
849 cropped_image->asLegacyBitmap(&backdrop_bitmap);
850 image_offset = filter_clip.origin();
851 }
852
853 gfx::Vector2dF clipping_offset =
854 (unclipped_rect.top_right() - backdrop_rect.top_right()) +
855 (backdrop_rect.bottom_left() - unclipped_rect.bottom_left());
856
857 sk_sp<cc::PaintFilter> paint_filter =
858 cc::RenderSurfaceFilters::BuildImageFilter(
859 *backdrop_filters,
860 gfx::SizeF(backdrop_bitmap.width(), backdrop_bitmap.height()),
861 clipping_offset);
862 if (!paint_filter)
863 return nullptr;
864 sk_sp<SkImageFilter> filter = paint_filter->cached_sk_filter_;
865
866 // TODO(989238): Software renderer does not support/implement kClamp_TileMode.
867 SkIRect result_rect;
868 sk_sp<SkImage> filtered_image =
869 ApplyImageFilter(filter.get(), quad, backdrop_bitmap,
870 /* offset_expanded_bounds = */ false, &result_rect);
871 if (!filtered_image)
872 return nullptr;
873
874 // Use an SkBitmap to paint the rrect-clipped filtered image.
875 SkImageInfo info =
876 SkImageInfo::MakeN32Premul(backdrop_rect.width(), backdrop_rect.height());
877 SkBitmap bitmap;
878 if (!bitmap.tryAllocPixels(info))
879 base::TerminateBecauseOutOfMemory(info.computeMinByteSize());
880
881 SkCanvas canvas(bitmap, skia::LegacyDisplayGlobals::GetSkSurfaceProps());
882
883 // Clip the filtered image to the (rounded) bounding box of the element.
884 if (backdrop_filter_bounds) {
885 canvas.setMatrix(SkMatrix(backdrop_filter_bounds_transform.matrix()));
886 canvas.clipRRect(SkRRect(*backdrop_filter_bounds), SkClipOp::kIntersect,
887 true /* antialias */);
888 canvas.resetMatrix();
889 }
890
891 // Paint the filtered backdrop image with opacity.
892 SkPaint paint;
893 if (quad->shared_quad_state->opacity < 1.0) {
894 paint.setImageFilter(
895 SkiaHelper::BuildOpacityFilter(quad->shared_quad_state->opacity));
896 }
897
898 // Now paint the pre-filtered image onto the canvas.
899 SkRect src_rect =
900 SkRect::MakeXYWH(0, 0, backdrop_bitmap.width(), backdrop_bitmap.height());
901 SkRect dst_rect = src_rect.makeOffset(image_offset.x(), image_offset.y());
902 canvas.drawImageRect(filtered_image, src_rect, dst_rect, &paint);
903
904 return SkImage::MakeFromBitmap(bitmap)->makeShader(
905 content_tile_mode, content_tile_mode, &filter_backdrop_transform);
906 }
907
UpdateRenderPassTextures(const AggregatedRenderPassList & render_passes_in_draw_order,const base::flat_map<AggregatedRenderPassId,RenderPassRequirements> & render_passes_in_frame)908 void SoftwareRenderer::UpdateRenderPassTextures(
909 const AggregatedRenderPassList& render_passes_in_draw_order,
910 const base::flat_map<AggregatedRenderPassId, RenderPassRequirements>&
911 render_passes_in_frame) {
912 std::vector<AggregatedRenderPassId> passes_to_delete;
913 for (const auto& pair : render_pass_bitmaps_) {
914 auto render_pass_it = render_passes_in_frame.find(pair.first);
915 if (render_pass_it == render_passes_in_frame.end()) {
916 passes_to_delete.push_back(pair.first);
917 continue;
918 }
919
920 gfx::Size required_size = render_pass_it->second.size;
921 // The RenderPassRequirements have a hint, which is only used for gpu
922 // compositing so it is ignored here.
923 const SkBitmap& bitmap = pair.second;
924
925 bool size_appropriate = bitmap.width() >= required_size.width() &&
926 bitmap.height() >= required_size.height();
927 if (!size_appropriate)
928 passes_to_delete.push_back(pair.first);
929 }
930
931 // Delete RenderPass bitmaps from the previous frame that will not be used
932 // again.
933 for (const AggregatedRenderPassId& id : passes_to_delete)
934 render_pass_bitmaps_.erase(id);
935 }
936
AllocateRenderPassResourceIfNeeded(const AggregatedRenderPassId & render_pass_id,const RenderPassRequirements & requirements)937 void SoftwareRenderer::AllocateRenderPassResourceIfNeeded(
938 const AggregatedRenderPassId& render_pass_id,
939 const RenderPassRequirements& requirements) {
940 auto it = render_pass_bitmaps_.find(render_pass_id);
941 if (it != render_pass_bitmaps_.end()) {
942 DCHECK(it->second.width() >= requirements.size.width() &&
943 it->second.height() >= requirements.size.height());
944 return;
945 }
946
947 // The |requirements.mipmap| is only used for gpu-based rendering, so not used
948 // here.
949 //
950 // ColorSpace correctness for software compositing is a performance nightmare,
951 // so we don't do it. If we did, then the color space of the current frame's
952 // |current_render_pass| should be stored somewhere, but we should not set it
953 // on the bitmap itself. Instead, we'd use it with a SkColorSpaceXformCanvas
954 // that wraps the SkCanvas drawing into the bitmap.
955 SkImageInfo info =
956 SkImageInfo::MakeN32(requirements.size.width(),
957 requirements.size.height(), kPremul_SkAlphaType);
958 SkBitmap bitmap;
959 if (!bitmap.tryAllocPixels(info))
960 base::TerminateBecauseOutOfMemory(info.computeMinByteSize());
961
962 render_pass_bitmaps_.emplace(render_pass_id, std::move(bitmap));
963 }
964
IsRenderPassResourceAllocated(const AggregatedRenderPassId & render_pass_id) const965 bool SoftwareRenderer::IsRenderPassResourceAllocated(
966 const AggregatedRenderPassId& render_pass_id) const {
967 auto it = render_pass_bitmaps_.find(render_pass_id);
968 return it != render_pass_bitmaps_.end();
969 }
970
GetRenderPassBackingPixelSize(const AggregatedRenderPassId & render_pass_id)971 gfx::Size SoftwareRenderer::GetRenderPassBackingPixelSize(
972 const AggregatedRenderPassId& render_pass_id) {
973 auto it = render_pass_bitmaps_.find(render_pass_id);
974 DCHECK(it != render_pass_bitmaps_.end());
975 SkBitmap& bitmap = it->second;
976 return gfx::Size(bitmap.width(), bitmap.height());
977 }
978
979 } // namespace viz
980