blob: 17283ddd2631f71a208b27633de1eb5dc6c2409f [file] [log] [blame]
// Copyright 2012 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 "cc/resources/picture_pile.h"
#include <algorithm>
#include <vector>
#include "cc/base/region.h"
#include "cc/debug/benchmark_instrumentation.h"
#include "cc/resources/picture_pile_impl.h"
namespace {
// Maximum number of pictures that can overlap before we collapse them into
// a larger one.
const size_t kMaxOverlapping = 2;
// Maximum percentage area of the base picture another picture in the picture
// list can be. If higher, we destroy the list and recreate from scratch.
const float kResetThreshold = 0.7f;
// Layout pixel buffer around the visible layer rect to record. Any base
// picture that intersects the visible layer rect expanded by this distance
// will be recorded.
const int kPixelDistanceToRecord = 8000;
} // namespace
namespace cc {
PicturePile::PicturePile() {
}
PicturePile::~PicturePile() {
}
bool PicturePile::Update(
ContentLayerClient* painter,
SkColor background_color,
bool contents_opaque,
const Region& invalidation,
gfx::Rect visible_layer_rect,
RenderingStatsInstrumentation* stats_instrumentation) {
background_color_ = background_color;
contents_opaque_ = contents_opaque;
gfx::Rect interest_rect = visible_layer_rect;
interest_rect.Inset(
-kPixelDistanceToRecord,
-kPixelDistanceToRecord,
-kPixelDistanceToRecord,
-kPixelDistanceToRecord);
bool modified_pile = false;
for (Region::Iterator i(invalidation); i.has_rect(); i.next()) {
gfx::Rect invalidation = i.rect();
// Split this inflated invalidation across tile boundaries and apply it
// to all tiles that it touches.
for (TilingData::Iterator iter(&tiling_, invalidation);
iter; ++iter) {
gfx::Rect tile =
tiling_.TileBoundsWithBorder(iter.index_x(), iter.index_y());
if (!tile.Intersects(interest_rect)) {
// This invalidation touches a tile outside the interest rect, so
// just remove the entire picture list.
picture_list_map_.erase(iter.index());
modified_pile = true;
continue;
}
gfx::Rect tile_invalidation = gfx::IntersectRects(invalidation, tile);
if (tile_invalidation.IsEmpty())
continue;
PictureListMap::iterator find = picture_list_map_.find(iter.index());
if (find == picture_list_map_.end())
continue;
PictureList& pic_list = find->second;
// Leave empty pic_lists empty in case there are multiple invalidations.
if (!pic_list.empty()) {
// Inflate all recordings from invalidations with a margin so that when
// scaled down to at least min_contents_scale, any final pixel touched
// by an invalidation can be fully rasterized by this picture.
tile_invalidation.Inset(-buffer_pixels(), -buffer_pixels());
DCHECK_GE(tile_invalidation.width(), buffer_pixels() * 2 + 1);
DCHECK_GE(tile_invalidation.height(), buffer_pixels() * 2 + 1);
InvalidateRect(pic_list, tile_invalidation);
modified_pile = true;
}
}
}
int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
// Walk through all pictures in the rect of interest and record.
for (TilingData::Iterator iter(&tiling_, interest_rect); iter; ++iter) {
// Create a picture in this list if it doesn't exist.
PictureList& pic_list = picture_list_map_[iter.index()];
if (pic_list.empty()) {
// Inflate the base picture with a margin, similar to invalidations, so
// that when scaled down to at least min_contents_scale, the enclosed
// rect still includes content all the way to the edge of the layer.
gfx::Rect tile = tiling_.TileBounds(iter.index_x(), iter.index_y());
tile.Inset(
-buffer_pixels(),
-buffer_pixels(),
-buffer_pixels(),
-buffer_pixels());
scoped_refptr<Picture> base_picture = Picture::Create(tile);
pic_list.push_back(base_picture);
}
for (PictureList::iterator pic = pic_list.begin();
pic != pic_list.end(); ++pic) {
if (!(*pic)->HasRecording()) {
modified_pile = true;
TRACE_EVENT0(benchmark_instrumentation::kCategory,
benchmark_instrumentation::kRecordLoop);
for (int i = 0; i < repeat_count; i++)
(*pic)->Record(painter, tile_grid_info_, stats_instrumentation);
(*pic)->GatherPixelRefs(tile_grid_info_, stats_instrumentation);
(*pic)->CloneForDrawing(num_raster_threads_);
}
}
}
UpdateRecordedRegion();
return modified_pile;
}
class FullyContainedPredicate {
public:
explicit FullyContainedPredicate(gfx::Rect rect) : layer_rect_(rect) {}
bool operator()(const scoped_refptr<Picture>& picture) {
return picture->LayerRect().IsEmpty() ||
layer_rect_.Contains(picture->LayerRect());
}
gfx::Rect layer_rect_;
};
void PicturePile::InvalidateRect(
PictureList& picture_list,
gfx::Rect invalidation) {
DCHECK(!picture_list.empty());
DCHECK(!invalidation.IsEmpty());
std::vector<PictureList::iterator> overlaps;
for (PictureList::iterator i = picture_list.begin();
i != picture_list.end(); ++i) {
if ((*i)->LayerRect().Contains(invalidation) && !(*i)->HasRecording())
return;
if ((*i)->LayerRect().Intersects(invalidation) && i != picture_list.begin())
overlaps.push_back(i);
}
gfx::Rect picture_rect = invalidation;
if (overlaps.size() >= kMaxOverlapping) {
for (size_t j = 0; j < overlaps.size(); j++)
picture_rect.Union((*overlaps[j])->LayerRect());
}
Picture* base_picture = picture_list.front().get();
int max_pixels = kResetThreshold * base_picture->LayerRect().size().GetArea();
if (picture_rect.size().GetArea() > max_pixels) {
// This picture list will be entirely recreated, so clear it.
picture_list.clear();
return;
}
FullyContainedPredicate pred(picture_rect);
picture_list.erase(std::remove_if(picture_list.begin(),
picture_list.end(),
pred),
picture_list.end());
picture_list.push_back(Picture::Create(picture_rect));
}
} // namespace cc