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