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