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/tile_manager.h" | 5 #include "cc/resources/tile_manager.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <limits> | 8 #include <limits> |
9 #include <string> | 9 #include <string> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/json/json_writer.h" | 12 #include "base/json/json_writer.h" |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
15 #include "cc/debug/traced_value.h" | 15 #include "cc/debug/traced_value.h" |
| 16 #include "cc/resources/direct_raster_worker_pool.h" |
16 #include "cc/resources/image_raster_worker_pool.h" | 17 #include "cc/resources/image_raster_worker_pool.h" |
17 #include "cc/resources/pixel_buffer_raster_worker_pool.h" | 18 #include "cc/resources/pixel_buffer_raster_worker_pool.h" |
| 19 #include "cc/resources/raster_worker_pool_delegate.h" |
18 #include "cc/resources/tile.h" | 20 #include "cc/resources/tile.h" |
19 #include "third_party/skia/include/core/SkCanvas.h" | |
20 #include "ui/gfx/rect_conversions.h" | 21 #include "ui/gfx/rect_conversions.h" |
21 | 22 |
22 namespace cc { | 23 namespace cc { |
23 | |
24 namespace { | 24 namespace { |
25 | 25 |
26 // Memory limit policy works by mapping some bin states to the NEVER bin. | 26 // Memory limit policy works by mapping some bin states to the NEVER bin. |
27 const ManagedTileBin kBinPolicyMap[NUM_TILE_MEMORY_LIMIT_POLICIES][NUM_BINS] = { | 27 const ManagedTileBin kBinPolicyMap[NUM_TILE_MEMORY_LIMIT_POLICIES][NUM_BINS] = { |
28 // [ALLOW_NOTHING] | 28 // [ALLOW_NOTHING] |
29 {NEVER_BIN, // [NOW_AND_READY_TO_DRAW_BIN] | 29 {NEVER_BIN, // [NOW_AND_READY_TO_DRAW_BIN] |
30 NEVER_BIN, // [NOW_BIN] | 30 NEVER_BIN, // [NOW_BIN] |
31 NEVER_BIN, // [SOON_BIN] | 31 NEVER_BIN, // [SOON_BIN] |
32 NEVER_BIN, // [EVENTUALLY_AND_ACTIVE_BIN] | 32 NEVER_BIN, // [EVENTUALLY_AND_ACTIVE_BIN] |
33 NEVER_BIN, // [EVENTUALLY_BIN] | 33 NEVER_BIN, // [EVENTUALLY_BIN] |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
150 ContextProvider* context_provider, | 150 ContextProvider* context_provider, |
151 RenderingStatsInstrumentation* rendering_stats_instrumentation, | 151 RenderingStatsInstrumentation* rendering_stats_instrumentation, |
152 bool use_map_image, | 152 bool use_map_image, |
153 bool use_rasterize_on_demand, | 153 bool use_rasterize_on_demand, |
154 size_t max_transfer_buffer_usage_bytes, | 154 size_t max_transfer_buffer_usage_bytes, |
155 size_t max_raster_usage_bytes, | 155 size_t max_raster_usage_bytes, |
156 unsigned map_image_texture_target) { | 156 unsigned map_image_texture_target) { |
157 return make_scoped_ptr(new TileManager( | 157 return make_scoped_ptr(new TileManager( |
158 client, | 158 client, |
159 resource_provider, | 159 resource_provider, |
160 use_map_image | 160 context_provider, |
161 ? ImageRasterWorkerPool::Create( | 161 use_map_image ? ImageRasterWorkerPool::Create(resource_provider, |
162 resource_provider, context_provider, map_image_texture_target) | 162 map_image_texture_target) |
163 : PixelBufferRasterWorkerPool::Create( | 163 : PixelBufferRasterWorkerPool::Create( |
164 resource_provider, | 164 resource_provider, max_transfer_buffer_usage_bytes), |
165 context_provider, | 165 DirectRasterWorkerPool::Create(resource_provider, context_provider), |
166 max_transfer_buffer_usage_bytes), | |
167 max_raster_usage_bytes, | 166 max_raster_usage_bytes, |
168 rendering_stats_instrumentation, | 167 rendering_stats_instrumentation, |
169 use_rasterize_on_demand)); | 168 use_rasterize_on_demand)); |
170 } | 169 } |
171 | 170 |
172 TileManager::TileManager( | 171 TileManager::TileManager( |
173 TileManagerClient* client, | 172 TileManagerClient* client, |
174 ResourceProvider* resource_provider, | 173 ResourceProvider* resource_provider, |
| 174 ContextProvider* context_provider, |
175 scoped_ptr<RasterWorkerPool> raster_worker_pool, | 175 scoped_ptr<RasterWorkerPool> raster_worker_pool, |
| 176 scoped_ptr<RasterWorkerPool> direct_raster_worker_pool, |
176 size_t max_raster_usage_bytes, | 177 size_t max_raster_usage_bytes, |
177 RenderingStatsInstrumentation* rendering_stats_instrumentation, | 178 RenderingStatsInstrumentation* rendering_stats_instrumentation, |
178 bool use_rasterize_on_demand) | 179 bool use_rasterize_on_demand) |
179 : client_(client), | 180 : client_(client), |
180 resource_pool_( | 181 resource_pool_( |
181 ResourcePool::Create(resource_provider, | 182 ResourcePool::Create(resource_provider, |
182 raster_worker_pool->GetResourceTarget(), | 183 raster_worker_pool->GetResourceTarget(), |
183 raster_worker_pool->GetResourceFormat())), | 184 raster_worker_pool->GetResourceFormat())), |
184 raster_worker_pool_(raster_worker_pool.Pass()), | 185 raster_worker_pool_(raster_worker_pool.Pass()), |
| 186 direct_raster_worker_pool_(direct_raster_worker_pool.Pass()), |
185 prioritized_tiles_dirty_(false), | 187 prioritized_tiles_dirty_(false), |
186 all_tiles_that_need_to_be_rasterized_have_memory_(true), | 188 all_tiles_that_need_to_be_rasterized_have_memory_(true), |
187 all_tiles_required_for_activation_have_memory_(true), | 189 all_tiles_required_for_activation_have_memory_(true), |
188 memory_required_bytes_(0), | 190 memory_required_bytes_(0), |
189 memory_nice_to_have_bytes_(0), | 191 memory_nice_to_have_bytes_(0), |
190 bytes_releasable_(0), | 192 bytes_releasable_(0), |
191 resources_releasable_(0), | 193 resources_releasable_(0), |
192 max_raster_usage_bytes_(max_raster_usage_bytes), | 194 max_raster_usage_bytes_(max_raster_usage_bytes), |
193 ever_exceeded_memory_budget_(false), | 195 ever_exceeded_memory_budget_(false), |
194 rendering_stats_instrumentation_(rendering_stats_instrumentation), | 196 rendering_stats_instrumentation_(rendering_stats_instrumentation), |
195 did_initialize_visible_tile_(false), | 197 did_initialize_visible_tile_(false), |
196 did_check_for_completed_tasks_since_last_schedule_tasks_(true), | 198 did_check_for_completed_tasks_since_last_schedule_tasks_(true), |
197 use_rasterize_on_demand_(use_rasterize_on_demand) { | 199 use_rasterize_on_demand_(use_rasterize_on_demand) { |
198 raster_worker_pool_->SetClient(this); | 200 RasterWorkerPool* raster_worker_pools[NUM_RASTER_WORKER_POOL_TYPES] = { |
| 201 raster_worker_pool_.get(), // RASTER_WORKER_POOL_TYPE_DEFAULT |
| 202 direct_raster_worker_pool_.get() // RASTER_WORKER_POOL_TYPE_DIRECT |
| 203 }; |
| 204 raster_worker_pool_delegate_ = RasterWorkerPoolDelegate::Create( |
| 205 this, raster_worker_pools, arraysize(raster_worker_pools)); |
199 } | 206 } |
200 | 207 |
201 TileManager::~TileManager() { | 208 TileManager::~TileManager() { |
202 // Reset global state and manage. This should cause | 209 // Reset global state and manage. This should cause |
203 // our memory usage to drop to zero. | 210 // our memory usage to drop to zero. |
204 global_state_ = GlobalStateThatImpactsTilePriority(); | 211 global_state_ = GlobalStateThatImpactsTilePriority(); |
205 | 212 |
206 CleanUpReleasedTiles(); | 213 CleanUpReleasedTiles(); |
207 DCHECK_EQ(0u, tiles_.size()); | 214 DCHECK_EQ(0u, tiles_.size()); |
208 | 215 |
209 RasterWorkerPool::RasterTask::Queue empty; | 216 RasterWorkerPool::RasterTask::Queue empty[NUM_RASTER_WORKER_POOL_TYPES]; |
210 raster_worker_pool_->ScheduleTasks(&empty); | 217 raster_worker_pool_delegate_->ScheduleTasks(empty); |
211 | 218 |
212 // This should finish all pending tasks and release any uninitialized | 219 // This should finish all pending tasks and release any uninitialized |
213 // resources. | 220 // resources. |
214 raster_worker_pool_->Shutdown(); | 221 raster_worker_pool_delegate_->Shutdown(); |
215 raster_worker_pool_->CheckForCompletedTasks(); | 222 raster_worker_pool_delegate_->CheckForCompletedTasks(); |
216 | 223 |
217 DCHECK_EQ(0u, bytes_releasable_); | 224 DCHECK_EQ(0u, bytes_releasable_); |
218 DCHECK_EQ(0u, resources_releasable_); | 225 DCHECK_EQ(0u, resources_releasable_); |
219 } | 226 } |
220 | 227 |
221 void TileManager::Release(Tile* tile) { | 228 void TileManager::Release(Tile* tile) { |
222 prioritized_tiles_dirty_ = true; | 229 prioritized_tiles_dirty_ = true; |
223 released_tiles_.push_back(tile); | 230 released_tiles_.push_back(tile); |
224 } | 231 } |
225 | 232 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 } | 275 } |
269 | 276 |
270 void TileManager::DidFinishRunningTasks() { | 277 void TileManager::DidFinishRunningTasks() { |
271 TRACE_EVENT0("cc", "TileManager::DidFinishRunningTasks"); | 278 TRACE_EVENT0("cc", "TileManager::DidFinishRunningTasks"); |
272 | 279 |
273 // When OOM, keep re-assigning memory until we reach a steady state | 280 // When OOM, keep re-assigning memory until we reach a steady state |
274 // where top-priority tiles are initialized. | 281 // where top-priority tiles are initialized. |
275 if (all_tiles_that_need_to_be_rasterized_have_memory_) | 282 if (all_tiles_that_need_to_be_rasterized_have_memory_) |
276 return; | 283 return; |
277 | 284 |
278 raster_worker_pool_->CheckForCompletedTasks(); | 285 raster_worker_pool_delegate_->CheckForCompletedTasks(); |
279 did_check_for_completed_tasks_since_last_schedule_tasks_ = true; | 286 did_check_for_completed_tasks_since_last_schedule_tasks_ = true; |
280 | 287 |
281 TileVector tiles_that_need_to_be_rasterized; | 288 TileVector tiles_that_need_to_be_rasterized; |
282 AssignGpuMemoryToTiles(&prioritized_tiles_, | 289 AssignGpuMemoryToTiles(&prioritized_tiles_, |
283 &tiles_that_need_to_be_rasterized); | 290 &tiles_that_need_to_be_rasterized); |
284 | 291 |
285 // |tiles_that_need_to_be_rasterized| will be empty when we reach a | 292 // |tiles_that_need_to_be_rasterized| will be empty when we reach a |
286 // steady memory state. Keep scheduling tasks until we reach this state. | 293 // steady memory state. Keep scheduling tasks until we reach this state. |
287 if (!tiles_that_need_to_be_rasterized.empty()) { | 294 if (!tiles_that_need_to_be_rasterized.empty()) { |
288 ScheduleTasks(tiles_that_need_to_be_rasterized); | 295 ScheduleTasks(tiles_that_need_to_be_rasterized); |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
465 // memory returns to soft limit after going over. | 472 // memory returns to soft limit after going over. |
466 resource_pool_->SetResourceUsageLimits( | 473 resource_pool_->SetResourceUsageLimits( |
467 global_state_.soft_memory_limit_in_bytes, | 474 global_state_.soft_memory_limit_in_bytes, |
468 global_state_.unused_memory_limit_in_bytes, | 475 global_state_.unused_memory_limit_in_bytes, |
469 global_state_.num_resources_limit); | 476 global_state_.num_resources_limit); |
470 } | 477 } |
471 | 478 |
472 // We need to call CheckForCompletedTasks() once in-between each call | 479 // We need to call CheckForCompletedTasks() once in-between each call |
473 // to ScheduleTasks() to prevent canceled tasks from being scheduled. | 480 // to ScheduleTasks() to prevent canceled tasks from being scheduled. |
474 if (!did_check_for_completed_tasks_since_last_schedule_tasks_) { | 481 if (!did_check_for_completed_tasks_since_last_schedule_tasks_) { |
475 raster_worker_pool_->CheckForCompletedTasks(); | 482 raster_worker_pool_delegate_->CheckForCompletedTasks(); |
476 did_check_for_completed_tasks_since_last_schedule_tasks_ = true; | 483 did_check_for_completed_tasks_since_last_schedule_tasks_ = true; |
477 } | 484 } |
478 | 485 |
479 UpdatePrioritizedTileSetIfNeeded(); | 486 UpdatePrioritizedTileSetIfNeeded(); |
480 | 487 |
481 TileVector tiles_that_need_to_be_rasterized; | 488 TileVector tiles_that_need_to_be_rasterized; |
482 AssignGpuMemoryToTiles(&prioritized_tiles_, | 489 AssignGpuMemoryToTiles(&prioritized_tiles_, |
483 &tiles_that_need_to_be_rasterized); | 490 &tiles_that_need_to_be_rasterized); |
484 | 491 |
485 // Finally, schedule rasterizer tasks. | 492 // Finally, schedule rasterizer tasks. |
486 ScheduleTasks(tiles_that_need_to_be_rasterized); | 493 ScheduleTasks(tiles_that_need_to_be_rasterized); |
487 | 494 |
488 TRACE_EVENT_INSTANT1("cc", | 495 TRACE_EVENT_INSTANT1("cc", |
489 "DidManage", | 496 "DidManage", |
490 TRACE_EVENT_SCOPE_THREAD, | 497 TRACE_EVENT_SCOPE_THREAD, |
491 "state", | 498 "state", |
492 TracedValue::FromValue(BasicStateAsValue().release())); | 499 TracedValue::FromValue(BasicStateAsValue().release())); |
493 | 500 |
494 TRACE_COUNTER_ID1("cc", | 501 TRACE_COUNTER_ID1("cc", |
495 "unused_memory_bytes", | 502 "unused_memory_bytes", |
496 this, | 503 this, |
497 resource_pool_->total_memory_usage_bytes() - | 504 resource_pool_->total_memory_usage_bytes() - |
498 resource_pool_->acquired_memory_usage_bytes()); | 505 resource_pool_->acquired_memory_usage_bytes()); |
499 } | 506 } |
500 | 507 |
501 bool TileManager::UpdateVisibleTiles() { | 508 bool TileManager::UpdateVisibleTiles() { |
502 TRACE_EVENT0("cc", "TileManager::UpdateVisibleTiles"); | 509 TRACE_EVENT0("cc", "TileManager::UpdateVisibleTiles"); |
503 | 510 |
504 raster_worker_pool_->CheckForCompletedTasks(); | 511 raster_worker_pool_delegate_->CheckForCompletedTasks(); |
505 did_check_for_completed_tasks_since_last_schedule_tasks_ = true; | 512 did_check_for_completed_tasks_since_last_schedule_tasks_ = true; |
506 | 513 |
507 TRACE_EVENT_INSTANT1( | 514 TRACE_EVENT_INSTANT1( |
508 "cc", | 515 "cc", |
509 "DidUpdateVisibleTiles", | 516 "DidUpdateVisibleTiles", |
510 TRACE_EVENT_SCOPE_THREAD, | 517 TRACE_EVENT_SCOPE_THREAD, |
511 "stats", | 518 "stats", |
512 TracedValue::FromValue(RasterTaskCompletionStatsAsValue( | 519 TracedValue::FromValue(RasterTaskCompletionStatsAsValue( |
513 update_visible_tiles_stats_).release())); | 520 update_visible_tiles_stats_).release())); |
514 update_visible_tiles_stats_ = RasterTaskCompletionStats(); | 521 update_visible_tiles_stats_ = RasterTaskCompletionStats(); |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
788 | 795 |
789 void TileManager::ScheduleTasks( | 796 void TileManager::ScheduleTasks( |
790 const TileVector& tiles_that_need_to_be_rasterized) { | 797 const TileVector& tiles_that_need_to_be_rasterized) { |
791 TRACE_EVENT1("cc", | 798 TRACE_EVENT1("cc", |
792 "TileManager::ScheduleTasks", | 799 "TileManager::ScheduleTasks", |
793 "count", | 800 "count", |
794 tiles_that_need_to_be_rasterized.size()); | 801 tiles_that_need_to_be_rasterized.size()); |
795 | 802 |
796 DCHECK(did_check_for_completed_tasks_since_last_schedule_tasks_); | 803 DCHECK(did_check_for_completed_tasks_since_last_schedule_tasks_); |
797 | 804 |
798 raster_tasks_.Reset(); | 805 for (size_t i = 0; i < NUM_RASTER_WORKER_POOL_TYPES; ++i) |
| 806 raster_queue_[i].Reset(); |
799 | 807 |
800 // Build a new task queue containing all task currently needed. Tasks | 808 // Build a new task queue containing all task currently needed. Tasks |
801 // are added in order of priority, highest priority task first. | 809 // are added in order of priority, highest priority task first. |
802 for (TileVector::const_iterator it = tiles_that_need_to_be_rasterized.begin(); | 810 for (TileVector::const_iterator it = tiles_that_need_to_be_rasterized.begin(); |
803 it != tiles_that_need_to_be_rasterized.end(); | 811 it != tiles_that_need_to_be_rasterized.end(); |
804 ++it) { | 812 ++it) { |
805 Tile* tile = *it; | 813 Tile* tile = *it; |
806 ManagedTileState& mts = tile->managed_state(); | 814 ManagedTileState& mts = tile->managed_state(); |
807 ManagedTileState::TileVersion& tile_version = | 815 ManagedTileState::TileVersion& tile_version = |
808 mts.tile_versions[mts.raster_mode]; | 816 mts.tile_versions[mts.raster_mode]; |
809 | 817 |
810 DCHECK(tile_version.requires_resource()); | 818 DCHECK(tile_version.requires_resource()); |
811 DCHECK(!tile_version.resource_); | 819 DCHECK(!tile_version.resource_); |
812 | 820 |
813 if (tile_version.raster_task_.is_null()) | 821 if (tile_version.raster_task_.is_null()) |
814 tile_version.raster_task_ = CreateRasterTask(tile); | 822 tile_version.raster_task_ = CreateRasterTask(tile); |
815 | 823 |
816 raster_tasks_.Append(tile_version.raster_task_, | 824 size_t pool_type = tile->use_gpu_rasterization() |
817 tile->required_for_activation()); | 825 ? RASTER_WORKER_POOL_TYPE_DIRECT |
| 826 : RASTER_WORKER_POOL_TYPE_DEFAULT; |
| 827 |
| 828 raster_queue_[pool_type] |
| 829 .Append(tile_version.raster_task_, tile->required_for_activation()); |
818 } | 830 } |
819 | 831 |
820 // We must reduce the amount of unused resoruces before calling | 832 // We must reduce the amount of unused resoruces before calling |
821 // ScheduleTasks to prevent usage from rising above limits. | 833 // ScheduleTasks to prevent usage from rising above limits. |
822 resource_pool_->ReduceResourceUsage(); | 834 resource_pool_->ReduceResourceUsage(); |
823 | 835 |
824 // Schedule running of |raster_tasks_|. This replaces any previously | 836 // Schedule running of |raster_tasks_|. This replaces any previously |
825 // scheduled tasks and effectively cancels all tasks not present | 837 // scheduled tasks and effectively cancels all tasks not present |
826 // in |raster_tasks_|. | 838 // in |raster_tasks_|. |
827 raster_worker_pool_->ScheduleTasks(&raster_tasks_); | 839 raster_worker_pool_delegate_->ScheduleTasks(raster_queue_); |
828 | 840 |
829 did_check_for_completed_tasks_since_last_schedule_tasks_ = false; | 841 did_check_for_completed_tasks_since_last_schedule_tasks_ = false; |
830 } | 842 } |
831 | 843 |
832 RasterWorkerPool::Task TileManager::CreateImageDecodeTask( | 844 RasterWorkerPool::Task TileManager::CreateImageDecodeTask( |
833 Tile* tile, | 845 Tile* tile, |
834 SkPixelRef* pixel_ref) { | 846 SkPixelRef* pixel_ref) { |
835 return RasterWorkerPool::CreateImageDecodeTask( | 847 return RasterWorkerPool::CreateImageDecodeTask( |
836 pixel_ref, | 848 pixel_ref, |
837 tile->layer_id(), | 849 tile->layer_id(), |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
875 return RasterWorkerPool::CreateRasterTask( | 887 return RasterWorkerPool::CreateRasterTask( |
876 const_resource, | 888 const_resource, |
877 tile->picture_pile(), | 889 tile->picture_pile(), |
878 tile->content_rect(), | 890 tile->content_rect(), |
879 tile->contents_scale(), | 891 tile->contents_scale(), |
880 mts.raster_mode, | 892 mts.raster_mode, |
881 mts.resolution, | 893 mts.resolution, |
882 tile->layer_id(), | 894 tile->layer_id(), |
883 static_cast<const void*>(tile), | 895 static_cast<const void*>(tile), |
884 tile->source_frame_number(), | 896 tile->source_frame_number(), |
885 tile->use_gpu_rasterization(), | |
886 rendering_stats_instrumentation_, | 897 rendering_stats_instrumentation_, |
887 base::Bind(&TileManager::OnRasterTaskCompleted, | 898 base::Bind(&TileManager::OnRasterTaskCompleted, |
888 base::Unretained(this), | 899 base::Unretained(this), |
889 tile->id(), | 900 tile->id(), |
890 base::Passed(&resource), | 901 base::Passed(&resource), |
891 mts.raster_mode), | 902 mts.raster_mode), |
892 &decode_tasks); | 903 &decode_tasks, |
| 904 context_provider_); |
893 } | 905 } |
894 | 906 |
895 void TileManager::OnImageDecodeTaskCompleted(int layer_id, | 907 void TileManager::OnImageDecodeTaskCompleted(int layer_id, |
896 SkPixelRef* pixel_ref, | 908 SkPixelRef* pixel_ref, |
897 bool was_canceled) { | 909 bool was_canceled) { |
898 // If the task was canceled, we need to clean it up | 910 // If the task was canceled, we need to clean it up |
899 // from |image_decode_tasks_|. | 911 // from |image_decode_tasks_|. |
900 if (!was_canceled) | 912 if (!was_canceled) |
901 return; | 913 return; |
902 | 914 |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
976 flags)); | 988 flags)); |
977 DCHECK(tiles_.find(tile->id()) == tiles_.end()); | 989 DCHECK(tiles_.find(tile->id()) == tiles_.end()); |
978 | 990 |
979 tiles_[tile->id()] = tile; | 991 tiles_[tile->id()] = tile; |
980 used_layer_counts_[tile->layer_id()]++; | 992 used_layer_counts_[tile->layer_id()]++; |
981 prioritized_tiles_dirty_ = true; | 993 prioritized_tiles_dirty_ = true; |
982 return tile; | 994 return tile; |
983 } | 995 } |
984 | 996 |
985 } // namespace cc | 997 } // namespace cc |
OLD | NEW |