Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(521)

Side by Side Diff: cc/resources/picture_layer_tiling.cc

Issue 644313002: cc: Use reverse spiral iterator in tiling eviction. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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>
11 11
12 #include "base/debug/trace_event.h" 12 #include "base/debug/trace_event.h"
13 #include "base/debug/trace_event_argument.h" 13 #include "base/debug/trace_event_argument.h"
14 #include "base/logging.h" 14 #include "base/logging.h"
15 #include "cc/base/math_util.h" 15 #include "cc/base/math_util.h"
16 #include "cc/resources/tile.h" 16 #include "cc/resources/tile.h"
17 #include "cc/resources/tile_priority.h" 17 #include "cc/resources/tile_priority.h"
18 #include "ui/gfx/point_conversions.h" 18 #include "ui/gfx/point_conversions.h"
19 #include "ui/gfx/rect_conversions.h" 19 #include "ui/gfx/rect_conversions.h"
20 #include "ui/gfx/safe_integer_conversions.h" 20 #include "ui/gfx/safe_integer_conversions.h"
21 #include "ui/gfx/size_conversions.h" 21 #include "ui/gfx/size_conversions.h"
22 22
23 namespace cc { 23 namespace cc {
24 namespace { 24 namespace {
25 25
26 const float kSoonBorderDistanceInScreenPixels = 312.f; 26 const float kSoonBorderDistanceInScreenPixels = 312.f;
27 27
28 class TileEvictionOrder {
29 public:
30 explicit TileEvictionOrder(TreePriority tree_priority)
31 : tree_priority_(tree_priority) {}
32 ~TileEvictionOrder() {}
33
34 bool operator()(const Tile* a, const Tile* b) {
35 const TilePriority& a_priority =
36 a->priority_for_tree_priority(tree_priority_);
37 const TilePriority& b_priority =
38 b->priority_for_tree_priority(tree_priority_);
39
40 DCHECK(a_priority.priority_bin == b_priority.priority_bin);
41 DCHECK(a->required_for_activation() == b->required_for_activation());
42
43 // Or if a is occluded and b is unoccluded.
44 bool a_is_occluded = a->is_occluded_for_tree_priority(tree_priority_);
45 bool b_is_occluded = b->is_occluded_for_tree_priority(tree_priority_);
46 if (a_is_occluded != b_is_occluded)
47 return a_is_occluded;
48
49 // Or if a is farther away from visible.
50 return a_priority.distance_to_visible > b_priority.distance_to_visible;
51 }
52
53 private:
54 TreePriority tree_priority_;
55 };
56
57 } // namespace 28 } // namespace
58 29
59 scoped_ptr<PictureLayerTiling> PictureLayerTiling::Create( 30 scoped_ptr<PictureLayerTiling> PictureLayerTiling::Create(
60 float contents_scale, 31 float contents_scale,
61 const gfx::Size& layer_bounds, 32 const gfx::Size& layer_bounds,
62 PictureLayerTilingClient* client) { 33 PictureLayerTilingClient* client) {
63 return make_scoped_ptr(new PictureLayerTiling(contents_scale, 34 return make_scoped_ptr(new PictureLayerTiling(contents_scale,
64 layer_bounds, 35 layer_bounds,
65 client)); 36 client));
66 } 37 }
67 38
68 PictureLayerTiling::PictureLayerTiling(float contents_scale, 39 PictureLayerTiling::PictureLayerTiling(float contents_scale,
69 const gfx::Size& layer_bounds, 40 const gfx::Size& layer_bounds,
70 PictureLayerTilingClient* client) 41 PictureLayerTilingClient* client)
71 : contents_scale_(contents_scale), 42 : contents_scale_(contents_scale),
72 layer_bounds_(layer_bounds), 43 layer_bounds_(layer_bounds),
73 resolution_(NON_IDEAL_RESOLUTION), 44 resolution_(NON_IDEAL_RESOLUTION),
74 client_(client), 45 client_(client),
75 tiling_data_(gfx::Size(), gfx::Size(), kBorderTexels), 46 tiling_data_(gfx::Size(), gfx::Size(), kBorderTexels),
76 last_impl_frame_time_in_seconds_(0.0), 47 last_impl_frame_time_in_seconds_(0.0),
77 content_to_screen_scale_(0.f), 48 content_to_screen_scale_(0.f),
78 can_require_tiles_for_activation_(false), 49 can_require_tiles_for_activation_(false),
79 has_visible_rect_tiles_(false), 50 has_visible_rect_tiles_(false),
80 has_skewport_rect_tiles_(false), 51 has_skewport_rect_tiles_(false),
81 has_soon_border_rect_tiles_(false), 52 has_soon_border_rect_tiles_(false),
82 has_eventually_rect_tiles_(false), 53 has_eventually_rect_tiles_(false) {
83 eviction_tiles_cache_valid_(false),
84 eviction_cache_tree_priority_(SAME_PRIORITY_FOR_BOTH_TREES) {
85 gfx::Size content_bounds = 54 gfx::Size content_bounds =
86 gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds, contents_scale)); 55 gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds, contents_scale));
87 gfx::Size tile_size = client_->CalculateTileSize(content_bounds); 56 gfx::Size tile_size = client_->CalculateTileSize(content_bounds);
88 if (tile_size.IsEmpty()) { 57 if (tile_size.IsEmpty()) {
89 layer_bounds_ = gfx::Size(); 58 layer_bounds_ = gfx::Size();
90 content_bounds = gfx::Size(); 59 content_bounds = gfx::Size();
91 } 60 }
92 61
93 DCHECK(!gfx::ToFlooredSize( 62 DCHECK(!gfx::ToFlooredSize(
94 gfx::ScaleSize(layer_bounds, contents_scale)).IsEmpty()) << 63 gfx::ScaleSize(layer_bounds, contents_scale)).IsEmpty()) <<
(...skipping 486 matching lines...) Expand 10 before | Expand all | Expand 10 after
581 gfx::Rect soon_border_rect = visible_rect_in_content_space; 550 gfx::Rect soon_border_rect = visible_rect_in_content_space;
582 float border = kSoonBorderDistanceInScreenPixels / content_to_screen_scale_; 551 float border = kSoonBorderDistanceInScreenPixels / content_to_screen_scale_;
583 soon_border_rect.Inset(-border, -border, -border, -border); 552 soon_border_rect.Inset(-border, -border, -border, -border);
584 553
585 // Update the tiling state. 554 // Update the tiling state.
586 SetLiveTilesRect(eventually_rect); 555 SetLiveTilesRect(eventually_rect);
587 556
588 last_impl_frame_time_in_seconds_ = current_frame_time_in_seconds; 557 last_impl_frame_time_in_seconds_ = current_frame_time_in_seconds;
589 last_visible_rect_in_content_space_ = visible_rect_in_content_space; 558 last_visible_rect_in_content_space_ = visible_rect_in_content_space;
590 559
591 eviction_tiles_cache_valid_ = false;
592
593 current_visible_rect_ = visible_rect_in_content_space; 560 current_visible_rect_ = visible_rect_in_content_space;
594 current_skewport_rect_ = skewport; 561 current_skewport_rect_ = skewport;
595 current_soon_border_rect_ = soon_border_rect; 562 current_soon_border_rect_ = soon_border_rect;
596 current_eventually_rect_ = eventually_rect; 563 current_eventually_rect_ = eventually_rect;
597 current_occlusion_in_layer_space_ = occlusion_in_layer_space; 564 current_occlusion_in_layer_space_ = occlusion_in_layer_space;
598 565
599 // Update has_*_tiles state. 566 // Update has_*_tiles state.
600 gfx::Rect tiling_rect(tiling_size()); 567 gfx::Rect tiling_rect(tiling_size());
601 568
602 has_visible_rect_tiles_ = tiling_rect.Intersects(current_visible_rect_); 569 has_visible_rect_tiles_ = tiling_rect.Intersects(current_visible_rect_);
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after
957 if (delta < event.distance) 924 if (delta < event.distance)
958 break; 925 break;
959 } 926 }
960 927
961 gfx::Rect result(origin_x, origin_y, width, height); 928 gfx::Rect result(origin_x, origin_y, width, height);
962 if (cache) 929 if (cache)
963 cache->previous_result = result; 930 cache->previous_result = result;
964 return result; 931 return result;
965 } 932 }
966 933
967 void PictureLayerTiling::UpdateEvictionCacheIfNeeded(
968 TreePriority tree_priority) {
969 if (eviction_tiles_cache_valid_ &&
970 eviction_cache_tree_priority_ == tree_priority)
971 return;
972
973 eviction_tiles_now_.clear();
974 eviction_tiles_now_and_required_for_activation_.clear();
975 eviction_tiles_soon_.clear();
976 eviction_tiles_soon_and_required_for_activation_.clear();
977 eviction_tiles_eventually_.clear();
978 eviction_tiles_eventually_and_required_for_activation_.clear();
979
980 for (TileMap::iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
981 Tile* tile = it->second.get();
982 UpdateTileAndTwinPriority(tile);
983 const TilePriority& priority =
984 tile->priority_for_tree_priority(tree_priority);
985 switch (priority.priority_bin) {
986 case TilePriority::EVENTUALLY:
987 if (tile->required_for_activation())
988 eviction_tiles_eventually_and_required_for_activation_.push_back(
989 tile);
990 else
991 eviction_tiles_eventually_.push_back(tile);
992 break;
993 case TilePriority::SOON:
994 if (tile->required_for_activation())
995 eviction_tiles_soon_and_required_for_activation_.push_back(tile);
996 else
997 eviction_tiles_soon_.push_back(tile);
998 break;
999 case TilePriority::NOW:
1000 if (tile->required_for_activation())
1001 eviction_tiles_now_and_required_for_activation_.push_back(tile);
1002 else
1003 eviction_tiles_now_.push_back(tile);
1004 break;
1005 }
1006 }
1007
1008 // TODO(vmpstr): Do this lazily. One option is to have a "sorted" flag that
1009 // can be updated for each of the queues.
1010 TileEvictionOrder sort_order(tree_priority);
1011 std::sort(eviction_tiles_now_.begin(), eviction_tiles_now_.end(), sort_order);
1012 std::sort(eviction_tiles_now_and_required_for_activation_.begin(),
1013 eviction_tiles_now_and_required_for_activation_.end(),
1014 sort_order);
1015 std::sort(
1016 eviction_tiles_soon_.begin(), eviction_tiles_soon_.end(), sort_order);
1017 std::sort(eviction_tiles_soon_and_required_for_activation_.begin(),
1018 eviction_tiles_soon_and_required_for_activation_.end(),
1019 sort_order);
1020 std::sort(eviction_tiles_eventually_.begin(),
1021 eviction_tiles_eventually_.end(),
1022 sort_order);
1023 std::sort(eviction_tiles_eventually_and_required_for_activation_.begin(),
1024 eviction_tiles_eventually_and_required_for_activation_.end(),
1025 sort_order);
1026
1027 eviction_tiles_cache_valid_ = true;
1028 eviction_cache_tree_priority_ = tree_priority;
1029 }
1030
1031 const std::vector<Tile*>* PictureLayerTiling::GetEvictionTiles(
1032 TreePriority tree_priority,
1033 EvictionCategory category) {
1034 UpdateEvictionCacheIfNeeded(tree_priority);
1035 switch (category) {
1036 case EVENTUALLY:
1037 return &eviction_tiles_eventually_;
1038 case EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION:
1039 return &eviction_tiles_eventually_and_required_for_activation_;
1040 case SOON:
1041 return &eviction_tiles_soon_;
1042 case SOON_AND_REQUIRED_FOR_ACTIVATION:
1043 return &eviction_tiles_soon_and_required_for_activation_;
1044 case NOW:
1045 return &eviction_tiles_now_;
1046 case NOW_AND_REQUIRED_FOR_ACTIVATION:
1047 return &eviction_tiles_now_and_required_for_activation_;
1048 }
1049 NOTREACHED();
1050 return &eviction_tiles_eventually_;
1051 }
1052
1053 PictureLayerTiling::TilingRasterTileIterator::TilingRasterTileIterator() 934 PictureLayerTiling::TilingRasterTileIterator::TilingRasterTileIterator()
1054 : tiling_(NULL), current_tile_(NULL) {} 935 : tiling_(NULL), current_tile_(NULL) {}
1055 936
1056 PictureLayerTiling::TilingRasterTileIterator::TilingRasterTileIterator( 937 PictureLayerTiling::TilingRasterTileIterator::TilingRasterTileIterator(
1057 PictureLayerTiling* tiling) 938 PictureLayerTiling* tiling)
1058 : tiling_(tiling), phase_(VISIBLE_RECT), current_tile_(NULL) { 939 : tiling_(tiling), phase_(VISIBLE_RECT), current_tile_(NULL) {
1059 if (!tiling_->has_visible_rect_tiles_) { 940 if (!tiling_->has_visible_rect_tiles_) {
1060 AdvancePhase(); 941 AdvancePhase();
1061 return; 942 return;
1062 } 943 }
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
1176 } 1057 }
1177 current_tile_ = tiling_->TileAt(next_index.first, next_index.second); 1058 current_tile_ = tiling_->TileAt(next_index.first, next_index.second);
1178 } 1059 }
1179 1060
1180 if (current_tile_) 1061 if (current_tile_)
1181 tiling_->UpdateTileAndTwinPriority(current_tile_); 1062 tiling_->UpdateTileAndTwinPriority(current_tile_);
1182 return *this; 1063 return *this;
1183 } 1064 }
1184 1065
1185 PictureLayerTiling::TilingEvictionTileIterator::TilingEvictionTileIterator() 1066 PictureLayerTiling::TilingEvictionTileIterator::TilingEvictionTileIterator()
1186 : eviction_tiles_(NULL), current_eviction_tiles_index_(0u) { 1067 : tiling_(nullptr),
1068 eviction_category_(EVENTUALLY),
1069 processing_occluded_now_tiles_(false),
1070 processing_soon_border_rect_(false),
1071 unoccluded_now_tiles_index_(0u),
1072 current_tile_(nullptr) {
1187 } 1073 }
1188 1074
1189 PictureLayerTiling::TilingEvictionTileIterator::TilingEvictionTileIterator( 1075 PictureLayerTiling::TilingEvictionTileIterator::TilingEvictionTileIterator(
1190 PictureLayerTiling* tiling, 1076 PictureLayerTiling* tiling,
1191 TreePriority tree_priority, 1077 TreePriority tree_priority,
ajuma 2014/10/23 23:12:54 Unused now, remove?
vmpstr 2014/10/29 18:19:41 Done.
1192 EvictionCategory category) 1078 EvictionCategory category)
1193 : eviction_tiles_(tiling->GetEvictionTiles(tree_priority, category)), 1079 : tiling_(tiling),
1194 // Note: initializing to "0 - 1" works as overflow is well defined for 1080 eviction_category_(category),
1195 // unsigned integers. 1081 processing_occluded_now_tiles_(true),
1196 current_eviction_tiles_index_(static_cast<size_t>(0) - 1) { 1082 processing_soon_border_rect_(true),
1197 DCHECK(eviction_tiles_); 1083 unoccluded_now_tiles_index_(0),
1198 ++(*this); 1084 current_tile_(nullptr) {
1085 switch (category) {
1086 case EVENTUALLY:
1087 if (tiling_->has_eventually_rect_tiles_) {
1088 spiral_iterator_ = TilingData::ReverseSpiralDifferenceIterator(
1089 &tiling_->tiling_data_,
1090 tiling_->current_eventually_rect_,
1091 tiling_->current_skewport_rect_,
1092 tiling_->current_soon_border_rect_);
1093 }
1094 AdvanceEventually(true);
1095 break;
1096 case EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION:
1097 break;
1098 case SOON:
1099 if (tiling_->has_soon_border_rect_tiles_) {
1100 spiral_iterator_ = TilingData::ReverseSpiralDifferenceIterator(
1101 &tiling_->tiling_data_,
1102 tiling_->current_soon_border_rect_,
1103 tiling_->current_skewport_rect_,
1104 tiling_->current_visible_rect_);
1105 }
1106 AdvanceSoon(true);
1107 break;
1108 case SOON_AND_REQUIRED_FOR_ACTIVATION:
ajuma 2014/10/23 23:12:54 Why is there nothing to do for this case? Is it be
vmpstr 2014/10/29 18:19:41 Yes and removed.
1109 break;
1110 case NOW:
1111 case NOW_AND_REQUIRED_FOR_ACTIVATION:
1112 if (tiling_->has_visible_rect_tiles_) {
1113 visible_iterator_ = TilingData::Iterator(&tiling_->tiling_data_,
1114 tiling_->current_visible_rect_,
1115 false /* include_borders */);
1116 }
1117 AdvanceNow(true);
1118 break;
1119 }
1120 }
1121
1122 void PictureLayerTiling::TilingEvictionTileIterator::AdvanceEventually(
1123 bool first_run) {
1124 if (!first_run)
1125 ++spiral_iterator_;
1126
1127 current_tile_ = nullptr;
1128 while (spiral_iterator_) {
1129 std::pair<int, int> next_index = spiral_iterator_.index();
1130 Tile* tile = tiling_->TileAt(next_index.first, next_index.second);
1131 if (tile && tile->HasResources()) {
1132 current_tile_ = tile;
1133 break;
1134 }
1135 ++spiral_iterator_;
1136 }
1137
1138 if (current_tile_)
1139 tiling_->UpdateTileAndTwinPriority(current_tile_);
1140 }
1141
1142 void PictureLayerTiling::TilingEvictionTileIterator::AdvanceSoon(
1143 bool first_run) {
1144 if (!first_run)
1145 ++spiral_iterator_;
1146
1147 current_tile_ = nullptr;
1148 while (spiral_iterator_) {
1149 std::pair<int, int> next_index = spiral_iterator_.index();
1150 Tile* tile = tiling_->TileAt(next_index.first, next_index.second);
1151 if (tile && tile->HasResources()) {
1152 current_tile_ = tile;
1153 break;
1154 }
1155 ++spiral_iterator_;
1156 if (!spiral_iterator_ && processing_soon_border_rect_) {
1157 if (tiling_->has_skewport_rect_tiles_) {
1158 spiral_iterator_ = TilingData::ReverseSpiralDifferenceIterator(
1159 &tiling_->tiling_data_,
1160 tiling_->current_skewport_rect_,
1161 tiling_->current_visible_rect_,
1162 tiling_->current_visible_rect_);
1163 }
1164 processing_soon_border_rect_ = false;
1165 }
1166 }
1167
1168 if (current_tile_)
1169 tiling_->UpdateTileAndTwinPriority(current_tile_);
1170 }
1171
1172 void PictureLayerTiling::TilingEvictionTileIterator::AdvanceNow(
1173 bool first_run) {
1174 if (!first_run) {
1175 if (processing_occluded_now_tiles_)
1176 ++visible_iterator_;
1177 else
1178 ++unoccluded_now_tiles_index_;
1179 }
1180
1181 current_tile_ = nullptr;
1182 while (visible_iterator_ ||
1183 (unoccluded_now_tiles_index_ < unoccluded_now_tiles_.size())) {
1184 if (!visible_iterator_)
1185 processing_occluded_now_tiles_ = false;
1186
1187 if (processing_occluded_now_tiles_) {
1188 std::pair<int, int> next_index = visible_iterator_.index();
1189 Tile* tile = tiling_->TileAt(next_index.first, next_index.second);
1190 if (!tile || !tile->HasResources()) {
1191 ++visible_iterator_;
1192 continue;
1193 }
1194
1195 bool tile_required_for_activation = false;
1196 if (tiling_->client_->GetTree() == PENDING_TREE)
1197 tile_required_for_activation =
1198 tiling_->IsTileRequiredForActivation(tile);
1199 if ((eviction_category_ == NOW_AND_REQUIRED_FOR_ACTIVATION &&
1200 !tile_required_for_activation) ||
1201 (eviction_category_ == NOW && tile_required_for_activation)) {
1202 ++visible_iterator_;
1203 continue;
1204 }
1205
1206 if (!tiling_->IsTileOccluded(tile)) {
1207 unoccluded_now_tiles_.push_back(tile);
1208 ++visible_iterator_;
1209 continue;
1210 }
1211
1212 current_tile_ = tile;
1213 break;
1214 }
1215
1216 Tile* tile = unoccluded_now_tiles_[unoccluded_now_tiles_index_];
1217 DCHECK(tile);
1218 if (!tile->HasResources()) {
1219 ++unoccluded_now_tiles_index_;
1220 continue;
1221 }
ajuma 2014/10/23 23:12:54 Why don't we need to consider whether the tile is
vmpstr 2014/10/29 18:19:41 Tile that is occluded is not required for activati
1222
1223 current_tile_ = tile;
1224 break;
1225 }
1226
1227 if (current_tile_)
1228 tiling_->UpdateTileAndTwinPriority(current_tile_);
1199 } 1229 }
1200 1230
1201 PictureLayerTiling::TilingEvictionTileIterator::~TilingEvictionTileIterator() { 1231 PictureLayerTiling::TilingEvictionTileIterator::~TilingEvictionTileIterator() {
1202 } 1232 }
1203 1233
1204 PictureLayerTiling::TilingEvictionTileIterator::operator bool() const { 1234 PictureLayerTiling::TilingEvictionTileIterator::operator bool() const {
1205 return eviction_tiles_ && 1235 return !!current_tile_;
1206 current_eviction_tiles_index_ != eviction_tiles_->size();
1207 } 1236 }
1208 1237
1209 Tile* PictureLayerTiling::TilingEvictionTileIterator::operator*() { 1238 Tile* PictureLayerTiling::TilingEvictionTileIterator::operator*() {
1210 DCHECK(*this); 1239 DCHECK(*this);
1211 return (*eviction_tiles_)[current_eviction_tiles_index_]; 1240 return current_tile_;
1212 } 1241 }
1213 1242
1214 const Tile* PictureLayerTiling::TilingEvictionTileIterator::operator*() const { 1243 const Tile* PictureLayerTiling::TilingEvictionTileIterator::operator*() const {
1215 DCHECK(*this); 1244 DCHECK(*this);
1216 return (*eviction_tiles_)[current_eviction_tiles_index_]; 1245 return current_tile_;
1217 } 1246 }
1218 1247
1219 PictureLayerTiling::TilingEvictionTileIterator& 1248 PictureLayerTiling::TilingEvictionTileIterator&
1220 PictureLayerTiling::TilingEvictionTileIterator:: 1249 PictureLayerTiling::TilingEvictionTileIterator::
1221 operator++() { 1250 operator++() {
1222 DCHECK(*this); 1251 switch (eviction_category_) {
1223 do { 1252 case EVENTUALLY:
1224 ++current_eviction_tiles_index_; 1253 AdvanceEventually(false);
1225 } while (current_eviction_tiles_index_ != eviction_tiles_->size() && 1254 break;
1226 !(*eviction_tiles_)[current_eviction_tiles_index_]->HasResources()); 1255 case EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION:
1227 1256 break;
1257 case SOON:
1258 AdvanceSoon(false);
1259 break;
1260 case SOON_AND_REQUIRED_FOR_ACTIVATION:
1261 break;
1262 case NOW:
1263 case NOW_AND_REQUIRED_FOR_ACTIVATION:
1264 AdvanceNow(false);
1265 break;
1266 }
1228 return *this; 1267 return *this;
1229 } 1268 }
1230 1269
1231 } // namespace cc 1270 } // namespace cc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698