OLD | NEW |
1 // Copyright 2011 The Chromium Authors. All rights reserved. | 1 // Copyright 2011 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/trees/thread_proxy.h" | 5 #include "cc/trees/thread_proxy.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/auto_reset.h" | 9 #include "base/auto_reset.h" |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
70 commit_requested_(false), | 70 commit_requested_(false), |
71 commit_request_sent_to_impl_thread_(false), | 71 commit_request_sent_to_impl_thread_(false), |
72 created_offscreen_context_provider_(false), | 72 created_offscreen_context_provider_(false), |
73 layer_tree_host_(layer_tree_host), | 73 layer_tree_host_(layer_tree_host), |
74 started_(false), | 74 started_(false), |
75 textures_acquired_(true), | 75 textures_acquired_(true), |
76 in_composite_and_readback_(false), | 76 in_composite_and_readback_(false), |
77 manage_tiles_pending_(false), | 77 manage_tiles_pending_(false), |
78 weak_factory_on_impl_thread_(this), | 78 weak_factory_on_impl_thread_(this), |
79 weak_factory_(this), | 79 weak_factory_(this), |
| 80 frame_did_draw_(false), |
80 begin_frame_sent_to_main_thread_completion_event_on_impl_thread_(NULL), | 81 begin_frame_sent_to_main_thread_completion_event_on_impl_thread_(NULL), |
81 readback_request_on_impl_thread_(NULL), | 82 readback_request_on_impl_thread_(NULL), |
82 commit_completion_event_on_impl_thread_(NULL), | 83 commit_completion_event_on_impl_thread_(NULL), |
83 completion_event_for_commit_held_on_tree_activation_(NULL), | 84 completion_event_for_commit_held_on_tree_activation_(NULL), |
84 texture_acquisition_completion_event_on_impl_thread_(NULL), | 85 texture_acquisition_completion_event_on_impl_thread_(NULL), |
85 next_frame_is_newly_committed_frame_on_impl_thread_(false), | 86 next_frame_is_newly_committed_frame_on_impl_thread_(false), |
86 throttle_frame_production_( | 87 throttle_frame_production_( |
87 layer_tree_host->settings().throttle_frame_production), | 88 layer_tree_host->settings().throttle_frame_production), |
88 begin_frame_scheduling_enabled_( | 89 begin_frame_scheduling_enabled_( |
89 layer_tree_host->settings().begin_frame_scheduling_enabled), | 90 layer_tree_host->settings().begin_frame_scheduling_enabled), |
(...skipping 26 matching lines...) Expand all Loading... |
116 if (defer_commits_) { | 117 if (defer_commits_) { |
117 TRACE_EVENT0("cc", "CompositeAndReadback_DeferCommit"); | 118 TRACE_EVENT0("cc", "CompositeAndReadback_DeferCommit"); |
118 return false; | 119 return false; |
119 } | 120 } |
120 | 121 |
121 if (!layer_tree_host_->InitializeOutputSurfaceIfNeeded()) { | 122 if (!layer_tree_host_->InitializeOutputSurfaceIfNeeded()) { |
122 TRACE_EVENT0("cc", "CompositeAndReadback_EarlyOut_LR_Uninitialized"); | 123 TRACE_EVENT0("cc", "CompositeAndReadback_EarlyOut_LR_Uninitialized"); |
123 return false; | 124 return false; |
124 } | 125 } |
125 | 126 |
126 // Perform a synchronous commit. | 127 // Perform a synchronous commit with an associated readback. |
| 128 ReadbackRequest request; |
| 129 request.rect = rect; |
| 130 request.pixels = pixels; |
127 { | 131 { |
128 DebugScopedSetMainThreadBlocked main_thread_blocked(this); | 132 DebugScopedSetMainThreadBlocked main_thread_blocked(this); |
129 CompletionEvent begin_frame_sent_to_main_thread_completion; | 133 CompletionEvent begin_frame_sent_to_main_thread_completion; |
130 Proxy::ImplThreadTaskRunner()->PostTask( | 134 Proxy::ImplThreadTaskRunner()->PostTask( |
131 FROM_HERE, | 135 FROM_HERE, |
132 base::Bind(&ThreadProxy::ForceCommitOnImplThread, | 136 base::Bind(&ThreadProxy::ForceCommitForReadbackOnImplThread, |
133 impl_thread_weak_ptr_, | 137 impl_thread_weak_ptr_, |
134 &begin_frame_sent_to_main_thread_completion)); | 138 &begin_frame_sent_to_main_thread_completion, |
| 139 &request)); |
135 begin_frame_sent_to_main_thread_completion.Wait(); | 140 begin_frame_sent_to_main_thread_completion.Wait(); |
136 } | 141 } |
137 | 142 |
138 in_composite_and_readback_ = true; | 143 in_composite_and_readback_ = true; |
| 144 // This is the forced commit. |
| 145 // Note: The Impl thread also queues a separate BeginFrameOnMainThread on the |
| 146 // main thread, which will be called after this CompositeAndReadback |
| 147 // completes, to replace the forced commit. |
139 BeginFrameOnMainThread(scoped_ptr<BeginFrameAndCommitState>()); | 148 BeginFrameOnMainThread(scoped_ptr<BeginFrameAndCommitState>()); |
140 in_composite_and_readback_ = false; | 149 in_composite_and_readback_ = false; |
141 | 150 |
142 // Composite and readback requires a second commit to undo any changes | 151 // Composite and readback requires a second commit to undo any changes |
143 // that it made. | 152 // that it made. |
144 can_cancel_commit_ = false; | 153 can_cancel_commit_ = false; |
145 | 154 |
146 // Perform a synchronous readback. | 155 request.completion.Wait(); |
147 ReadbackRequest request; | |
148 request.rect = rect; | |
149 request.pixels = pixels; | |
150 { | |
151 DebugScopedSetMainThreadBlocked main_thread_blocked(this); | |
152 Proxy::ImplThreadTaskRunner()->PostTask( | |
153 FROM_HERE, | |
154 base::Bind(&ThreadProxy::RequestReadbackOnImplThread, | |
155 impl_thread_weak_ptr_, | |
156 &request)); | |
157 request.completion.Wait(); | |
158 } | |
159 return request.success; | 156 return request.success; |
160 } | 157 } |
161 | 158 |
162 void ThreadProxy::ForceCommitOnImplThread(CompletionEvent* completion) { | 159 void ThreadProxy::ForceCommitForReadbackOnImplThread( |
163 TRACE_EVENT0("cc", "ThreadProxy::ForceCommitOnImplThread"); | 160 CompletionEvent* begin_frame_sent_completion, |
| 161 ReadbackRequest* request) { |
| 162 TRACE_EVENT0("cc", "ThreadProxy::ForceCommitForReadbackOnImplThread"); |
164 DCHECK(IsImplThread()); | 163 DCHECK(IsImplThread()); |
165 DCHECK(!begin_frame_sent_to_main_thread_completion_event_on_impl_thread_); | 164 DCHECK(!begin_frame_sent_to_main_thread_completion_event_on_impl_thread_); |
| 165 DCHECK(!readback_request_on_impl_thread_); |
166 | 166 |
167 scheduler_on_impl_thread_->SetNeedsForcedCommit(); | |
168 if (scheduler_on_impl_thread_->CommitPending()) { | |
169 completion->Signal(); | |
170 return; | |
171 } | |
172 | |
173 begin_frame_sent_to_main_thread_completion_event_on_impl_thread_ = completion; | |
174 } | |
175 | |
176 void ThreadProxy::RequestReadbackOnImplThread(ReadbackRequest* request) { | |
177 DCHECK(Proxy::IsImplThread()); | |
178 DCHECK(!readback_request_on_impl_thread_); | |
179 if (!layer_tree_host_impl_) { | 167 if (!layer_tree_host_impl_) { |
| 168 begin_frame_sent_completion->Signal(); |
180 request->success = false; | 169 request->success = false; |
181 request->completion.Signal(); | 170 request->completion.Signal(); |
182 return; | 171 return; |
183 } | 172 } |
184 | 173 |
185 readback_request_on_impl_thread_ = request; | 174 readback_request_on_impl_thread_ = request; |
186 scheduler_on_impl_thread_->SetNeedsRedraw(); | 175 |
187 scheduler_on_impl_thread_->SetNeedsForcedRedraw(); | 176 scheduler_on_impl_thread_->SetNeedsForcedCommitForReadback(); |
| 177 if (scheduler_on_impl_thread_->CommitPending()) { |
| 178 begin_frame_sent_completion->Signal(); |
| 179 return; |
| 180 } |
| 181 |
| 182 begin_frame_sent_to_main_thread_completion_event_on_impl_thread_ = |
| 183 begin_frame_sent_completion; |
188 } | 184 } |
189 | 185 |
190 void ThreadProxy::FinishAllRendering() { | 186 void ThreadProxy::FinishAllRendering() { |
191 DCHECK(Proxy::IsMainThread()); | 187 DCHECK(Proxy::IsMainThread()); |
192 DCHECK(!defer_commits_); | 188 DCHECK(!defer_commits_); |
193 | 189 |
194 // Make sure all GL drawing is finished on the impl thread. | 190 // Make sure all GL drawing is finished on the impl thread. |
195 DebugScopedSetMainThreadBlocked main_thread_blocked(this); | 191 DebugScopedSetMainThreadBlocked main_thread_blocked(this); |
196 CompletionEvent completion; | 192 CompletionEvent completion; |
197 Proxy::ImplThreadTaskRunner()->PostTask( | 193 Proxy::ImplThreadTaskRunner()->PostTask( |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
386 Proxy::MainThreadTaskRunner()->PostTask( | 382 Proxy::MainThreadTaskRunner()->PostTask( |
387 FROM_HERE, | 383 FROM_HERE, |
388 base::Bind(&ThreadProxy::DidCompleteSwapBuffers, main_thread_weak_ptr_)); | 384 base::Bind(&ThreadProxy::DidCompleteSwapBuffers, main_thread_weak_ptr_)); |
389 } | 385 } |
390 | 386 |
391 void ThreadProxy::SetNeedsBeginFrameOnImplThread(bool enable) { | 387 void ThreadProxy::SetNeedsBeginFrameOnImplThread(bool enable) { |
392 DCHECK(IsImplThread()); | 388 DCHECK(IsImplThread()); |
393 TRACE_EVENT1("cc", "ThreadProxy::SetNeedsBeginFrameOnImplThread", | 389 TRACE_EVENT1("cc", "ThreadProxy::SetNeedsBeginFrameOnImplThread", |
394 "enable", enable); | 390 "enable", enable); |
395 layer_tree_host_impl_->SetNeedsBeginFrame(enable); | 391 layer_tree_host_impl_->SetNeedsBeginFrame(enable); |
| 392 layer_tree_host_impl_->UpdateBackgroundAnimateTicking(!enable); |
396 } | 393 } |
397 | 394 |
398 void ThreadProxy::BeginFrameOnImplThread(const BeginFrameArgs& args) { | 395 void ThreadProxy::BeginFrameOnImplThread(const BeginFrameArgs& args) { |
399 DCHECK(IsImplThread()); | 396 DCHECK(IsImplThread()); |
400 TRACE_EVENT0("cc", "ThreadProxy::BeginFrameOnImplThread"); | 397 TRACE_EVENT0("cc", "ThreadProxy::BeginFrameOnImplThread"); |
| 398 |
| 399 base::TimeTicks monotonic_time = |
| 400 layer_tree_host_impl_->CurrentFrameTimeTicks(); |
| 401 base::Time wall_clock_time = layer_tree_host_impl_->CurrentFrameTime(); |
| 402 if (layer_tree_host_impl_->active_tree()->root_layer()) |
| 403 layer_tree_host_impl_->Animate(monotonic_time, wall_clock_time); |
| 404 |
| 405 // Reinitialize for the current frame. |
| 406 frame_did_draw_ = false; |
| 407 |
401 scheduler_on_impl_thread_->BeginFrame(args); | 408 scheduler_on_impl_thread_->BeginFrame(args); |
402 } | 409 } |
403 | 410 |
| 411 void ThreadProxy::DidBeginFrameDeadlineOnImplThread() { |
| 412 // Do not start animations if we skip drawing the frame to avoid |
| 413 // checkerboarding. |
| 414 if (layer_tree_host_impl_->active_tree()->root_layer()) { |
| 415 layer_tree_host_impl_->UpdateAnimationState( |
| 416 frame_did_draw_ || !layer_tree_host_impl_->CanDraw()); |
| 417 } |
| 418 layer_tree_host_impl_->ResetCurrentFrameTimeForNextFrame(); |
| 419 } |
| 420 |
404 void ThreadProxy::OnCanDrawStateChanged(bool can_draw) { | 421 void ThreadProxy::OnCanDrawStateChanged(bool can_draw) { |
405 DCHECK(IsImplThread()); | 422 DCHECK(IsImplThread()); |
406 TRACE_EVENT1( | 423 TRACE_EVENT1( |
407 "cc", "ThreadProxy::OnCanDrawStateChanged", "can_draw", can_draw); | 424 "cc", "ThreadProxy::OnCanDrawStateChanged", "can_draw", can_draw); |
408 scheduler_on_impl_thread_->SetCanDraw(can_draw); | 425 scheduler_on_impl_thread_->SetCanDraw(can_draw); |
409 layer_tree_host_impl_->UpdateBackgroundAnimateTicking( | 426 layer_tree_host_impl_->UpdateBackgroundAnimateTicking( |
410 !scheduler_on_impl_thread_->WillDrawIfNeeded()); | 427 !scheduler_on_impl_thread_->WillDrawIfNeeded()); |
411 } | 428 } |
412 | 429 |
| 430 void ThreadProxy::NotifyReadyToActivate() { |
| 431 TRACE_EVENT0("cc", "ThreadProxy::NotifyReadyToActivate"); |
| 432 scheduler_on_impl_thread_->NotifyReadyToActivate(); |
| 433 } |
| 434 |
413 void ThreadProxy::OnHasPendingTreeStateChanged(bool has_pending_tree) { | 435 void ThreadProxy::OnHasPendingTreeStateChanged(bool has_pending_tree) { |
414 DCHECK(IsImplThread()); | 436 DCHECK(IsImplThread()); |
415 TRACE_EVENT1("cc", "ThreadProxy::OnHasPendingTreeStateChanged", | 437 TRACE_EVENT1("cc", "ThreadProxy::OnHasPendingTreeStateChanged", |
416 "has_pending_tree", has_pending_tree); | 438 "has_pending_tree", has_pending_tree); |
417 scheduler_on_impl_thread_->SetHasPendingTree(has_pending_tree); | 439 scheduler_on_impl_thread_->SetHasTrees( |
| 440 has_pending_tree, |
| 441 layer_tree_host_impl_->active_tree() |
| 442 ? !layer_tree_host_impl_->active_tree()->root_layer() |
| 443 : true); |
418 } | 444 } |
419 | 445 |
420 void ThreadProxy::SetNeedsCommitOnImplThread() { | 446 void ThreadProxy::SetNeedsCommitOnImplThread() { |
421 DCHECK(IsImplThread()); | 447 DCHECK(IsImplThread()); |
422 TRACE_EVENT0("cc", "ThreadProxy::SetNeedsCommitOnImplThread"); | 448 TRACE_EVENT0("cc", "ThreadProxy::SetNeedsCommitOnImplThread"); |
423 scheduler_on_impl_thread_->SetNeedsCommit(); | 449 scheduler_on_impl_thread_->SetNeedsCommit(); |
424 } | 450 } |
425 | 451 |
426 void ThreadProxy::PostAnimationEventsToMainThreadOnImplThread( | 452 void ThreadProxy::PostAnimationEventsToMainThreadOnImplThread( |
427 scoped_ptr<AnimationEventsVector> events, | 453 scoped_ptr<AnimationEventsVector> events, |
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
661 TRACE_EVENT0("cc", "ThreadProxy::FinishAllRenderingOnImplThread"); | 687 TRACE_EVENT0("cc", "ThreadProxy::FinishAllRenderingOnImplThread"); |
662 DCHECK(IsImplThread()); | 688 DCHECK(IsImplThread()); |
663 layer_tree_host_impl_->FinishAllRendering(); | 689 layer_tree_host_impl_->FinishAllRendering(); |
664 completion->Signal(); | 690 completion->Signal(); |
665 } | 691 } |
666 | 692 |
667 void ThreadProxy::ScheduledActionSendBeginFrameToMainThread() { | 693 void ThreadProxy::ScheduledActionSendBeginFrameToMainThread() { |
668 TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionSendBeginFrameToMainThread"); | 694 TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionSendBeginFrameToMainThread"); |
669 scoped_ptr<BeginFrameAndCommitState> begin_frame_state( | 695 scoped_ptr<BeginFrameAndCommitState> begin_frame_state( |
670 new BeginFrameAndCommitState); | 696 new BeginFrameAndCommitState); |
| 697 |
671 begin_frame_state->monotonic_frame_begin_time = | 698 begin_frame_state->monotonic_frame_begin_time = |
672 layer_tree_host_impl_->CurrentPhysicalTimeTicks(); | 699 layer_tree_host_impl_->CurrentPhysicalTimeTicks(); |
673 begin_frame_state->scroll_info = | 700 begin_frame_state->scroll_info = |
674 layer_tree_host_impl_->ProcessScrollDeltas(); | 701 layer_tree_host_impl_->ProcessScrollDeltas(); |
675 | 702 |
676 if (!layer_tree_host_impl_->settings().impl_side_painting) { | 703 if (!layer_tree_host_impl_->settings().impl_side_painting) { |
677 DCHECK_GT(layer_tree_host_impl_->memory_allocation_limit_bytes(), 0u); | 704 DCHECK_GT(layer_tree_host_impl_->memory_allocation_limit_bytes(), 0u); |
678 } | 705 } |
| 706 |
679 begin_frame_state->memory_allocation_limit_bytes = | 707 begin_frame_state->memory_allocation_limit_bytes = |
680 layer_tree_host_impl_->memory_allocation_limit_bytes(); | 708 layer_tree_host_impl_->memory_allocation_limit_bytes(); |
681 Proxy::MainThreadTaskRunner()->PostTask( | 709 Proxy::MainThreadTaskRunner()->PostTask( |
682 FROM_HERE, | 710 FROM_HERE, |
683 base::Bind(&ThreadProxy::BeginFrameOnMainThread, | 711 base::Bind(&ThreadProxy::BeginFrameOnMainThread, |
684 main_thread_weak_ptr_, | 712 main_thread_weak_ptr_, |
685 base::Passed(&begin_frame_state))); | 713 base::Passed(&begin_frame_state))); |
686 | 714 |
687 if (begin_frame_sent_to_main_thread_completion_event_on_impl_thread_) { | 715 if (begin_frame_sent_to_main_thread_completion_event_on_impl_thread_) { |
688 begin_frame_sent_to_main_thread_completion_event_on_impl_thread_->Signal(); | 716 begin_frame_sent_to_main_thread_completion_event_on_impl_thread_->Signal(); |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
844 } | 872 } |
845 | 873 |
846 layer_tree_host_->CommitComplete(); | 874 layer_tree_host_->CommitComplete(); |
847 layer_tree_host_->DidBeginFrame(); | 875 layer_tree_host_->DidBeginFrame(); |
848 } | 876 } |
849 | 877 |
850 void ThreadProxy::StartCommitOnImplThread( | 878 void ThreadProxy::StartCommitOnImplThread( |
851 CompletionEvent* completion, | 879 CompletionEvent* completion, |
852 ResourceUpdateQueue* raw_queue, | 880 ResourceUpdateQueue* raw_queue, |
853 scoped_refptr<cc::ContextProvider> offscreen_context_provider) { | 881 scoped_refptr<cc::ContextProvider> offscreen_context_provider) { |
854 scoped_ptr<ResourceUpdateQueue> queue(raw_queue); | |
855 | |
856 TRACE_EVENT0("cc", "ThreadProxy::StartCommitOnImplThread"); | 882 TRACE_EVENT0("cc", "ThreadProxy::StartCommitOnImplThread"); |
857 DCHECK(!commit_completion_event_on_impl_thread_); | 883 DCHECK(!commit_completion_event_on_impl_thread_); |
858 DCHECK(IsImplThread() && IsMainThreadBlocked()); | 884 DCHECK(IsImplThread() && IsMainThreadBlocked()); |
859 DCHECK(scheduler_on_impl_thread_); | 885 DCHECK(scheduler_on_impl_thread_); |
860 DCHECK(scheduler_on_impl_thread_->CommitPending()); | 886 DCHECK(scheduler_on_impl_thread_->CommitPending()); |
861 | 887 |
862 if (!layer_tree_host_impl_) { | 888 if (!layer_tree_host_impl_) { |
863 TRACE_EVENT0("cc", "EarlyOut_NoLayerTree"); | 889 TRACE_EVENT0("cc", "EarlyOut_NoLayerTree"); |
864 completion->Signal(); | 890 completion->Signal(); |
| 891 |
865 return; | 892 return; |
866 } | 893 } |
867 | 894 |
| 895 scoped_ptr<ResourceUpdateQueue> queue(raw_queue); |
| 896 |
868 if (offscreen_context_provider.get()) | 897 if (offscreen_context_provider.get()) |
869 offscreen_context_provider->BindToCurrentThread(); | 898 offscreen_context_provider->BindToCurrentThread(); |
870 layer_tree_host_impl_->resource_provider()-> | 899 layer_tree_host_impl_->resource_provider()-> |
871 set_offscreen_context_provider(offscreen_context_provider); | 900 set_offscreen_context_provider(offscreen_context_provider); |
872 | 901 |
873 if (layer_tree_host_->contents_texture_manager()) { | 902 if (layer_tree_host_->contents_texture_manager()) { |
874 if (layer_tree_host_->contents_texture_manager()-> | 903 if (layer_tree_host_->contents_texture_manager()-> |
875 LinkedEvictedBackingsExist()) { | 904 LinkedEvictedBackingsExist()) { |
876 // Clear any uploads we were making to textures linked to evicted | 905 // Clear any uploads we were making to textures linked to evicted |
877 // resources | 906 // resources |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
924 current_resource_update_controller_on_impl_thread_->Finalize(); | 953 current_resource_update_controller_on_impl_thread_->Finalize(); |
925 current_resource_update_controller_on_impl_thread_.reset(); | 954 current_resource_update_controller_on_impl_thread_.reset(); |
926 | 955 |
927 layer_tree_host_impl_->BeginCommit(); | 956 layer_tree_host_impl_->BeginCommit(); |
928 layer_tree_host_->BeginCommitOnImplThread(layer_tree_host_impl_.get()); | 957 layer_tree_host_->BeginCommitOnImplThread(layer_tree_host_impl_.get()); |
929 layer_tree_host_->FinishCommitOnImplThread(layer_tree_host_impl_.get()); | 958 layer_tree_host_->FinishCommitOnImplThread(layer_tree_host_impl_.get()); |
930 layer_tree_host_impl_->CommitComplete(); | 959 layer_tree_host_impl_->CommitComplete(); |
931 | 960 |
932 SetInputThrottledUntilCommitOnImplThread(false); | 961 SetInputThrottledUntilCommitOnImplThread(false); |
933 | 962 |
934 layer_tree_host_impl_->UpdateBackgroundAnimateTicking( | |
935 !scheduler_on_impl_thread_->WillDrawIfNeeded()); | |
936 | |
937 next_frame_is_newly_committed_frame_on_impl_thread_ = true; | 963 next_frame_is_newly_committed_frame_on_impl_thread_ = true; |
938 | 964 |
939 if (layer_tree_host_->settings().impl_side_painting && | 965 if (layer_tree_host_->settings().impl_side_painting && |
940 layer_tree_host_->BlocksPendingCommit() && | 966 layer_tree_host_->BlocksPendingCommit() && |
941 layer_tree_host_impl_->pending_tree()) { | 967 layer_tree_host_impl_->pending_tree()) { |
942 // For some layer types in impl-side painting, the commit is held until | 968 // For some layer types in impl-side painting, the commit is held until |
943 // the pending tree is activated. It's also possible that the | 969 // the pending tree is activated. It's also possible that the |
944 // pending tree has already activated if there was no work to be done. | 970 // pending tree has already activated if there was no work to be done. |
945 TRACE_EVENT_INSTANT0("cc", "HoldCommit", TRACE_EVENT_SCOPE_THREAD); | 971 TRACE_EVENT_INSTANT0("cc", "HoldCommit", TRACE_EVENT_SCOPE_THREAD); |
946 completion_event_for_commit_held_on_tree_activation_ = | 972 completion_event_for_commit_held_on_tree_activation_ = |
947 commit_completion_event_on_impl_thread_; | 973 commit_completion_event_on_impl_thread_; |
948 commit_completion_event_on_impl_thread_ = NULL; | 974 commit_completion_event_on_impl_thread_ = NULL; |
949 } else { | 975 } else { |
950 commit_completion_event_on_impl_thread_->Signal(); | 976 commit_completion_event_on_impl_thread_->Signal(); |
951 commit_completion_event_on_impl_thread_ = NULL; | 977 commit_completion_event_on_impl_thread_ = NULL; |
952 } | 978 } |
953 | 979 |
954 commit_complete_time_ = base::TimeTicks::HighResNow(); | 980 commit_complete_time_ = base::TimeTicks::HighResNow(); |
955 begin_frame_to_commit_duration_history_.InsertSample( | 981 begin_frame_to_commit_duration_history_.InsertSample( |
956 commit_complete_time_ - begin_frame_sent_to_main_thread_time_); | 982 commit_complete_time_ - begin_frame_sent_to_main_thread_time_); |
957 | 983 |
| 984 // The commit may have added animations, requiring us to start |
| 985 // background ticking. |
| 986 layer_tree_host_impl_->UpdateBackgroundAnimateTicking( |
| 987 !scheduler_on_impl_thread_->WillDrawIfNeeded()); |
958 // SetVisible kicks off the next scheduler action, so this must be last. | 988 // SetVisible kicks off the next scheduler action, so this must be last. |
959 scheduler_on_impl_thread_->SetVisible(layer_tree_host_impl_->visible()); | 989 scheduler_on_impl_thread_->SetVisible(layer_tree_host_impl_->visible()); |
960 } | 990 } |
961 | 991 |
962 void ThreadProxy::ScheduledActionUpdateVisibleTiles() { | 992 void ThreadProxy::ScheduledActionUpdateVisibleTiles() { |
963 DCHECK(IsImplThread()); | 993 DCHECK(IsImplThread()); |
964 TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionUpdateVisibleTiles"); | 994 TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionUpdateVisibleTiles"); |
965 layer_tree_host_impl_->UpdateVisibleTiles(); | 995 layer_tree_host_impl_->UpdateVisibleTiles(); |
966 } | 996 } |
967 | 997 |
968 void ThreadProxy::ScheduledActionActivatePendingTreeIfNeeded() { | 998 void ThreadProxy::ScheduledActionActivatePendingTree() { |
969 DCHECK(IsImplThread()); | 999 DCHECK(IsImplThread()); |
970 TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionActivatePendingTreeIfNeeded"); | 1000 TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionActivatePendingTree"); |
971 layer_tree_host_impl_->ActivatePendingTreeIfNeeded(); | 1001 layer_tree_host_impl_->ActivatePendingTree(); |
972 } | 1002 } |
973 | 1003 |
974 void ThreadProxy::ScheduledActionBeginOutputSurfaceCreation() { | 1004 void ThreadProxy::ScheduledActionBeginOutputSurfaceCreation() { |
975 DCHECK(IsImplThread()); | 1005 DCHECK(IsImplThread()); |
976 Proxy::MainThreadTaskRunner()->PostTask( | 1006 Proxy::MainThreadTaskRunner()->PostTask( |
977 FROM_HERE, | 1007 FROM_HERE, |
978 base::Bind(&ThreadProxy::CreateAndInitializeOutputSurface, | 1008 base::Bind(&ThreadProxy::CreateAndInitializeOutputSurface, |
979 main_thread_weak_ptr_)); | 1009 main_thread_weak_ptr_)); |
980 } | 1010 } |
981 | 1011 |
982 ScheduledActionDrawAndSwapResult | 1012 DrawSwapReadbackResult ThreadProxy::ScheduledActionDrawSwapReadbackInternal( |
983 ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) { | 1013 bool forced_draw, |
984 TRACE_EVENT1( | 1014 bool swap_requested, |
985 "cc", "ThreadProxy::ScheduledActionDrawAndSwap", "forced", forced_draw); | 1015 bool readback_requested) { |
| 1016 TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionDrawSwapReadback"); |
986 | 1017 |
987 ScheduledActionDrawAndSwapResult result; | 1018 DrawSwapReadbackResult result; |
988 result.did_draw = false; | 1019 result.did_draw = false; |
989 result.did_swap = false; | 1020 result.did_swap = false; |
990 DCHECK(IsImplThread()); | 1021 DCHECK(IsImplThread()); |
991 DCHECK(layer_tree_host_impl_.get()); | 1022 DCHECK(layer_tree_host_impl_.get()); |
992 if (!layer_tree_host_impl_) | 1023 if (!layer_tree_host_impl_) |
993 return result; | 1024 return result; |
994 | 1025 |
995 DCHECK(layer_tree_host_impl_->renderer()); | 1026 DCHECK(layer_tree_host_impl_->renderer()); |
996 if (!layer_tree_host_impl_->renderer()) | 1027 if (!layer_tree_host_impl_->renderer()) |
997 return result; | 1028 return result; |
998 | 1029 |
999 base::TimeTicks monotonic_time = | |
1000 layer_tree_host_impl_->CurrentFrameTimeTicks(); | |
1001 base::Time wall_clock_time = layer_tree_host_impl_->CurrentFrameTime(); | |
1002 | |
1003 // TODO(enne): This should probably happen post-animate. | |
1004 if (layer_tree_host_impl_->pending_tree()) { | |
1005 layer_tree_host_impl_->ActivatePendingTreeIfNeeded(); | |
1006 if (layer_tree_host_impl_->pending_tree()) | |
1007 layer_tree_host_impl_->pending_tree()->UpdateDrawProperties(); | |
1008 } | |
1009 layer_tree_host_impl_->Animate(monotonic_time, wall_clock_time); | |
1010 layer_tree_host_impl_->UpdateBackgroundAnimateTicking(false); | |
1011 | |
1012 base::TimeTicks start_time = base::TimeTicks::HighResNow(); | 1030 base::TimeTicks start_time = base::TimeTicks::HighResNow(); |
1013 base::TimeDelta draw_duration_estimate = DrawDurationEstimate(); | 1031 base::TimeDelta draw_duration_estimate = DrawDurationEstimate(); |
1014 base::AutoReset<bool> mark_inside(&inside_draw_, true); | 1032 base::AutoReset<bool> mark_inside(&inside_draw_, true); |
1015 | 1033 |
1016 // This method is called on a forced draw, regardless of whether we are able | 1034 // This method is called on a forced draw, regardless of whether we are able |
1017 // to produce a frame, as the calling site on main thread is blocked until its | 1035 // to produce a frame, as the calling site on main thread is blocked until its |
1018 // request completes, and we signal completion here. If CanDraw() is false, we | 1036 // request completes, and we signal completion here. If CanDraw() is false, we |
1019 // will indicate success=false to the caller, but we must still signal | 1037 // will indicate success=false to the caller, but we must still signal |
1020 // completion to avoid deadlock. | 1038 // completion to avoid deadlock. |
1021 | 1039 |
1022 // We guard PrepareToDraw() with CanDraw() because it always returns a valid | 1040 // We guard PrepareToDraw() with CanDraw() because it always returns a valid |
1023 // frame, so can only be used when such a frame is possible. Since | 1041 // frame, so can only be used when such a frame is possible. Since |
1024 // DrawLayers() depends on the result of PrepareToDraw(), it is guarded on | 1042 // DrawLayers() depends on the result of PrepareToDraw(), it is guarded on |
1025 // CanDraw() as well. | 1043 // CanDraw() as well. |
1026 | 1044 |
1027 bool drawing_for_readback = !!readback_request_on_impl_thread_; | 1045 bool drawing_for_readback = |
| 1046 readback_requested && !!readback_request_on_impl_thread_; |
1028 bool can_do_readback = layer_tree_host_impl_->renderer()->CanReadPixels(); | 1047 bool can_do_readback = layer_tree_host_impl_->renderer()->CanReadPixels(); |
1029 | 1048 |
1030 LayerTreeHostImpl::FrameData frame; | 1049 LayerTreeHostImpl::FrameData frame; |
1031 bool draw_frame = false; | 1050 bool draw_frame = false; |
1032 bool start_ready_animations = true; | |
1033 | 1051 |
1034 if (layer_tree_host_impl_->CanDraw() && | 1052 if (layer_tree_host_impl_->CanDraw() && |
1035 (!drawing_for_readback || can_do_readback)) { | 1053 (!drawing_for_readback || can_do_readback)) { |
1036 // If it is for a readback, make sure we draw the portion being read back. | 1054 // If it is for a readback, make sure we draw the portion being read back. |
1037 gfx::Rect readback_rect; | 1055 gfx::Rect readback_rect; |
1038 if (drawing_for_readback) | 1056 if (drawing_for_readback) |
1039 readback_rect = readback_request_on_impl_thread_->rect; | 1057 readback_rect = readback_request_on_impl_thread_->rect; |
1040 | 1058 |
1041 // Do not start animations if we skip drawing the frame to avoid | |
1042 // checkerboarding. | |
1043 if (layer_tree_host_impl_->PrepareToDraw(&frame, readback_rect) || | 1059 if (layer_tree_host_impl_->PrepareToDraw(&frame, readback_rect) || |
1044 forced_draw) | 1060 forced_draw) |
1045 draw_frame = true; | 1061 draw_frame = true; |
1046 else | |
1047 start_ready_animations = false; | |
1048 } | 1062 } |
1049 | 1063 |
| 1064 frame_did_draw_ = draw_frame; |
| 1065 |
1050 if (draw_frame) { | 1066 if (draw_frame) { |
1051 layer_tree_host_impl_->DrawLayers( | 1067 layer_tree_host_impl_->DrawLayers( |
1052 &frame, | 1068 &frame, |
1053 scheduler_on_impl_thread_->LastBeginFrameOnImplThreadTime()); | 1069 scheduler_on_impl_thread_->LastBeginFrameOnImplThreadTime()); |
1054 result.did_draw = true; | 1070 result.did_draw = true; |
1055 } | 1071 } |
1056 layer_tree_host_impl_->DidDrawAllLayers(frame); | 1072 layer_tree_host_impl_->DidDrawAllLayers(frame); |
1057 | 1073 |
1058 layer_tree_host_impl_->UpdateAnimationState(start_ready_animations); | |
1059 | |
1060 // Check for a pending CompositeAndReadback. | 1074 // Check for a pending CompositeAndReadback. |
1061 if (readback_request_on_impl_thread_) { | 1075 if (drawing_for_readback) { |
1062 readback_request_on_impl_thread_->success = false; | 1076 result.did_readback = false; |
1063 if (draw_frame) { | 1077 if (draw_frame) { |
1064 layer_tree_host_impl_->Readback(readback_request_on_impl_thread_->pixels, | 1078 layer_tree_host_impl_->Readback(readback_request_on_impl_thread_->pixels, |
1065 readback_request_on_impl_thread_->rect); | 1079 readback_request_on_impl_thread_->rect); |
1066 readback_request_on_impl_thread_->success = | 1080 result.did_readback = !layer_tree_host_impl_->IsContextLost(); |
1067 !layer_tree_host_impl_->IsContextLost(); | |
1068 } | 1081 } |
| 1082 readback_request_on_impl_thread_->success = result.did_readback; |
1069 readback_request_on_impl_thread_->completion.Signal(); | 1083 readback_request_on_impl_thread_->completion.Signal(); |
1070 readback_request_on_impl_thread_ = NULL; | 1084 readback_request_on_impl_thread_ = NULL; |
1071 } else if (draw_frame) { | 1085 } else if (draw_frame && swap_requested) { |
1072 result.did_swap = layer_tree_host_impl_->SwapBuffers(frame); | 1086 result.did_swap = layer_tree_host_impl_->SwapBuffers(frame); |
1073 | 1087 |
1074 if (frame.contains_incomplete_tile) | 1088 if (frame.contains_incomplete_tile) |
1075 DidSwapUseIncompleteTileOnImplThread(); | 1089 DidSwapUseIncompleteTileOnImplThread(); |
1076 } | 1090 } |
1077 | 1091 |
1078 // Tell the main thread that the the newly-commited frame was drawn. | 1092 // Tell the main thread that the the newly-commited frame was drawn. |
1079 if (next_frame_is_newly_committed_frame_on_impl_thread_) { | 1093 if (next_frame_is_newly_committed_frame_on_impl_thread_) { |
1080 next_frame_is_newly_committed_frame_on_impl_thread_ = false; | 1094 next_frame_is_newly_committed_frame_on_impl_thread_ = false; |
1081 Proxy::MainThreadTaskRunner()->PostTask( | 1095 Proxy::MainThreadTaskRunner()->PostTask( |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1154 texture_acquisition_completion_event_on_impl_thread_ = completion; | 1168 texture_acquisition_completion_event_on_impl_thread_ = completion; |
1155 scheduler_on_impl_thread_->SetMainThreadNeedsLayerTextures(); | 1169 scheduler_on_impl_thread_->SetMainThreadNeedsLayerTextures(); |
1156 } | 1170 } |
1157 | 1171 |
1158 void ThreadProxy::ScheduledActionAcquireLayerTexturesForMainThread() { | 1172 void ThreadProxy::ScheduledActionAcquireLayerTexturesForMainThread() { |
1159 DCHECK(texture_acquisition_completion_event_on_impl_thread_); | 1173 DCHECK(texture_acquisition_completion_event_on_impl_thread_); |
1160 texture_acquisition_completion_event_on_impl_thread_->Signal(); | 1174 texture_acquisition_completion_event_on_impl_thread_->Signal(); |
1161 texture_acquisition_completion_event_on_impl_thread_ = NULL; | 1175 texture_acquisition_completion_event_on_impl_thread_ = NULL; |
1162 } | 1176 } |
1163 | 1177 |
1164 ScheduledActionDrawAndSwapResult | 1178 DrawSwapReadbackResult ThreadProxy::ScheduledActionDrawAndSwapIfPossible() { |
1165 ThreadProxy::ScheduledActionDrawAndSwapIfPossible() { | 1179 bool forced_draw = false; |
1166 return ScheduledActionDrawAndSwapInternal(false); | 1180 bool swap_requested = true; |
| 1181 bool readback_requested = false; |
| 1182 return ScheduledActionDrawSwapReadbackInternal( |
| 1183 forced_draw, swap_requested, readback_requested); |
1167 } | 1184 } |
1168 | 1185 |
1169 ScheduledActionDrawAndSwapResult | 1186 DrawSwapReadbackResult ThreadProxy::ScheduledActionDrawAndSwapForced() { |
1170 ThreadProxy::ScheduledActionDrawAndSwapForced() { | 1187 bool forced_draw = true; |
1171 return ScheduledActionDrawAndSwapInternal(true); | 1188 bool swap_requested = true; |
| 1189 bool readback_requested = false; |
| 1190 return ScheduledActionDrawSwapReadbackInternal( |
| 1191 forced_draw, swap_requested, readback_requested); |
| 1192 } |
| 1193 |
| 1194 DrawSwapReadbackResult ThreadProxy::ScheduledActionDrawAndReadback() { |
| 1195 bool forced_draw = true; |
| 1196 bool swap_requested = false; |
| 1197 bool readback_requested = true; |
| 1198 return ScheduledActionDrawSwapReadbackInternal( |
| 1199 forced_draw, swap_requested, readback_requested); |
1172 } | 1200 } |
1173 | 1201 |
1174 void ThreadProxy::DidAnticipatedDrawTimeChange(base::TimeTicks time) { | 1202 void ThreadProxy::DidAnticipatedDrawTimeChange(base::TimeTicks time) { |
1175 if (current_resource_update_controller_on_impl_thread_) | 1203 if (current_resource_update_controller_on_impl_thread_) |
1176 current_resource_update_controller_on_impl_thread_ | 1204 current_resource_update_controller_on_impl_thread_->PerformMoreUpdates( |
1177 ->PerformMoreUpdates(time); | 1205 time); |
1178 layer_tree_host_impl_->ResetCurrentFrameTimeForNextFrame(); | |
1179 } | 1206 } |
1180 | 1207 |
1181 base::TimeDelta ThreadProxy::DrawDurationEstimate() { | 1208 base::TimeDelta ThreadProxy::DrawDurationEstimate() { |
1182 base::TimeDelta historical_estimate = | 1209 base::TimeDelta historical_estimate = |
1183 draw_duration_history_.Percentile(kDrawDurationEstimationPercentile); | 1210 draw_duration_history_.Percentile(kDrawDurationEstimationPercentile); |
1184 base::TimeDelta padding = base::TimeDelta::FromMicroseconds( | 1211 base::TimeDelta padding = base::TimeDelta::FromMicroseconds( |
1185 kDrawDurationEstimatePaddingInMicroseconds); | 1212 kDrawDurationEstimatePaddingInMicroseconds); |
1186 return historical_estimate + padding; | 1213 return historical_estimate + padding; |
1187 } | 1214 } |
1188 | 1215 |
1189 base::TimeDelta ThreadProxy::BeginFrameToCommitDurationEstimate() { | 1216 base::TimeDelta ThreadProxy::BeginFrameToCommitDurationEstimate() { |
1190 return begin_frame_to_commit_duration_history_.Percentile( | 1217 return begin_frame_to_commit_duration_history_.Percentile( |
1191 kCommitAndActivationDurationEstimationPercentile); | 1218 kCommitAndActivationDurationEstimationPercentile); |
1192 } | 1219 } |
1193 | 1220 |
1194 base::TimeDelta ThreadProxy::CommitToActivateDurationEstimate() { | 1221 base::TimeDelta ThreadProxy::CommitToActivateDurationEstimate() { |
1195 return commit_to_activate_duration_history_.Percentile( | 1222 return commit_to_activate_duration_history_.Percentile( |
1196 kCommitAndActivationDurationEstimationPercentile); | 1223 kCommitAndActivationDurationEstimationPercentile); |
1197 } | 1224 } |
1198 | 1225 |
| 1226 void ThreadProxy::PostBeginFrameDeadline(const base::Closure& closure, |
| 1227 base::TimeTicks deadline) { |
| 1228 base::TimeDelta delta = deadline - base::TimeTicks::Now(); |
| 1229 if (delta <= base::TimeDelta()) |
| 1230 delta = base::TimeDelta(); |
| 1231 Proxy::ImplThreadTaskRunner()->PostDelayedTask(FROM_HERE, closure, delta); |
| 1232 } |
| 1233 |
1199 void ThreadProxy::ReadyToFinalizeTextureUpdates() { | 1234 void ThreadProxy::ReadyToFinalizeTextureUpdates() { |
1200 DCHECK(IsImplThread()); | 1235 DCHECK(IsImplThread()); |
1201 scheduler_on_impl_thread_->FinishCommit(); | 1236 scheduler_on_impl_thread_->FinishCommit(); |
1202 } | 1237 } |
1203 | 1238 |
1204 void ThreadProxy::DidCommitAndDrawFrame() { | 1239 void ThreadProxy::DidCommitAndDrawFrame() { |
1205 DCHECK(IsMainThread()); | 1240 DCHECK(IsMainThread()); |
1206 if (!layer_tree_host_) | 1241 if (!layer_tree_host_) |
1207 return; | 1242 return; |
1208 layer_tree_host_->DidCommitAndDrawFrame(); | 1243 layer_tree_host_->DidCommitAndDrawFrame(); |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1259 scheduler_on_impl_thread_->HasInitializedOutputSurface(); | 1294 scheduler_on_impl_thread_->HasInitializedOutputSurface(); |
1260 completion->Signal(); | 1295 completion->Signal(); |
1261 } | 1296 } |
1262 | 1297 |
1263 void ThreadProxy::InitializeImplOnImplThread(CompletionEvent* completion) { | 1298 void ThreadProxy::InitializeImplOnImplThread(CompletionEvent* completion) { |
1264 TRACE_EVENT0("cc", "ThreadProxy::InitializeImplOnImplThread"); | 1299 TRACE_EVENT0("cc", "ThreadProxy::InitializeImplOnImplThread"); |
1265 DCHECK(IsImplThread()); | 1300 DCHECK(IsImplThread()); |
1266 layer_tree_host_impl_ = layer_tree_host_->CreateLayerTreeHostImpl(this); | 1301 layer_tree_host_impl_ = layer_tree_host_->CreateLayerTreeHostImpl(this); |
1267 const LayerTreeSettings& settings = layer_tree_host_->settings(); | 1302 const LayerTreeSettings& settings = layer_tree_host_->settings(); |
1268 SchedulerSettings scheduler_settings; | 1303 SchedulerSettings scheduler_settings; |
| 1304 scheduler_settings.deadline_scheduling_enabled = |
| 1305 settings.deadline_scheduling_enabled; |
1269 scheduler_settings.impl_side_painting = settings.impl_side_painting; | 1306 scheduler_settings.impl_side_painting = settings.impl_side_painting; |
1270 scheduler_settings.timeout_and_draw_when_animation_checkerboards = | 1307 scheduler_settings.timeout_and_draw_when_animation_checkerboards = |
1271 settings.timeout_and_draw_when_animation_checkerboards; | 1308 settings.timeout_and_draw_when_animation_checkerboards; |
1272 scheduler_settings.using_synchronous_renderer_compositor = | 1309 scheduler_settings.using_synchronous_renderer_compositor = |
1273 settings.using_synchronous_renderer_compositor; | 1310 settings.using_synchronous_renderer_compositor; |
1274 scheduler_settings.throttle_frame_production = | 1311 scheduler_settings.throttle_frame_production = |
1275 settings.throttle_frame_production; | 1312 settings.throttle_frame_production; |
| 1313 scheduler_settings.use_begin_frame_workaround_for_crbug_249806 = true; |
1276 scheduler_on_impl_thread_ = Scheduler::Create(this, scheduler_settings); | 1314 scheduler_on_impl_thread_ = Scheduler::Create(this, scheduler_settings); |
1277 scheduler_on_impl_thread_->SetVisible(layer_tree_host_impl_->visible()); | 1315 scheduler_on_impl_thread_->SetVisible(layer_tree_host_impl_->visible()); |
1278 | 1316 |
1279 impl_thread_weak_ptr_ = weak_factory_on_impl_thread_.GetWeakPtr(); | 1317 impl_thread_weak_ptr_ = weak_factory_on_impl_thread_.GetWeakPtr(); |
1280 completion->Signal(); | 1318 completion->Signal(); |
1281 } | 1319 } |
1282 | 1320 |
1283 void ThreadProxy::InitializeOutputSurfaceOnImplThread( | 1321 void ThreadProxy::InitializeOutputSurfaceOnImplThread( |
1284 CompletionEvent* completion, | 1322 CompletionEvent* completion, |
1285 scoped_ptr<OutputSurface> output_surface, | 1323 scoped_ptr<OutputSurface> output_surface, |
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1515 DCHECK(layer_tree_host_impl_->settings().impl_side_painting); | 1553 DCHECK(layer_tree_host_impl_->settings().impl_side_painting); |
1516 completion_event_for_commit_held_on_tree_activation_->Signal(); | 1554 completion_event_for_commit_held_on_tree_activation_->Signal(); |
1517 completion_event_for_commit_held_on_tree_activation_ = NULL; | 1555 completion_event_for_commit_held_on_tree_activation_ = NULL; |
1518 } | 1556 } |
1519 | 1557 |
1520 commit_to_activate_duration_history_.InsertSample( | 1558 commit_to_activate_duration_history_.InsertSample( |
1521 base::TimeTicks::HighResNow() - commit_complete_time_); | 1559 base::TimeTicks::HighResNow() - commit_complete_time_); |
1522 } | 1560 } |
1523 | 1561 |
1524 } // namespace cc | 1562 } // namespace cc |
OLD | NEW |