summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvmpstr@chromium.org <vmpstr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-10 22:30:31 +0000
committervmpstr@chromium.org <vmpstr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-10 22:30:31 +0000
commit2db9225e3ea512a197021a22e8abd110e4f024d4 (patch)
treecdd975dee7917c6a34f0b99f1e16de933e297737
parente851d3c0b85912c4777017d948717a21122bafcb (diff)
downloadchromium_src-2db9225e3ea512a197021a22e8abd110e4f024d4.zip
chromium_src-2db9225e3ea512a197021a22e8abd110e4f024d4.tar.gz
chromium_src-2db9225e3ea512a197021a22e8abd110e4f024d4.tar.bz2
cc: Added invalidation frequency for picture pile.
This patch keeps track of the invalidation frequency of each of the picture pile grid cells. When the invalidation goes over a certain threshold (0.75) and the grid cell is further than specified amount of pixels from the viewport (512), then the cell is not re-recorded. For a cell that was previously skipped, either frequency dropping below the threshold or it coming closer than 512 pixels will cause it to re-record again. BUG=254320 Review URL: https://codereview.chromium.org/106393003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@239893 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--cc/layers/picture_layer.cc1
-rw-r--r--cc/resources/picture_pile.cc16
-rw-r--r--cc/resources/picture_pile.h1
-rw-r--r--cc/resources/picture_pile_base.cc69
-rw-r--r--cc/resources/picture_pile_base.h25
-rw-r--r--cc/resources/picture_pile_impl.cc13
-rw-r--r--cc/resources/picture_pile_unittest.cc139
-rw-r--r--cc/test/fake_picture_pile_impl.cc2
8 files changed, 232 insertions, 34 deletions
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
index 03bf6f2..1415c80 100644
--- a/cc/layers/picture_layer.cc
+++ b/cc/layers/picture_layer.cc
@@ -116,6 +116,7 @@ bool PictureLayer::Update(ResourceUpdateQueue* queue,
contents_opaque(),
pile_invalidation_,
visible_layer_rect,
+ update_source_frame_number_,
rendering_stats_instrumentation());
last_updated_visible_content_rect_ = visible_content_rect();
diff --git a/cc/resources/picture_pile.cc b/cc/resources/picture_pile.cc
index 99d3eff..68d75d7 100644
--- a/cc/resources/picture_pile.cc
+++ b/cc/resources/picture_pile.cc
@@ -11,6 +11,7 @@
#include "cc/base/region.h"
#include "cc/debug/rendering_stats_instrumentation.h"
#include "cc/resources/picture_pile_impl.h"
+#include "cc/resources/tile_priority.h"
namespace {
// Layout pixel buffer around the visible layer rect to record. Any base
@@ -151,6 +152,7 @@ bool PicturePile::Update(
bool contents_opaque,
const Region& invalidation,
gfx::Rect visible_layer_rect,
+ int frame_number,
RenderingStatsInstrumentation* stats_instrumentation) {
background_color_ = background_color;
contents_opaque_ = contents_opaque;
@@ -175,7 +177,8 @@ bool PicturePile::Update(
if (picture_it == picture_map_.end())
continue;
- invalidated = picture_it->second.Invalidate() || invalidated;
+ // Inform the grid cell that it has been invalidated in this frame.
+ invalidated = picture_it->second.Invalidate(frame_number) || invalidated;
}
}
@@ -186,8 +189,13 @@ bool PicturePile::Update(
for (TilingData::Iterator it(&tiling_, interest_rect);
it; ++it) {
const PictureMapKey& key = it.index();
- const PictureInfo& info = picture_map_[key];
- if (!info.picture.get()) {
+ PictureInfo& info = picture_map_[key];
+
+ gfx::Rect rect = PaddedRect(key);
+ int distance_to_visible =
+ rect.ManhattanInternalDistance(visible_layer_rect);
+
+ if (info.NeedsRecording(frame_number, distance_to_visible)) {
gfx::Rect tile = tiling_.TileBounds(key.first, key.second);
invalid_tiles.push_back(tile);
}
@@ -235,7 +243,7 @@ bool PicturePile::Update(
gfx::Rect tile = PaddedRect(key);
if (record_rect.Contains(tile)) {
PictureInfo& info = picture_map_[key];
- info.picture = picture;
+ info.SetPicture(picture);
}
}
}
diff --git a/cc/resources/picture_pile.h b/cc/resources/picture_pile.h
index 90fc015..7860e7f 100644
--- a/cc/resources/picture_pile.h
+++ b/cc/resources/picture_pile.h
@@ -26,6 +26,7 @@ class CC_EXPORT PicturePile : public PicturePileBase {
bool contents_opaque,
const Region& invalidation,
gfx::Rect visible_layer_rect,
+ int frame_number,
RenderingStatsInstrumentation* stats_instrumentation);
void set_num_raster_threads(int num_raster_threads) {
diff --git a/cc/resources/picture_pile_base.cc b/cc/resources/picture_pile_base.cc
index 5e38c04..7d60dca 100644
--- a/cc/resources/picture_pile_base.cc
+++ b/cc/resources/picture_pile_base.cc
@@ -25,6 +25,15 @@ const bool kDefaultClearCanvasSetting = false;
#else
const bool kDefaultClearCanvasSetting = true;
#endif
+
+// Invalidation frequency settings. kInvalidationFrequencyThreshold is a value
+// between 0 and 1 meaning invalidation frequency between 0% and 100% that
+// indicates when to stop invalidating offscreen regions.
+// kFrequentInvalidationDistanceThreshold defines what it means to be
+// "offscreen" in terms of distance to visible in css pixels.
+const float kInvalidationFrequencyThreshold = 0.75f;
+const int kFrequentInvalidationDistanceThreshold = 512;
+
} // namespace
namespace cc {
@@ -165,7 +174,7 @@ void PicturePileBase::UpdateRecordedRegion() {
for (PictureMap::const_iterator it = picture_map_.begin();
it != picture_map_.end();
++it) {
- if (it->second.picture.get()) {
+ if (it->second.GetPicture()) {
const PictureMapKey& key = it->first;
recorded_region_.Union(tile_bounds(key.first, key.second));
}
@@ -176,7 +185,7 @@ bool PicturePileBase::HasRecordingAt(int x, int y) {
PictureMap::const_iterator found = picture_map_.find(PictureMapKey(x, y));
if (found == picture_map_.end())
return false;
- return !!found->second.picture.get();
+ return !!found->second.GetPicture();
}
bool PicturePileBase::CanRaster(float contents_scale, gfx::Rect content_rect) {
@@ -210,7 +219,7 @@ scoped_ptr<base::Value> PicturePileBase::AsValue() const {
if (map_iter == picture_map_.end())
continue;
- Picture* picture = map_iter->second.picture.get();
+ Picture* picture = map_iter->second.GetPicture();
if (picture && (appended_pictures.count(picture) == 0)) {
appended_pictures.insert(picture);
pictures->Append(TracedValue::CreateIDRef(picture).release());
@@ -219,23 +228,61 @@ scoped_ptr<base::Value> PicturePileBase::AsValue() const {
return pictures.PassAs<base::Value>();
}
-PicturePileBase::PictureInfo::PictureInfo() {}
+PicturePileBase::PictureInfo::PictureInfo() : last_frame_number_(0) {}
PicturePileBase::PictureInfo::~PictureInfo() {}
-bool PicturePileBase::PictureInfo::Invalidate() {
- if (!picture.get())
- return false;
- picture = NULL;
- return true;
+void PicturePileBase::PictureInfo::AdvanceInvalidationHistory(
+ int frame_number) {
+ DCHECK_GE(frame_number, last_frame_number_);
+ if (frame_number == last_frame_number_)
+ return;
+
+ invalidation_history_ <<= (frame_number - last_frame_number_);
+ last_frame_number_ = frame_number;
+}
+
+bool PicturePileBase::PictureInfo::Invalidate(int frame_number) {
+ AdvanceInvalidationHistory(frame_number);
+ invalidation_history_.set(0);
+
+ bool did_invalidate = !!picture_;
+ picture_ = NULL;
+ return did_invalidate;
+}
+
+bool PicturePileBase::PictureInfo::NeedsRecording(int frame_number,
+ int distance_to_visible) {
+ AdvanceInvalidationHistory(frame_number);
+
+ // We only need recording if we don't have a picture. Furthermore, we only
+ // need a recording if we're within frequent invalidation distance threshold
+ // or the invalidation is not frequent enough (below invalidation frequency
+ // threshold).
+ return !picture_ &&
+ ((distance_to_visible <= kFrequentInvalidationDistanceThreshold) ||
+ (GetInvalidationFrequency() < kInvalidationFrequencyThreshold));
+}
+
+void PicturePileBase::PictureInfo::SetPicture(scoped_refptr<Picture> picture) {
+ picture_ = picture;
+}
+
+Picture* PicturePileBase::PictureInfo::GetPicture() const {
+ return picture_.get();
}
PicturePileBase::PictureInfo PicturePileBase::PictureInfo::CloneForThread(
int thread_index) const {
PictureInfo info = *this;
- if (picture.get())
- info.picture = picture->GetCloneForDrawingOnThread(thread_index);
+ if (picture_.get())
+ info.picture_ = picture_->GetCloneForDrawingOnThread(thread_index);
return info;
}
+float PicturePileBase::PictureInfo::GetInvalidationFrequency() const {
+ return invalidation_history_.count() /
+ static_cast<float>(INVALIDATION_FRAMES_TRACKED);
+}
+
} // namespace cc
diff --git a/cc/resources/picture_pile_base.h b/cc/resources/picture_pile_base.h
index 66a26e75..274c865 100644
--- a/cc/resources/picture_pile_base.h
+++ b/cc/resources/picture_pile_base.h
@@ -5,6 +5,7 @@
#ifndef CC_RESOURCES_PICTURE_PILE_BASE_H_
#define CC_RESOURCES_PICTURE_PILE_BASE_H_
+#include <bitset>
#include <list>
#include <utility>
@@ -50,14 +51,32 @@ class CC_EXPORT PicturePileBase : public base::RefCounted<PicturePileBase> {
scoped_ptr<base::Value> AsValue() const;
protected:
- struct CC_EXPORT PictureInfo {
+ class CC_EXPORT PictureInfo {
+ public:
+ enum {
+ INVALIDATION_FRAMES_TRACKED = 32
+ };
+
PictureInfo();
~PictureInfo();
- bool Invalidate();
+ bool Invalidate(int frame_number);
+ bool NeedsRecording(int frame_number, int distance_to_visible);
PictureInfo CloneForThread(int thread_index) const;
+ void SetPicture(scoped_refptr<Picture> picture);
+ Picture* GetPicture() const;
+
+ float GetInvalidationFrequencyForTesting() const {
+ return GetInvalidationFrequency();
+ }
+
+ private:
+ void AdvanceInvalidationHistory(int frame_number);
+ float GetInvalidationFrequency() const;
- scoped_refptr<Picture> picture;
+ int last_frame_number_;
+ scoped_refptr<Picture> picture_;
+ std::bitset<INVALIDATION_FRAMES_TRACKED> invalidation_history_;
};
typedef std::pair<int, int> PictureMapKey;
diff --git a/cc/resources/picture_pile_impl.cc b/cc/resources/picture_pile_impl.cc
index 44a52b8..5522186 100644
--- a/cc/resources/picture_pile_impl.cc
+++ b/cc/resources/picture_pile_impl.cc
@@ -170,7 +170,8 @@ void PicturePileImpl::CoalesceRasters(gfx::Rect canvas_rect,
if (map_iter == picture_map_.end())
continue;
PictureInfo& info = map_iter->second;
- if (!info.picture.get())
+ Picture* picture = info.GetPicture();
+ if (!picture)
continue;
// This is intentionally *enclosed* rect, so that the clip is aligned on
@@ -182,13 +183,13 @@ void PicturePileImpl::CoalesceRasters(gfx::Rect canvas_rect,
gfx::Rect content_clip =
gfx::ScaleToEnclosedRect(chunk_rect, contents_scale);
DCHECK(!content_clip.IsEmpty()) << "Layer rect: "
- << info.picture->LayerRect().ToString()
+ << picture->LayerRect().ToString()
<< "Contents scale: " << contents_scale;
content_clip.Intersect(canvas_rect);
- PictureRegionMap::iterator it = results->find(info.picture.get());
+ PictureRegionMap::iterator it = results->find(picture);
if (it == results->end()) {
- Region& region = (*results)[info.picture.get()];
+ Region& region = (*results)[picture];
region = content_rect;
region.Subtract(content_clip);
continue;
@@ -365,7 +366,7 @@ void PicturePileImpl::PixelRefIterator::AdvanceToTilePictureWithPixelRefs() {
if (it == picture_pile_->picture_map_.end())
continue;
- const Picture* picture = it->second.picture.get();
+ const Picture* picture = it->second.GetPicture();
if (!picture || (processed_pictures_.count(picture) != 0))
continue;
@@ -382,7 +383,7 @@ void PicturePileImpl::DidBeginTracing() {
for (PictureMap::iterator it = picture_map_.begin();
it != picture_map_.end();
++it) {
- Picture* picture = it->second.picture.get();
+ Picture* picture = it->second.GetPicture();
if (picture && (processed_pictures.count(picture) == 0)) {
picture->EmitTraceSnapshot();
processed_pictures.insert(picture);
diff --git a/cc/resources/picture_pile_unittest.cc b/cc/resources/picture_pile_unittest.cc
index 0c9b2e1..91ec355 100644
--- a/cc/resources/picture_pile_unittest.cc
+++ b/cc/resources/picture_pile_unittest.cc
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <map>
+#include <utility>
+
#include "cc/resources/picture_pile.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_rendering_stats_instrumentation.h"
@@ -46,6 +49,7 @@ TEST(PicturePileTest, SmallInvalidateInflated) {
false,
gfx::Rect(layer_size),
gfx::Rect(layer_size),
+ 1,
&stats_instrumentation);
// Invalidate something inside a tile.
@@ -55,6 +59,7 @@ TEST(PicturePileTest, SmallInvalidateInflated) {
false,
invalidate_rect,
gfx::Rect(layer_size),
+ 2,
&stats_instrumentation);
EXPECT_EQ(1, pile->tiling().num_tiles_x());
@@ -63,9 +68,9 @@ TEST(PicturePileTest, SmallInvalidateInflated) {
TestPicturePile::PictureInfo& picture_info =
pile->picture_map().find(TestPicturePile::PictureMapKey(0, 0))->second;
// We should have a picture.
- EXPECT_TRUE(!!picture_info.picture.get());
- gfx::Rect picture_rect =
- gfx::ScaleToEnclosedRect(picture_info.picture->LayerRect(), min_scale);
+ EXPECT_TRUE(!!picture_info.GetPicture());
+ gfx::Rect picture_rect = gfx::ScaleToEnclosedRect(
+ picture_info.GetPicture()->LayerRect(), min_scale);
// The the picture should be large enough that scaling it never makes a rect
// smaller than 1 px wide or tall.
@@ -93,6 +98,7 @@ TEST(PicturePileTest, LargeInvalidateInflated) {
false,
gfx::Rect(layer_size),
gfx::Rect(layer_size),
+ 1,
&stats_instrumentation);
// Invalidate something inside a tile.
@@ -102,6 +108,7 @@ TEST(PicturePileTest, LargeInvalidateInflated) {
false,
invalidate_rect,
gfx::Rect(layer_size),
+ 2,
&stats_instrumentation);
EXPECT_EQ(1, pile->tiling().num_tiles_x());
@@ -109,11 +116,11 @@ TEST(PicturePileTest, LargeInvalidateInflated) {
TestPicturePile::PictureInfo& picture_info =
pile->picture_map().find(TestPicturePile::PictureMapKey(0, 0))->second;
- EXPECT_TRUE(!!picture_info.picture.get());
+ EXPECT_TRUE(!!picture_info.GetPicture());
int expected_inflation = pile->buffer_pixels();
- scoped_refptr<Picture> base_picture = picture_info.picture;
+ Picture* base_picture = picture_info.GetPicture();
gfx::Rect base_picture_rect(layer_size);
base_picture_rect.Inset(-expected_inflation, -expected_inflation);
EXPECT_EQ(base_picture_rect.ToString(),
@@ -143,12 +150,23 @@ TEST(PicturePileTest, InvalidateOnTileBoundaryInflated) {
EXPECT_EQ(7, pile->buffer_pixels());
EXPECT_EQ(7, pile->tiling().border_texels());
- // Update the whole layer.
+ // Update the whole layer to create initial pictures.
+ pile->Update(&client,
+ background_color,
+ false,
+ gfx::Rect(layer_size),
+ gfx::Rect(layer_size),
+ 0,
+ &stats_instrumentation);
+
+ // Invalidate everything again to have a non zero invalidation
+ // frequency.
pile->Update(&client,
background_color,
false,
gfx::Rect(layer_size),
gfx::Rect(layer_size),
+ 1,
&stats_instrumentation);
// Invalidate something just over a tile boundary by a single pixel.
@@ -163,6 +181,7 @@ TEST(PicturePileTest, InvalidateOnTileBoundaryInflated) {
false,
invalidate_rect,
gfx::Rect(layer_size),
+ 2,
&stats_instrumentation);
for (int i = 0; i < pile->tiling().num_tiles_x(); ++i) {
@@ -171,9 +190,111 @@ TEST(PicturePileTest, InvalidateOnTileBoundaryInflated) {
pile->picture_map().find(
TestPicturePile::PictureMapKey(i, j))->second;
- // TODO(vmpstr): Fix this to check invalidation frequency instead
- // of the picture, since we always have one picture per tile.
- EXPECT_TRUE(!!picture_info.picture.get());
+ // Expect (1, 1) and (1, 0) to be invalidated once more
+ // than the rest of the tiles.
+ if (i == 1 && (j == 0 || j == 1)) {
+ EXPECT_FLOAT_EQ(
+ 2.0f / TestPicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED,
+ picture_info.GetInvalidationFrequencyForTesting());
+ } else {
+ EXPECT_FLOAT_EQ(
+ 1.0f / TestPicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED,
+ picture_info.GetInvalidationFrequencyForTesting());
+ }
+ }
+ }
+}
+
+TEST(PicturePileTest, StopRecordingOffscreenInvalidations) {
+ FakeContentLayerClient client;
+ FakeRenderingStatsInstrumentation stats_instrumentation;
+ scoped_refptr<TestPicturePile> pile = new TestPicturePile;
+ SkColor background_color = SK_ColorBLUE;
+
+ float min_scale = 0.125;
+ gfx::Size base_picture_size = pile->tiling().max_texture_size();
+
+ gfx::Size layer_size =
+ gfx::ToFlooredSize(gfx::ScaleSize(base_picture_size, 4.f));
+ pile->Resize(layer_size);
+ pile->SetTileGridSize(gfx::Size(1000, 1000));
+ pile->SetMinContentsScale(min_scale);
+
+ gfx::Rect viewport(0, 0, layer_size.width(), 1);
+
+ // Update the whole layer until the invalidation frequency is high.
+ int frame;
+ for (frame = 0; frame < 33; ++frame) {
+ pile->Update(&client,
+ background_color,
+ false,
+ gfx::Rect(layer_size),
+ viewport,
+ frame,
+ &stats_instrumentation);
+ }
+
+ // Make sure we have a high invalidation frequency.
+ for (int i = 0; i < pile->tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile->tiling().num_tiles_y(); ++j) {
+ TestPicturePile::PictureInfo& picture_info =
+ pile->picture_map().find(
+ TestPicturePile::PictureMapKey(i, j))->second;
+ EXPECT_FLOAT_EQ(1.0f, picture_info.GetInvalidationFrequencyForTesting())
+ << "i " << i << " j " << j;
+ }
+ }
+
+ // Update once more with a small viewport 0,0 layer_width by 1
+ pile->Update(&client,
+ background_color,
+ false,
+ gfx::Rect(layer_size),
+ viewport,
+ frame,
+ &stats_instrumentation);
+
+ for (int i = 0; i < pile->tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile->tiling().num_tiles_y(); ++j) {
+ TestPicturePile::PictureInfo& picture_info =
+ pile->picture_map().find(
+ TestPicturePile::PictureMapKey(i, j))->second;
+ EXPECT_FLOAT_EQ(1.0f, picture_info.GetInvalidationFrequencyForTesting());
+
+ // If the y far enough away we expect to find no picture (no re-recording
+ // happened). For close y, the picture should change.
+ if (j >= 2)
+ EXPECT_FALSE(picture_info.GetPicture()) << "i " << i << " j " << j;
+ else
+ EXPECT_TRUE(picture_info.GetPicture()) << "i " << i << " j " << j;
+ }
+ }
+
+ // Now update with no invalidation and full viewport
+ pile->Update(&client,
+ background_color,
+ false,
+ gfx::Rect(),
+ gfx::Rect(layer_size),
+ frame+1,
+ &stats_instrumentation);
+
+ for (int i = 0; i < pile->tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile->tiling().num_tiles_y(); ++j) {
+ TestPicturePile::PictureInfo& picture_info =
+ pile->picture_map().find(
+ TestPicturePile::PictureMapKey(i, j))->second;
+ // Expect the invalidation frequency to be less than 1, since we just
+ // updated with no invalidations.
+ float expected_frequency =
+ 1.0f -
+ 1.0f / TestPicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED;
+
+ EXPECT_FLOAT_EQ(expected_frequency,
+ picture_info.GetInvalidationFrequencyForTesting());
+
+ // We expect that there are pictures everywhere now.
+ EXPECT_TRUE(picture_info.GetPicture()) << "i " << i << " j " << j;
}
}
}
diff --git a/cc/test/fake_picture_pile_impl.cc b/cc/test/fake_picture_pile_impl.cc
index 88c7d38..bc751ed 100644
--- a/cc/test/fake_picture_pile_impl.cc
+++ b/cc/test/fake_picture_pile_impl.cc
@@ -78,7 +78,7 @@ void FakePicturePileImpl::AddRecordingAt(int x, int y) {
scoped_refptr<Picture> picture(Picture::Create(bounds));
picture->Record(&client_, tile_grid_info_);
picture->GatherPixelRefs(tile_grid_info_);
- picture_map_[std::pair<int, int>(x, y)].picture = picture;
+ picture_map_[std::pair<int, int>(x, y)].SetPicture(picture);
EXPECT_TRUE(HasRecordingAt(x, y));
UpdateRecordedRegion();