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/single_thread_proxy.h" | 5 #include "cc/trees/single_thread_proxy.h" |
6 | 6 |
7 #include "base/auto_reset.h" | 7 #include "base/auto_reset.h" |
8 #include "base/debug/trace_event.h" | 8 #include "base/debug/trace_event.h" |
9 #include "cc/debug/benchmark_instrumentation.h" | 9 #include "cc/debug/benchmark_instrumentation.h" |
10 #include "cc/output/context_provider.h" | 10 #include "cc/output/context_provider.h" |
11 #include "cc/output/output_surface.h" | 11 #include "cc/output/output_surface.h" |
12 #include "cc/quads/draw_quad.h" | 12 #include "cc/quads/draw_quad.h" |
13 #include "cc/resources/prioritized_resource_manager.h" | 13 #include "cc/resources/prioritized_resource_manager.h" |
14 #include "cc/resources/resource_update_controller.h" | 14 #include "cc/resources/resource_update_controller.h" |
15 #include "cc/trees/blocking_task_runner.h" | 15 #include "cc/trees/blocking_task_runner.h" |
16 #include "cc/trees/layer_tree_host.h" | 16 #include "cc/trees/layer_tree_host.h" |
17 #include "cc/trees/layer_tree_host_single_thread_client.h" | 17 #include "cc/trees/layer_tree_host_single_thread_client.h" |
18 #include "cc/trees/layer_tree_impl.h" | 18 #include "cc/trees/layer_tree_impl.h" |
19 #include "cc/trees/scoped_abort_remaining_swap_promises.h" | |
20 #include "ui/gfx/frame_time.h" | 19 #include "ui/gfx/frame_time.h" |
21 | 20 |
22 namespace cc { | 21 namespace cc { |
23 | 22 |
24 scoped_ptr<Proxy> SingleThreadProxy::Create( | 23 scoped_ptr<Proxy> SingleThreadProxy::Create( |
25 LayerTreeHost* layer_tree_host, | 24 LayerTreeHost* layer_tree_host, |
26 LayerTreeHostSingleThreadClient* client, | 25 LayerTreeHostSingleThreadClient* client, |
27 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) { | 26 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) { |
28 return make_scoped_ptr( | 27 return make_scoped_ptr( |
29 new SingleThreadProxy(layer_tree_host, client, main_task_runner)) | 28 new SingleThreadProxy(layer_tree_host, client, main_task_runner)) |
30 .PassAs<Proxy>(); | 29 .PassAs<Proxy>(); |
31 } | 30 } |
32 | 31 |
33 SingleThreadProxy::SingleThreadProxy( | 32 SingleThreadProxy::SingleThreadProxy( |
34 LayerTreeHost* layer_tree_host, | 33 LayerTreeHost* layer_tree_host, |
35 LayerTreeHostSingleThreadClient* client, | 34 LayerTreeHostSingleThreadClient* client, |
36 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) | 35 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) |
37 : Proxy(main_task_runner, NULL), | 36 : Proxy(main_task_runner, NULL), |
38 layer_tree_host_(layer_tree_host), | 37 layer_tree_host_(layer_tree_host), |
39 client_(client), | 38 client_(client), |
40 timing_history_(layer_tree_host->rendering_stats_instrumentation()), | |
41 next_frame_is_newly_committed_frame_(false), | 39 next_frame_is_newly_committed_frame_(false), |
42 inside_draw_(false), | 40 inside_draw_(false) { |
43 defer_commits_(false), | |
44 commit_was_deferred_(false), | |
45 commit_requested_(false), | |
46 weak_factory_(this) { | |
47 TRACE_EVENT0("cc", "SingleThreadProxy::SingleThreadProxy"); | 41 TRACE_EVENT0("cc", "SingleThreadProxy::SingleThreadProxy"); |
48 DCHECK(Proxy::IsMainThread()); | 42 DCHECK(Proxy::IsMainThread()); |
49 DCHECK(layer_tree_host); | 43 DCHECK(layer_tree_host); |
50 | 44 |
51 // Impl-side painting not supported without threaded compositing. | 45 // Impl-side painting not supported without threaded compositing. |
52 CHECK(!layer_tree_host->settings().impl_side_painting) | 46 CHECK(!layer_tree_host->settings().impl_side_painting) |
53 << "Threaded compositing must be enabled to use impl-side painting."; | 47 << "Threaded compositing must be enabled to use impl-side painting."; |
54 } | 48 } |
55 | 49 |
56 void SingleThreadProxy::Start() { | 50 void SingleThreadProxy::Start() { |
(...skipping 19 matching lines...) Expand all Loading... |
76 | 70 |
77 bool SingleThreadProxy::IsStarted() const { | 71 bool SingleThreadProxy::IsStarted() const { |
78 DCHECK(Proxy::IsMainThread()); | 72 DCHECK(Proxy::IsMainThread()); |
79 return layer_tree_host_impl_; | 73 return layer_tree_host_impl_; |
80 } | 74 } |
81 | 75 |
82 void SingleThreadProxy::SetLayerTreeHostClientReady() { | 76 void SingleThreadProxy::SetLayerTreeHostClientReady() { |
83 TRACE_EVENT0("cc", "SingleThreadProxy::SetLayerTreeHostClientReady"); | 77 TRACE_EVENT0("cc", "SingleThreadProxy::SetLayerTreeHostClientReady"); |
84 // Scheduling is controlled by the embedder in the single thread case, so | 78 // Scheduling is controlled by the embedder in the single thread case, so |
85 // nothing to do. | 79 // nothing to do. |
86 DCHECK(Proxy::IsMainThread()); | |
87 DebugScopedSetImplThread impl(this); | |
88 if (layer_tree_host_->settings().single_thread_proxy_scheduler && | |
89 !scheduler_on_impl_thread_) { | |
90 SchedulerSettings scheduler_settings(layer_tree_host_->settings()); | |
91 scheduler_on_impl_thread_ = Scheduler::Create(this, | |
92 scheduler_settings, | |
93 layer_tree_host_->id(), | |
94 MainThreadTaskRunner()); | |
95 scheduler_on_impl_thread_->SetCanStart(); | |
96 scheduler_on_impl_thread_->SetVisible(layer_tree_host_impl_->visible()); | |
97 } | |
98 } | 80 } |
99 | 81 |
100 void SingleThreadProxy::SetVisible(bool visible) { | 82 void SingleThreadProxy::SetVisible(bool visible) { |
101 TRACE_EVENT0("cc", "SingleThreadProxy::SetVisible"); | 83 TRACE_EVENT0("cc", "SingleThreadProxy::SetVisible"); |
102 DebugScopedSetImplThread impl(this); | 84 DebugScopedSetImplThread impl(this); |
103 layer_tree_host_impl_->SetVisible(visible); | 85 layer_tree_host_impl_->SetVisible(visible); |
104 if (scheduler_on_impl_thread_) | 86 |
105 scheduler_on_impl_thread_->SetVisible(layer_tree_host_impl_->visible()); | |
106 // Changing visibility could change ShouldComposite(). | 87 // Changing visibility could change ShouldComposite(). |
107 UpdateBackgroundAnimateTicking(); | 88 UpdateBackgroundAnimateTicking(); |
108 } | 89 } |
109 | 90 |
110 void SingleThreadProxy::CreateAndInitializeOutputSurface() { | 91 void SingleThreadProxy::CreateAndInitializeOutputSurface() { |
111 TRACE_EVENT0( | 92 TRACE_EVENT0( |
112 "cc", "SingleThreadProxy::CreateAndInitializeOutputSurface"); | 93 "cc", "SingleThreadProxy::CreateAndInitializeOutputSurface"); |
113 DCHECK(Proxy::IsMainThread()); | 94 DCHECK(Proxy::IsMainThread()); |
114 DCHECK(layer_tree_host_->output_surface_lost()); | 95 DCHECK(layer_tree_host_->output_surface_lost()); |
115 | 96 |
116 scoped_ptr<OutputSurface> output_surface = | 97 scoped_ptr<OutputSurface> output_surface = |
117 layer_tree_host_->CreateOutputSurface(); | 98 layer_tree_host_->CreateOutputSurface(); |
118 | 99 |
119 renderer_capabilities_for_main_thread_ = RendererCapabilities(); | 100 renderer_capabilities_for_main_thread_ = RendererCapabilities(); |
120 | 101 |
121 bool success = !!output_surface; | 102 bool success = !!output_surface; |
122 if (success) { | 103 if (success) { |
123 DebugScopedSetMainThreadBlocked main_thread_blocked(this); | 104 DebugScopedSetMainThreadBlocked main_thread_blocked(this); |
124 DebugScopedSetImplThread impl(this); | 105 DebugScopedSetImplThread impl(this); |
125 layer_tree_host_->DeleteContentsTexturesOnImplThread( | 106 layer_tree_host_->DeleteContentsTexturesOnImplThread( |
126 layer_tree_host_impl_->resource_provider()); | 107 layer_tree_host_impl_->resource_provider()); |
127 success = layer_tree_host_impl_->InitializeRenderer(output_surface.Pass()); | 108 success = layer_tree_host_impl_->InitializeRenderer(output_surface.Pass()); |
128 } | 109 } |
129 | 110 |
130 layer_tree_host_->OnCreateAndInitializeOutputSurfaceAttempted(success); | 111 layer_tree_host_->OnCreateAndInitializeOutputSurfaceAttempted(success); |
131 | 112 |
132 if (success) { | 113 if (!success) { |
133 if (scheduler_on_impl_thread_) | 114 // Force another recreation attempt to happen by requesting another commit. |
134 scheduler_on_impl_thread_->DidCreateAndInitializeOutputSurface(); | 115 SetNeedsCommit(); |
135 } else if (Proxy::MainThreadTaskRunner()) { | |
136 MainThreadTaskRunner()->PostTask( | |
137 FROM_HERE, | |
138 base::Bind(&SingleThreadProxy::CreateAndInitializeOutputSurface, | |
139 weak_factory_.GetWeakPtr())); | |
140 } | 116 } |
141 } | 117 } |
142 | 118 |
143 const RendererCapabilities& SingleThreadProxy::GetRendererCapabilities() const { | 119 const RendererCapabilities& SingleThreadProxy::GetRendererCapabilities() const { |
144 DCHECK(Proxy::IsMainThread()); | 120 DCHECK(Proxy::IsMainThread()); |
145 DCHECK(!layer_tree_host_->output_surface_lost()); | 121 DCHECK(!layer_tree_host_->output_surface_lost()); |
146 return renderer_capabilities_for_main_thread_; | 122 return renderer_capabilities_for_main_thread_; |
147 } | 123 } |
148 | 124 |
149 void SingleThreadProxy::SetNeedsAnimate() { | 125 void SingleThreadProxy::SetNeedsAnimate() { |
150 TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsAnimate"); | 126 TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsAnimate"); |
151 DCHECK(Proxy::IsMainThread()); | 127 DCHECK(Proxy::IsMainThread()); |
152 client_->ScheduleAnimation(); | 128 client_->ScheduleAnimation(); |
153 SetNeedsCommit(); | |
154 } | 129 } |
155 | 130 |
156 void SingleThreadProxy::SetNeedsUpdateLayers() { | 131 void SingleThreadProxy::SetNeedsUpdateLayers() { |
157 TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsUpdateLayers"); | 132 TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsUpdateLayers"); |
158 DCHECK(Proxy::IsMainThread()); | 133 DCHECK(Proxy::IsMainThread()); |
159 SetNeedsCommit(); | 134 client_->ScheduleComposite(); |
160 } | 135 } |
161 | 136 |
162 void SingleThreadProxy::DoCommit(base::TimeTicks frame_begin_time) { | 137 void SingleThreadProxy::DoCommit(scoped_ptr<ResourceUpdateQueue> queue) { |
163 TRACE_EVENT0("cc", "SingleThreadProxy::DoCommit"); | 138 TRACE_EVENT0("cc", "SingleThreadProxy::DoCommit"); |
164 DCHECK(Proxy::IsMainThread()); | 139 DCHECK(Proxy::IsMainThread()); |
165 layer_tree_host_->WillBeginMainFrame(); | |
166 layer_tree_host_->UpdateClientAnimations(frame_begin_time); | |
167 layer_tree_host_->AnimateLayers(frame_begin_time); | |
168 layer_tree_host_->Layout(); | |
169 commit_requested_ = false; | |
170 | |
171 if (PrioritizedResourceManager* contents_texture_manager = | |
172 layer_tree_host_->contents_texture_manager()) { | |
173 contents_texture_manager->UnlinkAndClearEvictedBackings(); | |
174 contents_texture_manager->SetMaxMemoryLimitBytes( | |
175 layer_tree_host_impl_->memory_allocation_limit_bytes()); | |
176 contents_texture_manager->SetExternalPriorityCutoff( | |
177 layer_tree_host_impl_->memory_allocation_priority_cutoff()); | |
178 } | |
179 | |
180 scoped_ptr<ResourceUpdateQueue> queue = | |
181 make_scoped_ptr(new ResourceUpdateQueue); | |
182 | |
183 layer_tree_host_->UpdateLayers(queue.get()); | |
184 | |
185 layer_tree_host_->WillCommit(); | |
186 | |
187 // Commit immediately. | 140 // Commit immediately. |
188 { | 141 { |
189 DebugScopedSetMainThreadBlocked main_thread_blocked(this); | 142 DebugScopedSetMainThreadBlocked main_thread_blocked(this); |
190 DebugScopedSetImplThread impl(this); | 143 DebugScopedSetImplThread impl(this); |
191 | 144 |
192 // This CapturePostTasks should be destroyed before CommitComplete() is | 145 // This CapturePostTasks should be destroyed before CommitComplete() is |
193 // called since that goes out to the embedder, and we want the embedder | 146 // called since that goes out to the embedder, and we want the embedder |
194 // to receive its callbacks before that. | 147 // to receive its callbacks before that. |
195 BlockingTaskRunner::CapturePostTasks blocked; | 148 BlockingTaskRunner::CapturePostTasks blocked; |
196 | 149 |
197 layer_tree_host_impl_->BeginCommit(); | 150 layer_tree_host_impl_->BeginCommit(); |
198 | 151 |
199 if (PrioritizedResourceManager* contents_texture_manager = | 152 if (PrioritizedResourceManager* contents_texture_manager = |
200 layer_tree_host_->contents_texture_manager()) { | 153 layer_tree_host_->contents_texture_manager()) { |
201 contents_texture_manager->PushTexturePrioritiesToBackings(); | 154 contents_texture_manager->PushTexturePrioritiesToBackings(); |
202 } | 155 } |
203 layer_tree_host_->BeginCommitOnImplThread(layer_tree_host_impl_.get()); | 156 layer_tree_host_->BeginCommitOnImplThread(layer_tree_host_impl_.get()); |
204 | 157 |
205 scoped_ptr<ResourceUpdateController> update_controller = | 158 scoped_ptr<ResourceUpdateController> update_controller = |
206 ResourceUpdateController::Create( | 159 ResourceUpdateController::Create( |
207 NULL, | 160 NULL, |
208 MainThreadTaskRunner(), | 161 Proxy::MainThreadTaskRunner(), |
209 queue.Pass(), | 162 queue.Pass(), |
210 layer_tree_host_impl_->resource_provider()); | 163 layer_tree_host_impl_->resource_provider()); |
211 update_controller->Finalize(); | 164 update_controller->Finalize(); |
212 | 165 |
213 if (layer_tree_host_impl_->EvictedUIResourcesExist()) | 166 if (layer_tree_host_impl_->EvictedUIResourcesExist()) |
214 layer_tree_host_->RecreateUIResources(); | 167 layer_tree_host_->RecreateUIResources(); |
215 | 168 |
216 layer_tree_host_->FinishCommitOnImplThread(layer_tree_host_impl_.get()); | 169 layer_tree_host_->FinishCommitOnImplThread(layer_tree_host_impl_.get()); |
217 | 170 |
218 layer_tree_host_impl_->CommitComplete(); | 171 layer_tree_host_impl_->CommitComplete(); |
219 | 172 |
220 UpdateBackgroundAnimateTicking(); | |
221 | |
222 #if DCHECK_IS_ON | 173 #if DCHECK_IS_ON |
223 // In the single-threaded case, the scale and scroll deltas should never be | 174 // In the single-threaded case, the scale and scroll deltas should never be |
224 // touched on the impl layer tree. | 175 // touched on the impl layer tree. |
225 scoped_ptr<ScrollAndScaleSet> scroll_info = | 176 scoped_ptr<ScrollAndScaleSet> scroll_info = |
226 layer_tree_host_impl_->ProcessScrollDeltas(); | 177 layer_tree_host_impl_->ProcessScrollDeltas(); |
227 DCHECK(!scroll_info->scrolls.size()); | 178 DCHECK(!scroll_info->scrolls.size()); |
228 DCHECK_EQ(1.f, scroll_info->page_scale_delta); | 179 DCHECK_EQ(1.f, scroll_info->page_scale_delta); |
229 #endif | 180 #endif |
230 | 181 |
231 RenderingStatsInstrumentation* stats_instrumentation = | 182 RenderingStatsInstrumentation* stats_instrumentation = |
232 layer_tree_host_->rendering_stats_instrumentation(); | 183 layer_tree_host_->rendering_stats_instrumentation(); |
233 benchmark_instrumentation::IssueMainThreadRenderingStatsEvent( | 184 benchmark_instrumentation::IssueMainThreadRenderingStatsEvent( |
234 stats_instrumentation->main_thread_rendering_stats()); | 185 stats_instrumentation->main_thread_rendering_stats()); |
235 stats_instrumentation->AccumulateAndClearMainThreadStats(); | 186 stats_instrumentation->AccumulateAndClearMainThreadStats(); |
236 } | 187 } |
237 layer_tree_host_->CommitComplete(); | 188 layer_tree_host_->CommitComplete(); |
238 layer_tree_host_->DidBeginMainFrame(); | |
239 timing_history_.DidCommit(); | |
240 | |
241 next_frame_is_newly_committed_frame_ = true; | 189 next_frame_is_newly_committed_frame_ = true; |
242 } | 190 } |
243 | 191 |
244 void SingleThreadProxy::SetNeedsCommit() { | 192 void SingleThreadProxy::SetNeedsCommit() { |
245 DCHECK(Proxy::IsMainThread()); | 193 DCHECK(Proxy::IsMainThread()); |
246 DebugScopedSetImplThread impl(this); | |
247 client_->ScheduleComposite(); | 194 client_->ScheduleComposite(); |
248 if (scheduler_on_impl_thread_) | |
249 scheduler_on_impl_thread_->SetNeedsCommit(); | |
250 commit_requested_ = true; | |
251 } | 195 } |
252 | 196 |
253 void SingleThreadProxy::SetNeedsRedraw(const gfx::Rect& damage_rect) { | 197 void SingleThreadProxy::SetNeedsRedraw(const gfx::Rect& damage_rect) { |
254 TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsRedraw"); | 198 TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsRedraw"); |
255 DCHECK(Proxy::IsMainThread()); | 199 SetNeedsRedrawRectOnImplThread(damage_rect); |
256 DebugScopedSetImplThread impl(this); | |
257 client_->ScheduleComposite(); | 200 client_->ScheduleComposite(); |
258 SetNeedsRedrawRectOnImplThread(damage_rect); | |
259 } | 201 } |
260 | 202 |
261 void SingleThreadProxy::SetNextCommitWaitsForActivation() { | 203 void SingleThreadProxy::SetNextCommitWaitsForActivation() { |
262 // There is no activation here other than commit. So do nothing. | 204 // There is no activation here other than commit. So do nothing. |
263 DCHECK(Proxy::IsMainThread()); | |
264 } | 205 } |
265 | 206 |
266 void SingleThreadProxy::SetDeferCommits(bool defer_commits) { | 207 void SingleThreadProxy::SetDeferCommits(bool defer_commits) { |
267 DCHECK(Proxy::IsMainThread()); | |
268 // Deferring commits only makes sense if there's a scheduler. | |
269 if (!scheduler_on_impl_thread_) | |
270 return; | |
271 if (defer_commits_ == defer_commits) | |
272 return; | |
273 | |
274 if (defer_commits) | |
275 TRACE_EVENT_ASYNC_BEGIN0("cc", "SingleThreadProxy::SetDeferCommits", this); | |
276 else | |
277 TRACE_EVENT_ASYNC_END0("cc", "SingleThreadProxy::SetDeferCommits", this); | |
278 | |
279 defer_commits_ = defer_commits; | |
280 if (!defer_commits_ && commit_was_deferred_) { | |
281 commit_was_deferred_ = false; | |
282 BeginMainFrame(); | |
283 } | |
284 } | 208 } |
285 | 209 |
286 bool SingleThreadProxy::CommitRequested() const { | 210 bool SingleThreadProxy::CommitRequested() const { |
287 DCHECK(Proxy::IsMainThread()); | 211 return false; |
288 return commit_requested_; | |
289 } | 212 } |
290 | 213 |
291 bool SingleThreadProxy::BeginMainFrameRequested() const { | 214 bool SingleThreadProxy::BeginMainFrameRequested() const { |
292 DCHECK(Proxy::IsMainThread()); | 215 return false; |
293 return commit_requested_; | |
294 } | 216 } |
295 | 217 |
296 size_t SingleThreadProxy::MaxPartialTextureUpdates() const { | 218 size_t SingleThreadProxy::MaxPartialTextureUpdates() const { |
297 return std::numeric_limits<size_t>::max(); | 219 return std::numeric_limits<size_t>::max(); |
298 } | 220 } |
299 | 221 |
300 void SingleThreadProxy::Stop() { | 222 void SingleThreadProxy::Stop() { |
301 TRACE_EVENT0("cc", "SingleThreadProxy::stop"); | 223 TRACE_EVENT0("cc", "SingleThreadProxy::stop"); |
302 DCHECK(Proxy::IsMainThread()); | 224 DCHECK(Proxy::IsMainThread()); |
303 { | 225 { |
304 DebugScopedSetMainThreadBlocked main_thread_blocked(this); | 226 DebugScopedSetMainThreadBlocked main_thread_blocked(this); |
305 DebugScopedSetImplThread impl(this); | 227 DebugScopedSetImplThread impl(this); |
306 | 228 |
307 BlockingTaskRunner::CapturePostTasks blocked; | 229 BlockingTaskRunner::CapturePostTasks blocked; |
308 layer_tree_host_->DeleteContentsTexturesOnImplThread( | 230 layer_tree_host_->DeleteContentsTexturesOnImplThread( |
309 layer_tree_host_impl_->resource_provider()); | 231 layer_tree_host_impl_->resource_provider()); |
310 scheduler_on_impl_thread_.reset(); | |
311 layer_tree_host_impl_.reset(); | 232 layer_tree_host_impl_.reset(); |
312 } | 233 } |
313 layer_tree_host_ = NULL; | 234 layer_tree_host_ = NULL; |
314 } | 235 } |
315 | 236 |
316 void SingleThreadProxy::OnCanDrawStateChanged(bool can_draw) { | 237 void SingleThreadProxy::OnCanDrawStateChanged(bool can_draw) { |
317 TRACE_EVENT1( | 238 TRACE_EVENT1( |
318 "cc", "SingleThreadProxy::OnCanDrawStateChanged", "can_draw", can_draw); | 239 "cc", "SingleThreadProxy::OnCanDrawStateChanged", "can_draw", can_draw); |
319 DCHECK(Proxy::IsImplThread()); | 240 DCHECK(Proxy::IsImplThread()); |
320 UpdateBackgroundAnimateTicking(); | 241 UpdateBackgroundAnimateTicking(); |
321 if (scheduler_on_impl_thread_) | |
322 scheduler_on_impl_thread_->SetCanDraw(can_draw); | |
323 } | 242 } |
324 | 243 |
325 void SingleThreadProxy::NotifyReadyToActivate() { | 244 void SingleThreadProxy::NotifyReadyToActivate() { |
326 // Impl-side painting only. | 245 // Thread-only feature. |
327 NOTREACHED(); | 246 NOTREACHED(); |
328 } | 247 } |
329 | 248 |
330 void SingleThreadProxy::SetNeedsRedrawOnImplThread() { | 249 void SingleThreadProxy::SetNeedsRedrawOnImplThread() { |
331 client_->ScheduleComposite(); | 250 client_->ScheduleComposite(); |
332 if (scheduler_on_impl_thread_) | |
333 scheduler_on_impl_thread_->SetNeedsRedraw(); | |
334 } | 251 } |
335 | 252 |
336 void SingleThreadProxy::SetNeedsAnimateOnImplThread() { | 253 void SingleThreadProxy::SetNeedsAnimateOnImplThread() { |
337 SetNeedsRedrawOnImplThread(); | 254 SetNeedsRedrawOnImplThread(); |
338 } | 255 } |
339 | 256 |
340 void SingleThreadProxy::SetNeedsManageTilesOnImplThread() { | 257 void SingleThreadProxy::SetNeedsManageTilesOnImplThread() { |
341 // Impl-side painting only. | 258 // Thread-only/Impl-side-painting-only feature. |
342 NOTREACHED(); | 259 NOTREACHED(); |
343 } | 260 } |
344 | 261 |
345 void SingleThreadProxy::SetNeedsRedrawRectOnImplThread( | 262 void SingleThreadProxy::SetNeedsRedrawRectOnImplThread( |
346 const gfx::Rect& damage_rect) { | 263 const gfx::Rect& damage_rect) { |
| 264 // TODO(brianderson): Once we move render_widget scheduling into this class, |
| 265 // we can treat redraw requests more efficiently than CommitAndRedraw |
| 266 // requests. |
347 layer_tree_host_impl_->SetViewportDamage(damage_rect); | 267 layer_tree_host_impl_->SetViewportDamage(damage_rect); |
348 SetNeedsRedrawOnImplThread(); | 268 SetNeedsCommit(); |
349 } | 269 } |
350 | 270 |
351 void SingleThreadProxy::DidInitializeVisibleTileOnImplThread() { | 271 void SingleThreadProxy::DidInitializeVisibleTileOnImplThread() { |
352 // Impl-side painting only. | 272 // Impl-side painting only. |
353 NOTREACHED(); | 273 NOTREACHED(); |
354 } | 274 } |
355 | 275 |
356 void SingleThreadProxy::SetNeedsCommitOnImplThread() { | 276 void SingleThreadProxy::SetNeedsCommitOnImplThread() { |
357 client_->ScheduleComposite(); | 277 client_->ScheduleComposite(); |
358 if (scheduler_on_impl_thread_) | |
359 scheduler_on_impl_thread_->SetNeedsCommit(); | |
360 } | 278 } |
361 | 279 |
362 void SingleThreadProxy::PostAnimationEventsToMainThreadOnImplThread( | 280 void SingleThreadProxy::PostAnimationEventsToMainThreadOnImplThread( |
363 scoped_ptr<AnimationEventsVector> events) { | 281 scoped_ptr<AnimationEventsVector> events) { |
364 TRACE_EVENT0( | 282 TRACE_EVENT0( |
365 "cc", "SingleThreadProxy::PostAnimationEventsToMainThreadOnImplThread"); | 283 "cc", "SingleThreadProxy::PostAnimationEventsToMainThreadOnImplThread"); |
366 DCHECK(Proxy::IsImplThread()); | 284 DCHECK(Proxy::IsImplThread()); |
367 DebugScopedSetMainThread main(this); | 285 DebugScopedSetMainThread main(this); |
368 layer_tree_host_->SetAnimationEvents(events.Pass()); | 286 layer_tree_host_->SetAnimationEvents(events.Pass()); |
369 } | 287 } |
(...skipping 16 matching lines...) Expand all Loading... |
386 } | 304 } |
387 | 305 |
388 bool SingleThreadProxy::IsInsideDraw() { return inside_draw_; } | 306 bool SingleThreadProxy::IsInsideDraw() { return inside_draw_; } |
389 | 307 |
390 void SingleThreadProxy::UpdateRendererCapabilitiesOnImplThread() { | 308 void SingleThreadProxy::UpdateRendererCapabilitiesOnImplThread() { |
391 DCHECK(IsImplThread()); | 309 DCHECK(IsImplThread()); |
392 renderer_capabilities_for_main_thread_ = | 310 renderer_capabilities_for_main_thread_ = |
393 layer_tree_host_impl_->GetRendererCapabilities().MainThreadCapabilities(); | 311 layer_tree_host_impl_->GetRendererCapabilities().MainThreadCapabilities(); |
394 } | 312 } |
395 | 313 |
396 void SingleThreadProxy::DidManageTiles() { | |
397 // Impl-side painting only. | |
398 NOTREACHED(); | |
399 } | |
400 | |
401 void SingleThreadProxy::DidLoseOutputSurfaceOnImplThread() { | 314 void SingleThreadProxy::DidLoseOutputSurfaceOnImplThread() { |
402 TRACE_EVENT0("cc", "SingleThreadProxy::DidLoseOutputSurfaceOnImplThread"); | 315 TRACE_EVENT0("cc", "SingleThreadProxy::DidLoseOutputSurfaceOnImplThread"); |
403 { | 316 // Cause a commit so we can notice the lost context. |
404 DebugScopedSetMainThread main(this); | 317 SetNeedsCommitOnImplThread(); |
405 // This must happen before we notify the scheduler as it may try to recreate | |
406 // the output surface if already in BEGIN_IMPL_FRAME_STATE_IDLE. | |
407 layer_tree_host_->DidLoseOutputSurface(); | |
408 } | |
409 client_->DidAbortSwapBuffers(); | 318 client_->DidAbortSwapBuffers(); |
410 if (scheduler_on_impl_thread_) | |
411 scheduler_on_impl_thread_->DidLoseOutputSurface(); | |
412 } | 319 } |
413 | 320 |
414 void SingleThreadProxy::DidSwapBuffersOnImplThread() { | 321 void SingleThreadProxy::DidSwapBuffersOnImplThread() { |
415 TRACE_EVENT0("cc", "SingleThreadProxy::DidSwapBuffersOnImplThread"); | |
416 if (scheduler_on_impl_thread_) | |
417 scheduler_on_impl_thread_->DidSwapBuffers(); | |
418 client_->DidPostSwapBuffers(); | 322 client_->DidPostSwapBuffers(); |
419 } | 323 } |
420 | 324 |
421 void SingleThreadProxy::DidSwapBuffersCompleteOnImplThread() { | 325 void SingleThreadProxy::DidSwapBuffersCompleteOnImplThread() { |
422 TRACE_EVENT0("cc", "SingleThreadProxy::DidSwapBuffersCompleteOnImplThread"); | 326 TRACE_EVENT0("cc", "SingleThreadProxy::DidSwapBuffersCompleteOnImplThread"); |
423 if (scheduler_on_impl_thread_) | 327 client_->DidCompleteSwapBuffers(); |
424 scheduler_on_impl_thread_->DidSwapBuffersComplete(); | |
425 layer_tree_host_->DidCompleteSwapBuffers(); | |
426 } | 328 } |
427 | 329 |
428 void SingleThreadProxy::BeginFrame(const BeginFrameArgs& args) { | 330 // Called by the legacy scheduling path (e.g. where render_widget does the |
429 TRACE_EVENT0("cc", "SingleThreadProxy::BeginFrame"); | 331 // scheduling) |
430 if (scheduler_on_impl_thread_) | |
431 scheduler_on_impl_thread_->BeginImplFrame(args); | |
432 } | |
433 | |
434 void SingleThreadProxy::CompositeImmediately(base::TimeTicks frame_begin_time) { | 332 void SingleThreadProxy::CompositeImmediately(base::TimeTicks frame_begin_time) { |
435 TRACE_EVENT0("cc", "SingleThreadProxy::CompositeImmediately"); | 333 TRACE_EVENT0("cc", "SingleThreadProxy::CompositeImmediately"); |
436 DCHECK(Proxy::IsMainThread()); | 334 DCHECK(Proxy::IsMainThread()); |
437 DCHECK(!layer_tree_host_->output_surface_lost()); | 335 DCHECK(!layer_tree_host_->output_surface_lost()); |
438 | 336 |
439 DoCommit(frame_begin_time); | 337 layer_tree_host_->AnimateLayers(frame_begin_time); |
| 338 |
| 339 if (PrioritizedResourceManager* contents_texture_manager = |
| 340 layer_tree_host_->contents_texture_manager()) { |
| 341 contents_texture_manager->UnlinkAndClearEvictedBackings(); |
| 342 contents_texture_manager->SetMaxMemoryLimitBytes( |
| 343 layer_tree_host_impl_->memory_allocation_limit_bytes()); |
| 344 contents_texture_manager->SetExternalPriorityCutoff( |
| 345 layer_tree_host_impl_->memory_allocation_priority_cutoff()); |
| 346 } |
| 347 |
| 348 scoped_ptr<ResourceUpdateQueue> queue = |
| 349 make_scoped_ptr(new ResourceUpdateQueue); |
| 350 layer_tree_host_->UpdateLayers(queue.get()); |
| 351 layer_tree_host_->WillCommit(); |
| 352 DoCommit(queue.Pass()); |
| 353 layer_tree_host_->DidBeginMainFrame(); |
440 | 354 |
441 LayerTreeHostImpl::FrameData frame; | 355 LayerTreeHostImpl::FrameData frame; |
442 DoComposite(frame_begin_time, &frame); | 356 if (DoComposite(frame_begin_time, &frame)) { |
| 357 { |
| 358 DebugScopedSetMainThreadBlocked main_thread_blocked(this); |
| 359 DebugScopedSetImplThread impl(this); |
| 360 |
| 361 // This CapturePostTasks should be destroyed before |
| 362 // DidCommitAndDrawFrame() is called since that goes out to the embedder, |
| 363 // and we want the embedder to receive its callbacks before that. |
| 364 // NOTE: This maintains consistent ordering with the ThreadProxy since |
| 365 // the DidCommitAndDrawFrame() must be post-tasked from the impl thread |
| 366 // there as the main thread is not blocked, so any posted tasks inside |
| 367 // the swap buffers will execute first. |
| 368 BlockingTaskRunner::CapturePostTasks blocked; |
| 369 |
| 370 layer_tree_host_impl_->SwapBuffers(frame); |
| 371 } |
| 372 DidSwapFrame(); |
| 373 } |
443 } | 374 } |
444 | 375 |
445 void SingleThreadProxy::AsValueInto(base::debug::TracedValue* state) const { | 376 void SingleThreadProxy::AsValueInto(base::debug::TracedValue* state) const { |
446 // The following line casts away const modifiers because it is just | 377 // The following line casts away const modifiers because it is just |
447 // setting debug state. We still want the AsValue() function and its | 378 // setting debug state. We still want the AsValue() function and its |
448 // call chain to be const throughout. | 379 // call chain to be const throughout. |
449 DebugScopedSetImplThread impl(const_cast<SingleThreadProxy*>(this)); | 380 DebugScopedSetImplThread impl(const_cast<SingleThreadProxy*>(this)); |
450 | 381 |
451 state->BeginDictionary("layer_tree_host_impl"); | 382 state->BeginDictionary("layer_tree_host_impl"); |
452 layer_tree_host_impl_->AsValueInto(state); | 383 layer_tree_host_impl_->AsValueInto(state); |
(...skipping 19 matching lines...) Expand all Loading... |
472 return layer_tree_host_impl_->visible() && | 403 return layer_tree_host_impl_->visible() && |
473 layer_tree_host_impl_->CanDraw(); | 404 layer_tree_host_impl_->CanDraw(); |
474 } | 405 } |
475 | 406 |
476 void SingleThreadProxy::UpdateBackgroundAnimateTicking() { | 407 void SingleThreadProxy::UpdateBackgroundAnimateTicking() { |
477 DCHECK(Proxy::IsImplThread()); | 408 DCHECK(Proxy::IsImplThread()); |
478 layer_tree_host_impl_->UpdateBackgroundAnimateTicking( | 409 layer_tree_host_impl_->UpdateBackgroundAnimateTicking( |
479 !ShouldComposite() && layer_tree_host_impl_->active_tree()->root_layer()); | 410 !ShouldComposite() && layer_tree_host_impl_->active_tree()->root_layer()); |
480 } | 411 } |
481 | 412 |
482 DrawResult SingleThreadProxy::DoComposite(base::TimeTicks frame_begin_time, | 413 bool SingleThreadProxy::DoComposite(base::TimeTicks frame_begin_time, |
483 LayerTreeHostImpl::FrameData* frame) { | 414 LayerTreeHostImpl::FrameData* frame) { |
484 TRACE_EVENT0("cc", "SingleThreadProxy::DoComposite"); | 415 TRACE_EVENT0("cc", "SingleThreadProxy::DoComposite"); |
485 DCHECK(!layer_tree_host_->output_surface_lost()); | 416 DCHECK(!layer_tree_host_->output_surface_lost()); |
486 | 417 |
| 418 bool lost_output_surface = false; |
487 { | 419 { |
488 DebugScopedSetImplThread impl(this); | 420 DebugScopedSetImplThread impl(this); |
489 base::AutoReset<bool> mark_inside(&inside_draw_, true); | 421 base::AutoReset<bool> mark_inside(&inside_draw_, true); |
490 | 422 |
491 // We guard PrepareToDraw() with CanDraw() because it always returns a valid | 423 // We guard PrepareToDraw() with CanDraw() because it always returns a valid |
492 // frame, so can only be used when such a frame is possible. Since | 424 // frame, so can only be used when such a frame is possible. Since |
493 // DrawLayers() depends on the result of PrepareToDraw(), it is guarded on | 425 // DrawLayers() depends on the result of PrepareToDraw(), it is guarded on |
494 // CanDraw() as well. | 426 // CanDraw() as well. |
495 if (!ShouldComposite()) { | 427 if (!ShouldComposite()) { |
496 UpdateBackgroundAnimateTicking(); | 428 UpdateBackgroundAnimateTicking(); |
497 return DRAW_ABORTED_CANT_DRAW; | 429 return false; |
498 } | 430 } |
499 | 431 |
500 timing_history_.DidStartDrawing(); | |
501 | |
502 layer_tree_host_impl_->Animate( | 432 layer_tree_host_impl_->Animate( |
503 layer_tree_host_impl_->CurrentFrameTimeTicks()); | 433 layer_tree_host_impl_->CurrentFrameTimeTicks()); |
504 UpdateBackgroundAnimateTicking(); | 434 UpdateBackgroundAnimateTicking(); |
505 | 435 |
506 if (!layer_tree_host_impl_->IsContextLost()) { | 436 if (!layer_tree_host_impl_->IsContextLost()) { |
507 layer_tree_host_impl_->PrepareToDraw(frame); | 437 layer_tree_host_impl_->PrepareToDraw(frame); |
508 layer_tree_host_impl_->DrawLayers(frame, frame_begin_time); | 438 layer_tree_host_impl_->DrawLayers(frame, frame_begin_time); |
509 layer_tree_host_impl_->DidDrawAllLayers(*frame); | 439 layer_tree_host_impl_->DidDrawAllLayers(*frame); |
510 } | 440 } |
| 441 lost_output_surface = layer_tree_host_impl_->IsContextLost(); |
511 | 442 |
512 bool start_ready_animations = true; | 443 bool start_ready_animations = true; |
513 layer_tree_host_impl_->UpdateAnimationState(start_ready_animations); | 444 layer_tree_host_impl_->UpdateAnimationState(start_ready_animations); |
514 | 445 |
515 layer_tree_host_impl_->ResetCurrentFrameTimeForNextFrame(); | 446 layer_tree_host_impl_->ResetCurrentFrameTimeForNextFrame(); |
516 | |
517 timing_history_.DidFinishDrawing(); | |
518 } | 447 } |
519 | 448 |
520 { | 449 if (lost_output_surface) { |
521 DebugScopedSetImplThread impl(this); | 450 layer_tree_host_->DidLoseOutputSurface(); |
| 451 return false; |
| 452 } |
522 | 453 |
523 if (layer_tree_host_impl_->IsContextLost()) { | 454 return true; |
524 DidLoseOutputSurfaceOnImplThread(); | |
525 } else { | |
526 // This CapturePostTasks should be destroyed before | |
527 // DidCommitAndDrawFrame() is called since that goes out to the | |
528 // embedder, | |
529 // and we want the embedder to receive its callbacks before that. | |
530 // NOTE: This maintains consistent ordering with the ThreadProxy since | |
531 // the DidCommitAndDrawFrame() must be post-tasked from the impl thread | |
532 // there as the main thread is not blocked, so any posted tasks inside | |
533 // the swap buffers will execute first. | |
534 DebugScopedSetMainThreadBlocked main_thread_blocked(this); | |
535 | |
536 BlockingTaskRunner::CapturePostTasks blocked; | |
537 layer_tree_host_impl_->SwapBuffers(*frame); | |
538 } | |
539 } | |
540 DidCommitAndDrawFrame(); | |
541 | |
542 return DRAW_SUCCESS; | |
543 } | 455 } |
544 | 456 |
545 void SingleThreadProxy::DidCommitAndDrawFrame() { | 457 void SingleThreadProxy::DidSwapFrame() { |
546 if (next_frame_is_newly_committed_frame_) { | 458 if (next_frame_is_newly_committed_frame_) { |
547 DebugScopedSetMainThread main(this); | |
548 next_frame_is_newly_committed_frame_ = false; | 459 next_frame_is_newly_committed_frame_ = false; |
549 layer_tree_host_->DidCommitAndDrawFrame(); | 460 layer_tree_host_->DidCommitAndDrawFrame(); |
550 } | 461 } |
551 } | 462 } |
552 | 463 |
553 bool SingleThreadProxy::MainFrameWillHappenForTesting() { | 464 bool SingleThreadProxy::MainFrameWillHappenForTesting() { |
554 return false; | 465 return false; |
555 } | 466 } |
556 | 467 |
557 void SingleThreadProxy::SetNeedsBeginFrame(bool enable) { | |
558 layer_tree_host_impl_->SetNeedsBeginFrame(enable); | |
559 } | |
560 | |
561 void SingleThreadProxy::WillBeginImplFrame(const BeginFrameArgs& args) { | |
562 layer_tree_host_impl_->WillBeginImplFrame(args); | |
563 } | |
564 | |
565 void SingleThreadProxy::ScheduledActionSendBeginMainFrame() { | |
566 TRACE_EVENT0("cc", "SingleThreadProxy::ScheduledActionSendBeginMainFrame"); | |
567 // Although this proxy is single-threaded, it's problematic to synchronously | |
568 // have BeginMainFrame happen after ScheduledActionSendBeginMainFrame. This | |
569 // could cause a commit to occur in between a series of SetNeedsCommit calls | |
570 // (i.e. property modifications) causing some to fall on one frame and some to | |
571 // fall on the next. Doing it asynchronously instead matches the semantics of | |
572 // ThreadProxy::SetNeedsCommit where SetNeedsCommit will not cause a | |
573 // synchronous commit. | |
574 MainThreadTaskRunner()->PostTask( | |
575 FROM_HERE, | |
576 base::Bind(&SingleThreadProxy::BeginMainFrame, | |
577 weak_factory_.GetWeakPtr())); | |
578 } | |
579 | |
580 void SingleThreadProxy::BeginMainFrame() { | |
581 if (defer_commits_) { | |
582 DCHECK(!commit_was_deferred_); | |
583 commit_was_deferred_ = true; | |
584 layer_tree_host_->DidDeferCommit(); | |
585 return; | |
586 } | |
587 | |
588 // This checker assumes NotifyReadyToCommit below causes a synchronous commit. | |
589 ScopedAbortRemainingSwapPromises swap_promise_checker(layer_tree_host_); | |
590 | |
591 if (!layer_tree_host_->visible()) { | |
592 TRACE_EVENT_INSTANT0("cc", "EarlyOut_NotVisible", TRACE_EVENT_SCOPE_THREAD); | |
593 BeginMainFrameAbortedOnImplThread(); | |
594 return; | |
595 } | |
596 | |
597 if (layer_tree_host_->output_surface_lost()) { | |
598 TRACE_EVENT_INSTANT0( | |
599 "cc", "EarlyOut_OutputSurfaceLost", TRACE_EVENT_SCOPE_THREAD); | |
600 BeginMainFrameAbortedOnImplThread(); | |
601 return; | |
602 } | |
603 | |
604 timing_history_.DidBeginMainFrame(); | |
605 | |
606 DCHECK(scheduler_on_impl_thread_); | |
607 scheduler_on_impl_thread_->NotifyBeginMainFrameStarted(); | |
608 scheduler_on_impl_thread_->NotifyReadyToCommit(); | |
609 } | |
610 | |
611 void SingleThreadProxy::BeginMainFrameAbortedOnImplThread() { | |
612 DebugScopedSetImplThread impl(this); | |
613 DCHECK(scheduler_on_impl_thread_->CommitPending()); | |
614 DCHECK(!layer_tree_host_impl_->pending_tree()); | |
615 | |
616 // TODO(enne): SingleThreadProxy does not support cancelling commits yet so | |
617 // did_handle is always false. | |
618 bool did_handle = false; | |
619 layer_tree_host_impl_->BeginMainFrameAborted(did_handle); | |
620 scheduler_on_impl_thread_->BeginMainFrameAborted(did_handle); | |
621 } | |
622 | |
623 DrawResult SingleThreadProxy::ScheduledActionDrawAndSwapIfPossible() { | |
624 DebugScopedSetImplThread impl(this); | |
625 if (layer_tree_host_impl_->IsContextLost()) { | |
626 DidCommitAndDrawFrame(); | |
627 return DRAW_SUCCESS; | |
628 } | |
629 | |
630 LayerTreeHostImpl::FrameData frame; | |
631 return DoComposite(layer_tree_host_impl_->CurrentFrameTimeTicks(), &frame); | |
632 } | |
633 | |
634 DrawResult SingleThreadProxy::ScheduledActionDrawAndSwapForced() { | |
635 NOTREACHED(); | |
636 return INVALID_RESULT; | |
637 } | |
638 | |
639 void SingleThreadProxy::ScheduledActionCommit() { | |
640 DebugScopedSetMainThread main(this); | |
641 DoCommit(layer_tree_host_impl_->CurrentFrameTimeTicks()); | |
642 } | |
643 | |
644 void SingleThreadProxy::ScheduledActionAnimate() { | |
645 TRACE_EVENT0("cc", "ScheduledActionAnimate"); | |
646 layer_tree_host_impl_->Animate( | |
647 layer_tree_host_impl_->CurrentFrameTimeTicks()); | |
648 } | |
649 | |
650 void SingleThreadProxy::ScheduledActionUpdateVisibleTiles() { | |
651 // Impl-side painting only. | |
652 NOTREACHED(); | |
653 } | |
654 | |
655 void SingleThreadProxy::ScheduledActionActivateSyncTree() { | |
656 } | |
657 | |
658 void SingleThreadProxy::ScheduledActionBeginOutputSurfaceCreation() { | |
659 DebugScopedSetMainThread main(this); | |
660 DCHECK(scheduler_on_impl_thread_); | |
661 // If possible, create the output surface in a post task. Synchronously | |
662 // creating the output surface makes tests more awkward since this differs | |
663 // from the ThreadProxy behavior. However, sometimes there is no | |
664 // task runner. | |
665 if (Proxy::MainThreadTaskRunner()) { | |
666 MainThreadTaskRunner()->PostTask( | |
667 FROM_HERE, | |
668 base::Bind(&SingleThreadProxy::CreateAndInitializeOutputSurface, | |
669 weak_factory_.GetWeakPtr())); | |
670 } else { | |
671 CreateAndInitializeOutputSurface(); | |
672 } | |
673 } | |
674 | |
675 void SingleThreadProxy::ScheduledActionManageTiles() { | |
676 // Impl-side painting only. | |
677 NOTREACHED(); | |
678 } | |
679 | |
680 void SingleThreadProxy::DidAnticipatedDrawTimeChange(base::TimeTicks time) { | |
681 } | |
682 | |
683 base::TimeDelta SingleThreadProxy::DrawDurationEstimate() { | |
684 return timing_history_.DrawDurationEstimate(); | |
685 } | |
686 | |
687 base::TimeDelta SingleThreadProxy::BeginMainFrameToCommitDurationEstimate() { | |
688 return timing_history_.BeginMainFrameToCommitDurationEstimate(); | |
689 } | |
690 | |
691 base::TimeDelta SingleThreadProxy::CommitToActivateDurationEstimate() { | |
692 return timing_history_.CommitToActivateDurationEstimate(); | |
693 } | |
694 | |
695 void SingleThreadProxy::DidBeginImplFrameDeadline() { | |
696 layer_tree_host_impl_->ResetCurrentFrameTimeForNextFrame(); | |
697 } | |
698 | |
699 } // namespace cc | 468 } // namespace cc |
OLD | NEW |