| 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/tile_manager.h" | 5 #include "cc/tile_manager.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/debug/trace_event.h" | 10 #include "base/debug/trace_event.h" |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 146 "<unknown TileRasterState value>")); | 146 "<unknown TileRasterState value>")); |
| 147 } | 147 } |
| 148 } | 148 } |
| 149 | 149 |
| 150 TileManager::TileManager( | 150 TileManager::TileManager( |
| 151 TileManagerClient* client, | 151 TileManagerClient* client, |
| 152 ResourceProvider* resource_provider, | 152 ResourceProvider* resource_provider, |
| 153 size_t num_raster_threads, | 153 size_t num_raster_threads, |
| 154 bool use_cheapness_estimator, | 154 bool use_cheapness_estimator, |
| 155 bool use_color_estimator, | 155 bool use_color_estimator, |
| 156 bool prediction_benchmarking) | 156 bool prediction_benchmarking, |
| 157 RenderingStatsInstrumentation* rendering_stats_instrumentation) |
| 157 : client_(client), | 158 : client_(client), |
| 158 resource_pool_(ResourcePool::Create(resource_provider)), | 159 resource_pool_(ResourcePool::Create(resource_provider)), |
| 159 raster_worker_pool_(RasterWorkerPool::Create(this, num_raster_threads)), | 160 raster_worker_pool_(RasterWorkerPool::Create(this, num_raster_threads)), |
| 160 manage_tiles_pending_(false), | 161 manage_tiles_pending_(false), |
| 161 manage_tiles_call_count_(0), | 162 manage_tiles_call_count_(0), |
| 162 bytes_pending_upload_(0), | 163 bytes_pending_upload_(0), |
| 163 has_performed_uploads_since_last_flush_(false), | 164 has_performed_uploads_since_last_flush_(false), |
| 164 ever_exceeded_memory_budget_(false), | 165 ever_exceeded_memory_budget_(false), |
| 165 record_rendering_stats_(false), | |
| 166 use_cheapness_estimator_(use_cheapness_estimator), | 166 use_cheapness_estimator_(use_cheapness_estimator), |
| 167 use_color_estimator_(use_color_estimator), | 167 use_color_estimator_(use_color_estimator), |
| 168 allow_cheap_tasks_(true), | 168 allow_cheap_tasks_(true), |
| 169 did_schedule_cheap_tasks_(false), | 169 did_schedule_cheap_tasks_(false), |
| 170 prediction_benchmarking_(prediction_benchmarking), | 170 prediction_benchmarking_(prediction_benchmarking), |
| 171 pending_tasks_(0), | 171 pending_tasks_(0), |
| 172 max_pending_tasks_(kMaxNumPendingTasksPerThread * num_raster_threads) { | 172 max_pending_tasks_(kMaxNumPendingTasksPerThread * num_raster_threads), |
| 173 rendering_stats_instrumentation_(rendering_stats_instrumentation) { |
| 173 for (int i = 0; i < NUM_STATES; ++i) { | 174 for (int i = 0; i < NUM_STATES; ++i) { |
| 174 for (int j = 0; j < NUM_TREES; ++j) { | 175 for (int j = 0; j < NUM_TREES; ++j) { |
| 175 for (int k = 0; k < NUM_BINS; ++k) | 176 for (int k = 0; k < NUM_BINS; ++k) |
| 176 raster_state_count_[i][j][k] = 0; | 177 raster_state_count_[i][j][k] = 0; |
| 177 } | 178 } |
| 178 } | 179 } |
| 179 } | 180 } |
| 180 | 181 |
| 181 TileManager::~TileManager() { | 182 TileManager::~TileManager() { |
| 182 // Reset global state and manage. This should cause | 183 // Reset global state and manage. This should cause |
| (...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 493 size_t memoryUsedBytes; | 494 size_t memoryUsedBytes; |
| 494 GetMemoryStats(&memoryRequiredBytes, | 495 GetMemoryStats(&memoryRequiredBytes, |
| 495 &memoryNiceToHaveBytes, | 496 &memoryNiceToHaveBytes, |
| 496 &memoryUsedBytes); | 497 &memoryUsedBytes); |
| 497 requirements->SetInteger("memory_required_bytes", memoryRequiredBytes); | 498 requirements->SetInteger("memory_required_bytes", memoryRequiredBytes); |
| 498 requirements->SetInteger("memory_nice_to_have_bytes", memoryNiceToHaveBytes); | 499 requirements->SetInteger("memory_nice_to_have_bytes", memoryNiceToHaveBytes); |
| 499 requirements->SetInteger("memory_used_bytes", memoryUsedBytes); | 500 requirements->SetInteger("memory_used_bytes", memoryUsedBytes); |
| 500 return requirements.PassAs<base::Value>(); | 501 return requirements.PassAs<base::Value>(); |
| 501 } | 502 } |
| 502 | 503 |
| 503 void TileManager::SetRecordRenderingStats(bool record_rendering_stats) { | |
| 504 if (record_rendering_stats_ == record_rendering_stats) | |
| 505 return; | |
| 506 | |
| 507 record_rendering_stats_ = record_rendering_stats; | |
| 508 raster_worker_pool_->SetRecordRenderingStats(record_rendering_stats); | |
| 509 } | |
| 510 | |
| 511 void TileManager::GetRenderingStats(RenderingStats* stats) { | |
| 512 CHECK(record_rendering_stats_); | |
| 513 raster_worker_pool_->GetRenderingStats(stats); | |
| 514 stats->totalDeferredImageCacheHitCount = | |
| 515 rendering_stats_.totalDeferredImageCacheHitCount; | |
| 516 stats->totalImageGatheringCount = rendering_stats_.totalImageGatheringCount; | |
| 517 stats->totalImageGatheringTime = | |
| 518 rendering_stats_.totalImageGatheringTime; | |
| 519 } | |
| 520 | |
| 521 bool TileManager::HasPendingWorkScheduled(WhichTree tree) const { | 504 bool TileManager::HasPendingWorkScheduled(WhichTree tree) const { |
| 522 // Always true when ManageTiles() call is pending. | 505 // Always true when ManageTiles() call is pending. |
| 523 if (manage_tiles_pending_) | 506 if (manage_tiles_pending_) |
| 524 return true; | 507 return true; |
| 525 | 508 |
| 526 for (int i = 0; i < NUM_STATES; ++i) { | 509 for (int i = 0; i < NUM_STATES; ++i) { |
| 527 switch (i) { | 510 switch (i) { |
| 528 case WAITING_FOR_RASTER_STATE: | 511 case WAITING_FOR_RASTER_STATE: |
| 529 case RASTER_STATE: | 512 case RASTER_STATE: |
| 530 case UPLOAD_STATE: | 513 case UPLOAD_STATE: |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 731 managed_tile_state.picture_pile_analysis.solid_color); | 714 managed_tile_state.picture_pile_analysis.solid_color); |
| 732 else if (managed_tile_state.picture_pile_analysis.is_transparent) | 715 else if (managed_tile_state.picture_pile_analysis.is_transparent) |
| 733 tile->drawing_info().set_transparent(); | 716 tile->drawing_info().set_transparent(); |
| 734 } | 717 } |
| 735 } | 718 } |
| 736 | 719 |
| 737 void TileManager::GatherPixelRefsForTile(Tile* tile) { | 720 void TileManager::GatherPixelRefsForTile(Tile* tile) { |
| 738 TRACE_EVENT0("cc", "TileManager::GatherPixelRefsForTile"); | 721 TRACE_EVENT0("cc", "TileManager::GatherPixelRefsForTile"); |
| 739 ManagedTileState& managed_tile_state = tile->managed_state(); | 722 ManagedTileState& managed_tile_state = tile->managed_state(); |
| 740 if (managed_tile_state.need_to_gather_pixel_refs) { | 723 if (managed_tile_state.need_to_gather_pixel_refs) { |
| 741 base::TimeTicks gather_begin_time; | 724 base::TimeTicks start_time = |
| 742 if (record_rendering_stats_) | 725 rendering_stats_instrumentation_->StartRecording(); |
| 743 gather_begin_time = base::TimeTicks::HighResNow(); | |
| 744 tile->picture_pile()->GatherPixelRefs( | 726 tile->picture_pile()->GatherPixelRefs( |
| 745 tile->content_rect_, | 727 tile->content_rect_, |
| 746 tile->contents_scale_, | 728 tile->contents_scale_, |
| 747 managed_tile_state.pending_pixel_refs); | 729 managed_tile_state.pending_pixel_refs); |
| 748 managed_tile_state.need_to_gather_pixel_refs = false; | 730 managed_tile_state.need_to_gather_pixel_refs = false; |
| 749 if (record_rendering_stats_) { | 731 base::TimeDelta duration = |
| 750 rendering_stats_.totalImageGatheringCount++; | 732 rendering_stats_instrumentation_->EndRecording(start_time); |
| 751 rendering_stats_.totalImageGatheringTime += | 733 rendering_stats_instrumentation_->AddImageGathering(duration); |
| 752 base::TimeTicks::HighResNow() - gather_begin_time; | |
| 753 } | |
| 754 } | 734 } |
| 755 } | 735 } |
| 756 | 736 |
| 757 void TileManager::DispatchImageDecodeTasksForTile(Tile* tile) { | 737 void TileManager::DispatchImageDecodeTasksForTile(Tile* tile) { |
| 758 GatherPixelRefsForTile(tile); | 738 GatherPixelRefsForTile(tile); |
| 759 std::list<skia::LazyPixelRef*>& pending_pixel_refs = | 739 std::list<skia::LazyPixelRef*>& pending_pixel_refs = |
| 760 tile->managed_state().pending_pixel_refs; | 740 tile->managed_state().pending_pixel_refs; |
| 761 std::list<skia::LazyPixelRef*>::iterator it = pending_pixel_refs.begin(); | 741 std::list<skia::LazyPixelRef*>::iterator it = pending_pixel_refs.begin(); |
| 762 while (it != pending_pixel_refs.end()) { | 742 while (it != pending_pixel_refs.end()) { |
| 763 if (pending_decode_tasks_.end() != pending_decode_tasks_.find( | 743 if (pending_decode_tasks_.end() != pending_decode_tasks_.find( |
| 764 (*it)->getGenerationID())) { | 744 (*it)->getGenerationID())) { |
| 765 ++it; | 745 ++it; |
| 766 continue; | 746 continue; |
| 767 } | 747 } |
| 768 // TODO(qinmin): passing correct image size to PrepareToDecode(). | 748 // TODO(qinmin): passing correct image size to PrepareToDecode(). |
| 769 if ((*it)->PrepareToDecode(skia::LazyPixelRef::PrepareParams())) { | 749 if ((*it)->PrepareToDecode(skia::LazyPixelRef::PrepareParams())) { |
| 770 rendering_stats_.totalDeferredImageCacheHitCount++; | 750 rendering_stats_instrumentation_->IncrementDeferredImageCacheHitCount(); |
| 771 pending_pixel_refs.erase(it++); | 751 pending_pixel_refs.erase(it++); |
| 772 } else { | 752 } else { |
| 773 if (pending_tasks_ >= max_pending_tasks_) | 753 if (pending_tasks_ >= max_pending_tasks_) |
| 774 return; | 754 return; |
| 775 DispatchOneImageDecodeTask(tile, *it); | 755 DispatchOneImageDecodeTask(tile, *it); |
| 776 ++it; | 756 ++it; |
| 777 } | 757 } |
| 778 } | 758 } |
| 779 } | 759 } |
| 780 | 760 |
| 781 void TileManager::DispatchOneImageDecodeTask( | 761 void TileManager::DispatchOneImageDecodeTask( |
| 782 scoped_refptr<Tile> tile, skia::LazyPixelRef* pixel_ref) { | 762 scoped_refptr<Tile> tile, skia::LazyPixelRef* pixel_ref) { |
| 783 TRACE_EVENT0("cc", "TileManager::DispatchOneImageDecodeTask"); | 763 TRACE_EVENT0("cc", "TileManager::DispatchOneImageDecodeTask"); |
| 784 uint32_t pixel_ref_id = pixel_ref->getGenerationID(); | 764 uint32_t pixel_ref_id = pixel_ref->getGenerationID(); |
| 785 DCHECK(pending_decode_tasks_.end() == | 765 DCHECK(pending_decode_tasks_.end() == |
| 786 pending_decode_tasks_.find(pixel_ref_id)); | 766 pending_decode_tasks_.find(pixel_ref_id)); |
| 787 pending_decode_tasks_[pixel_ref_id] = pixel_ref; | 767 pending_decode_tasks_[pixel_ref_id] = pixel_ref; |
| 788 | 768 |
| 789 raster_worker_pool_->PostTaskAndReply( | 769 raster_worker_pool_->PostTaskAndReply( |
| 790 base::Bind(&TileManager::RunImageDecodeTask, pixel_ref), | 770 base::Bind(&TileManager::RunImageDecodeTask, |
| 771 pixel_ref, |
| 772 rendering_stats_instrumentation_), |
| 791 base::Bind(&TileManager::OnImageDecodeTaskCompleted, | 773 base::Bind(&TileManager::OnImageDecodeTaskCompleted, |
| 792 base::Unretained(this), | 774 base::Unretained(this), |
| 793 tile, | 775 tile, |
| 794 pixel_ref_id)); | 776 pixel_ref_id)); |
| 795 pending_tasks_++; | 777 pending_tasks_++; |
| 796 } | 778 } |
| 797 | 779 |
| 798 void TileManager::OnImageDecodeTaskCompleted( | 780 void TileManager::OnImageDecodeTaskCompleted( |
| 799 scoped_refptr<Tile> tile, uint32_t pixel_ref_id) { | 781 scoped_refptr<Tile> tile, uint32_t pixel_ref_id) { |
| 800 TRACE_EVENT0("cc", "TileManager::OnImageDecodeTaskCompleted"); | 782 TRACE_EVENT0("cc", "TileManager::OnImageDecodeTaskCompleted"); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 844 allow_cheap_tasks_ && | 826 allow_cheap_tasks_ && |
| 845 global_state_.tree_priority != SMOOTHNESS_TAKES_PRIORITY && | 827 global_state_.tree_priority != SMOOTHNESS_TAKES_PRIORITY && |
| 846 managed_tile_state.picture_pile_analysis.is_cheap_to_raster; | 828 managed_tile_state.picture_pile_analysis.is_cheap_to_raster; |
| 847 raster_worker_pool_->PostRasterTaskAndReply( | 829 raster_worker_pool_->PostRasterTaskAndReply( |
| 848 tile->picture_pile(), | 830 tile->picture_pile(), |
| 849 is_cheap_task, | 831 is_cheap_task, |
| 850 base::Bind(&TileManager::RunRasterTask, | 832 base::Bind(&TileManager::RunRasterTask, |
| 851 buffer, | 833 buffer, |
| 852 tile->content_rect(), | 834 tile->content_rect(), |
| 853 tile->contents_scale(), | 835 tile->contents_scale(), |
| 854 GetRasterTaskMetadata(*tile)), | 836 GetRasterTaskMetadata(*tile), |
| 837 rendering_stats_instrumentation_), |
| 855 base::Bind(&TileManager::OnRasterTaskCompleted, | 838 base::Bind(&TileManager::OnRasterTaskCompleted, |
| 856 base::Unretained(this), | 839 base::Unretained(this), |
| 857 tile, | 840 tile, |
| 858 base::Passed(&resource), | 841 base::Passed(&resource), |
| 859 manage_tiles_call_count_)); | 842 manage_tiles_call_count_)); |
| 860 if (is_cheap_task && !did_schedule_cheap_tasks_) { | 843 if (is_cheap_task && !did_schedule_cheap_tasks_) { |
| 861 raster_worker_pool_->SetRunCheapTasksTimeLimit( | 844 raster_worker_pool_->SetRunCheapTasksTimeLimit( |
| 862 base::TimeTicks::Now() + | 845 base::TimeTicks::Now() + |
| 863 base::TimeDelta::FromMilliseconds(kRunCheapTasksTimeMs)); | 846 base::TimeDelta::FromMilliseconds(kRunCheapTasksTimeMs)); |
| 864 did_schedule_cheap_tasks_ = true; | 847 did_schedule_cheap_tasks_ = true; |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 960 --raster_state_count_[mts.raster_state][tree][mts.tree_bin[tree]]; | 943 --raster_state_count_[mts.raster_state][tree][mts.tree_bin[tree]]; |
| 961 DCHECK_GE(raster_state_count_[mts.raster_state][tree][mts.tree_bin[tree]], 0); | 944 DCHECK_GE(raster_state_count_[mts.raster_state][tree][mts.tree_bin[tree]], 0); |
| 962 | 945 |
| 963 // Increment count for new bin. | 946 // Increment count for new bin. |
| 964 ++raster_state_count_[mts.raster_state][tree][new_tree_bin]; | 947 ++raster_state_count_[mts.raster_state][tree][new_tree_bin]; |
| 965 | 948 |
| 966 mts.tree_bin[tree] = new_tree_bin; | 949 mts.tree_bin[tree] = new_tree_bin; |
| 967 } | 950 } |
| 968 | 951 |
| 969 // static | 952 // static |
| 970 void TileManager::RunRasterTask(uint8* buffer, | 953 void TileManager::RunRasterTask( |
| 971 const gfx::Rect& rect, | 954 uint8* buffer, |
| 972 float contents_scale, | 955 const gfx::Rect& rect, |
| 973 const RasterTaskMetadata& metadata, | 956 float contents_scale, |
| 974 PicturePileImpl* picture_pile, | 957 const RasterTaskMetadata& metadata, |
| 975 RenderingStats* stats) { | 958 RenderingStatsInstrumentation* stats_instrumentation, |
| 959 PicturePileImpl* picture_pile) { |
| 976 TRACE_EVENT2( | 960 TRACE_EVENT2( |
| 977 "cc", "TileManager::RunRasterTask", | 961 "cc", "TileManager::RunRasterTask", |
| 978 "is_on_pending_tree", | 962 "is_on_pending_tree", |
| 979 metadata.is_tile_in_pending_tree_now_bin, | 963 metadata.is_tile_in_pending_tree_now_bin, |
| 980 "is_low_res", | 964 "is_low_res", |
| 981 metadata.tile_resolution == LOW_RESOLUTION); | 965 metadata.tile_resolution == LOW_RESOLUTION); |
| 982 devtools_instrumentation::ScopedRasterTask raster_task(metadata.layer_id); | 966 devtools_instrumentation::ScopedRasterTask raster_task(metadata.layer_id); |
| 983 | 967 |
| 984 DCHECK(picture_pile); | 968 DCHECK(picture_pile); |
| 985 DCHECK(buffer); | 969 DCHECK(buffer); |
| 986 | 970 |
| 987 SkBitmap bitmap; | 971 SkBitmap bitmap; |
| 988 bitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height()); | 972 bitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height()); |
| 989 bitmap.setPixels(buffer); | 973 bitmap.setPixels(buffer); |
| 990 SkDevice device(bitmap); | 974 SkDevice device(bitmap); |
| 991 SkCanvas canvas(&device); | 975 SkCanvas canvas(&device); |
| 992 | 976 |
| 993 base::TimeTicks begin_time; | 977 base::TimeTicks start_time = stats_instrumentation->StartRecording(); |
| 994 if (stats) | |
| 995 begin_time = base::TimeTicks::HighResNow(); | |
| 996 | 978 |
| 997 int64 total_pixels_rasterized = 0; | 979 int64 total_pixels_rasterized = 0; |
| 998 picture_pile->Raster(&canvas, rect, contents_scale, | 980 picture_pile->Raster(&canvas, rect, contents_scale, |
| 999 &total_pixels_rasterized); | 981 &total_pixels_rasterized); |
| 1000 | 982 |
| 1001 if (stats) { | 983 base::TimeDelta duration = stats_instrumentation->EndRecording(start_time); |
| 1002 stats->totalPixelsRasterized += total_pixels_rasterized; | |
| 1003 | 984 |
| 1004 base::TimeTicks end_time = base::TimeTicks::HighResNow(); | 985 if (stats_instrumentation->record_rendering_stats()) { |
| 1005 base::TimeDelta duration = end_time - begin_time; | 986 stats_instrumentation->AddRaster(duration, |
| 1006 stats->totalRasterizeTime += duration; | 987 total_pixels_rasterized, |
| 1007 if (metadata.is_tile_in_pending_tree_now_bin) | 988 metadata.is_tile_in_pending_tree_now_bin); |
| 1008 stats->totalRasterizeTimeForNowBinsOnPendingTree += duration; | |
| 1009 | 989 |
| 1010 UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.PictureRasterTimeMS", | 990 UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.PictureRasterTimeMS", |
| 1011 duration.InMilliseconds(), | 991 duration.InMilliseconds(), |
| 1012 0, | 992 0, |
| 1013 10, | 993 10, |
| 1014 10); | 994 10); |
| 1015 | 995 |
| 1016 if (metadata.prediction_benchmarking) { | 996 if (metadata.prediction_benchmarking) { |
| 1017 PicturePileImpl::Analysis analysis; | 997 PicturePileImpl::Analysis analysis; |
| 1018 picture_pile->AnalyzeInRect(rect, contents_scale, &analysis); | 998 picture_pile->AnalyzeInRect(rect, contents_scale, &analysis); |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1091 | 1071 |
| 1092 if (is_predicted_transparent) | 1072 if (is_predicted_transparent) |
| 1093 UMA_HISTOGRAM_BOOLEAN( | 1073 UMA_HISTOGRAM_BOOLEAN( |
| 1094 "Renderer4.ColorPredictor.PredictedTransparentIsActually", | 1074 "Renderer4.ColorPredictor.PredictedTransparentIsActually", |
| 1095 is_transparent); | 1075 is_transparent); |
| 1096 UMA_HISTOGRAM_BOOLEAN("Renderer4.ColorPredictor.IsActuallyTransparent", | 1076 UMA_HISTOGRAM_BOOLEAN("Renderer4.ColorPredictor.IsActuallyTransparent", |
| 1097 is_transparent); | 1077 is_transparent); |
| 1098 } | 1078 } |
| 1099 | 1079 |
| 1100 // static | 1080 // static |
| 1101 void TileManager::RunImageDecodeTask(skia::LazyPixelRef* pixel_ref, | 1081 void TileManager::RunImageDecodeTask( |
| 1102 RenderingStats* stats) { | 1082 skia::LazyPixelRef* pixel_ref, |
| 1083 RenderingStatsInstrumentation* stats_instrumentation) { |
| 1103 TRACE_EVENT0("cc", "TileManager::RunImageDecodeTask"); | 1084 TRACE_EVENT0("cc", "TileManager::RunImageDecodeTask"); |
| 1104 base::TimeTicks decode_begin_time; | 1085 base::TimeTicks start_time = stats_instrumentation->StartRecording(); |
| 1105 if (stats) | |
| 1106 decode_begin_time = base::TimeTicks::HighResNow(); | |
| 1107 pixel_ref->Decode(); | 1086 pixel_ref->Decode(); |
| 1108 if (stats) { | 1087 base::TimeDelta duration = stats_instrumentation->EndRecording(start_time); |
| 1109 stats->totalDeferredImageDecodeCount++; | 1088 stats_instrumentation->AddDeferredImageDecode(duration); |
| 1110 stats->totalDeferredImageDecodeTime += | |
| 1111 base::TimeTicks::HighResNow() - decode_begin_time; | |
| 1112 } | |
| 1113 } | 1089 } |
| 1114 | 1090 |
| 1115 } // namespace cc | 1091 } // namespace cc |
| OLD | NEW |