Index: ui/android/window_android.cc |
diff --git a/ui/android/window_android.cc b/ui/android/window_android.cc |
index 16f2633fec05372137c464ca0adea152b276a429..79110f33a613a5486e13e446eb63662ee168cfdf 100644 |
--- a/ui/android/window_android.cc |
+++ b/ui/android/window_android.cc |
@@ -4,12 +4,19 @@ |
#include "ui/android/window_android.h" |
+#include <unordered_set> |
+ |
#include "base/android/context_utils.h" |
#include "base/android/jni_android.h" |
#include "base/android/jni_array.h" |
#include "base/android/jni_string.h" |
#include "base/android/jni_weak_ref.h" |
#include "base/android/scoped_java_ref.h" |
+#include "base/auto_reset.h" |
+#include "base/observer_list.h" |
+#include "base/stl_util.h" |
+#include "cc/output/begin_frame_args.h" |
+#include "cc/scheduler/begin_frame_source.h" |
#include "jni/WindowAndroid_jni.h" |
#include "ui/android/window_android_compositor.h" |
#include "ui/android/window_android_observer.h" |
@@ -21,8 +28,107 @@ using base::android::JavaParamRef; |
using base::android::JavaRef; |
using base::android::ScopedJavaLocalRef; |
+class WindowAndroid::WindowBeginFrameSource : public cc::BeginFrameSource { |
+ public: |
+ explicit WindowBeginFrameSource(WindowAndroid* window) |
+ : window_(window), |
+ observer_count_(0), |
+ in_on_vsync_(false) {} |
+ ~WindowBeginFrameSource() override {} |
+ |
+ // cc::BeginFrameSource implementation. |
+ void AddObserver(cc::BeginFrameObserver* obs) override; |
+ void RemoveObserver(cc::BeginFrameObserver* obs) override; |
+ void DidFinishFrame(cc::BeginFrameObserver* obs, |
+ size_t remaining_frames) override {} |
+ bool IsThrottled() const override { return true; } |
+ |
+ void OnVSync(base::TimeTicks frame_time, |
+ base::TimeDelta vsync_period); |
+ |
+ private: |
+ WindowAndroid* const window_; |
+ base::ObserverList<cc::BeginFrameObserver> observers_; |
+ int observer_count_; |
+ cc::BeginFrameArgs last_begin_frame_args_; |
+ bool in_on_vsync_; |
+}; |
+ |
+void WindowAndroid::WindowBeginFrameSource::AddObserver( |
+ cc::BeginFrameObserver* obs) { |
+ DCHECK(obs); |
+ DCHECK(!observers_.HasObserver(obs)); |
+ |
+ observers_.AddObserver(obs); |
+ observer_count_++; |
+ obs->OnBeginFrameSourcePausedChanged(false); |
+ window_->SetNeedsBeginFrames(true); |
+ |
+ // Send a MISSED BeginFrame if possible and necessary. If an observer is added |
+ // during OnVSync(), it will get a NORMAL BeginFrame from OnVSync() instead. |
+ if (!in_on_vsync_ && last_begin_frame_args_.IsValid()) { |
+ cc::BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs(); |
+ if (!last_args.IsValid() || |
+ last_args.frame_time < last_begin_frame_args_.frame_time) { |
+ last_begin_frame_args_.type = cc::BeginFrameArgs::MISSED; |
+ // TODO(crbug.com/602485): A deadline doesn't make too much sense |
+ // for a missed BeginFrame (the intention rather is 'immediately'), |
+ // but currently the retro frame logic is very strict in discarding |
+ // BeginFrames. |
+ last_begin_frame_args_.deadline = |
+ base::TimeTicks::Now() + last_begin_frame_args_.interval; |
+ obs->OnBeginFrame(last_begin_frame_args_); |
+ } |
+ } |
+} |
+ |
+void WindowAndroid::WindowBeginFrameSource::RemoveObserver( |
+ cc::BeginFrameObserver* obs) { |
+ DCHECK(obs); |
+ DCHECK(observers_.HasObserver(obs)); |
+ |
+ observers_.RemoveObserver(obs); |
+ observer_count_--; |
+ if (observer_count_ <= 0) |
+ window_->SetNeedsBeginFrames(false); |
+} |
+ |
+void WindowAndroid::WindowBeginFrameSource::OnVSync( |
+ base::TimeTicks frame_time, |
+ base::TimeDelta vsync_period) { |
+ // frame time is in the past, so give the next vsync period as the deadline. |
+ base::TimeTicks deadline = frame_time + vsync_period; |
+ last_begin_frame_args_ = |
+ cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, |
+ vsync_period, cc::BeginFrameArgs::NORMAL); |
+ DCHECK(last_begin_frame_args_.IsValid()); |
+ |
+ // We support adding/removing observers during observer iteration through |
+ // base::ObserverList. We also prevent observers that are added during |
+ // observer iteration from receiving a MISSED BeginFrame by means of |
+ // |in_on_vsync_| - they will receive the current NORMAL one during the |
+ // iteration instead. Note that SynchronousCompositorBrowserFilter relies on |
+ // this behavior. |
+ // TODO(eseckler): Remove SynchronousCompositorBrowserFilter's dependency on |
+ // this and replace observers_ by a std::unordered_set, iterate on a copy. |
+ base::AutoReset<bool> auto_reset(&in_on_vsync_, true); |
+ std::unordered_set<cc::BeginFrameObserver*> notified_observers; |
+ for (auto& obs : observers_) { |
+ // An observer may remove and add itself during a single OnBeginFrame, in |
+ // which case it'd appear twice during the iteration. To deal with this, we |
+ // keep track of whether we already notified it. |
+ if (!base::ContainsKey(notified_observers, &obs)) { |
+ obs.OnBeginFrame(last_begin_frame_args_); |
+ notified_observers.insert(&obs); |
+ } |
+ } |
+} |
+ |
WindowAndroid::WindowAndroid(JNIEnv* env, jobject obj, int display_id) |
- : display_id_(display_id), compositor_(NULL) { |
+ : display_id_(display_id), |
+ compositor_(NULL), |
+ begin_frame_source_(new WindowBeginFrameSource(this)), |
+ needs_begin_frames_(false) { |
java_window_.Reset(env, obj); |
} |
@@ -69,6 +175,10 @@ void WindowAndroid::RemoveObserver(WindowAndroidObserver* observer) { |
observer_list_.RemoveObserver(observer); |
} |
+cc::BeginFrameSource* WindowAndroid::GetBeginFrameSource() { |
+ return begin_frame_source_.get(); |
+} |
+ |
void WindowAndroid::AttachCompositor(WindowAndroidCompositor* compositor) { |
if (compositor_ && compositor != compositor_) |
DetachCompositor(); |
@@ -90,6 +200,15 @@ void WindowAndroid::RequestVSyncUpdate() { |
Java_WindowAndroid_requestVSyncUpdate(env, GetJavaObject()); |
} |
+void WindowAndroid::SetNeedsBeginFrames(bool needs_begin_frames) { |
+ if (needs_begin_frames_ == needs_begin_frames) |
+ return; |
+ |
+ needs_begin_frames_ = needs_begin_frames; |
+ if (needs_begin_frames_) |
+ RequestVSyncUpdate(); |
+} |
+ |
void WindowAndroid::SetNeedsAnimate() { |
if (compositor_) |
compositor_->SetNeedsAnimate(); |
@@ -107,10 +226,9 @@ void WindowAndroid::OnVSync(JNIEnv* env, |
base::TimeTicks frame_time(base::TimeTicks::FromInternalValue(time_micros)); |
base::TimeDelta vsync_period( |
base::TimeDelta::FromMicroseconds(period_micros)); |
- for (WindowAndroidObserver& observer : observer_list_) |
- observer.OnVSync(frame_time, vsync_period); |
- if (compositor_) |
- compositor_->OnVSync(frame_time, vsync_period); |
+ begin_frame_source_->OnVSync(frame_time, vsync_period); |
+ if (needs_begin_frames_) |
+ RequestVSyncUpdate(); |
} |
void WindowAndroid::OnVisibilityChanged(JNIEnv* env, |