| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 <utility> | 5 #include <utility> |
| 6 | 6 |
| 7 #include "cc/resources/tiling_set_eviction_queue.h" | 7 #include "cc/resources/tiling_set_eviction_queue.h" |
| 8 | 8 |
| 9 namespace cc { | 9 namespace cc { |
| 10 namespace { |
| 11 |
| 12 bool IsSharedOutOfOrderTile(WhichTree tree, const Tile* tile) { |
| 13 if (!tile->is_shared()) |
| 14 return false; |
| 15 |
| 16 // The priority for tile priority of a shared tile will be a combined |
| 17 // priority thus return shared tiles from a higher priority tree as |
| 18 // it is out of order for a lower priority tree. |
| 19 WhichTree twin_tree = tree == ACTIVE_TREE ? PENDING_TREE : ACTIVE_TREE; |
| 20 const TilePriority& priority = tile->priority(tree); |
| 21 const TilePriority& twin_priority = tile->priority(twin_tree); |
| 22 if (priority.priority_bin != twin_priority.priority_bin) |
| 23 return priority.priority_bin > twin_priority.priority_bin; |
| 24 const bool occluded = tile->is_occluded(tree); |
| 25 const bool twin_occluded = tile->is_occluded(twin_tree); |
| 26 if (occluded != twin_occluded) |
| 27 return occluded; |
| 28 if (priority.distance_to_visible != twin_priority.distance_to_visible) |
| 29 return priority.distance_to_visible > twin_priority.distance_to_visible; |
| 30 |
| 31 // If priorities are the same, it does not matter which tree returns |
| 32 // the tile. Let's pick the pending tree. |
| 33 return tree != PENDING_TREE; |
| 34 } |
| 35 |
| 36 } // namespace |
| 10 | 37 |
| 11 TilingSetEvictionQueue::TilingSetEvictionQueue( | 38 TilingSetEvictionQueue::TilingSetEvictionQueue( |
| 12 PictureLayerTilingSet* tiling_set, | 39 PictureLayerTilingSet* tiling_set, |
| 13 TreePriority tree_priority, | |
| 14 bool skip_shared_out_of_order_tiles) | 40 bool skip_shared_out_of_order_tiles) |
| 15 : tiling_set_(tiling_set), | 41 : tiling_set_(tiling_set), |
| 16 tree_(tiling_set->client()->GetTree()), | 42 tree_(tiling_set->client()->GetTree()), |
| 17 tree_priority_(tree_priority), | |
| 18 skip_all_shared_tiles_( | |
| 19 skip_shared_out_of_order_tiles && | |
| 20 tree_priority == (tree_ == ACTIVE_TREE ? NEW_CONTENT_TAKES_PRIORITY | |
| 21 : SMOOTHNESS_TAKES_PRIORITY)), | |
| 22 skip_shared_out_of_order_tiles_(skip_shared_out_of_order_tiles), | 43 skip_shared_out_of_order_tiles_(skip_shared_out_of_order_tiles), |
| 23 processing_soon_border_rect_(false), | 44 processing_soon_border_rect_(false), |
| 24 processing_tiling_with_required_for_activation_tiles_(false), | 45 processing_required_for_activation_tiles_(false), |
| 25 tiling_index_with_required_for_activation_tiles_(0u), | |
| 26 current_priority_bin_(TilePriority::EVENTUALLY), | 46 current_priority_bin_(TilePriority::EVENTUALLY), |
| 27 current_tiling_index_(0u), | 47 current_tiling_index_(0u), |
| 28 current_tiling_range_type_(PictureLayerTilingSet::HIGHER_THAN_HIGH_RES), | 48 current_tiling_range_type_(PictureLayerTilingSet::HIGHER_THAN_HIGH_RES), |
| 29 current_eviction_tile_(nullptr) { | 49 current_eviction_tile_(nullptr) { |
| 30 // Early out if the layer has no tilings. | 50 // Early out if the layer has no tilings. |
| 31 if (!tiling_set_->num_tilings()) | 51 if (!tiling_set_->num_tilings()) |
| 32 return; | 52 return; |
| 33 | 53 |
| 34 tiling_index_with_required_for_activation_tiles_ = | |
| 35 TilingIndexWithRequiredForActivationTiles(); | |
| 36 | |
| 37 current_tiling_index_ = CurrentTilingRange().start - 1u; | 54 current_tiling_index_ = CurrentTilingRange().start - 1u; |
| 38 AdvanceToNextValidTiling(); | 55 AdvanceToNextValidTiling(); |
| 39 } | 56 } |
| 40 | 57 |
| 41 TilingSetEvictionQueue::~TilingSetEvictionQueue() { | 58 TilingSetEvictionQueue::~TilingSetEvictionQueue() { |
| 42 } | 59 } |
| 43 | 60 |
| 44 bool TilingSetEvictionQueue::IsEmpty() const { | 61 bool TilingSetEvictionQueue::IsEmpty() const { |
| 45 return !current_eviction_tile_; | 62 return !current_eviction_tile_; |
| 46 } | 63 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 60 const Tile* TilingSetEvictionQueue::Top() const { | 77 const Tile* TilingSetEvictionQueue::Top() const { |
| 61 DCHECK(!IsEmpty()); | 78 DCHECK(!IsEmpty()); |
| 62 return current_eviction_tile_; | 79 return current_eviction_tile_; |
| 63 } | 80 } |
| 64 | 81 |
| 65 bool TilingSetEvictionQueue::AdvanceToNextEvictionTile() { | 82 bool TilingSetEvictionQueue::AdvanceToNextEvictionTile() { |
| 66 // Advance to the next eviction tile within the current priority bin and | 83 // Advance to the next eviction tile within the current priority bin and |
| 67 // tiling. This is done while advancing to a new tiling and while popping | 84 // tiling. This is done while advancing to a new tiling and while popping |
| 68 // the current tile. | 85 // the current tile. |
| 69 | 86 |
| 70 bool required_for_activation = | 87 bool required_for_activation = processing_required_for_activation_tiles_; |
| 71 processing_tiling_with_required_for_activation_tiles_; | |
| 72 | 88 |
| 73 for (;;) { | 89 for (;;) { |
| 74 while (spiral_iterator_) { | 90 while (spiral_iterator_) { |
| 75 std::pair<int, int> next_index = spiral_iterator_.index(); | 91 std::pair<int, int> next_index = spiral_iterator_.index(); |
| 76 Tile* tile = current_tiling_->TileAt(next_index.first, next_index.second); | 92 Tile* tile = current_tiling_->TileAt(next_index.first, next_index.second); |
| 77 ++spiral_iterator_; | 93 ++spiral_iterator_; |
| 78 if (!tile || !tile->HasResource()) | 94 if (!tile || !tile->HasResource()) |
| 79 continue; | 95 continue; |
| 80 if (skip_all_shared_tiles_ && tile->is_shared()) | 96 current_tiling_->UpdateTileAndTwinPriority(tile); |
| 97 if (skip_shared_out_of_order_tiles_ && |
| 98 IsSharedOutOfOrderTile(tree_, tile)) |
| 81 continue; | 99 continue; |
| 82 current_tiling_->UpdateTileAndTwinPriority(tile); | 100 DCHECK_EQ(tile->required_for_activation(), required_for_activation); |
| 83 if (skip_shared_out_of_order_tiles_ && IsSharedOutOfOrderTile(tile)) | |
| 84 continue; | |
| 85 if (tile->required_for_activation() != required_for_activation) | |
| 86 continue; | |
| 87 current_eviction_tile_ = tile; | 101 current_eviction_tile_ = tile; |
| 88 return true; | 102 return true; |
| 89 } | 103 } |
| 90 if (processing_soon_border_rect_) { | 104 if (processing_soon_border_rect_) { |
| 91 // Advance from soon border rect to skewport rect. | 105 // Advance from soon border rect to skewport rect. |
| 92 processing_soon_border_rect_ = false; | 106 processing_soon_border_rect_ = false; |
| 93 if (current_tiling_->has_skewport_rect_tiles_) { | 107 if (current_tiling_->has_skewport_rect_tiles_) { |
| 94 spiral_iterator_ = TilingData::ReverseSpiralDifferenceIterator( | 108 spiral_iterator_ = TilingData::ReverseSpiralDifferenceIterator( |
| 95 ¤t_tiling_->tiling_data_, | 109 ¤t_tiling_->tiling_data_, |
| 96 current_tiling_->current_skewport_rect_, | 110 current_tiling_->current_skewport_rect_, |
| 97 current_tiling_->current_visible_rect_, | 111 current_tiling_->current_visible_rect_, |
| 98 current_tiling_->current_visible_rect_); | 112 current_tiling_->current_visible_rect_); |
| 99 continue; | 113 continue; |
| 100 } | 114 } |
| 101 } | 115 } |
| 102 break; | 116 break; |
| 103 } | 117 } |
| 104 | 118 |
| 105 TilePriority::PriorityBin max_tile_priority_bin = | 119 TilePriority::PriorityBin max_tile_priority_bin = |
| 106 current_tiling_->client_->GetMaxTilePriorityBin(); | 120 current_tiling_->client_->GetMaxTilePriorityBin(); |
| 107 while (visible_iterator_) { | 121 while (visible_iterator_) { |
| 108 std::pair<int, int> next_index = visible_iterator_.index(); | 122 std::pair<int, int> next_index = visible_iterator_.index(); |
| 109 Tile* tile = current_tiling_->TileAt(next_index.first, next_index.second); | 123 Tile* tile = current_tiling_->TileAt(next_index.first, next_index.second); |
| 110 ++visible_iterator_; | 124 ++visible_iterator_; |
| 111 if (!tile || !tile->HasResource()) | 125 if (!tile || !tile->HasResource()) |
| 112 continue; | 126 continue; |
| 113 if (skip_all_shared_tiles_ && tile->is_shared()) | |
| 114 continue; | |
| 115 // If the max tile priority is not NOW, updated priorities for tiles | 127 // If the max tile priority is not NOW, updated priorities for tiles |
| 116 // returned by the visible iterator will not have NOW (but EVENTUALLY) | 128 // returned by the visible iterator will not have NOW (but EVENTUALLY) |
| 117 // priority bin and cannot therefore be required for activation tiles nor | 129 // priority bin and cannot therefore be required for activation tiles nor |
| 118 // occluded NOW tiles in the current tiling. | 130 // occluded NOW tiles in the current tiling. |
| 119 if (max_tile_priority_bin <= TilePriority::NOW) { | 131 if (max_tile_priority_bin <= TilePriority::NOW) { |
| 120 // If the current tiling is a pending tree tiling, required for | 132 // If the current tiling is a pending tree tiling, required for |
| 121 // activation tiles can be detected without updating tile priorities. | 133 // activation tiles can be detected without updating tile priorities. |
| 122 if (tree_ == PENDING_TREE && | 134 if (tree_ == PENDING_TREE && |
| 123 current_tiling_->IsTileRequiredForActivationIfVisible(tile) != | 135 current_tiling_->IsTileRequiredForActivationIfVisible(tile) != |
| 124 required_for_activation) { | 136 required_for_activation) { |
| 125 continue; | 137 continue; |
| 126 } | 138 } |
| 127 // Unoccluded NOW tiles should be evicted (and thus returned) only after | 139 // Unoccluded NOW tiles should be evicted (and thus returned) only after |
| 128 // all occluded NOW tiles. | 140 // all occluded NOW tiles. |
| 129 if (!current_tiling_->IsTileOccluded(tile)) { | 141 if (!current_tiling_->IsTileOccluded(tile)) { |
| 130 unoccluded_now_tiles_.push_back(tile); | 142 unoccluded_now_tiles_.push_back(tile); |
| 131 continue; | 143 continue; |
| 132 } | 144 } |
| 133 } | 145 } |
| 134 current_tiling_->UpdateTileAndTwinPriority(tile); | 146 current_tiling_->UpdateTileAndTwinPriority(tile); |
| 135 if (skip_shared_out_of_order_tiles_ && IsSharedOutOfOrderTile(tile)) | 147 if (skip_shared_out_of_order_tiles_ && IsSharedOutOfOrderTile(tree_, tile)) |
| 136 continue; | 148 continue; |
| 137 if (tile->required_for_activation() != required_for_activation) | 149 if (tile->required_for_activation() != required_for_activation) |
| 138 continue; | 150 continue; |
| 139 current_eviction_tile_ = tile; | 151 current_eviction_tile_ = tile; |
| 140 return true; | 152 return true; |
| 141 } | 153 } |
| 142 | 154 |
| 143 while (!unoccluded_now_tiles_.empty()) { | 155 while (!unoccluded_now_tiles_.empty()) { |
| 144 // All (unoccluded) NOW tiles have the same priority bin (NOW) and the same | 156 // All (unoccluded) NOW tiles have the same priority bin (NOW) and the same |
| 145 // distance to visible (0.0), so it does not matter that tiles are popped | 157 // distance to visible (0.0), so it does not matter that tiles are popped |
| 146 // in reversed (FILO) order. | 158 // in reversed (FILO) order. |
| 147 Tile* tile = unoccluded_now_tiles_.back(); | 159 Tile* tile = unoccluded_now_tiles_.back(); |
| 148 unoccluded_now_tiles_.pop_back(); | 160 unoccluded_now_tiles_.pop_back(); |
| 149 DCHECK(tile); | 161 DCHECK(tile); |
| 150 if (!tile->HasResource()) | 162 if (!tile->HasResource()) |
| 151 continue; | 163 continue; |
| 152 current_tiling_->UpdateTileAndTwinPriority(tile); | 164 current_tiling_->UpdateTileAndTwinPriority(tile); |
| 153 if (skip_shared_out_of_order_tiles_ && IsSharedOutOfOrderTile(tile)) | 165 if (skip_shared_out_of_order_tiles_ && IsSharedOutOfOrderTile(tree_, tile)) |
| 154 continue; | 166 continue; |
| 155 if (tile->required_for_activation() != required_for_activation) | 167 if (tile->required_for_activation() != required_for_activation) |
| 156 continue; | 168 continue; |
| 157 current_eviction_tile_ = tile; | 169 current_eviction_tile_ = tile; |
| 158 return true; | 170 return true; |
| 159 } | 171 } |
| 160 | 172 |
| 161 current_eviction_tile_ = nullptr; | 173 current_eviction_tile_ = nullptr; |
| 162 return false; | 174 return false; |
| 163 } | 175 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 199 PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES; | 211 PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES; |
| 200 return true; | 212 return true; |
| 201 case PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES: | 213 case PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES: |
| 202 current_tiling_range_type_ = PictureLayerTilingSet::LOW_RES; | 214 current_tiling_range_type_ = PictureLayerTilingSet::LOW_RES; |
| 203 return true; | 215 return true; |
| 204 case PictureLayerTilingSet::LOW_RES: | 216 case PictureLayerTilingSet::LOW_RES: |
| 205 current_tiling_range_type_ = PictureLayerTilingSet::HIGH_RES; | 217 current_tiling_range_type_ = PictureLayerTilingSet::HIGH_RES; |
| 206 return true; | 218 return true; |
| 207 case PictureLayerTilingSet::HIGH_RES: | 219 case PictureLayerTilingSet::HIGH_RES: |
| 208 // Process required for activation tiles (unless that has already been | 220 // Process required for activation tiles (unless that has already been |
| 209 // done for the current priority bin) if there is a tiling with required | 221 // done). Only pending tree NOW tiles may be required for activation. |
| 210 // for activation tiles and that tiling may have required for activation | 222 if (!processing_required_for_activation_tiles_ && |
| 211 // tiles having the current priority bin (in the pending tree only NOW | 223 current_priority_bin_ == TilePriority::NOW && tree_ == PENDING_TREE) { |
| 212 // tiles may be required for activation). | 224 processing_required_for_activation_tiles_ = true; |
| 213 if (!processing_tiling_with_required_for_activation_tiles_ && | |
| 214 tiling_index_with_required_for_activation_tiles_ < | |
| 215 tiling_set_->num_tilings() && | |
| 216 (current_priority_bin_ == TilePriority::NOW || | |
| 217 tree_ == ACTIVE_TREE)) { | |
| 218 processing_tiling_with_required_for_activation_tiles_ = true; | |
| 219 return true; | 225 return true; |
| 220 } | 226 } |
| 221 processing_tiling_with_required_for_activation_tiles_ = false; | 227 processing_required_for_activation_tiles_ = false; |
| 222 | 228 |
| 223 if (!AdvanceToNextPriorityBin()) | 229 if (!AdvanceToNextPriorityBin()) |
| 224 return false; | 230 return false; |
| 225 | 231 |
| 226 current_tiling_range_type_ = PictureLayerTilingSet::HIGHER_THAN_HIGH_RES; | 232 current_tiling_range_type_ = PictureLayerTilingSet::HIGHER_THAN_HIGH_RES; |
| 227 return true; | 233 return true; |
| 228 } | 234 } |
| 229 NOTREACHED(); | 235 NOTREACHED(); |
| 230 return false; | 236 return false; |
| 231 } | 237 } |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 283 if (AdvanceToNextEvictionTile()) | 289 if (AdvanceToNextEvictionTile()) |
| 284 return true; | 290 return true; |
| 285 } | 291 } |
| 286 break; | 292 break; |
| 287 } | 293 } |
| 288 } | 294 } |
| 289 } | 295 } |
| 290 | 296 |
| 291 PictureLayerTilingSet::TilingRange | 297 PictureLayerTilingSet::TilingRange |
| 292 TilingSetEvictionQueue::CurrentTilingRange() const { | 298 TilingSetEvictionQueue::CurrentTilingRange() const { |
| 293 if (processing_tiling_with_required_for_activation_tiles_) | |
| 294 return PictureLayerTilingSet::TilingRange( | |
| 295 tiling_index_with_required_for_activation_tiles_, | |
| 296 tiling_index_with_required_for_activation_tiles_ + 1); | |
| 297 return tiling_set_->GetTilingRange(current_tiling_range_type_); | 299 return tiling_set_->GetTilingRange(current_tiling_range_type_); |
| 298 } | 300 } |
| 299 | 301 |
| 300 size_t TilingSetEvictionQueue::CurrentTilingIndex() const { | 302 size_t TilingSetEvictionQueue::CurrentTilingIndex() const { |
| 301 DCHECK_NE(current_tiling_index_, CurrentTilingRange().end); | 303 DCHECK_NE(current_tiling_index_, CurrentTilingRange().end); |
| 302 switch (current_tiling_range_type_) { | 304 switch (current_tiling_range_type_) { |
| 303 case PictureLayerTilingSet::HIGHER_THAN_HIGH_RES: | 305 case PictureLayerTilingSet::HIGHER_THAN_HIGH_RES: |
| 304 case PictureLayerTilingSet::LOW_RES: | 306 case PictureLayerTilingSet::LOW_RES: |
| 305 case PictureLayerTilingSet::HIGH_RES: | 307 case PictureLayerTilingSet::HIGH_RES: |
| 306 return current_tiling_index_; | 308 return current_tiling_index_; |
| 307 // Tilings in the following ranges are accessed in reverse order. | 309 // Tilings in the following ranges are accessed in reverse order. |
| 308 case PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES: | 310 case PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES: |
| 309 case PictureLayerTilingSet::LOWER_THAN_LOW_RES: { | 311 case PictureLayerTilingSet::LOWER_THAN_LOW_RES: { |
| 310 PictureLayerTilingSet::TilingRange tiling_range = CurrentTilingRange(); | 312 PictureLayerTilingSet::TilingRange tiling_range = CurrentTilingRange(); |
| 311 size_t current_tiling_range_offset = | 313 size_t current_tiling_range_offset = |
| 312 current_tiling_index_ - tiling_range.start; | 314 current_tiling_index_ - tiling_range.start; |
| 313 return tiling_range.end - 1 - current_tiling_range_offset; | 315 return tiling_range.end - 1 - current_tiling_range_offset; |
| 314 } | 316 } |
| 315 } | 317 } |
| 316 NOTREACHED(); | 318 NOTREACHED(); |
| 317 return 0; | 319 return 0; |
| 318 } | 320 } |
| 319 | 321 |
| 320 bool TilingSetEvictionQueue::IsSharedOutOfOrderTile(const Tile* tile) const { | |
| 321 if (!tile->is_shared()) | |
| 322 return false; | |
| 323 | |
| 324 switch (tree_priority_) { | |
| 325 case SMOOTHNESS_TAKES_PRIORITY: | |
| 326 DCHECK_EQ(ACTIVE_TREE, tree_); | |
| 327 return false; | |
| 328 case NEW_CONTENT_TAKES_PRIORITY: | |
| 329 DCHECK_EQ(PENDING_TREE, tree_); | |
| 330 return false; | |
| 331 case SAME_PRIORITY_FOR_BOTH_TREES: | |
| 332 break; | |
| 333 } | |
| 334 | |
| 335 // The priority for tile priority of a shared tile will be a combined | |
| 336 // priority thus return shared tiles from a higher priority tree as | |
| 337 // it is out of order for a lower priority tree. | |
| 338 WhichTree twin_tree = tree_ == ACTIVE_TREE ? PENDING_TREE : ACTIVE_TREE; | |
| 339 const TilePriority& priority = tile->priority(tree_); | |
| 340 const TilePriority& twin_priority = tile->priority(twin_tree); | |
| 341 if (priority.priority_bin != twin_priority.priority_bin) | |
| 342 return priority.priority_bin > twin_priority.priority_bin; | |
| 343 const bool occluded = tile->is_occluded(tree_); | |
| 344 const bool twin_occluded = tile->is_occluded(twin_tree); | |
| 345 if (occluded != twin_occluded) | |
| 346 return occluded; | |
| 347 if (priority.distance_to_visible != twin_priority.distance_to_visible) | |
| 348 return priority.distance_to_visible > twin_priority.distance_to_visible; | |
| 349 | |
| 350 // If priorities are the same, it does not matter which tree returns | |
| 351 // the tile. Let's pick the pending tree. | |
| 352 return tree_ != PENDING_TREE; | |
| 353 } | |
| 354 | |
| 355 size_t TilingSetEvictionQueue::TilingIndexWithRequiredForActivationTiles() | |
| 356 const { | |
| 357 // Returns the tiling index of the tiling with requuired for activation tiles. | |
| 358 // If no such tiling exists, returns the past-the-last index (num_tilings). | |
| 359 size_t num_tilings = tiling_set_->num_tilings(); | |
| 360 | |
| 361 if (tree_ == PENDING_TREE) { | |
| 362 // For the pending tree, the tiling with required for activation tiles is | |
| 363 // the high res one. | |
| 364 PictureLayerTilingSet::TilingRange high_res_tiling_range = | |
| 365 tiling_set_->GetTilingRange(PictureLayerTilingSet::HIGH_RES); | |
| 366 if (high_res_tiling_range.start != high_res_tiling_range.end) | |
| 367 return high_res_tiling_range.start; | |
| 368 } else { | |
| 369 DCHECK_EQ(ACTIVE_TREE, tree_); | |
| 370 // Only pending tree tiles can be required for activation. They can appear | |
| 371 // also in the active tree only if they are shared. If we skip all shared | |
| 372 // tiles, there is no need to find them as they will not be returned. | |
| 373 if (skip_all_shared_tiles_) | |
| 374 return num_tilings; | |
| 375 | |
| 376 // For the active tree, the tiling with required for activation tiles is | |
| 377 // the one whose twin tiling is the high res pending tiling. | |
| 378 for (size_t i = 0; i < num_tilings; ++i) { | |
| 379 const PictureLayerTiling* tiling = tiling_set_->tiling_at(i); | |
| 380 const PictureLayerTiling* pending_tiling = | |
| 381 tiling_set_->client()->GetPendingOrActiveTwinTiling(tiling); | |
| 382 if (pending_tiling && pending_tiling->resolution() == HIGH_RESOLUTION) | |
| 383 return i; | |
| 384 } | |
| 385 } | |
| 386 | |
| 387 return num_tilings; | |
| 388 } | |
| 389 | |
| 390 } // namespace cc | 322 } // namespace cc |
| OLD | NEW |