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 33namespace cc { 34 35namespace { 36 37static inline bool IsScalarNearlyInteger(SkScalar scalar) { 38 return SkScalarNearlyZero(scalar - SkScalarRound(scalar)); 39} 40 41bool 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 53scoped_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 61SoftwareRenderer::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 83SoftwareRenderer::~SoftwareRenderer() {} 84 85const RendererCapabilities& SoftwareRenderer::Capabilities() const { 86 return capabilities_; 87} 88 89void 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 95void 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 105void 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 112void SoftwareRenderer::ReceiveSwapBuffersAck(const CompositorFrameAck& ack) { 113 output_device_->ReclaimSoftwareFrame(ack.last_software_frame_id); 114} 115 116bool SoftwareRenderer::FlippedFramebuffer() const { 117 return false; 118} 119 120void SoftwareRenderer::EnsureScissorTestEnabled() { 121 is_scissor_enabled_ = true; 122 SetClipRect(scissor_rect_); 123} 124 125void 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 135void SoftwareRenderer::Finish() {} 136 137void SoftwareRenderer::BindFramebufferToOutputSurface(DrawingFrame* frame) { 138 DCHECK(!client_->ExternalStencilTestEnabled()); 139 current_framebuffer_lock_.reset(); 140 current_canvas_ = root_canvas_; 141} 142 143bool 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 159void SoftwareRenderer::SetScissorTestRect(gfx::Rect scissor_rect) { 160 is_scissor_enabled_ = true; 161 scissor_rect_ = scissor_rect; 162 SetClipRect(scissor_rect); 163} 164 165void 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 173void 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 182void 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 194void SoftwareRenderer::SetDrawViewport(gfx::Rect window_space_viewport) {} 195 196bool 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 211void 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 278void 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 286void 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 305void 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 331void 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 339void 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 ¤t_paint_); 377 378 if (needs_layer) 379 current_canvas_->restore(); 380} 381 382void 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 ¤t_paint_); 394} 395 396void 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 462void 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 474void 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 495void 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 505void SoftwareRenderer::SetVisible(bool visible) { 506 if (visible_ == visible) 507 return; 508 visible_ = visible; 509} 510 511void 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