software_renderer.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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 "cc/output/software_renderer.h"
6
7#include "base/debug/trace_event.h"
8#include "cc/base/math_util.h"
9#include "cc/output/compositor_frame.h"
10#include "cc/output/compositor_frame_ack.h"
11#include "cc/output/compositor_frame_metadata.h"
12#include "cc/output/copy_output_request.h"
13#include "cc/output/output_surface.h"
14#include "cc/output/software_output_device.h"
15#include "cc/quads/debug_border_draw_quad.h"
16#include "cc/quads/picture_draw_quad.h"
17#include "cc/quads/render_pass_draw_quad.h"
18#include "cc/quads/solid_color_draw_quad.h"
19#include "cc/quads/texture_draw_quad.h"
20#include "cc/quads/tile_draw_quad.h"
21#include "third_party/skia/include/core/SkCanvas.h"
22#include "third_party/skia/include/core/SkColor.h"
23#include "third_party/skia/include/core/SkDevice.h"
24#include "third_party/skia/include/core/SkMatrix.h"
25#include "third_party/skia/include/core/SkShader.h"
26#include "third_party/skia/include/effects/SkLayerRasterizer.h"
27#include "ui/gfx/rect_conversions.h"
28#include "ui/gfx/skia_util.h"
29#include "ui/gfx/transform.h"
30
31namespace cc {
32
33namespace {
34
35bool IsScaleAndTranslate(const SkMatrix& matrix) {
36  return SkScalarNearlyZero(matrix[SkMatrix::kMSkewX]) &&
37         SkScalarNearlyZero(matrix[SkMatrix::kMSkewY]) &&
38         SkScalarNearlyZero(matrix[SkMatrix::kMPersp0]) &&
39         SkScalarNearlyZero(matrix[SkMatrix::kMPersp1]) &&
40         SkScalarNearlyZero(matrix[SkMatrix::kMPersp2] - 1.0f);
41}
42
43}  // anonymous namespace
44
45scoped_ptr<SoftwareRenderer> SoftwareRenderer::Create(
46    RendererClient* client,
47    OutputSurface* output_surface,
48    ResourceProvider* resource_provider) {
49  return make_scoped_ptr(
50      new SoftwareRenderer(client, output_surface, resource_provider));
51}
52
53SoftwareRenderer::SoftwareRenderer(RendererClient* client,
54                                   OutputSurface* output_surface,
55                                   ResourceProvider* resource_provider)
56  : DirectRenderer(client, output_surface, resource_provider),
57    visible_(true),
58    is_scissor_enabled_(false),
59    is_viewport_changed_(true),
60    output_device_(output_surface->software_device()),
61    current_canvas_(NULL) {
62  if (resource_provider_) {
63    capabilities_.max_texture_size = resource_provider_->max_texture_size();
64    capabilities_.best_texture_format =
65        resource_provider_->best_texture_format();
66  }
67  capabilities_.using_set_visibility = true;
68  // The updater can access bitmaps while the SoftwareRenderer is using them.
69  capabilities_.allow_partial_texture_updates = true;
70  capabilities_.using_partial_swap = true;
71  if (Settings().compositor_frame_message && client_->HasImplThread())
72    capabilities_.using_swap_complete_callback = true;
73  compositor_frame_.software_frame_data.reset(new SoftwareFrameData());
74}
75
76SoftwareRenderer::~SoftwareRenderer() {}
77
78const RendererCapabilities& SoftwareRenderer::Capabilities() const {
79  return capabilities_;
80}
81
82void SoftwareRenderer::ViewportChanged() {
83  is_viewport_changed_ = true;
84}
85
86void SoftwareRenderer::BeginDrawingFrame(DrawingFrame* frame) {
87  TRACE_EVENT0("cc", "SoftwareRenderer::BeginDrawingFrame");
88  if (is_viewport_changed_) {
89    is_viewport_changed_ = false;
90    output_device_->Resize(client_->DeviceViewport().size());
91  }
92  root_canvas_ = output_device_->BeginPaint(
93      gfx::ToEnclosingRect(frame->root_damage_rect));
94}
95
96void SoftwareRenderer::FinishDrawingFrame(DrawingFrame* frame) {
97  TRACE_EVENT0("cc", "SoftwareRenderer::FinishDrawingFrame");
98  current_framebuffer_lock_.reset();
99  current_canvas_ = NULL;
100  root_canvas_ = NULL;
101  if (Settings().compositor_frame_message) {
102    compositor_frame_.metadata = client_->MakeCompositorFrameMetadata();
103    output_device_->EndPaint(compositor_frame_.software_frame_data.get());
104  } else {
105    output_device_->EndPaint(NULL);
106  }
107}
108
109void SoftwareRenderer::SwapBuffers(const ui::LatencyInfo& latency_info) {
110  if (Settings().compositor_frame_message)
111    output_surface_->SendFrameToParentCompositor(&compositor_frame_);
112}
113
114void SoftwareRenderer::ReceiveCompositorFrameAck(
115    const CompositorFrameAck& ack) {
116  output_device_->ReclaimSoftwareFrame(ack.last_software_frame_id);
117}
118
119bool SoftwareRenderer::FlippedFramebuffer() const {
120  return false;
121}
122
123void SoftwareRenderer::EnsureScissorTestEnabled() {
124  is_scissor_enabled_ = true;
125  SetClipRect(scissor_rect_);
126}
127
128void SoftwareRenderer::EnsureScissorTestDisabled() {
129  // There is no explicit notion of enabling/disabling scissoring in software
130  // rendering, but the underlying effect we want is to clear any existing
131  // clipRect on the current SkCanvas. This is done by setting clipRect to
132  // the viewport's dimensions.
133  is_scissor_enabled_ = false;
134  SkDevice* device = current_canvas_->getDevice();
135  SetClipRect(gfx::Rect(device->width(), device->height()));
136}
137
138void SoftwareRenderer::Finish() {}
139
140void SoftwareRenderer::BindFramebufferToOutputSurface(DrawingFrame* frame) {
141  current_framebuffer_lock_.reset();
142  current_canvas_ = root_canvas_;
143}
144
145bool SoftwareRenderer::BindFramebufferToTexture(
146    DrawingFrame* frame,
147    const ScopedResource* texture,
148    gfx::Rect target_rect) {
149  current_framebuffer_lock_.reset();
150  current_framebuffer_lock_ = make_scoped_ptr(
151      new ResourceProvider::ScopedWriteLockSoftware(
152          resource_provider_, texture->id()));
153  current_canvas_ = current_framebuffer_lock_->sk_canvas();
154  InitializeViewport(frame,
155                     target_rect,
156                     gfx::Rect(target_rect.size()),
157                     target_rect.size(),
158                     false);
159  return true;
160}
161
162void SoftwareRenderer::SetScissorTestRect(gfx::Rect scissor_rect) {
163  is_scissor_enabled_ = true;
164  scissor_rect_ = scissor_rect;
165  SetClipRect(scissor_rect);
166}
167
168void SoftwareRenderer::SetClipRect(gfx::Rect rect) {
169  // Skia applies the current matrix to clip rects so we reset it temporary.
170  SkMatrix current_matrix = current_canvas_->getTotalMatrix();
171  current_canvas_->resetMatrix();
172  current_canvas_->clipRect(gfx::RectToSkRect(rect), SkRegion::kReplace_Op);
173  current_canvas_->setMatrix(current_matrix);
174}
175
176void SoftwareRenderer::ClearCanvas(SkColor color) {
177  // SkCanvas::clear doesn't respect the current clipping region
178  // so we SkCanvas::drawColor instead if scissoring is active.
179  if (is_scissor_enabled_)
180    current_canvas_->drawColor(color, SkXfermode::kSrc_Mode);
181  else
182    current_canvas_->clear(color);
183}
184
185void SoftwareRenderer::ClearFramebuffer(DrawingFrame* frame) {
186  if (frame->current_render_pass->has_transparent_background) {
187    ClearCanvas(SkColorSetARGB(0, 0, 0, 0));
188  } else {
189#ifndef NDEBUG
190    // On DEBUG builds, opaque render passes are cleared to blue
191    // to easily see regions that were not drawn on the screen.
192    ClearCanvas(SkColorSetARGB(255, 0, 0, 255));
193#endif
194  }
195}
196
197void SoftwareRenderer::SetDrawViewport(gfx::Rect window_space_viewport) {}
198
199bool SoftwareRenderer::IsSoftwareResource(
200    ResourceProvider::ResourceId resource_id) const {
201  switch (resource_provider_->GetResourceType(resource_id)) {
202    case ResourceProvider::GLTexture:
203      return false;
204    case ResourceProvider::Bitmap:
205      return true;
206  }
207
208  LOG(FATAL) << "Invalid resource type.";
209  return false;
210}
211
212void SoftwareRenderer::DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) {
213  TRACE_EVENT0("cc", "SoftwareRenderer::DoDrawQuad");
214  gfx::Transform quad_rect_matrix;
215  QuadRectTransform(&quad_rect_matrix, quad->quadTransform(), quad->rect);
216  gfx::Transform contents_device_transform =
217      frame->window_matrix * frame->projection_matrix * quad_rect_matrix;
218  contents_device_transform.FlattenTo2d();
219  SkMatrix sk_device_matrix;
220  gfx::TransformToFlattenedSkMatrix(contents_device_transform,
221                                    &sk_device_matrix);
222  current_canvas_->setMatrix(sk_device_matrix);
223
224  current_paint_.reset();
225  if (!IsScaleAndTranslate(sk_device_matrix)) {
226    // TODO(danakj): Until we can enable AA only on exterior edges of the
227    // layer, disable AA if any interior edges are present. crbug.com/248175
228    bool all_four_edges_are_exterior = quad->IsTopEdge() &&
229                                       quad->IsLeftEdge() &&
230                                       quad->IsBottomEdge() &&
231                                       quad->IsRightEdge();
232    if (all_four_edges_are_exterior)
233      current_paint_.setAntiAlias(true);
234    current_paint_.setFilterBitmap(true);
235  }
236
237  if (quad->ShouldDrawWithBlending()) {
238    current_paint_.setAlpha(quad->opacity() * 255);
239    current_paint_.setXfermodeMode(SkXfermode::kSrcOver_Mode);
240  } else {
241    current_paint_.setXfermodeMode(SkXfermode::kSrc_Mode);
242  }
243
244  switch (quad->material) {
245    case DrawQuad::DEBUG_BORDER:
246      DrawDebugBorderQuad(frame, DebugBorderDrawQuad::MaterialCast(quad));
247      break;
248    case DrawQuad::PICTURE_CONTENT:
249      DrawPictureQuad(frame, PictureDrawQuad::MaterialCast(quad));
250      break;
251    case DrawQuad::SOLID_COLOR:
252      DrawSolidColorQuad(frame, SolidColorDrawQuad::MaterialCast(quad));
253      break;
254    case DrawQuad::TEXTURE_CONTENT:
255      DrawTextureQuad(frame, TextureDrawQuad::MaterialCast(quad));
256      break;
257    case DrawQuad::TILED_CONTENT:
258      DrawTileQuad(frame, TileDrawQuad::MaterialCast(quad));
259      break;
260    case DrawQuad::RENDER_PASS:
261      DrawRenderPassQuad(frame, RenderPassDrawQuad::MaterialCast(quad));
262      break;
263    default:
264      DrawUnsupportedQuad(frame, quad);
265      break;
266  }
267
268  current_canvas_->resetMatrix();
269}
270
271void SoftwareRenderer::DrawDebugBorderQuad(const DrawingFrame* frame,
272                                           const DebugBorderDrawQuad* quad) {
273  // We need to apply the matrix manually to have pixel-sized stroke width.
274  SkPoint vertices[4];
275  gfx::RectFToSkRect(QuadVertexRect()).toQuad(vertices);
276  SkPoint transformed_vertices[4];
277  current_canvas_->getTotalMatrix().mapPoints(transformed_vertices,
278                                              vertices,
279                                              4);
280  current_canvas_->resetMatrix();
281
282  current_paint_.setColor(quad->color);
283  current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color));
284  current_paint_.setStyle(SkPaint::kStroke_Style);
285  current_paint_.setStrokeWidth(quad->width);
286  current_canvas_->drawPoints(SkCanvas::kPolygon_PointMode,
287                              4, transformed_vertices, current_paint_);
288}
289
290void SoftwareRenderer::DrawPictureQuad(const DrawingFrame* frame,
291                                       const PictureDrawQuad* quad) {
292  SkMatrix content_matrix;
293  content_matrix.setRectToRect(
294      gfx::RectFToSkRect(quad->tex_coord_rect),
295      gfx::RectFToSkRect(QuadVertexRect()),
296      SkMatrix::kFill_ScaleToFit);
297  current_canvas_->concat(content_matrix);
298
299  if (quad->ShouldDrawWithBlending()) {
300    TRACE_EVENT0("cc", "SoftwareRenderer::DrawPictureQuad with blending");
301    SkBitmap temp_bitmap;
302    temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
303                          quad->texture_size.width(),
304                          quad->texture_size.height());
305    temp_bitmap.allocPixels();
306    SkDevice temp_device(temp_bitmap);
307    SkCanvas temp_canvas(&temp_device);
308
309    quad->picture_pile->RasterToBitmap(
310        &temp_canvas, quad->content_rect, quad->contents_scale, NULL);
311
312    current_paint_.setFilterBitmap(true);
313    current_canvas_->drawBitmap(temp_bitmap, 0, 0, &current_paint_);
314  } else {
315    TRACE_EVENT0("cc",
316                 "SoftwareRenderer::DrawPictureQuad direct from PicturePile");
317    quad->picture_pile->RasterDirect(
318        current_canvas_, quad->content_rect, quad->contents_scale, NULL);
319  }
320}
321
322void SoftwareRenderer::DrawSolidColorQuad(const DrawingFrame* frame,
323                                          const SolidColorDrawQuad* quad) {
324  current_paint_.setColor(quad->color);
325  current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color));
326  current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
327                            current_paint_);
328}
329
330void SoftwareRenderer::DrawTextureQuad(const DrawingFrame* frame,
331                                       const TextureDrawQuad* quad) {
332  if (!IsSoftwareResource(quad->resource_id)) {
333    DrawUnsupportedQuad(frame, quad);
334    return;
335  }
336
337  // FIXME: Add support for non-premultiplied alpha.
338  ResourceProvider::ScopedReadLockSoftware lock(resource_provider_,
339                                                quad->resource_id);
340  const SkBitmap* bitmap = lock.sk_bitmap();
341  gfx::RectF uv_rect = gfx::ScaleRect(gfx::BoundingRect(quad->uv_top_left,
342                                                        quad->uv_bottom_right),
343                                      bitmap->width(),
344                                      bitmap->height());
345  SkRect sk_uv_rect = gfx::RectFToSkRect(uv_rect);
346  if (quad->flipped)
347    current_canvas_->scale(1, -1);
348  current_canvas_->drawBitmapRectToRect(*bitmap, &sk_uv_rect,
349                                        gfx::RectFToSkRect(QuadVertexRect()),
350                                        &current_paint_);
351}
352
353void SoftwareRenderer::DrawTileQuad(const DrawingFrame* frame,
354                                    const TileDrawQuad* quad) {
355  DCHECK(!output_surface_->ForcedDrawToSoftwareDevice());
356  DCHECK(IsSoftwareResource(quad->resource_id));
357  ResourceProvider::ScopedReadLockSoftware lock(resource_provider_,
358                                                quad->resource_id);
359
360  SkRect uv_rect = gfx::RectFToSkRect(quad->tex_coord_rect);
361  current_paint_.setFilterBitmap(true);
362  current_canvas_->drawBitmapRectToRect(*lock.sk_bitmap(), &uv_rect,
363                                        gfx::RectFToSkRect(QuadVertexRect()),
364                                        &current_paint_);
365}
366
367void SoftwareRenderer::DrawRenderPassQuad(const DrawingFrame* frame,
368                                          const RenderPassDrawQuad* quad) {
369  CachedResource* content_texture =
370      render_pass_textures_.get(quad->render_pass_id);
371  if (!content_texture || !content_texture->id())
372    return;
373
374  DCHECK(IsSoftwareResource(content_texture->id()));
375  ResourceProvider::ScopedReadLockSoftware lock(resource_provider_,
376                                                content_texture->id());
377
378  SkRect dest_rect = gfx::RectFToSkRect(QuadVertexRect());
379  SkRect content_rect = SkRect::MakeWH(quad->rect.width(), quad->rect.height());
380
381  SkMatrix content_mat;
382  content_mat.setRectToRect(content_rect, dest_rect,
383                            SkMatrix::kFill_ScaleToFit);
384
385  const SkBitmap* content = lock.sk_bitmap();
386  skia::RefPtr<SkShader> shader = skia::AdoptRef(
387      SkShader::CreateBitmapShader(*content,
388                                   SkShader::kClamp_TileMode,
389                                   SkShader::kClamp_TileMode));
390  shader->setLocalMatrix(content_mat);
391  current_paint_.setShader(shader.get());
392
393  SkImageFilter* filter = quad->filter.get();
394  if (filter)
395    current_paint_.setImageFilter(filter);
396
397  if (quad->mask_resource_id) {
398    ResourceProvider::ScopedReadLockSoftware mask_lock(resource_provider_,
399                                                       quad->mask_resource_id);
400
401    const SkBitmap* mask = mask_lock.sk_bitmap();
402
403    SkRect mask_rect = SkRect::MakeXYWH(
404        quad->mask_uv_rect.x() * mask->width(),
405        quad->mask_uv_rect.y() * mask->height(),
406        quad->mask_uv_rect.width() * mask->width(),
407        quad->mask_uv_rect.height() * mask->height());
408
409    SkMatrix mask_mat;
410    mask_mat.setRectToRect(mask_rect, dest_rect, SkMatrix::kFill_ScaleToFit);
411
412    skia::RefPtr<SkShader> mask_shader = skia::AdoptRef(
413        SkShader::CreateBitmapShader(*mask,
414                                     SkShader::kClamp_TileMode,
415                                     SkShader::kClamp_TileMode));
416    mask_shader->setLocalMatrix(mask_mat);
417
418    SkPaint mask_paint;
419    mask_paint.setShader(mask_shader.get());
420
421    skia::RefPtr<SkLayerRasterizer> mask_rasterizer =
422        skia::AdoptRef(new SkLayerRasterizer);
423    mask_rasterizer->addLayer(mask_paint);
424
425    current_paint_.setRasterizer(mask_rasterizer.get());
426    current_canvas_->drawRect(dest_rect, current_paint_);
427  } else {
428    // FIXME: Apply background filters and blend with content
429    current_canvas_->drawRect(dest_rect, current_paint_);
430  }
431}
432
433void SoftwareRenderer::DrawUnsupportedQuad(const DrawingFrame* frame,
434                                           const DrawQuad* quad) {
435#ifdef NDEBUG
436  current_paint_.setColor(SK_ColorWHITE);
437#else
438  current_paint_.setColor(SK_ColorMAGENTA);
439#endif
440  current_paint_.setAlpha(quad->opacity() * 255);
441  current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
442                            current_paint_);
443}
444
445void SoftwareRenderer::CopyCurrentRenderPassToBitmap(
446    DrawingFrame* frame,
447    scoped_ptr<CopyOutputRequest> request) {
448  scoped_ptr<SkBitmap> bitmap(new SkBitmap);
449  bitmap->setConfig(SkBitmap::kARGB_8888_Config,
450                    current_viewport_rect_.width(),
451                    current_viewport_rect_.height());
452  current_canvas_->readPixels(
453      bitmap.get(), current_viewport_rect_.x(), current_viewport_rect_.y());
454
455  request->SendBitmapResult(bitmap.Pass());
456}
457
458void SoftwareRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) {
459  TRACE_EVENT0("cc", "SoftwareRenderer::GetFramebufferPixels");
460  SkBitmap subset_bitmap;
461  rect += current_viewport_rect_.OffsetFromOrigin();
462  output_device_->CopyToBitmap(rect, &subset_bitmap);
463  subset_bitmap.copyPixelsTo(pixels,
464                             4 * rect.width() * rect.height(),
465                             4 * rect.width());
466}
467
468void SoftwareRenderer::SetVisible(bool visible) {
469  if (visible_ == visible)
470    return;
471  visible_ = visible;
472}
473
474}  // namespace cc
475