1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "cc/resources/picture_pile_base.h"
6
7#include <algorithm>
8#include <set>
9#include <vector>
10
11#include "base/logging.h"
12#include "base/values.h"
13#include "cc/base/math_util.h"
14#include "cc/debug/traced_value.h"
15#include "third_party/skia/include/core/SkColor.h"
16#include "ui/gfx/rect_conversions.h"
17
18namespace {
19// Dimensions of the tiles in this picture pile as well as the dimensions of
20// the base picture in each tile.
21const int kBasePictureSize = 512;
22const int kTileGridBorderPixels = 1;
23#ifdef NDEBUG
24const bool kDefaultClearCanvasSetting = false;
25#else
26const bool kDefaultClearCanvasSetting = true;
27#endif
28
29// Invalidation frequency settings. kInvalidationFrequencyThreshold is a value
30// between 0 and 1 meaning invalidation frequency between 0% and 100% that
31// indicates when to stop invalidating offscreen regions.
32// kFrequentInvalidationDistanceThreshold defines what it means to be
33// "offscreen" in terms of distance to visible in css pixels.
34const float kInvalidationFrequencyThreshold = 0.75f;
35const int kFrequentInvalidationDistanceThreshold = 512;
36
37}  // namespace
38
39namespace cc {
40
41PicturePileBase::PicturePileBase()
42    : min_contents_scale_(0),
43      background_color_(SkColorSetARGBInline(0, 0, 0, 0)),
44      slow_down_raster_scale_factor_for_debug_(0),
45      contents_opaque_(false),
46      contents_fill_bounds_completely_(false),
47      show_debug_picture_borders_(false),
48      clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
49      has_any_recordings_(false) {
50  tiling_.SetMaxTextureSize(gfx::Size(kBasePictureSize, kBasePictureSize));
51  tile_grid_info_.fTileInterval.setEmpty();
52  tile_grid_info_.fMargin.setEmpty();
53  tile_grid_info_.fOffset.setZero();
54}
55
56PicturePileBase::PicturePileBase(const PicturePileBase* other)
57    : picture_map_(other->picture_map_),
58      tiling_(other->tiling_),
59      recorded_viewport_(other->recorded_viewport_),
60      min_contents_scale_(other->min_contents_scale_),
61      tile_grid_info_(other->tile_grid_info_),
62      background_color_(other->background_color_),
63      slow_down_raster_scale_factor_for_debug_(
64          other->slow_down_raster_scale_factor_for_debug_),
65      contents_opaque_(other->contents_opaque_),
66      contents_fill_bounds_completely_(other->contents_fill_bounds_completely_),
67      show_debug_picture_borders_(other->show_debug_picture_borders_),
68      clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_),
69      has_any_recordings_(other->has_any_recordings_) {}
70
71PicturePileBase::PicturePileBase(const PicturePileBase* other,
72                                 unsigned thread_index)
73    : tiling_(other->tiling_),
74      recorded_viewport_(other->recorded_viewport_),
75      min_contents_scale_(other->min_contents_scale_),
76      tile_grid_info_(other->tile_grid_info_),
77      background_color_(other->background_color_),
78      slow_down_raster_scale_factor_for_debug_(
79          other->slow_down_raster_scale_factor_for_debug_),
80      contents_opaque_(other->contents_opaque_),
81      contents_fill_bounds_completely_(other->contents_fill_bounds_completely_),
82      show_debug_picture_borders_(other->show_debug_picture_borders_),
83      clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_),
84      has_any_recordings_(other->has_any_recordings_) {
85  for (PictureMap::const_iterator it = other->picture_map_.begin();
86       it != other->picture_map_.end();
87       ++it) {
88    picture_map_[it->first] = it->second.CloneForThread(thread_index);
89  }
90}
91
92PicturePileBase::~PicturePileBase() {
93}
94
95void PicturePileBase::SetTilingRect(const gfx::Rect& new_tiling_rect) {
96  if (tiling_rect() == new_tiling_rect)
97    return;
98
99  gfx::Rect old_tiling_rect = tiling_rect();
100  tiling_.SetTilingRect(new_tiling_rect);
101
102  has_any_recordings_ = false;
103
104  // Don't waste time in Resize figuring out what these hints should be.
105  recorded_viewport_ = gfx::Rect();
106
107  if (new_tiling_rect.origin() != old_tiling_rect.origin()) {
108    picture_map_.clear();
109    return;
110  }
111
112  // Find all tiles that contain any pixels outside the new rect.
113  std::vector<PictureMapKey> to_erase;
114  int min_toss_x = tiling_.FirstBorderTileXIndexFromSrcCoord(
115      std::min(old_tiling_rect.right(), new_tiling_rect.right()));
116  int min_toss_y = tiling_.FirstBorderTileYIndexFromSrcCoord(
117      std::min(old_tiling_rect.bottom(), new_tiling_rect.bottom()));
118  for (PictureMap::const_iterator it = picture_map_.begin();
119       it != picture_map_.end();
120       ++it) {
121    const PictureMapKey& key = it->first;
122    if (key.first < min_toss_x && key.second < min_toss_y) {
123      has_any_recordings_ |= !!it->second.GetPicture();
124      continue;
125    }
126    to_erase.push_back(key);
127  }
128
129  for (size_t i = 0; i < to_erase.size(); ++i)
130    picture_map_.erase(to_erase[i]);
131}
132
133void PicturePileBase::SetMinContentsScale(float min_contents_scale) {
134  DCHECK(min_contents_scale);
135  if (min_contents_scale_ == min_contents_scale)
136    return;
137
138  // Picture contents are played back scaled. When the final contents scale is
139  // less than 1 (i.e. low res), then multiple recorded pixels will be used
140  // to raster one final pixel.  To avoid splitting a final pixel across
141  // pictures (which would result in incorrect rasterization due to blending), a
142  // buffer margin is added so that any picture can be snapped to integral
143  // final pixels.
144  //
145  // For example, if a 1/4 contents scale is used, then that would be 3 buffer
146  // pixels, since that's the minimum number of pixels to add so that resulting
147  // content can be snapped to a four pixel aligned grid.
148  int buffer_pixels = static_cast<int>(ceil(1 / min_contents_scale) - 1);
149  buffer_pixels = std::max(0, buffer_pixels);
150  SetBufferPixels(buffer_pixels);
151  min_contents_scale_ = min_contents_scale;
152}
153
154// static
155void PicturePileBase::ComputeTileGridInfo(
156    const gfx::Size& tile_grid_size,
157    SkTileGridFactory::TileGridInfo* info) {
158  DCHECK(info);
159  info->fTileInterval.set(tile_grid_size.width() - 2 * kTileGridBorderPixels,
160                          tile_grid_size.height() - 2 * kTileGridBorderPixels);
161  DCHECK_GT(info->fTileInterval.width(), 0);
162  DCHECK_GT(info->fTileInterval.height(), 0);
163  info->fMargin.set(kTileGridBorderPixels, kTileGridBorderPixels);
164  // Offset the tile grid coordinate space to take into account the fact
165  // that the top-most and left-most tiles do not have top and left borders
166  // respectively.
167  info->fOffset.set(-kTileGridBorderPixels, -kTileGridBorderPixels);
168}
169
170void PicturePileBase::SetTileGridSize(const gfx::Size& tile_grid_size) {
171  ComputeTileGridInfo(tile_grid_size, &tile_grid_info_);
172}
173
174void PicturePileBase::SetBufferPixels(int new_buffer_pixels) {
175  if (new_buffer_pixels == buffer_pixels())
176    return;
177
178  Clear();
179  tiling_.SetBorderTexels(new_buffer_pixels);
180}
181
182void PicturePileBase::Clear() {
183  picture_map_.clear();
184  recorded_viewport_ = gfx::Rect();
185}
186
187bool PicturePileBase::HasRecordingAt(int x, int y) {
188  PictureMap::const_iterator found = picture_map_.find(PictureMapKey(x, y));
189  if (found == picture_map_.end())
190    return false;
191  return !!found->second.GetPicture();
192}
193
194bool PicturePileBase::CanRaster(float contents_scale,
195                                const gfx::Rect& content_rect) {
196  if (tiling_.tiling_rect().IsEmpty())
197    return false;
198  gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
199      content_rect, 1.f / contents_scale);
200  layer_rect.Intersect(tiling_.tiling_rect());
201
202  // Common case inside of viewport to avoid the slower map lookups.
203  if (recorded_viewport_.Contains(layer_rect)) {
204    // Sanity check that there are no false positives in recorded_viewport_.
205    DCHECK(CanRasterSlowTileCheck(layer_rect));
206    return true;
207  }
208
209  return CanRasterSlowTileCheck(layer_rect);
210}
211
212bool PicturePileBase::CanRasterSlowTileCheck(
213    const gfx::Rect& layer_rect) const {
214  bool include_borders = false;
215  for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders);
216       tile_iter;
217       ++tile_iter) {
218    PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
219    if (map_iter == picture_map_.end())
220      return false;
221    if (!map_iter->second.GetPicture())
222      return false;
223  }
224  return true;
225}
226
227gfx::Rect PicturePileBase::PaddedRect(const PictureMapKey& key) {
228  gfx::Rect tile = tiling_.TileBounds(key.first, key.second);
229  return PadRect(tile);
230}
231
232gfx::Rect PicturePileBase::PadRect(const gfx::Rect& rect) {
233  gfx::Rect padded_rect = rect;
234  padded_rect.Inset(
235      -buffer_pixels(), -buffer_pixels(), -buffer_pixels(), -buffer_pixels());
236  return padded_rect;
237}
238
239scoped_ptr<base::Value> PicturePileBase::AsValue() const {
240  scoped_ptr<base::ListValue> pictures(new base::ListValue());
241  gfx::Rect tiling_rect(tiling_.tiling_rect());
242  std::set<void*> appended_pictures;
243  bool include_borders = true;
244  for (TilingData::Iterator tile_iter(&tiling_, tiling_rect, include_borders);
245       tile_iter;
246       ++tile_iter) {
247    PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
248    if (map_iter == picture_map_.end())
249      continue;
250
251    Picture* picture = map_iter->second.GetPicture();
252    if (picture && (appended_pictures.count(picture) == 0)) {
253      appended_pictures.insert(picture);
254      pictures->Append(TracedValue::CreateIDRef(picture).release());
255    }
256  }
257  return pictures.PassAs<base::Value>();
258}
259
260PicturePileBase::PictureInfo::PictureInfo() : last_frame_number_(0) {}
261
262PicturePileBase::PictureInfo::~PictureInfo() {}
263
264void PicturePileBase::PictureInfo::AdvanceInvalidationHistory(
265    int frame_number) {
266  DCHECK_GE(frame_number, last_frame_number_);
267  if (frame_number == last_frame_number_)
268    return;
269
270  invalidation_history_ <<= (frame_number - last_frame_number_);
271  last_frame_number_ = frame_number;
272}
273
274bool PicturePileBase::PictureInfo::Invalidate(int frame_number) {
275  AdvanceInvalidationHistory(frame_number);
276  invalidation_history_.set(0);
277
278  bool did_invalidate = !!picture_;
279  picture_ = NULL;
280  return did_invalidate;
281}
282
283bool PicturePileBase::PictureInfo::NeedsRecording(int frame_number,
284                                                  int distance_to_visible) {
285  AdvanceInvalidationHistory(frame_number);
286
287  // We only need recording if we don't have a picture. Furthermore, we only
288  // need a recording if we're within frequent invalidation distance threshold
289  // or the invalidation is not frequent enough (below invalidation frequency
290  // threshold).
291  return !picture_ &&
292         ((distance_to_visible <= kFrequentInvalidationDistanceThreshold) ||
293          (GetInvalidationFrequency() < kInvalidationFrequencyThreshold));
294}
295
296void PicturePileBase::PictureInfo::SetPicture(scoped_refptr<Picture> picture) {
297  picture_ = picture;
298}
299
300Picture* PicturePileBase::PictureInfo::GetPicture() const {
301  return picture_.get();
302}
303
304PicturePileBase::PictureInfo PicturePileBase::PictureInfo::CloneForThread(
305    int thread_index) const {
306  PictureInfo info = *this;
307  if (picture_.get())
308    info.picture_ = picture_->GetCloneForDrawingOnThread(thread_index);
309  return info;
310}
311
312float PicturePileBase::PictureInfo::GetInvalidationFrequency() const {
313  return invalidation_history_.count() /
314         static_cast<float>(INVALIDATION_FRAMES_TRACKED);
315}
316
317}  // namespace cc
318