summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cc/debug_colors.cc4
-rw-r--r--cc/debug_colors.h3
-rw-r--r--cc/layer_tree_host_impl.cc7
-rw-r--r--cc/layer_tree_settings.cc2
-rw-r--r--cc/layer_tree_settings.h2
-rw-r--r--cc/picture.cc20
-rw-r--r--cc/picture.h11
-rw-r--r--cc/picture_layer_impl.cc26
-rw-r--r--cc/picture_layer_tiling_set.cc2
-rw-r--r--cc/picture_pile_impl.cc59
-rw-r--r--cc/picture_pile_impl.h13
-rw-r--r--cc/switches.cc6
-rw-r--r--cc/switches.h2
-rw-r--r--cc/test/fake_picture_layer_tiling_client.cc2
-rw-r--r--cc/tile.cc6
-rw-r--r--cc/tile.h15
-rw-r--r--cc/tile_manager.cc176
-rw-r--r--cc/tile_manager.h18
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--content/browser/renderer_host/render_process_host_impl.cc2
-rw-r--r--content/renderer/gpu/render_widget_compositor.cc4
-rw-r--r--skia/ext/analysis_canvas.cc269
-rw-r--r--skia/ext/analysis_canvas.h22
-rw-r--r--skia/ext/analysis_canvas_unittest.cc313
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()) {
}
diff --git a/cc/tile.cc b/cc/tile.cc
index 8185791..22d0b99 100644
--- a/cc/tile.cc
+++ b/cc/tile.cc
@@ -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
diff --git a/cc/tile.h b/cc/tile.h
index 1c988c2dc..5e9d4d7 100644
--- a/cc/tile.h
+++ b/cc/tile.h
@@ -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