diff options
-rw-r--r-- | cc/debug_colors.cc | 4 | ||||
-rw-r--r-- | cc/debug_colors.h | 3 | ||||
-rw-r--r-- | cc/layer_tree_host_impl.cc | 7 | ||||
-rw-r--r-- | cc/layer_tree_settings.cc | 2 | ||||
-rw-r--r-- | cc/layer_tree_settings.h | 2 | ||||
-rw-r--r-- | cc/picture.cc | 20 | ||||
-rw-r--r-- | cc/picture.h | 11 | ||||
-rw-r--r-- | cc/picture_layer_impl.cc | 26 | ||||
-rw-r--r-- | cc/picture_layer_tiling_set.cc | 2 | ||||
-rw-r--r-- | cc/picture_pile_impl.cc | 59 | ||||
-rw-r--r-- | cc/picture_pile_impl.h | 13 | ||||
-rw-r--r-- | cc/switches.cc | 6 | ||||
-rw-r--r-- | cc/switches.h | 2 | ||||
-rw-r--r-- | cc/test/fake_picture_layer_tiling_client.cc | 2 | ||||
-rw-r--r-- | cc/tile.cc | 6 | ||||
-rw-r--r-- | cc/tile.h | 15 | ||||
-rw-r--r-- | cc/tile_manager.cc | 176 | ||||
-rw-r--r-- | cc/tile_manager.h | 18 | ||||
-rw-r--r-- | chrome/chrome_tests_unit.gypi | 1 | ||||
-rw-r--r-- | content/browser/renderer_host/render_process_host_impl.cc | 2 | ||||
-rw-r--r-- | content/renderer/gpu/render_widget_compositor.cc | 4 | ||||
-rw-r--r-- | skia/ext/analysis_canvas.cc | 269 | ||||
-rw-r--r-- | skia/ext/analysis_canvas.h | 22 | ||||
-rw-r--r-- | skia/ext/analysis_canvas_unittest.cc | 313 |
24 files changed, 900 insertions, 85 deletions
diff --git a/cc/debug_colors.cc b/cc/debug_colors.cc index 01b6667..8c998fe 100644 --- a/cc/debug_colors.cc +++ b/cc/debug_colors.cc @@ -68,6 +68,10 @@ int DebugColors::MissingTileBorderWidth(const LayerTreeImpl* tree_impl) { return SkColor DebugColors::CulledTileBorderColor() { return SkColorSetARGB(120, 160, 100, 0); } int DebugColors::CulledTileBorderWidth(const LayerTreeImpl* tree_impl) { return Scale(1, tree_impl); } +// Solid color tile borders are grey. +SkColor DebugColors::SolidColorTileBorderColor() { return SkColorSetARGB(128, 128, 128, 128); } +int DebugColors::SolidColorTileBorderWidth(const LayerTreeImpl* tree_impl) { return Scale(1, tree_impl); } + // ======= Checkerboard colors ======= // Non-debug checkerboards are grey. diff --git a/cc/debug_colors.h b/cc/debug_colors.h index da9dbd8..3f62ed4 100644 --- a/cc/debug_colors.h +++ b/cc/debug_colors.h @@ -53,6 +53,9 @@ class DebugColors { static SkColor CulledTileBorderColor(); static int CulledTileBorderWidth(const LayerTreeImpl* tree_impl); + static SkColor SolidColorTileBorderColor(); + static int SolidColorTileBorderWidth(const LayerTreeImpl* tree_impl); + static SkColor DefaultCheckerboardColor(); static SkColor EvictedTileCheckerboardColor(); static SkColor InvalidatedTileCheckerboardColor(); diff --git a/cc/layer_tree_host_impl.cc b/cc/layer_tree_host_impl.cc index 507e352..d3bbd6a 100644 --- a/cc/layer_tree_host_impl.cc +++ b/cc/layer_tree_host_impl.cc @@ -1077,7 +1077,12 @@ bool LayerTreeHostImpl::initializeRenderer(scoped_ptr<OutputSurface> outputSurfa return false; if (m_settings.implSidePainting) { - m_tileManager.reset(new TileManager(this, resourceProvider.get(), m_settings.numRasterThreads, m_settings.useCheapnessEstimator)); + m_tileManager.reset(new TileManager(this, + resourceProvider.get(), + m_settings.numRasterThreads, + m_settings.useCheapnessEstimator, + m_settings.useColorEstimator, + m_settings.predictionBenchmarking)); m_tileManager->SetRecordRenderingStats(m_debugState.recordRenderingStats()); } diff --git a/cc/layer_tree_settings.cc b/cc/layer_tree_settings.cc index b2af0bd..57710fd 100644 --- a/cc/layer_tree_settings.cc +++ b/cc/layer_tree_settings.cc @@ -33,7 +33,9 @@ LayerTreeSettings::LayerTreeSettings() , solidColorScrollbarThicknessDIP(-1) , calculateTopControlsPosition(false) , useCheapnessEstimator(false) + , useColorEstimator(false) , useMemoryManagement(true) + , predictionBenchmarking(false) , minimumContentsScale(0.0625f) , lowResContentsScaleFactor(0.125f) , topControlsHeight(0.f) diff --git a/cc/layer_tree_settings.h b/cc/layer_tree_settings.h index fa46fee..1536ef0 100644 --- a/cc/layer_tree_settings.h +++ b/cc/layer_tree_settings.h @@ -37,7 +37,9 @@ class CC_EXPORT LayerTreeSettings { int solidColorScrollbarThicknessDIP; bool calculateTopControlsPosition; bool useCheapnessEstimator; + bool useColorEstimator; bool useMemoryManagement; + bool predictionBenchmarking; float minimumContentsScale; float lowResContentsScaleFactor; float topControlsHeight; diff --git a/cc/picture.cc b/cc/picture.cc index 1004610..d1f5b79 100644 --- a/cc/picture.cc +++ b/cc/picture.cc @@ -130,17 +130,15 @@ void Picture::Raster( canvas->restore(); } -bool Picture::IsCheapInRect(const gfx::Rect& layer_rect) const { - TRACE_EVENT0("cc", "Picture::IsCheapInRect"); - - SkBitmap emptyBitmap; - emptyBitmap.setConfig(SkBitmap::kNo_Config, layer_rect.width(), - layer_rect.height()); - skia::AnalysisDevice device(emptyBitmap); - skia::AnalysisCanvas canvas(&device); - - canvas.drawPicture(*picture_); - return canvas.isCheap(); +void Picture::AnalyzeInRect(skia::AnalysisCanvas* canvas, + const gfx::Rect& content_rect, + float contents_scale) { + canvas->save(); + canvas->clipRect(gfx::RectToSkRect(content_rect)); + canvas->scale(contents_scale, contents_scale); + canvas->translate(layer_rect_.x(), layer_rect_.y()); + canvas->drawPicture(*picture_); + canvas->restore(); } void Picture::GatherPixelRefs(const gfx::Rect& layer_rect, diff --git a/cc/picture.h b/cc/picture.h index 600a229..28b998f 100644 --- a/cc/picture.h +++ b/cc/picture.h @@ -16,6 +16,10 @@ #include "third_party/skia/include/core/SkTileGridPicture.h" #include "ui/gfx/rect.h" +namespace skia { +class AnalysisCanvas; +} + namespace cc { class ContentLayerClient; @@ -47,10 +51,9 @@ class CC_EXPORT Picture // Apply this contents scale and raster the content rect into the canvas. void Raster(SkCanvas* canvas, gfx::Rect content_rect, float contents_scale); - // Estimate the cost of rasterizing. To predict the cost of a particular - // call to Raster(), pass this the bounds of the canvas that will - // be rastered into. - bool IsCheapInRect(const gfx::Rect& layer_rect) const; + void AnalyzeInRect(skia::AnalysisCanvas* canvas, + const gfx::Rect& content_rect, + float contents_scale); void GatherPixelRefs( const gfx::Rect& layer_rect, diff --git a/cc/picture_layer_impl.cc b/cc/picture_layer_impl.cc index e4bb444..0765c9d2 100644 --- a/cc/picture_layer_impl.cc +++ b/cc/picture_layer_impl.cc @@ -114,8 +114,11 @@ void PictureLayerImpl::appendQuads(QuadSink& quadSink, ++iter) { SkColor color; float width; - if (*iter && iter->GetResourceId()) { - if (iter->priority(ACTIVE_TREE).resolution == HIGH_RESOLUTION) { + if (*iter && iter->IsReadyToDraw()) { + if (iter->is_solid_color() || iter->is_transparent()) { + color = DebugColors::SolidColorTileBorderColor(); + width = DebugColors::SolidColorTileBorderWidth(layerTreeImpl()); + } else if (iter->priority(ACTIVE_TREE).resolution == HIGH_RESOLUTION) { color = DebugColors::HighResTileBorderColor(); width = DebugColors::HighResTileBorderWidth(layerTreeImpl()); } else if (iter->priority(ACTIVE_TREE).resolution == LOW_RESOLUTION) { @@ -152,12 +155,23 @@ void PictureLayerImpl::appendQuads(QuadSink& quadSink, layerDeviceAlignment); iter; ++iter) { - ResourceProvider::ResourceId resource = 0; - if (*iter) - resource = iter->GetResourceId(); gfx::Rect geometry_rect = iter.geometry_rect(); + ResourceProvider::ResourceId resource = 0; + if (*iter) { + if (iter->is_solid_color()) { + scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); + quad->SetNew(sharedQuadState, geometry_rect, iter->solid_color()); + quadSink.append(quad.PassAs<DrawQuad>(), appendQuadsData); + if (!seen_tilings.size() || seen_tilings.back() != iter.CurrentTiling()) + seen_tilings.push_back(iter.CurrentTiling()); + continue; + } else if (iter->is_transparent()) { + continue; + } + resource = iter->GetResourceId(); + } if (!resource) { if (drawCheckerboardForMissingTiles()) { // TODO(enne): Figure out how to show debug "invalidated checker" color @@ -509,7 +523,7 @@ bool PictureLayerImpl::areVisibleResourcesReady() const { iter; ++iter) { // A null tile (i.e. no recording) is considered "ready". - if (!*iter || iter->GetResourceId()) + if (!*iter || iter->IsReadyToDraw()) missing_region.Subtract(iter.geometry_rect()); } } diff --git a/cc/picture_layer_tiling_set.cc b/cc/picture_layer_tiling_set.cc index 9f74260..a5ade55 100644 --- a/cc/picture_layer_tiling_set.cc +++ b/cc/picture_layer_tiling_set.cc @@ -207,7 +207,7 @@ PictureLayerTilingSet::Iterator& PictureLayerTilingSet::Iterator::operator++() { // Loop until we find a valid place to stop. while (true) { - while (tiling_iter_ && (!*tiling_iter_ || !tiling_iter_->GetResourceId())) { + while (tiling_iter_ && (!*tiling_iter_ || !tiling_iter_->IsReadyToDraw())) { missing_region_.Union(tiling_iter_.geometry_rect()); ++tiling_iter_; } diff --git a/cc/picture_pile_impl.cc b/cc/picture_pile_impl.cc index 544d2a1..56d5045 100644 --- a/cc/picture_pile_impl.cc +++ b/cc/picture_pile_impl.cc @@ -7,6 +7,7 @@ #include "cc/picture_pile_impl.h" #include "cc/region.h" #include "cc/rendering_stats.h" +#include "skia/ext/analysis_canvas.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSize.h" #include "ui/gfx/rect_conversions.h" @@ -212,28 +213,66 @@ skia::RefPtr<SkPicture> PicturePileImpl::GetFlattenedPicture() { return picture; } -bool PicturePileImpl::IsCheapInRect( - gfx::Rect content_rect, float contents_scale) const { +void PicturePileImpl::AnalyzeInRect(const gfx::Rect& content_rect, + float contents_scale, + PicturePileImpl::Analysis* analysis) { + DCHECK(analysis); + + TRACE_EVENT0("cc", "PicturePileImpl::AnalyzeInRect"); + gfx::Rect layer_rect = gfx::ToEnclosingRect( gfx::ScaleRect(content_rect, 1.f / contents_scale)); + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, content_rect.width(), + content_rect.height()); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + canvas.translate(-content_rect.x(), -content_rect.y()); + canvas.clipRect(gfx::RectToSkRect(content_rect)); + + // The loop here is similar to the raster loop. + Region unclipped(content_rect); for (TilingData::Iterator tile_iter(&tiling_, layer_rect); tile_iter; ++tile_iter) { - PictureListMap::const_iterator map_iter = + PictureListMap::iterator map_iter = picture_list_map_.find(tile_iter.index()); if (map_iter == picture_list_map_.end()) continue; + PictureList& pic_list = map_iter->second; + if (pic_list.empty()) + continue; - const PictureList& pic_list = map_iter->second; - for (PictureList::const_iterator i = pic_list.begin(); - i != pic_list.end(); ++i) { - if (!(*i)->LayerRect().Intersects(layer_rect) || !(*i)->HasRecording()) + for (PictureList::reverse_iterator i = pic_list.rbegin(); + i != pic_list.rend(); ++i) { + gfx::Rect content_clip = gfx::ToEnclosedRect( + gfx::ScaleRect((*i)->LayerRect(), contents_scale)); + DCHECK(!content_clip.IsEmpty()); + if (!unclipped.Intersects(content_clip)) continue; - if (!(*i)->IsCheapInRect(layer_rect)) - return false; + + (*i)->AnalyzeInRect(&canvas, + content_rect, + contents_scale); + + canvas.clipRect( + gfx::RectToSkRect(content_clip), + SkRegion::kDifference_Op); + unclipped.Subtract(content_clip); + } } - return true; + + analysis->is_transparent = canvas.isTransparent(); + analysis->is_solid_color = canvas.getColorIfSolid(&analysis->solid_color); + analysis->is_cheap_to_raster = canvas.isCheap(); +} + +PicturePileImpl::Analysis::Analysis() : + is_solid_color(false), + is_transparent(false), + is_cheap_to_raster(false) { } } // namespace cc diff --git a/cc/picture_pile_impl.h b/cc/picture_pile_impl.h index 952de14..8b023b4 100644 --- a/cc/picture_pile_impl.h +++ b/cc/picture_pile_impl.h @@ -48,7 +48,18 @@ class CC_EXPORT PicturePileImpl : public PicturePileBase { slow_down_raster_scale_factor_for_debug_ = factor; } - bool IsCheapInRect(gfx::Rect content_rect, float contents_scale) const; + struct Analysis { + Analysis(); + + bool is_solid_color; + bool is_transparent; + bool is_cheap_to_raster; + SkColor solid_color; + }; + + void AnalyzeInRect(const gfx::Rect& content_rect, + float contents_scale, + Analysis* analysis); protected: friend class PicturePile; diff --git a/cc/switches.cc b/cc/switches.cc index 7602e42..a0dd49b 100644 --- a/cc/switches.cc +++ b/cc/switches.cc @@ -36,6 +36,9 @@ const char kEnableRightAlignedScheduling[] = "enable-right-aligned-scheduling"; const char kEnableTopControlsPositionCalculation[] = "enable-top-controls-position-calculation"; +// Enable solid tile color, transparent tile, and cheapness prediction metrics. +const char kEnablePredictionBenchmarking[] = "enable-prediction-benchmarking"; + // The height of the movable top controls. const char kTopControlsHeight[] = "top-controls-height"; @@ -86,6 +89,9 @@ const char kSlowDownRasterScaleFactor[] = "slow-down-raster-scale-factor"; // Schedule rasterization jobs according to their estimated processing cost. const char kUseCheapnessEstimator[] = "use-cheapness-estimator"; +// Predict whether the tile will be either solid color or transparent. +const char kUseColorEstimator[] = "use-color-estimator"; + // The scale factor for low resolution tile contents. const char kLowResolutionContentsScaleFactor[] = "low-resolution-contents-scale-factor"; diff --git a/cc/switches.h b/cc/switches.h index 517660d..1f637a1 100644 --- a/cc/switches.h +++ b/cc/switches.h @@ -22,6 +22,7 @@ CC_EXPORT extern const char kDisableImplSidePainting[]; CC_EXPORT extern const char kEnableImplSidePainting[]; CC_EXPORT extern const char kEnablePartialSwap[]; CC_EXPORT extern const char kEnablePerTilePainting[]; +CC_EXPORT extern const char kEnablePredictionBenchmarking[]; CC_EXPORT extern const char kEnableRightAlignedScheduling[]; CC_EXPORT extern const char kEnableTopControlsPositionCalculation[]; CC_EXPORT extern const char kJankInsteadOfCheckerboard[]; @@ -39,6 +40,7 @@ CC_EXPORT extern const char kTopControlsShowThreshold[]; CC_EXPORT extern const char kTraceAllRenderedFrames[]; CC_EXPORT extern const char kSlowDownRasterScaleFactor[]; CC_EXPORT extern const char kUseCheapnessEstimator[]; +CC_EXPORT extern const char kUseColorEstimator[]; CC_EXPORT extern const char kLowResolutionContentsScaleFactor[]; CC_EXPORT extern const char kCompositeToMailbox[]; diff --git a/cc/test/fake_picture_layer_tiling_client.cc b/cc/test/fake_picture_layer_tiling_client.cc index e3c2f6b..b0e6c8b 100644 --- a/cc/test/fake_picture_layer_tiling_client.cc +++ b/cc/test/fake_picture_layer_tiling_client.cc @@ -21,7 +21,7 @@ class FakeInfinitePicturePileImpl : public PicturePileImpl }; FakePictureLayerTilingClient::FakePictureLayerTilingClient() - : tile_manager_(&tile_manager_client_, NULL, 1, false), + : tile_manager_(&tile_manager_client_, NULL, 1, false, false, false), pile_(new FakeInfinitePicturePileImpl()) { } @@ -45,4 +45,10 @@ scoped_ptr<base::Value> Tile::AsValue() const { return res.PassAs<base::Value>(); } +bool Tile::IsReadyToDraw() const { + return GetResourceId() != 0 || + is_solid_color() || + is_transparent(); +} + } // namespace cc @@ -61,6 +61,21 @@ class CC_EXPORT Tile : public base::RefCounted<Tile> { return managed_state_.resource->id(); } + bool IsReadyToDraw() const; + + bool is_solid_color() const { + return managed_state_.picture_pile_analysis.is_solid_color; + } + + bool is_transparent() const { + return managed_state_.picture_pile_analysis.is_transparent; + } + + SkColor solid_color() const { + DCHECK(managed_state_.picture_pile_analysis.is_solid_color); + return managed_state_.picture_pile_analysis.solid_color; + } + const gfx::Rect& opaque_rect() const { return opaque_rect_; } bool contents_swizzled() const { return managed_state_.contents_swizzled; } diff --git a/cc/tile_manager.cc b/cc/tile_manager.cc index d06e18d..963dd9a 100644 --- a/cc/tile_manager.cc +++ b/cc/tile_manager.cc @@ -145,7 +145,8 @@ ManagedTileState::ManagedTileState() raster_state(IDLE_STATE), resolution(NON_IDEAL_RESOLUTION), time_to_needed_in_seconds(std::numeric_limits<float>::infinity()), - distance_to_visible_in_pixels(std::numeric_limits<float>::infinity()) { + distance_to_visible_in_pixels(std::numeric_limits<float>::infinity()), + picture_pile_analyzed(false) { for (int i = 0; i < NUM_TREES; ++i) { tree_bin[i] = NEVER_BIN; bin[i] = NEVER_BIN; @@ -170,6 +171,10 @@ scoped_ptr<base::Value> ManagedTileState::AsValue() const { state->Set("resolution", TileResolutionAsValue(resolution).release()); state->Set("time_to_needed_in_seconds", MathUtil::asValueSafely(time_to_needed_in_seconds).release()); state->Set("distance_to_visible_in_pixels", MathUtil::asValueSafely(distance_to_visible_in_pixels).release()); + state->SetBoolean("is_picture_pile_analyzed", picture_pile_analyzed); + state->SetBoolean("is_cheap_to_raster", picture_pile_analysis.is_cheap_to_raster); + state->SetBoolean("is_transparent", picture_pile_analysis.is_transparent); + state->SetBoolean("is_solid_color", picture_pile_analysis.is_solid_color); return state.PassAs<base::Value>(); } @@ -177,7 +182,9 @@ TileManager::TileManager( TileManagerClient* client, ResourceProvider* resource_provider, size_t num_raster_threads, - bool use_cheapness_estimator) + bool use_cheapness_estimator, + bool use_color_estimator, + bool prediction_benchmarking) : client_(client), resource_pool_(ResourcePool::Create(resource_provider)), raster_worker_pool_(RasterWorkerPool::Create(this, num_raster_threads)), @@ -188,8 +195,10 @@ TileManager::TileManager( ever_exceeded_memory_budget_(false), record_rendering_stats_(false), use_cheapness_estimator_(use_cheapness_estimator), + use_color_estimator_(use_color_estimator), allow_cheap_tasks_(true), - did_schedule_cheap_tasks_(false) { + did_schedule_cheap_tasks_(false), + prediction_benchmarking_(prediction_benchmarking) { for (int i = 0; i < NUM_STATES; ++i) { for (int j = 0; j < NUM_TREES; ++j) { for (int k = 0; k < NUM_BINS; ++k) @@ -371,12 +380,13 @@ void TileManager::ManageTiles() { for (int i = 0; i < NUM_BIN_PRIORITIES; ++i) mts.bin[i] = bin_map[mts.bin[i]]; - if (tile->priority(ACTIVE_TREE).is_live || - tile->priority(PENDING_TREE).is_live || - tile->managed_state().resource || - tile->managed_state().resource_is_being_initialized) { - live_or_allocated_tiles_.push_back(tile); - } + if (!tile->managed_state().resource && + !tile->managed_state().resource_is_being_initialized && + !tile->priority(ACTIVE_TREE).is_live && + !tile->priority(PENDING_TREE).is_live) + continue; + + live_or_allocated_tiles_.push_back(tile); } TRACE_COUNTER_ID1("cc", "LiveOrAllocatedTileCount", this, live_or_allocated_tiles_.size()); @@ -459,6 +469,9 @@ void TileManager::GetMemoryStats( for (size_t i = 0; i < live_or_allocated_tiles_.size(); i++) { const Tile* tile = live_or_allocated_tiles_[i]; const ManagedTileState& mts = tile->managed_state(); + if (tile->is_transparent() || tile->is_solid_color()) + continue; + size_t tile_bytes = tile->bytes_consumed_if_allocated(); if (mts.gpu_memmgr_stats_bin == NOW_BIN) *memoryRequiredBytes += tile_bytes; @@ -579,9 +592,13 @@ void TileManager::AssignGpuMemoryToTiles() { for (TileVector::iterator it = live_or_allocated_tiles_.begin(); it != live_or_allocated_tiles_.end(); ++it) { Tile* tile = *it; - if (!tile->managed_state().can_be_freed) + if (tile->is_solid_color() || tile->is_transparent()) + continue; + + ManagedTileState& managed_tile_state = tile->managed_state(); + if (!managed_tile_state.can_be_freed) unreleasable_bytes += tile->bytes_consumed_if_allocated(); - if (tile->managed_state().raster_state == WAITING_FOR_RASTER_STATE) + if (managed_tile_state.raster_state == WAITING_FOR_RASTER_STATE) DidTileRasterStateChange(tile, IDLE_STATE); } @@ -590,8 +607,11 @@ void TileManager::AssignGpuMemoryToTiles() { size_t bytes_left = bytes_allocatable; for (TileVector::iterator it = live_or_allocated_tiles_.begin(); it != live_or_allocated_tiles_.end(); ++it) { Tile* tile = *it; - size_t tile_bytes = tile->bytes_consumed_if_allocated(); + if (tile->is_solid_color() || tile->is_transparent()) + continue; + ManagedTileState& managed_tile_state = tile->managed_state(); + size_t tile_bytes = tile->bytes_consumed_if_allocated(); if (!managed_tile_state.can_be_freed) continue; if (managed_tile_state.bin[HIGH_PRIORITY_BIN] == NEVER_BIN && @@ -661,12 +681,13 @@ void TileManager::DispatchMoreTasks() { // Because tiles in the image decoding list have higher priorities, we // need to process those tiles first before we start to handle the tiles - // in the need_to_be_rasterized queue. + // in the need_to_be_rasterized queue. Note that solid/transparent tiles + // will not be put into the decoding list. for(TileList::iterator it = tiles_with_image_decoding_tasks_.begin(); it != tiles_with_image_decoding_tasks_.end(); ) { + ManagedTileState& managed_tile_state = (*it)->managed_state(); DispatchImageDecodeTasksForTile(*it); - ManagedTileState& managed_state = (*it)->managed_state(); - if (managed_state.pending_pixel_refs.empty()) { + if (managed_tile_state.pending_pixel_refs.empty()) { if (!CanDispatchRasterTask(*it)) return; DispatchOneRasterTask(*it); @@ -676,13 +697,23 @@ void TileManager::DispatchMoreTasks() { } } - // Process all tiles in the need_to_be_rasterized queue. If a tile has - // image decoding tasks, put it to the back of the image decoding list. + // Process all tiles in the need_to_be_rasterized queue. If a tile is + // solid/transparent, then we are done (we don't need to rasterize + // the tile). If a tile has image decoding tasks, put it to the back + // of the image decoding list. while (!tiles_that_need_to_be_rasterized_.empty()) { Tile* tile = tiles_that_need_to_be_rasterized_.back(); + ManagedTileState& managed_tile_state = tile->managed_state(); + + AnalyzeTile(tile); + if (tile->is_solid_color() || tile->is_transparent()) { + DidTileRasterStateChange(tile, IDLE_STATE); + tiles_that_need_to_be_rasterized_.pop_back(); + continue; + } + DispatchImageDecodeTasksForTile(tile); - ManagedTileState& managed_state = tile->managed_state(); - if (!managed_state.pending_pixel_refs.empty()) { + if (!managed_tile_state.pending_pixel_refs.empty()) { tiles_with_image_decoding_tasks_.push_back(tile); } else { if (!CanDispatchRasterTask(tile)) @@ -693,18 +724,36 @@ void TileManager::DispatchMoreTasks() { } } +void TileManager::AnalyzeTile(Tile* tile) { + ManagedTileState& managed_tile_state = tile->managed_state(); + if ((use_cheapness_estimator_ || use_color_estimator_) && + !managed_tile_state.picture_pile_analyzed) { + tile->picture_pile()->AnalyzeInRect( + tile->content_rect(), + tile->contents_scale(), + &managed_tile_state.picture_pile_analysis); + managed_tile_state.picture_pile_analysis.is_cheap_to_raster &= + use_cheapness_estimator_; + managed_tile_state.picture_pile_analysis.is_solid_color &= + use_color_estimator_; + managed_tile_state.picture_pile_analysis.is_transparent &= + use_color_estimator_; + managed_tile_state.picture_pile_analyzed = true; + } +} + void TileManager::GatherPixelRefsForTile(Tile* tile) { TRACE_EVENT0("cc", "TileManager::GatherPixelRefsForTile"); - ManagedTileState& managed_state = tile->managed_state(); - if (managed_state.need_to_gather_pixel_refs) { + ManagedTileState& managed_tile_state = tile->managed_state(); + if (managed_tile_state.need_to_gather_pixel_refs) { base::TimeTicks gather_begin_time; if (record_rendering_stats_) gather_begin_time = base::TimeTicks::HighResNow(); tile->picture_pile()->GatherPixelRefs( tile->content_rect_, tile->contents_scale_, - managed_state.pending_pixel_refs); - managed_state.need_to_gather_pixel_refs = false; + managed_tile_state.pending_pixel_refs); + managed_tile_state.need_to_gather_pixel_refs = false; if (record_rendering_stats_) { rendering_stats_.totalImageGatheringCount++; rendering_stats_.totalImageGatheringTime += @@ -794,12 +843,12 @@ void TileManager::DispatchOneRasterTask(scoped_refptr<Tile> tile) { uint8* buffer = resource_pool_->resource_provider()->MapPixelBuffer(resource_id); - bool is_cheap = use_cheapness_estimator_ && allow_cheap_tasks_ && - tile->picture_pile()->IsCheapInRect(tile->content_rect_, - tile->contents_scale()); + ManagedTileState& managed_tile_state = tile->managed_state(); + bool is_cheap_to_raster = + managed_tile_state.picture_pile_analysis.is_cheap_to_raster; raster_worker_pool_->PostRasterTaskAndReply( tile->picture_pile(), - is_cheap, + allow_cheap_tasks_ && is_cheap_to_raster, base::Bind(&TileManager::RunRasterTask, buffer, tile->content_rect(), @@ -810,14 +859,14 @@ void TileManager::DispatchOneRasterTask(scoped_refptr<Tile> tile) { tile, base::Passed(&resource), manage_tiles_call_count_)); - did_schedule_cheap_tasks_ |= is_cheap; + did_schedule_cheap_tasks_ |= (allow_cheap_tasks_ && is_cheap_to_raster); } TileManager::RasterTaskMetadata TileManager::GetRasterTaskMetadata( const Tile& tile) const { RasterTaskMetadata metadata; const ManagedTileState& mts = tile.managed_state(); - metadata.use_cheapness_estimator = use_cheapness_estimator_; + metadata.prediction_benchmarking = prediction_benchmarking_; metadata.is_tile_in_pending_tree_now_bin = mts.tree_bin[PENDING_TREE] == NOW_BIN; metadata.tile_resolution = mts.resolution; @@ -958,11 +1007,22 @@ void TileManager::RunRasterTask(uint8* buffer, 10, 10); - if (metadata.use_cheapness_estimator) { - bool is_predicted_cheap = - picture_pile->IsCheapInRect(rect, contents_scale); + if (metadata.prediction_benchmarking) { + PicturePileImpl::Analysis analysis; + picture_pile->AnalyzeInRect(rect, contents_scale, &analysis); + bool is_predicted_cheap = analysis.is_cheap_to_raster; bool is_actually_cheap = duration.InMillisecondsF() <= 1.0f; RecordCheapnessPredictorResults(is_predicted_cheap, is_actually_cheap); + + DCHECK_EQ(bitmap.rowBytes(), + bitmap.width() * bitmap.bytesPerPixel()); + + RecordSolidColorPredictorResults( + reinterpret_cast<SkColor*>(bitmap.getPixels()), + bitmap.getSize() / bitmap.bytesPerPixel(), + analysis.is_solid_color, + analysis.solid_color, + analysis.is_transparent); } } } @@ -980,6 +1040,58 @@ void TileManager::RecordCheapnessPredictorResults(bool is_predicted_cheap, } // static +void TileManager::RecordSolidColorPredictorResults( + const SkColor* actual_colors, + size_t color_count, + bool is_predicted_solid, + SkColor predicted_color, + bool is_predicted_transparent) { + DCHECK_GT(color_count, 0u); + + bool is_actually_solid = true; + bool is_transparent = true; + + SkColor actual_color = *actual_colors; + for (unsigned int i = 0; i < color_count; ++i) { + SkColor current_color = actual_colors[i]; + if (current_color != actual_color || + SkColorGetA(current_color) != 255) + is_actually_solid = false; + + if (SkColorGetA(current_color) != 0) + is_transparent = false; + + if(!is_actually_solid && !is_transparent) + break; + } + + if (is_predicted_solid && !is_actually_solid) + UMA_HISTOGRAM_BOOLEAN("Renderer4.ColorPredictor.WrongActualNotSolid", true); + else if (is_predicted_solid && + is_actually_solid && + predicted_color != actual_color) + UMA_HISTOGRAM_BOOLEAN("Renderer4.ColorPredictor.WrongColor", true); + else if (!is_predicted_solid && is_actually_solid) + UMA_HISTOGRAM_BOOLEAN("Renderer4.ColorPredictor.WrongActualSolid", true); + + bool correct_guess = (is_predicted_solid && is_actually_solid && + predicted_color == actual_color) || + (!is_predicted_solid && !is_actually_solid); + UMA_HISTOGRAM_BOOLEAN("Renderer4.ColorPredictor.Accuracy", correct_guess); + + if (correct_guess) + UMA_HISTOGRAM_BOOLEAN("Renderer4.ColorPredictor.IsCorrectSolid", + is_predicted_solid); + + if (is_predicted_transparent) + UMA_HISTOGRAM_BOOLEAN( + "Renderer4.ColorPredictor.PredictedTransparentIsActually", + is_transparent); + UMA_HISTOGRAM_BOOLEAN("Renderer4.ColorPredictor.IsActuallyTransparent", + is_transparent); +} + +// static void TileManager::RunImageDecodeTask(skia::LazyPixelRef* pixel_ref, RenderingStats* stats) { TRACE_EVENT0("cc", "TileManager::RunImageDecodeTask"); diff --git a/cc/tile_manager.h b/cc/tile_manager.h index 6d2c621..095f9a8 100644 --- a/cc/tile_manager.h +++ b/cc/tile_manager.h @@ -14,6 +14,7 @@ #include "base/memory/scoped_ptr.h" #include "base/values.h" #include "cc/memory_history.h" +#include "cc/picture_pile_impl.h" #include "cc/rendering_stats.h" #include "cc/resource_pool.h" #include "cc/tile_priority.h" @@ -92,6 +93,8 @@ class CC_EXPORT ManagedTileState { TileResolution resolution; float time_to_needed_in_seconds; float distance_to_visible_in_pixels; + PicturePileImpl::Analysis picture_pile_analysis; + bool picture_pile_analyzed; }; // This class manages tiles, deciding which should get rasterized and which @@ -103,7 +106,9 @@ class CC_EXPORT TileManager : public WorkerPoolClient { TileManager(TileManagerClient* client, ResourceProvider *resource_provider, size_t num_raster_threads, - bool use_cheapess_estimator); + bool use_cheapess_estimator, + bool use_color_estimator, + bool prediction_benchmarking); virtual ~TileManager(); const GlobalStateThatImpactsTilePriority& GlobalState() const { @@ -148,13 +153,14 @@ class CC_EXPORT TileManager : public WorkerPoolClient { // Data that is passed to raster tasks. struct RasterTaskMetadata { - bool use_cheapness_estimator; + bool prediction_benchmarking; bool is_tile_in_pending_tree_now_bin; TileResolution tile_resolution; int layer_id; }; RasterTaskMetadata GetRasterTaskMetadata(const Tile& tile) const; + void SortTiles(); void AssignGpuMemoryToTiles(); void FreeResourcesForTile(Tile* tile); @@ -165,6 +171,7 @@ class CC_EXPORT TileManager : public WorkerPoolClient { manage_tiles_pending_ = true; } void DispatchMoreTasks(); + void AnalyzeTile(Tile* tile); void GatherPixelRefsForTile(Tile* tile); void DispatchImageDecodeTasksForTile(Tile* tile); void DispatchOneImageDecodeTask( @@ -197,6 +204,11 @@ class CC_EXPORT TileManager : public WorkerPoolClient { static void RecordCheapnessPredictorResults(bool is_predicted_cheap, bool is_actually_cheap); + static void RecordSolidColorPredictorResults(const SkColor* actual_colors, + size_t color_count, + bool is_predicted_solid, + SkColor predicted_color, + bool is_predicted_transparent); TileManagerClient* client_; scoped_ptr<ResourcePool> resource_pool_; @@ -231,9 +243,11 @@ class CC_EXPORT TileManager : public WorkerPoolClient { RenderingStats rendering_stats_; bool use_cheapness_estimator_; + bool use_color_estimator_; bool did_schedule_cheap_tasks_; bool allow_cheap_tasks_; int raster_state_count_[NUM_STATES][NUM_TREES][NUM_BINS]; + bool prediction_benchmarking_; DISALLOW_COPY_AND_ASSIGN(TileManager); }; diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 74b2afd..c9e5901 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1674,6 +1674,7 @@ '../google_apis/gaia/oauth2_mint_token_fetcher_unittest.cc', '../google_apis/gaia/oauth2_mint_token_flow_unittest.cc', '../google_apis/gaia/oauth2_revocation_fetcher_unittest.cc', + '../skia/ext/analysis_canvas_unittest.cc', '../skia/ext/bitmap_platform_device_mac_unittest.cc', '../skia/ext/convolver_unittest.cc', '../skia/ext/image_operations_unittest.cc', diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index dcc1838..9b3547a 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -860,6 +860,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( cc::switches::kEnableImplSidePainting, cc::switches::kEnablePartialSwap, cc::switches::kEnablePerTilePainting, + cc::switches::kEnablePredictionBenchmarking, cc::switches::kEnableRightAlignedScheduling, cc::switches::kEnableTopControlsPositionCalculation, cc::switches::kLowResolutionContentsScaleFactor, @@ -877,6 +878,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( cc::switches::kTraceAllRenderedFrames, cc::switches::kTraceOverdraw, cc::switches::kUseCheapnessEstimator, + cc::switches::kUseColorEstimator, cc::switches::kCompositeToMailbox, }; renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames, diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc index 04b2c09..e054885 100644 --- a/content/renderer/gpu/render_widget_compositor.cc +++ b/content/renderer/gpu/render_widget_compositor.cc @@ -128,6 +128,10 @@ scoped_ptr<RenderWidgetCompositor> RenderWidgetCompositor::Create( settings.implSidePainting = cc::switches::IsImplSidePaintingEnabled(); settings.useCheapnessEstimator = cmd->HasSwitch(cc::switches::kUseCheapnessEstimator); + settings.useColorEstimator = + cmd->HasSwitch(cc::switches::kUseColorEstimator); + settings.predictionBenchmarking = + cmd->HasSwitch(cc::switches::kEnablePredictionBenchmarking); settings.calculateTopControlsPosition = cmd->HasSwitch(cc::switches::kEnableTopControlsPositionCalculation); diff --git a/skia/ext/analysis_canvas.cc b/skia/ext/analysis_canvas.cc index 72b9eeb..2a2c7f3 100644 --- a/skia/ext/analysis_canvas.cc +++ b/skia/ext/analysis_canvas.cc @@ -7,6 +7,7 @@ #include "third_party/skia/include/core/SkDevice.h" #include "third_party/skia/include/core/SkDraw.h" #include "third_party/skia/include/core/SkRRect.h" +#include "third_party/skia/src/core/SkRasterClip.h" #include "ui/gfx/rect_conversions.h" namespace { @@ -16,13 +17,67 @@ namespace { // 25x as long as Z620. const int gPictureCostThreshold = 1000; +static bool isSolidColorPaint(const SkPaint& paint) { + SkXfermode::Mode xferMode; + + // getXfermode can return a NULL, but that is handled + // gracefully by AsMode (NULL turns into kSrcOver mode). + SkXfermode::AsMode(paint.getXfermode(), &xferMode); + + // Paint is solid color if the following holds: + // - Alpha is 1.0, style is fill, and there are no special effects + // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent + // to kSrc if source alpha is 1.0, which is already checked). + return (paint.getAlpha() == 255 && + !paint.getShader() && + !paint.getLooper() && + !paint.getMaskFilter() && + !paint.getColorFilter() && + paint.getStyle() == SkPaint::kFill_Style && + (xferMode == SkXfermode::kSrc_Mode || + xferMode == SkXfermode::kSrcOver_Mode)); } +static bool isFullQuad(const SkDraw& draw, + const SkRect& canvasRect, + const SkRect& drawnRect) { + + // If the transform results in a non-axis aligned + // rect, then be conservative and return false. + if (!draw.fMatrix->rectStaysRect()) + return false; + + SkRect drawBitmapRect; + draw.fBitmap->getBounds(&drawBitmapRect); + SkRect clipRect = SkRect::Make(draw.fRC->getBounds()); + SkRect deviceRect; + draw.fMatrix->mapRect(&deviceRect, drawnRect); + + // The drawn rect covers the full canvas, if the following conditions hold: + // - Clip rect is an actual rectangle. + // - The rect we're drawing (post-transform) contains the clip rect. + // That is, all of clip rect will be colored by the rect. + // - Clip rect contains the canvas rect. + // That is, we're not clipping to a portion of this canvas. + // - The bitmap into which the draw call happens is at least as + // big as the canvas rect + return draw.fRC->isRect() && + deviceRect.contains(clipRect) && + clipRect.contains(canvasRect) && + drawBitmapRect.contains(canvasRect); +} + +} // namespace + namespace skia { AnalysisDevice::AnalysisDevice(const SkBitmap& bm) : INHERITED(bm) - , estimatedCost_(0) { + , estimatedCost_(0) + , isForcedNotSolid_(false) + , isForcedNotTransparent_(false) + , isSolidColor_(false) + , isTransparent_(false) { } @@ -34,32 +89,112 @@ int AnalysisDevice::getEstimatedCost() const { return estimatedCost_; } +bool AnalysisDevice::getColorIfSolid(SkColor* color) const { + if (isSolidColor_) + *color = color_; + return isSolidColor_; +} + +bool AnalysisDevice::isTransparent() const { + return isTransparent_; +} + +void AnalysisDevice::setForceNotSolid(bool flag) { + isForcedNotSolid_ = flag; + if (isForcedNotSolid_) + isSolidColor_ = false; +} + +void AnalysisDevice::setForceNotTransparent(bool flag) { + isForcedNotTransparent_ = flag; + if (isForcedNotTransparent_) + isTransparent_ = false; +} + void AnalysisDevice::clear(SkColor color) { - ++estimatedCost_; + ++estimatedCost_; + + isTransparent_ = (!isForcedNotTransparent_ && SkColorGetA(color) == 0); + + if (!isForcedNotSolid_ && SkColorGetA(color) == 255) { + isSolidColor_ = true; + color_ = color; + } + else { + isSolidColor_ = false; + } } void AnalysisDevice::drawPaint(const SkDraw&, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count, const SkPoint[], const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } -void AnalysisDevice::drawRect(const SkDraw&, const SkRect& r, +void AnalysisDevice::drawRect(const SkDraw& draw, const SkRect& rect, const SkPaint& paint) { + // FIXME: if there's a pending image decode & resize, more expensive if (paint.getMaskFilter()) { estimatedCost_ += 300; } ++estimatedCost_; + + bool doesCoverCanvas = isFullQuad(draw, + SkRect::MakeWH(width(), height()), + rect); + + SkXfermode::Mode xferMode; + SkXfermode::AsMode(paint.getXfermode(), &xferMode); + + // This canvas will become transparent if the following holds: + // - The quad is a full tile quad + // - We're not in "forced not transparent" mode + // - Transfer mode is clear (0 color, 0 alpha) + // + // If the paint alpha is not 0, or if the transfrer mode is + // not src, then this canvas will not be transparent. + // + // In all other cases, we keep the current transparent value + if (doesCoverCanvas && + !isForcedNotTransparent_ && + xferMode == SkXfermode::kClear_Mode) { + isTransparent_ = true; + } + else if (paint.getAlpha() != 0 || + xferMode != SkXfermode::kSrc_Mode) { + isTransparent_ = false; + } + + // This bitmap is solid if and only if the following holds. + // Note that this might be overly conservative: + // - We're not in "forced not solid" mode + // - Paint is solid color + // - The quad is a full tile quad + if (!isForcedNotSolid_ && + isSolidColorPaint(paint) && + doesCoverCanvas) { + isSolidColor_ = true; + color_ = paint.getColor(); + } + else { + isSolidColor_ = false; + } } void AnalysisDevice::drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawPath(const SkDraw&, const SkPath& path, @@ -73,31 +208,42 @@ void AnalysisDevice::drawPath(const SkDraw&, const SkPath& path, estimatedCost_ += 300; } ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap, const SkIRect* srcRectOrNull, - const SkMatrix& matrix, const SkPaint& paint) - { + const SkMatrix& matrix, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } -void AnalysisDevice::drawBitmapRect(const SkDraw&, const SkBitmap&, +void AnalysisDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap&, const SkRect* srcOrNull, const SkRect& dst, const SkPaint& paint) { ++estimatedCost_; + + // Call drawRect to determine transparency, + // but reset solid color to false. + drawRect(draw, dst, paint); + isSolidColor_ = false; } void AnalysisDevice::drawText(const SkDraw&, const void* text, size_t len, - SkScalar x, SkScalar y, const SkPaint& paint) - { + SkScalar x, SkScalar y, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, @@ -106,21 +252,26 @@ void AnalysisDevice::drawPosText(const SkDraw& draw, const void* text, size_t le // FIXME: On Z620, every glyph cache miss costs us about 10us. // We don't have a good mechanism for predicting glyph cache misses. ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } #ifdef SK_BUILD_FOR_ANDROID void AnalysisDevice::drawPosTextOnPath(const SkDraw& draw, const void* text, size_t len, const SkPoint pos[], const SkPaint& paint, - const SkPath& path, const SkMatrix* matrix) - { + const SkPath& path, const SkMatrix* matrix) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } #endif @@ -131,19 +282,25 @@ void AnalysisDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, const uint16_t indices[], int indexCount, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawDevice(const SkDraw&, SkDevice*, int x, int y, const SkPaint&) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } - +const int AnalysisCanvas::kNoLayer = -1; AnalysisCanvas::AnalysisCanvas(AnalysisDevice* device) - : INHERITED(device) { - + : INHERITED(device) + , savedStackSize_(0) + , forceNotSolidStackLevel_(kNoLayer) + , forceNotTransparentStackLevel_(kNoLayer) { } AnalysisCanvas::~AnalysisCanvas() { @@ -154,11 +311,18 @@ bool AnalysisCanvas::isCheap() const { return getEstimatedCost() < gPictureCostThreshold; } +bool AnalysisCanvas::getColorIfSolid(SkColor* color) const { + return (static_cast<AnalysisDevice*>(getDevice()))->getColorIfSolid(color); +} + +bool AnalysisCanvas::isTransparent() const { + return (static_cast<AnalysisDevice*>(getDevice()))->isTransparent(); +} + int AnalysisCanvas::getEstimatedCost() const { return (static_cast<AnalysisDevice*>(getDevice()))->getEstimatedCost(); } - bool AnalysisCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) { return INHERITED::clipRect(rect, op, doAA); @@ -166,25 +330,98 @@ bool AnalysisCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool AnalysisCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) { + // clipPaths can make our calls to isFullQuad invalid (ie have false + // positives). As a precaution, force the setting to be non-solid + // and non-transparent until we pop this + if (forceNotSolidStackLevel_ == kNoLayer) { + forceNotSolidStackLevel_ = savedStackSize_; + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotSolid(true); + } + if (forceNotTransparentStackLevel_ == kNoLayer) { + forceNotTransparentStackLevel_ = savedStackSize_; + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotTransparent(true); + } + return INHERITED::clipRect(path.getBounds(), op, doAA); } bool AnalysisCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { + // clipRRect can make our calls to isFullQuad invalid (ie have false + // positives). As a precaution, force the setting to be non-solid + // and non-transparent until we pop this + if (forceNotSolidStackLevel_ == kNoLayer) { + forceNotSolidStackLevel_ = savedStackSize_; + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotSolid(true); + } + if (forceNotTransparentStackLevel_ == kNoLayer) { + forceNotTransparentStackLevel_ = savedStackSize_; + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotTransparent(true); + } + return INHERITED::clipRect(rrect.getBounds(), op, doAA); } -int AnalysisCanvas::saveLayer(const SkRect* bounds, const SkPaint*, +int AnalysisCanvas::save(SkCanvas::SaveFlags flags) { + ++savedStackSize_; + return INHERITED::save(flags); +} + +int AnalysisCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlags flags) { + ++savedStackSize_; + + // If after we draw to the saved layer, we have to blend with the current + // layer, then we can conservatively say that the canvas will not be of + // solid color. + if ((paint && !isSolidColorPaint(*paint)) || + (bounds && !bounds->contains( + SkRect::MakeWH(getDevice()->width(), getDevice()->height())))) { + if (forceNotSolidStackLevel_ == kNoLayer) { + forceNotSolidStackLevel_ = savedStackSize_; + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotSolid(true); + } + } + + // If after we draw to the save layer, we have to blend with the current + // layer using any part of the current layer's alpha, then we can + // conservatively say that the canvas will not be transparent. + SkXfermode::Mode xferMode = SkXfermode::kSrc_Mode; + if (paint) + SkXfermode::AsMode(paint->getXfermode(), &xferMode); + if (xferMode != SkXfermode::kSrc_Mode) { + if (forceNotTransparentStackLevel_ == kNoLayer) { + forceNotTransparentStackLevel_ = savedStackSize_; + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotTransparent(true); + } + } + // Actually saving a layer here could cause a new bitmap to be created // and real rendering to occur. - int count = SkCanvas::save(flags); + int count = INHERITED::save(flags); if (bounds) { INHERITED::clipRectBounds(bounds, flags, NULL); } return count; } +void AnalysisCanvas::restore() { + INHERITED::restore(); + + DCHECK(savedStackSize_); + if (savedStackSize_) { + --savedStackSize_; + if (savedStackSize_ < forceNotSolidStackLevel_) { + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotSolid(false); + forceNotSolidStackLevel_ = kNoLayer; + } + if (savedStackSize_ < forceNotTransparentStackLevel_) { + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotTransparent(false); + forceNotTransparentStackLevel_ = kNoLayer; + } + } +} + } // namespace skia diff --git a/skia/ext/analysis_canvas.h b/skia/ext/analysis_canvas.h index e2eedd2..fd7ee25 100644 --- a/skia/ext/analysis_canvas.h +++ b/skia/ext/analysis_canvas.h @@ -26,6 +26,8 @@ class SK_API AnalysisCanvas : public SkCanvas { // Returns true if the estimated cost of drawing is below an // arbitrary threshold. bool isCheap() const; + bool getColorIfSolid(SkColor* color) const; + bool isTransparent() const; // Returns the estimated cost of drawing, in arbitrary units. int getEstimatedCost() const; @@ -42,8 +44,17 @@ class SK_API AnalysisCanvas : public SkCanvas { virtual int saveLayer(const SkRect* bounds, const SkPaint*, SkCanvas::SaveFlags flags) OVERRIDE; + virtual int save(SaveFlags flags = kMatrixClip_SaveFlag) OVERRIDE; + + virtual void restore() OVERRIDE; + private: typedef SkCanvas INHERITED; + static const int kNoLayer; + + int savedStackSize_; + int forceNotSolidStackLevel_; + int forceNotTransparentStackLevel_; }; class SK_API AnalysisDevice : public SkDevice { @@ -52,6 +63,11 @@ class SK_API AnalysisDevice : public SkDevice { virtual ~AnalysisDevice(); int getEstimatedCost() const; + bool getColorIfSolid(SkColor* color) const; + bool isTransparent() const; + + void setForceNotSolid(bool flag); + void setForceNotTransparent(bool flag); protected: virtual void clear(SkColor color) OVERRIDE; @@ -105,6 +121,12 @@ class SK_API AnalysisDevice : public SkDevice { private: typedef SkDevice INHERITED; + + bool isForcedNotSolid_; + bool isForcedNotTransparent_; + bool isSolidColor_; + SkColor color_; + bool isTransparent_; }; } // namespace skia diff --git a/skia/ext/analysis_canvas_unittest.cc b/skia/ext/analysis_canvas_unittest.cc new file mode 100644 index 0000000..17cfcb5 --- /dev/null +++ b/skia/ext/analysis_canvas_unittest.cc @@ -0,0 +1,313 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/compiler_specific.h" +#include "skia/ext/analysis_canvas.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +void solidColorFill(skia::AnalysisCanvas& canvas) { + canvas.clear(SkColorSetARGB(255, 255, 255, 255)); +} + +void transparentFill(skia::AnalysisCanvas& canvas) { + canvas.clear(SkColorSetARGB(0, 0, 0, 0)); +} + +} // namespace +namespace skia { + +TEST(AnalysisCanvasTest, EmptyCanvas) { + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + SkColor color; + EXPECT_FALSE(canvas.getColorIfSolid(&color)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); +} + +TEST(AnalysisCanvasTest, ClearCanvas) { + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + // Transparent color + SkColor color = SkColorSetARGB(0, 12, 34, 56); + canvas.clear(color); + + SkColor outputColor; + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_TRUE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + // Solid color + color = SkColorSetARGB(255, 65, 43, 21); + canvas.clear(color); + + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + EXPECT_EQ(outputColor, color); + + // Translucent color + color = SkColorSetARGB(128, 11, 22, 33); + canvas.clear(color); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + // Test helper methods + solidColorFill(canvas); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + transparentFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_TRUE(canvas.isTransparent()); +} + +TEST(AnalysisCanvasTest, ComplexActions) { + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + // Draw paint test. + SkColor color = SkColorSetARGB(255, 11, 22, 33); + SkPaint paint; + paint.setColor(color); + + canvas.drawPaint(paint); + + SkColor outputColor; + //TODO(vmpstr): This should return true. (crbug.com/180597) + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + // Draw points test. + SkPoint points[4] = { + SkPoint::Make(0, 0), + SkPoint::Make(255, 0), + SkPoint::Make(255, 255), + SkPoint::Make(0, 255) + }; + + solidColorFill(canvas); + canvas.drawPoints(SkCanvas::kLines_PointMode, 4, points, paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + // Draw oval test. + solidColorFill(canvas); + canvas.drawOval(SkRect::MakeWH(255, 255), paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + // Draw bitmap test. + solidColorFill(canvas); + SkBitmap secondBitmap; + secondBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + canvas.drawBitmap(secondBitmap, 0, 0); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); +} + +TEST(AnalysisCanvasTest, SimpleDrawRect) { + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + SkColor color = SkColorSetARGB(255, 11, 22, 33); + SkPaint paint; + paint.setColor(color); + canvas.clipRect(SkRect::MakeWH(255, 255)); + canvas.drawRect(SkRect::MakeWH(255, 255), paint); + + SkColor outputColor; + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + EXPECT_EQ(color, outputColor); + + color = SkColorSetARGB(255, 22, 33, 44); + paint.setColor(color); + canvas.translate(-128, -128); + canvas.drawRect(SkRect::MakeWH(382, 382), paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + color = SkColorSetARGB(255, 33, 44, 55); + paint.setColor(color); + canvas.drawRect(SkRect::MakeWH(383, 383), paint); + + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + EXPECT_EQ(color, outputColor); + + color = SkColorSetARGB(0, 0, 0, 0); + paint.setColor(color); + canvas.drawRect(SkRect::MakeWH(383, 383), paint); + + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + EXPECT_EQ(outputColor, SkColorSetARGB(255, 33, 44, 55)); + + color = SkColorSetARGB(128, 128, 128, 128); + paint.setColor(color); + canvas.drawRect(SkRect::MakeWH(383, 383), paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + paint.setXfermodeMode(SkXfermode::kClear_Mode); + canvas.drawRect(SkRect::MakeWH(382, 382), paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + canvas.drawRect(SkRect::MakeWH(383, 383), paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_TRUE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + canvas.translate(128, 128); + color = SkColorSetARGB(255, 11, 22, 33); + paint.setColor(color); + paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); + canvas.drawRect(SkRect::MakeWH(255, 255), paint); + + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + EXPECT_EQ(color, outputColor); + + canvas.rotate(50); + canvas.drawRect(SkRect::MakeWH(255, 255), paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); +} + +TEST(AnalysisCanvasTest, ClipPath) { + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + SkPath path; + path.moveTo(0, 0); + path.lineTo(255, 0); + path.lineTo(255, 255); + path.lineTo(0, 255); + + SkColor outputColor; + solidColorFill(canvas); + canvas.clipPath(path); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + + canvas.save(); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + + canvas.clipPath(path); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + + canvas.restore(); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + + solidColorFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); +} + +TEST(AnalysisCanvasTest, SaveLayerRestore) { + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + SkColor outputColor; + solidColorFill(canvas); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + + SkRect bounds = SkRect::MakeWH(255, 255); + SkPaint paint; + paint.setColor(SkColorSetARGB(255, 255, 255, 255)); + paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); + + // This should force non-transparency + canvas.saveLayer(&bounds, &paint, SkCanvas::kMatrix_SaveFlag); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + transparentFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + solidColorFill(canvas); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + paint.setXfermodeMode(SkXfermode::kDst_Mode); + + // This should force non-solid color + canvas.saveLayer(&bounds, &paint, SkCanvas::kMatrix_SaveFlag); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + transparentFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + solidColorFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + canvas.restore(); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + transparentFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + solidColorFill(canvas); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + canvas.restore(); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + transparentFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_TRUE(canvas.isTransparent()); + + solidColorFill(canvas); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); +} + +} // namespace skia |