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/debug/trace_event_argument.h"
12#include "base/logging.h"
13#include "base/values.h"
14#include "cc/base/math_util.h"
15#include "cc/debug/traced_value.h"
16#include "third_party/skia/include/core/SkColor.h"
17#include "ui/gfx/rect_conversions.h"
18
19namespace {
20// Dimensions of the tiles in this picture pile as well as the dimensions of
21// the base picture in each tile.
22const int kBasePictureSize = 512;
23const int kTileGridBorderPixels = 1;
24#ifdef NDEBUG
25const bool kDefaultClearCanvasSetting = false;
26#else
27const bool kDefaultClearCanvasSetting = true;
28#endif
29
30// Invalidation frequency settings. kInvalidationFrequencyThreshold is a value
31// between 0 and 1 meaning invalidation frequency between 0% and 100% that
32// indicates when to stop invalidating offscreen regions.
33// kFrequentInvalidationDistanceThreshold defines what it means to be
34// "offscreen" in terms of distance to visible in css pixels.
35const float kInvalidationFrequencyThreshold = 0.75f;
36const int kFrequentInvalidationDistanceThreshold = 512;
37
38}  // namespace
39
40namespace cc {
41
42PicturePileBase::PicturePileBase()
43    : min_contents_scale_(0),
44      background_color_(SkColorSetARGBInline(0, 0, 0, 0)),
45      slow_down_raster_scale_factor_for_debug_(0),
46      contents_opaque_(false),
47      contents_fill_bounds_completely_(false),
48      show_debug_picture_borders_(false),
49      clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
50      has_any_recordings_(false),
51      has_text_(false),
52      is_mask_(false),
53      is_solid_color_(false),
54      solid_color_(SK_ColorTRANSPARENT) {
55  tiling_.SetMaxTextureSize(gfx::Size(kBasePictureSize, kBasePictureSize));
56  tile_grid_info_.fTileInterval.setEmpty();
57  tile_grid_info_.fMargin.setEmpty();
58  tile_grid_info_.fOffset.setZero();
59}
60
61PicturePileBase::PicturePileBase(const PicturePileBase* other)
62    : picture_map_(other->picture_map_),
63      tiling_(other->tiling_),
64      recorded_viewport_(other->recorded_viewport_),
65      min_contents_scale_(other->min_contents_scale_),
66      tile_grid_info_(other->tile_grid_info_),
67      background_color_(other->background_color_),
68      slow_down_raster_scale_factor_for_debug_(
69          other->slow_down_raster_scale_factor_for_debug_),
70      contents_opaque_(other->contents_opaque_),
71      contents_fill_bounds_completely_(other->contents_fill_bounds_completely_),
72      show_debug_picture_borders_(other->show_debug_picture_borders_),
73      clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_),
74      has_any_recordings_(other->has_any_recordings_),
75      has_text_(other->has_text_),
76      is_mask_(other->is_mask_),
77      is_solid_color_(other->is_solid_color_),
78      solid_color_(other->solid_color_) {
79}
80
81PicturePileBase::~PicturePileBase() {
82}
83
84void PicturePileBase::SetMinContentsScale(float min_contents_scale) {
85  DCHECK(min_contents_scale);
86  if (min_contents_scale_ == min_contents_scale)
87    return;
88
89  // Picture contents are played back scaled. When the final contents scale is
90  // less than 1 (i.e. low res), then multiple recorded pixels will be used
91  // to raster one final pixel.  To avoid splitting a final pixel across
92  // pictures (which would result in incorrect rasterization due to blending), a
93  // buffer margin is added so that any picture can be snapped to integral
94  // final pixels.
95  //
96  // For example, if a 1/4 contents scale is used, then that would be 3 buffer
97  // pixels, since that's the minimum number of pixels to add so that resulting
98  // content can be snapped to a four pixel aligned grid.
99  int buffer_pixels = static_cast<int>(ceil(1 / min_contents_scale) - 1);
100  buffer_pixels = std::max(0, buffer_pixels);
101  SetBufferPixels(buffer_pixels);
102  min_contents_scale_ = min_contents_scale;
103}
104
105// static
106void PicturePileBase::ComputeTileGridInfo(
107    const gfx::Size& tile_grid_size,
108    SkTileGridFactory::TileGridInfo* info) {
109  DCHECK(info);
110  info->fTileInterval.set(tile_grid_size.width() - 2 * kTileGridBorderPixels,
111                          tile_grid_size.height() - 2 * kTileGridBorderPixels);
112  DCHECK_GT(info->fTileInterval.width(), 0);
113  DCHECK_GT(info->fTileInterval.height(), 0);
114  info->fMargin.set(kTileGridBorderPixels, kTileGridBorderPixels);
115  // Offset the tile grid coordinate space to take into account the fact
116  // that the top-most and left-most tiles do not have top and left borders
117  // respectively.
118  info->fOffset.set(-kTileGridBorderPixels, -kTileGridBorderPixels);
119}
120
121void PicturePileBase::SetTileGridSize(const gfx::Size& tile_grid_size) {
122  ComputeTileGridInfo(tile_grid_size, &tile_grid_info_);
123}
124
125void PicturePileBase::SetBufferPixels(int new_buffer_pixels) {
126  if (new_buffer_pixels == buffer_pixels())
127    return;
128
129  Clear();
130  tiling_.SetBorderTexels(new_buffer_pixels);
131}
132
133void PicturePileBase::Clear() {
134  picture_map_.clear();
135  recorded_viewport_ = gfx::Rect();
136}
137
138bool PicturePileBase::HasRecordingAt(int x, int y) {
139  PictureMap::const_iterator found = picture_map_.find(PictureMapKey(x, y));
140  if (found == picture_map_.end())
141    return false;
142  return !!found->second.GetPicture();
143}
144
145bool PicturePileBase::CanRaster(float contents_scale,
146                                const gfx::Rect& content_rect) {
147  if (tiling_.tiling_size().IsEmpty())
148    return false;
149  gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
150      content_rect, 1.f / contents_scale);
151  layer_rect.Intersect(gfx::Rect(tiling_.tiling_size()));
152
153  // Common case inside of viewport to avoid the slower map lookups.
154  if (recorded_viewport_.Contains(layer_rect)) {
155    // Sanity check that there are no false positives in recorded_viewport_.
156    DCHECK(CanRasterSlowTileCheck(layer_rect));
157    return true;
158  }
159
160  return CanRasterSlowTileCheck(layer_rect);
161}
162
163bool PicturePileBase::CanRasterSlowTileCheck(
164    const gfx::Rect& layer_rect) const {
165  bool include_borders = false;
166  for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders);
167       tile_iter;
168       ++tile_iter) {
169    PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
170    if (map_iter == picture_map_.end())
171      return false;
172    if (!map_iter->second.GetPicture())
173      return false;
174  }
175  return true;
176}
177
178gfx::Rect PicturePileBase::PaddedRect(const PictureMapKey& key) const {
179  gfx::Rect tile = tiling_.TileBounds(key.first, key.second);
180  return PadRect(tile);
181}
182
183gfx::Rect PicturePileBase::PadRect(const gfx::Rect& rect) const {
184  gfx::Rect padded_rect = rect;
185  padded_rect.Inset(
186      -buffer_pixels(), -buffer_pixels(), -buffer_pixels(), -buffer_pixels());
187  return padded_rect;
188}
189
190void PicturePileBase::AsValueInto(base::debug::TracedValue* pictures) const {
191  gfx::Rect tiling_rect(tiling_.tiling_size());
192  std::set<const void*> appended_pictures;
193  bool include_borders = true;
194  for (TilingData::Iterator tile_iter(&tiling_, tiling_rect, include_borders);
195       tile_iter;
196       ++tile_iter) {
197    PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
198    if (map_iter == picture_map_.end())
199      continue;
200
201    const Picture* picture = map_iter->second.GetPicture();
202    if (picture && (appended_pictures.count(picture) == 0)) {
203      appended_pictures.insert(picture);
204      TracedValue::AppendIDRef(picture, pictures);
205    }
206  }
207}
208
209PicturePileBase::PictureInfo::PictureInfo() : last_frame_number_(0) {}
210
211PicturePileBase::PictureInfo::~PictureInfo() {}
212
213void PicturePileBase::PictureInfo::AdvanceInvalidationHistory(
214    int frame_number) {
215  DCHECK_GE(frame_number, last_frame_number_);
216  if (frame_number == last_frame_number_)
217    return;
218
219  invalidation_history_ <<= (frame_number - last_frame_number_);
220  last_frame_number_ = frame_number;
221}
222
223bool PicturePileBase::PictureInfo::Invalidate(int frame_number) {
224  AdvanceInvalidationHistory(frame_number);
225  invalidation_history_.set(0);
226
227  bool did_invalidate = !!picture_.get();
228  picture_ = NULL;
229  return did_invalidate;
230}
231
232bool PicturePileBase::PictureInfo::NeedsRecording(int frame_number,
233                                                  int distance_to_visible) {
234  AdvanceInvalidationHistory(frame_number);
235
236  // We only need recording if we don't have a picture. Furthermore, we only
237  // need a recording if we're within frequent invalidation distance threshold
238  // or the invalidation is not frequent enough (below invalidation frequency
239  // threshold).
240  return !picture_.get() &&
241         ((distance_to_visible <= kFrequentInvalidationDistanceThreshold) ||
242          (GetInvalidationFrequency() < kInvalidationFrequencyThreshold));
243}
244
245void PicturePileBase::PictureInfo::SetPicture(scoped_refptr<Picture> picture) {
246  picture_ = picture;
247}
248
249const Picture* PicturePileBase::PictureInfo::GetPicture() const {
250  return picture_.get();
251}
252
253float PicturePileBase::PictureInfo::GetInvalidationFrequency() const {
254  return invalidation_history_.count() /
255         static_cast<float>(INVALIDATION_FRAMES_TRACKED);
256}
257
258}  // namespace cc
259