1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/surfaces/display_scheduler.h"
9 #include "base/stl_util.h"
10 #include "base/trace_event/trace_event.h"
11 #include "cc/output/output_surface.h"
15 DisplayScheduler::DisplayScheduler(DisplaySchedulerClient
* client
,
16 BeginFrameSource
* begin_frame_source
,
17 base::SingleThreadTaskRunner
* task_runner
,
18 int max_pending_swaps
)
20 begin_frame_source_(begin_frame_source
),
21 task_runner_(task_runner
),
22 output_surface_lost_(false),
23 root_surface_resources_locked_(true),
24 inside_begin_frame_deadline_interval_(false),
26 expecting_root_surface_damage_because_of_resize_(false),
27 all_active_child_surfaces_ready_to_draw_(false),
29 max_pending_swaps_(max_pending_swaps
),
30 root_surface_damaged_(false),
31 expect_damage_from_root_surface_(false),
32 weak_ptr_factory_(this) {
33 begin_frame_source_
->AddObserver(this);
34 begin_frame_deadline_closure_
= base::Bind(
35 &DisplayScheduler::OnBeginFrameDeadline
, weak_ptr_factory_
.GetWeakPtr());
38 DisplayScheduler::~DisplayScheduler() {
39 begin_frame_source_
->RemoveObserver(this);
42 // If we try to draw when the root surface resources are locked, the
44 void DisplayScheduler::SetRootSurfaceResourcesLocked(bool locked
) {
45 TRACE_EVENT1("cc", "DisplayScheduler::SetRootSurfaceResourcesLocked",
47 root_surface_resources_locked_
= locked
;
48 ScheduleBeginFrameDeadline();
51 // This is used to force an immediate swap before a resize.
52 void DisplayScheduler::ForceImmediateSwapIfPossible() {
53 TRACE_EVENT0("cc", "DisplayScheduler::ForceImmediateSwapIfPossible");
54 bool in_begin
= inside_begin_frame_deadline_interval_
;
57 begin_frame_source_
->DidFinishFrame(0);
60 void DisplayScheduler::DisplayResized() {
61 expecting_root_surface_damage_because_of_resize_
= true;
62 expect_damage_from_root_surface_
= true;
63 ScheduleBeginFrameDeadline();
66 // Notification that there was a resize or the root surface changed and
67 // that we should just draw immediately.
68 void DisplayScheduler::SetNewRootSurface(SurfaceId root_surface_id
) {
69 TRACE_EVENT0("cc", "DisplayScheduler::SetNewRootSurface");
70 root_surface_id_
= root_surface_id
;
71 SurfaceDamaged(root_surface_id
);
74 // Indicates that there was damage to one of the surfaces.
75 // Has some logic to wait for multiple active surfaces before
76 // triggering the deadline.
77 void DisplayScheduler::SurfaceDamaged(SurfaceId surface_id
) {
78 TRACE_EVENT1("cc", "DisplayScheduler::SurfaceDamaged", "surface_id",
83 if (surface_id
== root_surface_id_
) {
84 root_surface_damaged_
= true;
85 expecting_root_surface_damage_because_of_resize_
= false;
87 child_surface_ids_damaged_
.insert(surface_id
);
89 // TODO(mithro): Use hints from SetNeedsBeginFrames and SwapAborts.
90 all_active_child_surfaces_ready_to_draw_
= base::STLIncludes(
91 child_surface_ids_damaged_
, child_surface_ids_to_expect_damage_from_
);
94 begin_frame_source_
->SetNeedsBeginFrames(!output_surface_lost_
);
95 ScheduleBeginFrameDeadline();
98 void DisplayScheduler::OutputSurfaceLost() {
99 TRACE_EVENT0("cc", "DisplayScheduler::OutputSurfaceLost");
100 output_surface_lost_
= true;
101 begin_frame_source_
->SetNeedsBeginFrames(false);
102 ScheduleBeginFrameDeadline();
105 void DisplayScheduler::DrawAndSwap() {
106 TRACE_EVENT0("cc", "DisplayScheduler::DrawAndSwap");
107 DCHECK_LT(pending_swaps_
, max_pending_swaps_
);
108 DCHECK(!output_surface_lost_
);
110 bool success
= client_
->DrawAndSwap();
114 child_surface_ids_to_expect_damage_from_
=
115 base::STLSetIntersection
<std::vector
<SurfaceId
>>(
116 child_surface_ids_damaged_
, child_surface_ids_damaged_prev_
);
118 child_surface_ids_damaged_prev_
.swap(child_surface_ids_damaged_
);
119 child_surface_ids_damaged_
.clear();
122 all_active_child_surfaces_ready_to_draw_
=
123 child_surface_ids_to_expect_damage_from_
.empty();
125 expect_damage_from_root_surface_
= root_surface_damaged_
;
126 root_surface_damaged_
= false;
129 bool DisplayScheduler::OnBeginFrameDerivedImpl(const BeginFrameArgs
& args
) {
130 base::TimeTicks now
= base::TimeTicks::Now();
131 TRACE_EVENT2("cc", "DisplayScheduler::BeginFrame", "args", args
.AsValue(),
134 // If we get another BeginFrame before the previous deadline,
135 // synchronously trigger the previous deadline before progressing.
136 if (inside_begin_frame_deadline_interval_
) {
137 OnBeginFrameDeadline();
140 // Schedule the deadline.
141 current_begin_frame_args_
= args
;
142 current_begin_frame_args_
.deadline
-=
143 BeginFrameArgs::DefaultEstimatedParentDrawTime();
144 inside_begin_frame_deadline_interval_
= true;
145 ScheduleBeginFrameDeadline();
150 base::TimeTicks
DisplayScheduler::DesiredBeginFrameDeadlineTime() {
151 if (output_surface_lost_
) {
152 TRACE_EVENT_INSTANT0("cc", "Lost output surface", TRACE_EVENT_SCOPE_THREAD
);
153 return base::TimeTicks();
156 if (pending_swaps_
>= max_pending_swaps_
) {
157 TRACE_EVENT_INSTANT0("cc", "Swap throttled", TRACE_EVENT_SCOPE_THREAD
);
158 return current_begin_frame_args_
.frame_time
+
159 current_begin_frame_args_
.interval
;
163 TRACE_EVENT_INSTANT0("cc", "No damage yet", TRACE_EVENT_SCOPE_THREAD
);
164 return current_begin_frame_args_
.frame_time
+
165 current_begin_frame_args_
.interval
;
168 if (root_surface_resources_locked_
) {
169 TRACE_EVENT_INSTANT0("cc", "Root surface resources locked",
170 TRACE_EVENT_SCOPE_THREAD
);
171 return current_begin_frame_args_
.frame_time
+
172 current_begin_frame_args_
.interval
;
175 bool root_ready_to_draw
=
176 !expect_damage_from_root_surface_
|| root_surface_damaged_
;
178 if (all_active_child_surfaces_ready_to_draw_
&& root_ready_to_draw
) {
179 TRACE_EVENT_INSTANT0("cc", "All active surfaces ready",
180 TRACE_EVENT_SCOPE_THREAD
);
181 return base::TimeTicks();
184 // TODO(mithro): Be smarter about resize deadlines.
185 if (expecting_root_surface_damage_because_of_resize_
) {
186 TRACE_EVENT_INSTANT0("cc", "Entire display damaged",
187 TRACE_EVENT_SCOPE_THREAD
);
188 return current_begin_frame_args_
.frame_time
+
189 current_begin_frame_args_
.interval
;
192 // Use an earlier deadline if we are only waiting for the root surface
193 // in case our expect_damage_from_root_surface heuristic is incorrect.
194 // TODO(mithro): Replace this with SetNeedsBeginFrame and SwapAbort
196 if (all_active_child_surfaces_ready_to_draw_
&&
197 expect_damage_from_root_surface_
) {
198 TRACE_EVENT_INSTANT0("cc", "Waiting for damage from root surface",
199 TRACE_EVENT_SCOPE_THREAD
);
200 // This adjusts the deadline by DefaultEstimatedParentDrawTime for
201 // a second time. The first one represented the Surfaces draw to display
202 // latency. This one represents root surface commit+raster+draw latency.
203 // We treat the root surface differently since it lives on the same thread
204 // as Surfaces and waiting for it too long may push out the Surfaces draw.
205 // If we also assume the root surface is fast to start a commit after the
206 // beginning of a frame, it'll have a chance to lock its resources, which
207 // will cause us to wait for it to unlock its resources above.
208 // TODO(mithro): Replace hard coded estimates.
209 return current_begin_frame_args_
.deadline
-
210 BeginFrameArgs::DefaultEstimatedParentDrawTime();
213 TRACE_EVENT_INSTANT0("cc", "More damage expected soon",
214 TRACE_EVENT_SCOPE_THREAD
);
215 return current_begin_frame_args_
.deadline
;
218 void DisplayScheduler::ScheduleBeginFrameDeadline() {
219 TRACE_EVENT0("cc", "DisplayScheduler::ScheduleBeginFrameDeadline");
221 // We need to wait for the next BeginFrame before scheduling a deadline.
222 if (!inside_begin_frame_deadline_interval_
) {
223 TRACE_EVENT_INSTANT0("cc", "Waiting for next BeginFrame",
224 TRACE_EVENT_SCOPE_THREAD
);
225 DCHECK(begin_frame_deadline_task_
.IsCancelled());
229 // Determine the deadline we want to use.
230 base::TimeTicks desired_deadline
= DesiredBeginFrameDeadlineTime();
232 // Avoid re-scheduling the deadline if it's already correctly scheduled.
233 if (!begin_frame_deadline_task_
.IsCancelled() &&
234 desired_deadline
== begin_frame_deadline_task_time_
) {
235 TRACE_EVENT_INSTANT0("cc", "Using existing deadline",
236 TRACE_EVENT_SCOPE_THREAD
);
240 // Schedule the deadline.
241 begin_frame_deadline_task_time_
= desired_deadline
;
242 begin_frame_deadline_task_
.Cancel();
243 begin_frame_deadline_task_
.Reset(begin_frame_deadline_closure_
);
245 base::TimeDelta delta
=
246 std::max(base::TimeDelta(), desired_deadline
- base::TimeTicks::Now());
247 task_runner_
->PostDelayedTask(FROM_HERE
,
248 begin_frame_deadline_task_
.callback(), delta
);
249 TRACE_EVENT2("cc", "Using new deadline", "delta", delta
.ToInternalValue(),
250 "desired_deadline", desired_deadline
);
253 void DisplayScheduler::AttemptDrawAndSwap() {
254 inside_begin_frame_deadline_interval_
= false;
255 begin_frame_deadline_task_
.Cancel();
256 begin_frame_deadline_task_time_
= base::TimeTicks();
258 if (needs_draw_
&& !output_surface_lost_
) {
259 if (pending_swaps_
< max_pending_swaps_
&& !root_surface_resources_locked_
)
262 // We are going idle, so reset expectations.
263 child_surface_ids_to_expect_damage_from_
.clear();
264 child_surface_ids_damaged_prev_
.clear();
265 child_surface_ids_damaged_
.clear();
266 all_active_child_surfaces_ready_to_draw_
= true;
267 expect_damage_from_root_surface_
= false;
269 begin_frame_source_
->SetNeedsBeginFrames(false);
273 void DisplayScheduler::OnBeginFrameDeadline() {
274 TRACE_EVENT0("cc", "DisplayScheduler::OnBeginFrameDeadline");
276 AttemptDrawAndSwap();
277 begin_frame_source_
->DidFinishFrame(0);
280 void DisplayScheduler::DidSwapBuffers() {
282 TRACE_EVENT1("cc", "DisplayScheduler::DidSwapBuffers", "pending_frames",
286 void DisplayScheduler::DidSwapBuffersComplete() {
288 TRACE_EVENT1("cc", "DisplayScheduler::DidSwapBuffersComplete",
289 "pending_frames", pending_swaps_
);
290 ScheduleBeginFrameDeadline();