OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "cc/resources/picture_layer_tiling.h" | 5 #include "cc/resources/picture_layer_tiling.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <limits> | 9 #include <limits> |
10 #include <set> | 10 #include <set> |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 // Or if a is farther away from visible. | 49 // Or if a is farther away from visible. |
50 return a_priority.distance_to_visible > b_priority.distance_to_visible; | 50 return a_priority.distance_to_visible > b_priority.distance_to_visible; |
51 } | 51 } |
52 | 52 |
53 private: | 53 private: |
54 TreePriority tree_priority_; | 54 TreePriority tree_priority_; |
55 }; | 55 }; |
56 | 56 |
57 } // namespace | 57 } // namespace |
58 | 58 |
| 59 const int |
| 60 PictureLayerTiling::TileInvalidationState::kFrequentInvalidationThreshold = |
| 61 2; |
| 62 |
| 63 PictureLayerTiling::TileInvalidationState::TileInvalidationState() |
| 64 : frame_count_(0) { |
| 65 } |
| 66 |
| 67 void PictureLayerTiling::TileInvalidationState::Invalidate( |
| 68 base::TimeTicks current_frame_time) { |
| 69 if (current_frame_time == last_frame_time_) |
| 70 return; |
| 71 |
| 72 // Check the time intervals between consecutive invalidations: |
| 73 // - If the intervals are shorter than kIntervalMin for a number of |
| 74 // consecutive invalidations, it's a "frequently invalidated" tile. |
| 75 // - If the interval between two invalidations is longer than kIntervalMax, |
| 76 // it's not a "frequently invalidated" tile anymore. |
| 77 base::TimeDelta time_since = current_frame_time - last_frame_time_; |
| 78 const base::TimeDelta kIntervalMin = base::TimeDelta::FromMilliseconds(700); |
| 79 const base::TimeDelta kIntervalMax = base::TimeDelta::FromMilliseconds(2000); |
| 80 const base::TimeDelta interval = |
| 81 FrequentlyInvalidated() ? kIntervalMax : kIntervalMin; |
| 82 frame_count_ = |
| 83 time_since < interval |
| 84 ? std::min(frame_count_ + 1, kFrequentInvalidationThreshold) |
| 85 : 0; |
| 86 last_frame_time_ = current_frame_time; |
| 87 } |
| 88 |
| 89 bool PictureLayerTiling::TileInvalidationState::FrequentlyInvalidated() const { |
| 90 return frame_count_ >= kFrequentInvalidationThreshold; |
| 91 } |
| 92 |
59 scoped_ptr<PictureLayerTiling> PictureLayerTiling::Create( | 93 scoped_ptr<PictureLayerTiling> PictureLayerTiling::Create( |
60 float contents_scale, | 94 float contents_scale, |
61 const gfx::Size& layer_bounds, | 95 const gfx::Size& layer_bounds, |
62 PictureLayerTilingClient* client) { | 96 PictureLayerTilingClient* client) { |
63 return make_scoped_ptr(new PictureLayerTiling(contents_scale, | 97 return make_scoped_ptr(new PictureLayerTiling(contents_scale, |
64 layer_bounds, | 98 layer_bounds, |
65 client)); | 99 client)); |
66 } | 100 } |
67 | 101 |
68 PictureLayerTiling::PictureLayerTiling(float contents_scale, | 102 PictureLayerTiling::PictureLayerTiling(float contents_scale, |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
131 DCHECK(!candidate_tile->is_shared()); | 165 DCHECK(!candidate_tile->is_shared()); |
132 DCHECK_EQ(i, candidate_tile->tiling_i_index()); | 166 DCHECK_EQ(i, candidate_tile->tiling_i_index()); |
133 DCHECK_EQ(j, candidate_tile->tiling_j_index()); | 167 DCHECK_EQ(j, candidate_tile->tiling_j_index()); |
134 candidate_tile->set_shared(true); | 168 candidate_tile->set_shared(true); |
135 tiles_[key] = candidate_tile; | 169 tiles_[key] = candidate_tile; |
136 return candidate_tile; | 170 return candidate_tile; |
137 } | 171 } |
138 } | 172 } |
139 } | 173 } |
140 | 174 |
| 175 int flags = 0; |
| 176 auto found = tile_invalidation_states_.find(key); |
| 177 if (found != tile_invalidation_states_.end()) { |
| 178 if (found->second.FrequentlyInvalidated()) |
| 179 flags |= Tile::FREQUENTLY_INVALIDATED; |
| 180 } |
| 181 |
141 // Create a new tile because our twin didn't have a valid one. | 182 // Create a new tile because our twin didn't have a valid one. |
142 scoped_refptr<Tile> tile = client_->CreateTile(this, tile_rect); | 183 scoped_refptr<Tile> tile = client_->CreateTile(this, tile_rect, flags); |
143 if (tile.get()) { | 184 if (tile.get()) { |
144 DCHECK(!tile->is_shared()); | 185 DCHECK(!tile->is_shared()); |
145 tile->set_tiling_index(i, j); | 186 tile->set_tiling_index(i, j); |
146 tiles_[key] = tile; | 187 tiles_[key] = tile; |
147 } | 188 } |
148 eviction_tiles_cache_valid_ = false; | 189 eviction_tiles_cache_valid_ = false; |
149 return tile.get(); | 190 return tile.get(); |
150 } | 191 } |
151 | 192 |
152 void PictureLayerTiling::CreateMissingTilesInLiveTilesRect() { | 193 void PictureLayerTiling::CreateMissingTilesInLiveTilesRect() { |
(...skipping 10 matching lines...) Expand all Loading... |
163 continue; | 204 continue; |
164 CreateTile(key.first, key.second, twin_tiling); | 205 CreateTile(key.first, key.second, twin_tiling); |
165 } | 206 } |
166 | 207 |
167 VerifyLiveTilesRect(); | 208 VerifyLiveTilesRect(); |
168 } | 209 } |
169 | 210 |
170 void PictureLayerTiling::UpdateTilesToCurrentRasterSource( | 211 void PictureLayerTiling::UpdateTilesToCurrentRasterSource( |
171 RasterSource* raster_source, | 212 RasterSource* raster_source, |
172 const Region& layer_invalidation, | 213 const Region& layer_invalidation, |
173 const gfx::Size& new_layer_bounds) { | 214 const gfx::Size& new_layer_bounds, |
| 215 base::TimeTicks frame_time) { |
174 DCHECK(!new_layer_bounds.IsEmpty()); | 216 DCHECK(!new_layer_bounds.IsEmpty()); |
175 | 217 |
176 gfx::Size content_bounds = | 218 gfx::Size content_bounds = |
177 gfx::ToCeiledSize(gfx::ScaleSize(new_layer_bounds, contents_scale_)); | 219 gfx::ToCeiledSize(gfx::ScaleSize(new_layer_bounds, contents_scale_)); |
178 gfx::Size tile_size = client_->CalculateTileSize(content_bounds); | 220 gfx::Size tile_size = client_->CalculateTileSize(content_bounds); |
179 | 221 |
180 if (new_layer_bounds != layer_bounds_) { | 222 if (new_layer_bounds != layer_bounds_) { |
181 if (tile_size.IsEmpty()) { | 223 if (tile_size.IsEmpty()) { |
182 layer_bounds_ = gfx::Size(); | 224 layer_bounds_ = gfx::Size(); |
183 content_bounds = gfx::Size(); | 225 content_bounds = gfx::Size(); |
(...skipping 27 matching lines...) Expand all Loading... |
211 } | 253 } |
212 | 254 |
213 // There is no recycled twin since this is run on the pending tiling. | 255 // There is no recycled twin since this is run on the pending tiling. |
214 PictureLayerTiling* recycled_twin = NULL; | 256 PictureLayerTiling* recycled_twin = NULL; |
215 DCHECK_EQ(recycled_twin, client_->GetRecycledTwinTiling(this)); | 257 DCHECK_EQ(recycled_twin, client_->GetRecycledTwinTiling(this)); |
216 DCHECK_EQ(PENDING_TREE, client_->GetTree()); | 258 DCHECK_EQ(PENDING_TREE, client_->GetTree()); |
217 | 259 |
218 // Drop tiles outside the new layer bounds if the layer shrank. | 260 // Drop tiles outside the new layer bounds if the layer shrank. |
219 for (int i = after_right + 1; i <= before_right; ++i) { | 261 for (int i = after_right + 1; i <= before_right; ++i) { |
220 for (int j = before_top; j <= before_bottom; ++j) | 262 for (int j = before_top; j <= before_bottom; ++j) |
221 RemoveTileAt(i, j, recycled_twin); | 263 RemoveTileAt(i, j, recycled_twin, true); |
222 } | 264 } |
223 for (int i = before_left; i <= after_right; ++i) { | 265 for (int i = before_left; i <= after_right; ++i) { |
224 for (int j = after_bottom + 1; j <= before_bottom; ++j) | 266 for (int j = after_bottom + 1; j <= before_bottom; ++j) |
225 RemoveTileAt(i, j, recycled_twin); | 267 RemoveTileAt(i, j, recycled_twin, true); |
226 } | 268 } |
227 | 269 |
228 // If the layer grew, the live_tiles_rect_ is not changed, but a new row | 270 // If the layer grew, the live_tiles_rect_ is not changed, but a new row |
229 // and/or column of tiles may now exist inside the same live_tiles_rect_. | 271 // and/or column of tiles may now exist inside the same live_tiles_rect_. |
230 const PictureLayerTiling* twin_tiling = | 272 const PictureLayerTiling* twin_tiling = |
231 client_->GetPendingOrActiveTwinTiling(this); | 273 client_->GetPendingOrActiveTwinTiling(this); |
232 if (after_right > before_right) { | 274 if (after_right > before_right) { |
233 DCHECK_EQ(after_right, before_right + 1); | 275 DCHECK_EQ(after_right, before_right + 1); |
234 for (int j = before_top; j <= after_bottom; ++j) | 276 for (int j = before_top; j <= after_bottom; ++j) |
235 CreateTile(after_right, j, twin_tiling); | 277 CreateTile(after_right, j, twin_tiling); |
236 } | 278 } |
237 if (after_bottom > before_bottom) { | 279 if (after_bottom > before_bottom) { |
238 DCHECK_EQ(after_bottom, before_bottom + 1); | 280 DCHECK_EQ(after_bottom, before_bottom + 1); |
239 for (int i = before_left; i <= before_right; ++i) | 281 for (int i = before_left; i <= before_right; ++i) |
240 CreateTile(i, after_bottom, twin_tiling); | 282 CreateTile(i, after_bottom, twin_tiling); |
241 } | 283 } |
242 } | 284 } |
243 | 285 |
244 if (tile_size != tiling_data_.max_texture_size()) { | 286 if (tile_size != tiling_data_.max_texture_size()) { |
245 tiling_data_.SetMaxTextureSize(tile_size); | 287 tiling_data_.SetMaxTextureSize(tile_size); |
246 // When the tile size changes, the TilingData positions no longer work | 288 // When the tile size changes, the TilingData positions no longer work |
247 // as valid keys to the TileMap, so just drop all tiles. | 289 // as valid keys to the TileMap, so just drop all tiles. |
248 Reset(); | 290 Reset(); |
249 } else { | 291 } else { |
250 Invalidate(layer_invalidation); | 292 Invalidate(layer_invalidation, frame_time); |
251 } | 293 } |
252 | 294 |
253 for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) | 295 for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) |
254 it->second->set_raster_source(raster_source); | 296 it->second->set_raster_source(raster_source); |
255 VerifyLiveTilesRect(); | 297 VerifyLiveTilesRect(); |
256 } | 298 } |
257 | 299 |
258 void PictureLayerTiling::RemoveTilesInRegion(const Region& layer_region) { | 300 void PictureLayerTiling::RemoveTilesInRegion(const Region& layer_region) { |
259 bool recreate_invalidated_tiles = false; | 301 bool recreate_invalidated_tiles = false; |
260 DoInvalidate(layer_region, recreate_invalidated_tiles); | 302 DoInvalidate(layer_region, recreate_invalidated_tiles, base::TimeTicks()); |
261 } | 303 } |
262 | 304 |
263 void PictureLayerTiling::Invalidate(const Region& layer_region) { | 305 void PictureLayerTiling::Invalidate(const Region& layer_region, |
| 306 base::TimeTicks frame_time) { |
264 bool recreate_invalidated_tiles = true; | 307 bool recreate_invalidated_tiles = true; |
265 DoInvalidate(layer_region, recreate_invalidated_tiles); | 308 DoInvalidate(layer_region, recreate_invalidated_tiles, frame_time); |
266 } | 309 } |
267 | 310 |
268 void PictureLayerTiling::DoInvalidate(const Region& layer_region, | 311 void PictureLayerTiling::DoInvalidate(const Region& layer_region, |
269 bool recreate_invalidated_tiles) { | 312 bool recreate_invalidated_tiles, |
| 313 base::TimeTicks frame_time) { |
| 314 bool update_invalidation_states = frame_time != base::TimeTicks(); |
270 std::vector<TileMapKey> new_tile_keys; | 315 std::vector<TileMapKey> new_tile_keys; |
271 gfx::Rect expanded_live_tiles_rect = | 316 gfx::Rect expanded_live_tiles_rect = |
272 tiling_data_.ExpandRectIgnoringBordersToTileBounds(live_tiles_rect_); | 317 tiling_data_.ExpandRectIgnoringBordersToTileBounds(live_tiles_rect_); |
273 for (Region::Iterator iter(layer_region); iter.has_rect(); iter.next()) { | 318 for (Region::Iterator iter(layer_region); iter.has_rect(); iter.next()) { |
274 gfx::Rect layer_rect = iter.rect(); | 319 gfx::Rect layer_rect = iter.rect(); |
275 gfx::Rect content_rect = | 320 gfx::Rect content_rect = |
276 gfx::ScaleToEnclosingRect(layer_rect, contents_scale_); | 321 gfx::ScaleToEnclosingRect(layer_rect, contents_scale_); |
277 // Consider tiles inside the live tiles rect even if only their border | 322 // Consider tiles inside the live tiles rect even if only their border |
278 // pixels intersect the invalidation. But don't consider tiles outside | 323 // pixels intersect the invalidation. But don't consider tiles outside |
279 // the live tiles rect with the same conditions, as they won't exist. | 324 // the live tiles rect with the same conditions, as they won't exist. |
280 int border_pixels = tiling_data_.border_texels(); | 325 int border_pixels = tiling_data_.border_texels(); |
281 content_rect.Inset(-border_pixels, -border_pixels); | 326 content_rect.Inset(-border_pixels, -border_pixels); |
282 // Avoid needless work by not bothering to invalidate where there aren't | 327 // Avoid needless work by not bothering to invalidate where there aren't |
283 // tiles. | 328 // tiles. |
284 content_rect.Intersect(expanded_live_tiles_rect); | 329 content_rect.Intersect(expanded_live_tiles_rect); |
285 if (content_rect.IsEmpty()) | 330 if (content_rect.IsEmpty()) |
286 continue; | 331 continue; |
287 // Since the content_rect includes border pixels already, don't include | 332 // Since the content_rect includes border pixels already, don't include |
288 // borders when iterating to avoid double counting them. | 333 // borders when iterating to avoid double counting them. |
289 bool include_borders = false; | 334 bool include_borders = false; |
290 for (TilingData::Iterator iter( | 335 for (TilingData::Iterator iter( |
291 &tiling_data_, content_rect, include_borders); | 336 &tiling_data_, content_rect, include_borders); |
292 iter; | 337 iter; |
293 ++iter) { | 338 ++iter) { |
| 339 if (update_invalidation_states) |
| 340 tile_invalidation_states_[iter.index()].Invalidate(frame_time); |
| 341 |
294 // There is no recycled twin since this is run on the pending tiling. | 342 // There is no recycled twin since this is run on the pending tiling. |
295 PictureLayerTiling* recycled_twin = NULL; | 343 PictureLayerTiling* recycled_twin = NULL; |
296 DCHECK_EQ(recycled_twin, client_->GetRecycledTwinTiling(this)); | 344 DCHECK_EQ(recycled_twin, client_->GetRecycledTwinTiling(this)); |
297 DCHECK_EQ(PENDING_TREE, client_->GetTree()); | 345 DCHECK_EQ(PENDING_TREE, client_->GetTree()); |
298 if (RemoveTileAt(iter.index_x(), iter.index_y(), recycled_twin)) | 346 if (RemoveTileAt(iter.index_x(), iter.index_y(), recycled_twin, false)) |
299 new_tile_keys.push_back(iter.index()); | 347 new_tile_keys.push_back(iter.index()); |
300 } | 348 } |
301 } | 349 } |
302 | 350 |
303 if (recreate_invalidated_tiles && !new_tile_keys.empty()) { | 351 if (recreate_invalidated_tiles && !new_tile_keys.empty()) { |
304 for (size_t i = 0; i < new_tile_keys.size(); ++i) { | 352 for (size_t i = 0; i < new_tile_keys.size(); ++i) { |
305 // Don't try to share a tile with the twin layer, it's been invalidated so | 353 // Don't try to share a tile with the twin layer, it's been invalidated so |
306 // we have to make our own tile here. | 354 // we have to make our own tile here. |
307 const PictureLayerTiling* twin_tiling = NULL; | 355 const PictureLayerTiling* twin_tiling = NULL; |
308 CreateTile(new_tile_keys[i].first, new_tile_keys[i].second, twin_tiling); | 356 CreateTile(new_tile_keys[i].first, new_tile_keys[i].second, twin_tiling); |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
455 | 503 |
456 return texture_rect; | 504 return texture_rect; |
457 } | 505 } |
458 | 506 |
459 gfx::Size PictureLayerTiling::CoverageIterator::texture_size() const { | 507 gfx::Size PictureLayerTiling::CoverageIterator::texture_size() const { |
460 return tiling_->tiling_data_.max_texture_size(); | 508 return tiling_->tiling_data_.max_texture_size(); |
461 } | 509 } |
462 | 510 |
463 bool PictureLayerTiling::RemoveTileAt(int i, | 511 bool PictureLayerTiling::RemoveTileAt(int i, |
464 int j, | 512 int j, |
465 PictureLayerTiling* recycled_twin) { | 513 PictureLayerTiling* recycled_twin, |
| 514 bool remove_invalidation_state) { |
466 TileMap::iterator found = tiles_.find(TileMapKey(i, j)); | 515 TileMap::iterator found = tiles_.find(TileMapKey(i, j)); |
467 if (found == tiles_.end()) | 516 if (found == tiles_.end()) |
468 return false; | 517 return false; |
469 found->second->set_shared(false); | 518 found->second->set_shared(false); |
470 tiles_.erase(found); | 519 tiles_.erase(found); |
471 eviction_tiles_cache_valid_ = false; | 520 eviction_tiles_cache_valid_ = false; |
| 521 |
| 522 if (remove_invalidation_state) { |
| 523 auto found = tile_invalidation_states_.find(TileMapKey(i, j)); |
| 524 if (found != tile_invalidation_states_.end()) |
| 525 tile_invalidation_states_.erase(found); |
| 526 } |
| 527 |
472 if (recycled_twin) { | 528 if (recycled_twin) { |
473 // Recycled twin does not also have a recycled twin, so pass NULL. | 529 // Recycled twin does not also have a recycled twin, so pass NULL. |
474 recycled_twin->RemoveTileAt(i, j, NULL); | 530 recycled_twin->RemoveTileAt(i, j, NULL, remove_invalidation_state); |
475 } | 531 } |
476 return true; | 532 return true; |
477 } | 533 } |
478 | 534 |
479 void PictureLayerTiling::Reset() { | 535 void PictureLayerTiling::Reset() { |
480 live_tiles_rect_ = gfx::Rect(); | 536 live_tiles_rect_ = gfx::Rect(); |
481 PictureLayerTiling* recycled_twin = client_->GetRecycledTwinTiling(this); | 537 PictureLayerTiling* recycled_twin = client_->GetRecycledTwinTiling(this); |
482 for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) { | 538 for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) { |
483 it->second->set_shared(false); | 539 it->second->set_shared(false); |
484 if (recycled_twin) | 540 if (recycled_twin) { |
485 recycled_twin->RemoveTileAt(it->first.first, it->first.second, NULL); | 541 recycled_twin->RemoveTileAt(it->first.first, it->first.second, NULL, |
| 542 true); |
| 543 } |
486 } | 544 } |
487 tiles_.clear(); | 545 tiles_.clear(); |
| 546 tile_invalidation_states_.clear(); |
488 eviction_tiles_cache_valid_ = false; | 547 eviction_tiles_cache_valid_ = false; |
489 } | 548 } |
490 | 549 |
491 gfx::Rect PictureLayerTiling::ComputeSkewport( | 550 gfx::Rect PictureLayerTiling::ComputeSkewport( |
492 double current_frame_time_in_seconds, | 551 double current_frame_time_in_seconds, |
493 const gfx::Rect& visible_rect_in_content_space) const { | 552 const gfx::Rect& visible_rect_in_content_space) const { |
494 gfx::Rect skewport = visible_rect_in_content_space; | 553 gfx::Rect skewport = visible_rect_in_content_space; |
495 if (last_impl_frame_time_in_seconds_ == 0.0) | 554 if (last_impl_frame_time_in_seconds_ == 0.0) |
496 return skewport; | 555 return skewport; |
497 | 556 |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
621 if (live_tiles_rect_ == new_live_tiles_rect) | 680 if (live_tiles_rect_ == new_live_tiles_rect) |
622 return; | 681 return; |
623 | 682 |
624 // Iterate to delete all tiles outside of our new live_tiles rect. | 683 // Iterate to delete all tiles outside of our new live_tiles rect. |
625 PictureLayerTiling* recycled_twin = client_->GetRecycledTwinTiling(this); | 684 PictureLayerTiling* recycled_twin = client_->GetRecycledTwinTiling(this); |
626 for (TilingData::DifferenceIterator iter(&tiling_data_, | 685 for (TilingData::DifferenceIterator iter(&tiling_data_, |
627 live_tiles_rect_, | 686 live_tiles_rect_, |
628 new_live_tiles_rect); | 687 new_live_tiles_rect); |
629 iter; | 688 iter; |
630 ++iter) { | 689 ++iter) { |
631 RemoveTileAt(iter.index_x(), iter.index_y(), recycled_twin); | 690 RemoveTileAt(iter.index_x(), iter.index_y(), recycled_twin, true); |
632 } | 691 } |
633 | 692 |
634 const PictureLayerTiling* twin_tiling = | 693 const PictureLayerTiling* twin_tiling = |
635 client_->GetPendingOrActiveTwinTiling(this); | 694 client_->GetPendingOrActiveTwinTiling(this); |
636 | 695 |
637 // Iterate to allocate new tiles for all regions with newly exposed area. | 696 // Iterate to allocate new tiles for all regions with newly exposed area. |
638 for (TilingData::DifferenceIterator iter(&tiling_data_, | 697 for (TilingData::DifferenceIterator iter(&tiling_data_, |
639 new_live_tiles_rect, | 698 new_live_tiles_rect, |
640 live_tiles_rect_); | 699 live_tiles_rect_); |
641 iter; | 700 iter; |
(...skipping 568 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1210 } | 1269 } |
1211 current_tile_ = tiling_->TileAt(next_index.first, next_index.second); | 1270 current_tile_ = tiling_->TileAt(next_index.first, next_index.second); |
1212 } | 1271 } |
1213 | 1272 |
1214 if (current_tile_) | 1273 if (current_tile_) |
1215 tiling_->UpdateTileAndTwinPriority(current_tile_); | 1274 tiling_->UpdateTileAndTwinPriority(current_tile_); |
1216 return *this; | 1275 return *this; |
1217 } | 1276 } |
1218 | 1277 |
1219 } // namespace cc | 1278 } // namespace cc |
OLD | NEW |