1 // Copyright 2012 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.
5 #include "cc/resources/picture_pile.h"
11 #include "cc/base/region.h"
12 #include "cc/debug/rendering_stats_instrumentation.h"
13 #include "cc/resources/picture_pile_impl.h"
14 #include "cc/resources/raster_worker_pool.h"
15 #include "cc/resources/tile_priority.h"
18 // Layout pixel buffer around the visible layer rect to record. Any base
19 // picture that intersects the visible layer rect expanded by this distance
21 const int kPixelDistanceToRecord
= 8000;
23 // TODO(humper): The density threshold here is somewhat arbitrary; need a
24 // way to set // this from the command line so we can write a benchmark
25 // script and find a sweet spot.
26 const float kDensityThreshold
= 0.5f
;
28 bool rect_sort_y(const gfx::Rect
& r1
, const gfx::Rect
& r2
) {
29 return r1
.y() < r2
.y() || (r1
.y() == r2
.y() && r1
.x() < r2
.x());
32 bool rect_sort_x(const gfx::Rect
& r1
, const gfx::Rect
& r2
) {
33 return r1
.x() < r2
.x() || (r1
.x() == r2
.x() && r1
.y() < r2
.y());
36 float PerformClustering(const std::vector
<gfx::Rect
>& tiles
,
37 std::vector
<gfx::Rect
>* clustered_rects
) {
38 // These variables track the record area and invalid area
39 // for the entire clustering
40 int total_record_area
= 0;
41 int total_invalid_area
= 0;
43 // These variables track the record area and invalid area
44 // for the current cluster being constructed.
45 gfx::Rect cur_record_rect
;
46 int cluster_record_area
= 0, cluster_invalid_area
= 0;
48 for (std::vector
<gfx::Rect
>::const_iterator it
= tiles
.begin();
51 gfx::Rect invalid_tile
= *it
;
53 // For each tile, we consider adding the invalid tile to the
54 // current record rectangle. Only add it if the amount of empty
55 // space created is below a density threshold.
56 int tile_area
= invalid_tile
.width() * invalid_tile
.height();
58 gfx::Rect proposed_union
= cur_record_rect
;
59 proposed_union
.Union(invalid_tile
);
60 int proposed_area
= proposed_union
.width() * proposed_union
.height();
61 float proposed_density
=
62 static_cast<float>(cluster_invalid_area
+ tile_area
) /
63 static_cast<float>(proposed_area
);
65 if (proposed_density
>= kDensityThreshold
) {
66 // It's okay to add this invalid tile to the
67 // current recording rectangle.
68 cur_record_rect
= proposed_union
;
69 cluster_record_area
= proposed_area
;
70 cluster_invalid_area
+= tile_area
;
71 total_invalid_area
+= tile_area
;
73 // Adding this invalid tile to the current recording rectangle
74 // would exceed our badness threshold, so put the current rectangle
75 // in the list of recording rects, and start a new one.
76 clustered_rects
->push_back(cur_record_rect
);
77 total_record_area
+= cluster_record_area
;
78 cur_record_rect
= invalid_tile
;
79 cluster_invalid_area
= tile_area
;
80 cluster_record_area
= tile_area
;
84 DCHECK(!cur_record_rect
.IsEmpty());
85 clustered_rects
->push_back(cur_record_rect
);
86 total_record_area
+= cluster_record_area
;;
88 DCHECK_NE(total_record_area
, 0);
90 return static_cast<float>(total_invalid_area
) /
91 static_cast<float>(total_record_area
);
94 float ClusterTiles(const std::vector
<gfx::Rect
>& invalid_tiles
,
95 std::vector
<gfx::Rect
>* record_rects
) {
96 TRACE_EVENT1("cc", "ClusterTiles",
98 invalid_tiles
.size());
100 if (invalid_tiles
.size() <= 1) {
101 // Quickly handle the special case for common
102 // single-invalidation update, and also the less common
103 // case of no tiles passed in.
104 *record_rects
= invalid_tiles
;
108 // Sort the invalid tiles by y coordinate.
109 std::vector
<gfx::Rect
> invalid_tiles_vertical
= invalid_tiles
;
110 std::sort(invalid_tiles_vertical
.begin(),
111 invalid_tiles_vertical
.end(),
114 float vertical_density
;
115 std::vector
<gfx::Rect
> vertical_clustering
;
116 vertical_density
= PerformClustering(invalid_tiles_vertical
,
117 &vertical_clustering
);
119 // If vertical density is optimal, then we can return early.
120 if (vertical_density
== 1.f
) {
121 *record_rects
= vertical_clustering
;
122 return vertical_density
;
125 // Now try again with a horizontal sort, see which one is best
126 std::vector
<gfx::Rect
> invalid_tiles_horizontal
= invalid_tiles
;
127 std::sort(invalid_tiles_horizontal
.begin(),
128 invalid_tiles_horizontal
.end(),
131 float horizontal_density
;
132 std::vector
<gfx::Rect
> horizontal_clustering
;
133 horizontal_density
= PerformClustering(invalid_tiles_horizontal
,
134 &horizontal_clustering
);
136 if (vertical_density
< horizontal_density
) {
137 *record_rects
= horizontal_clustering
;
138 return horizontal_density
;
141 *record_rects
= vertical_clustering
;
142 return vertical_density
;
149 PicturePile::PicturePile() : is_suitable_for_gpu_rasterization_(true) {}
151 PicturePile::~PicturePile() {
154 bool PicturePile::UpdateAndExpandInvalidation(
155 ContentLayerClient
* painter
,
156 Region
* invalidation
,
157 SkColor background_color
,
158 bool contents_opaque
,
159 bool contents_fill_bounds_completely
,
160 const gfx::Size
& layer_size
,
161 const gfx::Rect
& visible_layer_rect
,
163 Picture::RecordingMode recording_mode
,
164 RenderingStatsInstrumentation
* stats_instrumentation
) {
165 background_color_
= background_color
;
166 contents_opaque_
= contents_opaque
;
167 contents_fill_bounds_completely_
= contents_fill_bounds_completely
;
169 bool updated
= false;
171 Region resize_invalidation
;
172 gfx::Size old_tiling_size
= tiling_size();
173 if (old_tiling_size
!= layer_size
) {
174 tiling_
.SetTilingSize(layer_size
);
178 gfx::Rect interest_rect
= visible_layer_rect
;
180 -kPixelDistanceToRecord
,
181 -kPixelDistanceToRecord
,
182 -kPixelDistanceToRecord
,
183 -kPixelDistanceToRecord
);
184 recorded_viewport_
= interest_rect
;
185 recorded_viewport_
.Intersect(gfx::Rect(tiling_size()));
187 gfx::Rect interest_rect_over_tiles
=
188 tiling_
.ExpandRectToTileBounds(interest_rect
);
190 if (old_tiling_size
!= layer_size
) {
191 has_any_recordings_
= false;
193 // Drop recordings that are outside the new layer bounds or that changed
195 std::vector
<PictureMapKey
> to_erase
;
196 int min_toss_x
= tiling_
.num_tiles_x();
197 if (tiling_size().width() > old_tiling_size
.width()) {
199 tiling_
.FirstBorderTileXIndexFromSrcCoord(old_tiling_size
.width());
201 int min_toss_y
= tiling_
.num_tiles_y();
202 if (tiling_size().height() > old_tiling_size
.height()) {
204 tiling_
.FirstBorderTileYIndexFromSrcCoord(old_tiling_size
.height());
206 for (PictureMap::const_iterator it
= picture_map_
.begin();
207 it
!= picture_map_
.end();
209 const PictureMapKey
& key
= it
->first
;
210 if (key
.first
< min_toss_x
&& key
.second
< min_toss_y
) {
211 has_any_recordings_
|= !!it
->second
.GetPicture();
214 to_erase
.push_back(key
);
217 for (size_t i
= 0; i
< to_erase
.size(); ++i
)
218 picture_map_
.erase(to_erase
[i
]);
220 // If a recording is dropped and not re-recorded below, invalidate that
221 // full recording to cause any raster tiles that would use it to be
223 // If the recording will be replaced below, just invalidate newly exposed
224 // areas to force raster tiles that include the old recording to know
225 // there is new recording to display.
226 gfx::Rect old_tiling_rect_over_tiles
=
227 tiling_
.ExpandRectToTileBounds(gfx::Rect(old_tiling_size
));
228 if (min_toss_x
< tiling_
.num_tiles_x()) {
229 int unrecorded_left
= std::max(tiling_
.TilePositionX(min_toss_x
),
230 interest_rect_over_tiles
.right());
231 int exposed_left
= old_tiling_size
.width();
232 int left
= std::min(unrecorded_left
, exposed_left
);
234 tiling_
.TilePositionX(min_toss_x
) + tiling_
.TileSizeX(min_toss_x
);
235 int exposed_right
= tiling_size().width();
236 int right
= std::min(tile_right
, exposed_right
);
237 gfx::Rect
right_side(left
,
238 old_tiling_rect_over_tiles
.y(),
240 old_tiling_rect_over_tiles
.height());
241 resize_invalidation
.Union(right_side
);
243 if (min_toss_y
< tiling_
.num_tiles_y()) {
244 int unrecorded_top
= std::max(tiling_
.TilePositionY(min_toss_y
),
245 interest_rect_over_tiles
.bottom());
246 int exposed_top
= old_tiling_size
.height();
247 int top
= std::min(unrecorded_top
, exposed_top
);
249 tiling_
.TilePositionY(min_toss_y
) + tiling_
.TileSizeY(min_toss_y
);
250 int exposed_bottom
= tiling_size().height();
251 int bottom
= std::min(tile_bottom
, exposed_bottom
);
252 gfx::Rect
bottom_side(old_tiling_rect_over_tiles
.x(),
254 old_tiling_rect_over_tiles
.width(),
256 resize_invalidation
.Union(bottom_side
);
260 Region invalidation_expanded_to_full_tiles
;
261 for (Region::Iterator
i(*invalidation
); i
.has_rect(); i
.next()) {
262 gfx::Rect invalid_rect
= i
.rect();
264 // Expand invalidation that is outside tiles that intersect the interest
265 // rect. These tiles are no longer valid and should be considerered fully
266 // invalid, so we can know to not keep around raster tiles that intersect
267 // with these recording tiles.
268 gfx::Rect invalid_rect_outside_interest_rect_tiles
= invalid_rect
;
269 // TODO(danakj): We should have a Rect-subtract-Rect-to-2-rects operator
270 // instead of using Rect::Subtract which gives you the bounding box of the
272 invalid_rect_outside_interest_rect_tiles
.Subtract(interest_rect_over_tiles
);
273 invalidation_expanded_to_full_tiles
.Union(tiling_
.ExpandRectToTileBounds(
274 invalid_rect_outside_interest_rect_tiles
));
276 // Split this inflated invalidation across tile boundaries and apply it
277 // to all tiles that it touches.
278 bool include_borders
= true;
279 for (TilingData::Iterator
iter(&tiling_
, invalid_rect
, include_borders
);
282 const PictureMapKey
& key
= iter
.index();
284 PictureMap::iterator picture_it
= picture_map_
.find(key
);
285 if (picture_it
== picture_map_
.end())
288 // Inform the grid cell that it has been invalidated in this frame.
289 updated
= picture_it
->second
.Invalidate(frame_number
) || updated
;
290 // Invalidate drops the picture so the whole tile better be invalidated if
291 // it won't be re-recorded below.
293 tiling_
.TileBounds(key
.first
, key
.second
).Intersects(interest_rect
) ||
294 invalidation_expanded_to_full_tiles
.Contains(
295 tiling_
.TileBounds(key
.first
, key
.second
)));
299 invalidation
->Union(invalidation_expanded_to_full_tiles
);
300 invalidation
->Union(resize_invalidation
);
302 // Make a list of all invalid tiles; we will attempt to
303 // cluster these into multiple invalidation regions.
304 std::vector
<gfx::Rect
> invalid_tiles
;
305 bool include_borders
= true;
306 for (TilingData::Iterator
it(&tiling_
, interest_rect
, include_borders
); it
;
308 const PictureMapKey
& key
= it
.index();
309 PictureInfo
& info
= picture_map_
[key
];
311 gfx::Rect rect
= PaddedRect(key
);
312 int distance_to_visible
=
313 rect
.ManhattanInternalDistance(visible_layer_rect
);
315 if (info
.NeedsRecording(frame_number
, distance_to_visible
)) {
316 gfx::Rect tile
= tiling_
.TileBounds(key
.first
, key
.second
);
317 invalid_tiles
.push_back(tile
);
318 } else if (!info
.GetPicture()) {
319 if (recorded_viewport_
.Intersects(rect
)) {
320 // Recorded viewport is just an optimization for a fully recorded
321 // interest rect. In this case, a tile in that rect has declined
322 // to be recorded (probably due to frequent invalidations).
323 // TODO(enne): Shrink the recorded_viewport_ rather than clearing.
324 recorded_viewport_
= gfx::Rect();
327 // If a tile in the interest rect is not recorded, the entire tile needs
328 // to be considered invalid, so that we know not to keep around raster
329 // tiles that intersect this recording tile.
330 invalidation
->Union(tiling_
.TileBounds(it
.index_x(), it
.index_y()));
334 std::vector
<gfx::Rect
> record_rects
;
335 ClusterTiles(invalid_tiles
, &record_rects
);
337 if (record_rects
.empty())
340 for (std::vector
<gfx::Rect
>::iterator it
= record_rects
.begin();
341 it
!= record_rects
.end();
343 gfx::Rect record_rect
= *it
;
344 record_rect
= PadRect(record_rect
);
346 int repeat_count
= std::max(1, slow_down_raster_scale_factor_for_debug_
);
347 scoped_refptr
<Picture
> picture
;
348 int num_raster_threads
= RasterWorkerPool::GetNumRasterThreads();
350 // Note: Currently, gathering of pixel refs when using a single
351 // raster thread doesn't provide any benefit. This might change
352 // in the future but we avoid it for now to reduce the cost of
354 bool gather_pixel_refs
= num_raster_threads
> 1;
357 base::TimeDelta best_duration
= base::TimeDelta::Max();
358 for (int i
= 0; i
< repeat_count
; i
++) {
359 base::TimeTicks start_time
= stats_instrumentation
->StartRecording();
360 picture
= Picture::Create(record_rect
,
366 // Note the '&&' with previous is-suitable state.
367 // This means that once a picture-pile becomes unsuitable for gpu
368 // rasterization due to some content, it will continue to be unsuitable
369 // even if that content is replaced by gpu-friendly content.
370 // This is an optimization to avoid iterating though all pictures in
371 // the pile after each invalidation.
372 is_suitable_for_gpu_rasterization_
&=
373 picture
->IsSuitableForGpuRasterization();
374 base::TimeDelta duration
=
375 stats_instrumentation
->EndRecording(start_time
);
376 best_duration
= std::min(duration
, best_duration
);
378 int recorded_pixel_count
=
379 picture
->LayerRect().width() * picture
->LayerRect().height();
380 stats_instrumentation
->AddRecord(best_duration
, recorded_pixel_count
);
383 bool found_tile_for_recorded_picture
= false;
385 bool include_borders
= true;
386 for (TilingData::Iterator
it(&tiling_
, record_rect
, include_borders
); it
;
388 const PictureMapKey
& key
= it
.index();
389 gfx::Rect tile
= PaddedRect(key
);
390 if (record_rect
.Contains(tile
)) {
391 PictureInfo
& info
= picture_map_
[key
];
392 info
.SetPicture(picture
);
393 found_tile_for_recorded_picture
= true;
396 DCHECK(found_tile_for_recorded_picture
);
399 has_any_recordings_
= true;
400 DCHECK(CanRasterSlowTileCheck(recorded_viewport_
));
404 void PicturePile::SetEmptyBounds() {
405 tiling_
.SetTilingSize(gfx::Size());
406 picture_map_
.clear();
407 has_any_recordings_
= false;
408 recorded_viewport_
= gfx::Rect();