Index: cc/surfaces/display_scheduler.cc |
diff --git a/cc/surfaces/display_scheduler.cc b/cc/surfaces/display_scheduler.cc |
index d26d1b6306973debcf5abde447dfa0c928b981f7..3f792426d88bd7820c08542b66a87e3a460f51d1 100644 |
--- a/cc/surfaces/display_scheduler.cc |
+++ b/cc/surfaces/display_scheduler.cc |
@@ -6,6 +6,7 @@ |
#include <vector> |
+#include "base/auto_reset.h" |
#include "base/stl_util.h" |
#include "base/trace_event/trace_event.h" |
#include "cc/output/output_surface.h" |
@@ -17,6 +18,7 @@ DisplayScheduler::DisplayScheduler(BeginFrameSource* begin_frame_source, |
int max_pending_swaps) |
: begin_frame_source_(begin_frame_source), |
task_runner_(task_runner), |
+ inside_surface_damaged_(false), |
output_surface_lost_(false), |
root_surface_resources_locked_(true), |
inside_begin_frame_deadline_interval_(false), |
@@ -82,6 +84,11 @@ void DisplayScheduler::SurfaceDamaged(const SurfaceId& surface_id) { |
TRACE_EVENT1("cc", "DisplayScheduler::SurfaceDamaged", "surface_id", |
surface_id.ToString()); |
+ // We may cause a new BeginFrame to be run inside this method, but to help |
+ // avoid being reentrant to the caller of SurfaceDamaged, track when this is |
+ // happening with |inside_surface_damaged_|. |
+ base::AutoReset<bool>(&inside_surface_damaged_, true); |
+ |
needs_draw_ = true; |
if (surface_id == root_surface_id_) { |
@@ -138,6 +145,22 @@ bool DisplayScheduler::OnBeginFrameDerivedImpl(const BeginFrameArgs& args) { |
TRACE_EVENT2("cc", "DisplayScheduler::BeginFrame", "args", args.AsValue(), |
"now", now); |
+ if (inside_surface_damaged_) { |
+ // Repost this so that we don't run a missed BeginFrame on the same |
+ // callstack. Otherwise we end up running unexpected scheduler actions |
+ // immediately while inside some other action (such as submitting a |
+ // CompositorFrame for a SurfaceFactory). |
+ DCHECK_EQ(args.type, BeginFrameArgs::MISSED); |
+ DCHECK(missed_begin_frame_task_.IsCancelled()); |
+ missed_begin_frame_task_.Reset(base::Bind( |
+ base::IgnoreResult(&DisplayScheduler::OnBeginFrameDerivedImpl), |
+ // The CancelableCallback will not run after it is destroyed, which |
+ // happens when |this| is destroyed. |
+ base::Unretained(this), args)); |
+ task_runner_->PostTask(FROM_HERE, missed_begin_frame_task_.callback()); |
+ return true; |
+ } |
+ |
// If we get another BeginFrame before the previous deadline, |
// synchronously trigger the previous deadline before progressing. |
if (inside_begin_frame_deadline_interval_) { |
@@ -151,6 +174,12 @@ bool DisplayScheduler::OnBeginFrameDerivedImpl(const BeginFrameArgs& args) { |
inside_begin_frame_deadline_interval_ = true; |
ScheduleBeginFrameDeadline(); |
+ // If we get another BeginFrame before a posted missed frame, just drop the |
+ // missed frame. Also if this was the missed frame, drop the Callback inside |
+ // it. Do this last because this might be the missed frame and we don't want |
+ // to destroy |args| prematurely. |
+ missed_begin_frame_task_.Cancel(); |
+ |
return true; |
} |
@@ -283,6 +312,9 @@ void DisplayScheduler::AttemptDrawAndSwap() { |
if (observing_begin_frame_source_) { |
observing_begin_frame_source_ = false; |
begin_frame_source_->RemoveObserver(this); |
+ // A missed BeginFrame may be queued, so drop that too if we're going to |
+ // stop listening. |
+ missed_begin_frame_task_.Cancel(); |
} |
} |
} |