diff options
14 files changed, 169 insertions, 392 deletions
diff --git a/chrome/android/shell/javatests/src/org/chromium/chrome/shell/ChromeShellUrlTest.java b/chrome/android/shell/javatests/src/org/chromium/chrome/shell/ChromeShellUrlTest.java index a7be42a..c133ec7 100644 --- a/chrome/android/shell/javatests/src/org/chromium/chrome/shell/ChromeShellUrlTest.java +++ b/chrome/android/shell/javatests/src/org/chromium/chrome/shell/ChromeShellUrlTest.java @@ -10,6 +10,7 @@ import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.Feature; import org.chromium.content.browser.ContentViewCore; import org.chromium.content.browser.ContentViewRenderView; +import org.chromium.ui.base.WindowAndroid; import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; @@ -95,14 +96,17 @@ public class ChromeShellUrlTest extends ChromeShellTestBase { runTestOnUiThread(new Runnable() { @Override public void run() { + WindowAndroid windowAndroid = new WindowAndroid( + getInstrumentation().getTargetContext().getApplicationContext()); ContentViewRenderView contentViewRenderView = new ContentViewRenderView(getInstrumentation().getTargetContext(), - activity.getWindowAndroid()); + windowAndroid); contentViewRenderView.setCurrentContentViewCore( activity.getActiveContentViewCore()); } }); } catch (Throwable e) { + e.printStackTrace(); fail("Could not create a ContentViewRenderView: " + e); } } diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc index a291f97..9d3dd4d 100644 --- a/content/browser/android/content_view_core_impl.cc +++ b/content/browser/android/content_view_core_impl.cc @@ -87,10 +87,6 @@ namespace content { namespace { -const unsigned int kDefaultVSyncIntervalMicros = 16666u; -// TODO(brianderson): Use adaptive draw-time estimation. -const float kDefaultBrowserCompositeVSyncFraction = 1.0f / 3; - const void* kContentViewUserDataKey = &kContentViewUserDataKey; int GetRenderProcessIdFromRenderViewHost(RenderViewHost* host) { @@ -223,10 +219,6 @@ ContentViewCoreImpl::ContentViewCoreImpl(JNIEnv* env, web_contents_(static_cast<WebContentsImpl*>(web_contents)), root_layer_(cc::Layer::Create()), dpi_scale_(GetPrimaryDisplayDeviceScaleFactor()), - vsync_interval_(base::TimeDelta::FromMicroseconds( - kDefaultVSyncIntervalMicros)), - expected_browser_composite_time_(base::TimeDelta::FromMicroseconds( - kDefaultVSyncIntervalMicros * kDefaultBrowserCompositeVSyncFraction)), view_android_(view_android), window_android_(window_android), device_orientation_(0), @@ -435,7 +427,6 @@ void ContentViewCoreImpl::OnTabCrashed() { ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); if (obj.is_null()) return; - Java_ContentViewCore_resetVSyncNotification(env, obj.obj()); } // All positions and sizes are in CSS pixels. @@ -855,30 +846,6 @@ void ContentViewCoreImpl::LoadUrl( GetWebContents()->GetController().LoadURLWithParams(params); } -void ContentViewCoreImpl::AddBeginFrameSubscriber() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); - if (obj.is_null()) - return; - Java_ContentViewCore_addVSyncSubscriber(env, obj.obj()); -} - -void ContentViewCoreImpl::RemoveBeginFrameSubscriber() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); - if (obj.is_null()) - return; - Java_ContentViewCore_removeVSyncSubscriber(env, obj.obj()); -} - -void ContentViewCoreImpl::SetNeedsAnimate() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); - if (obj.is_null()) - return; - Java_ContentViewCore_setNeedsAnimate(env, obj.obj()); -} - ui::ViewAndroid* ContentViewCoreImpl::GetViewAndroid() const { // view_android_ should never be null for Chrome. DCHECK(view_android_); @@ -1326,54 +1293,6 @@ void ContentViewCoreImpl::RemoveJavascriptInterface(JNIEnv* env, ConvertJavaStringToUTF16(env, name)); } -void ContentViewCoreImpl::UpdateVSyncParameters(JNIEnv* env, jobject /* obj */, - jlong timebase_micros, - jlong interval_micros) { - RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid(); - if (!view) - return; - - RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( - view->GetRenderWidgetHost()); - - host->UpdateVSyncParameters( - base::TimeTicks::FromInternalValue(timebase_micros), - base::TimeDelta::FromMicroseconds(interval_micros)); - - vsync_interval_ = - base::TimeDelta::FromMicroseconds(interval_micros); - expected_browser_composite_time_ = - vsync_interval_ * kDefaultBrowserCompositeVSyncFraction; -} - -void ContentViewCoreImpl::OnVSync(JNIEnv* env, jobject /* obj */, - jlong frame_time_micros) { - base::TimeTicks frame_time = - base::TimeTicks::FromInternalValue(frame_time_micros); - SendBeginFrame(frame_time); -} - -void ContentViewCoreImpl::SendBeginFrame(base::TimeTicks frame_time) { - RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid(); - if (!view) - return; - - base::TimeTicks display_time = frame_time + vsync_interval_; - base::TimeTicks deadline = display_time - expected_browser_composite_time_; - - view->SendBeginFrame( - cc::BeginFrameArgs::Create(frame_time, deadline, vsync_interval_)); -} - -jboolean ContentViewCoreImpl::OnAnimate(JNIEnv* env, jobject /* obj */, - jlong frame_time_micros) { - RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid(); - if (!view) - return false; - - return view->Animate(base::TimeTicks::FromInternalValue(frame_time_micros)); -} - void ContentViewCoreImpl::WasResized(JNIEnv* env, jobject obj) { RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid(); if (view) { diff --git a/content/browser/android/content_view_core_impl.h b/content/browser/android/content_view_core_impl.h index f7d75e0..dd39522 100644 --- a/content/browser/android/content_view_core_impl.h +++ b/content/browser/android/content_view_core_impl.h @@ -195,10 +195,6 @@ class ContentViewCoreImpl : public ContentViewCore, jint max_entries); base::android::ScopedJavaLocalRef<jstring> GetOriginalUrlForActiveNavigationEntry(JNIEnv* env, jobject obj); - void UpdateVSyncParameters(JNIEnv* env, jobject obj, jlong timebase_micros, - jlong interval_micros); - void OnVSync(JNIEnv* env, jobject /* obj */, jlong frame_time_micros); - jboolean OnAnimate(JNIEnv* env, jobject /* obj */, jlong frame_time_micros); void WasResized(JNIEnv* env, jobject obj); jboolean IsRenderWidgetHostViewReady(JNIEnv* env, jobject obj); void ExitFullscreen(JNIEnv* env, jobject obj); @@ -308,9 +304,6 @@ class ContentViewCoreImpl : public ContentViewCore, void AttachLayer(scoped_refptr<cc::Layer> layer); void RemoveLayer(scoped_refptr<cc::Layer> layer); - void AddBeginFrameSubscriber(); - void RemoveBeginFrameSubscriber(); - void SetNeedsAnimate(); private: class ContentViewUserData; @@ -338,8 +331,6 @@ class ContentViewCoreImpl : public ContentViewCore, blink::WebGestureEvent MakeGestureEvent( blink::WebInputEvent::Type type, int64 time_ms, float x, float y) const; - void SendBeginFrame(base::TimeTicks frame_time); - gfx::Size GetViewportSizePix() const; gfx::Size GetViewportSizeOffsetPix() const; @@ -371,10 +362,6 @@ class ContentViewCoreImpl : public ContentViewCore, // Device scale factor. float dpi_scale_; - // Variables used to keep track of frame timestamps and deadlines. - base::TimeDelta vsync_interval_; - base::TimeDelta expected_browser_composite_time_; - // The Android view that can be used to add and remove decoration layers // like AutofillPopup. ui::ViewAndroid* view_android_; diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc index 48caee2..9953fae 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.cc +++ b/content/browser/renderer_host/render_widget_host_view_android.cc @@ -531,39 +531,20 @@ void RenderWidgetHostViewAndroid::OnDidOverscroll( device_scale_factor), gfx::ScaleVector2d(params.current_fling_velocity, device_scale_factor))) { - content_view_core_->SetNeedsAnimate(); + SetNeedsAnimate(); } } -void RenderWidgetHostViewAndroid::SendBeginFrame( - const cc::BeginFrameArgs& args) { - TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::SendBeginFrame"); - if (!host_) +void RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame(bool enabled) { + if (enabled == needs_begin_frame_) return; - if (flush_input_requested_) { - flush_input_requested_ = false; - host_->FlushInput(); - content_view_core_->RemoveBeginFrameSubscriber(); - } - - host_->Send(new ViewMsg_BeginFrame(host_->GetRoutingID(), args)); -} - -void RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame(bool enabled) { TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame", "enabled", enabled); - // ContentViewCoreImpl handles multiple subscribers to the BeginFrame, so - // we have to make sure calls to ContentViewCoreImpl's - // {Add,Remove}BeginFrameSubscriber are balanced, even if - // RenderWidgetHostViewAndroid's may not be. - if (content_view_core_ && needs_begin_frame_ != enabled) { - if (enabled) - content_view_core_->AddBeginFrameSubscriber(); - else - content_view_core_->RemoveBeginFrameSubscriber(); - needs_begin_frame_ = enabled; - } + if (content_view_core_ && enabled) + content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); + + needs_begin_frame_ = enabled; } void RenderWidgetHostViewAndroid::OnStartContentIntent( @@ -1083,6 +1064,10 @@ void RenderWidgetHostViewAndroid::RemoveLayers() { overscroll_effect_->Disable(); } +void RenderWidgetHostViewAndroid::SetNeedsAnimate() { + content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); +} + bool RenderWidgetHostViewAndroid::Animate(base::TimeTicks frame_time) { return overscroll_effect_->Animate(frame_time); } @@ -1194,7 +1179,6 @@ void RenderWidgetHostViewAndroid::OnSetNeedsFlushInput() { return; TRACE_EVENT0("input", "RenderWidgetHostViewAndroid::OnSetNeedsFlushInput"); flush_input_requested_ = true; - content_view_core_->AddBeginFrameSubscriber(); } void RenderWidgetHostViewAndroid::CreateBrowserAccessibilityManagerIfNeeded() { @@ -1285,6 +1269,14 @@ void RenderWidgetHostViewAndroid::SendTouchEvent( const blink::WebTouchEvent& event) { if (host_) host_->ForwardTouchEventWithLatencyInfo(event, CreateLatencyInfo(event)); + + // Send a proactive BeginFrame on the next vsync to reduce latency. + // This is good enough as long as the first touch event has Begin semantics + // and the actual scroll happens on the next vsync. + // TODO: Is this actually still needed? + if (content_view_core_) { + content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); + } } void RenderWidgetHostViewAndroid::SendMouseEvent( @@ -1351,6 +1343,8 @@ void RenderWidgetHostViewAndroid::SetContentViewCore( if (content_view_core_ && !using_synchronous_compositor_) { content_view_core_->GetWindowAndroid()->AddObserver(this); observing_root_window_ = true; + if (needs_begin_frame_) + content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); } if (resize && content_view_core_) @@ -1386,6 +1380,37 @@ void RenderWidgetHostViewAndroid::OnWillDestroyWindow() { observing_root_window_ = false; } +void RenderWidgetHostViewAndroid::OnVSync(base::TimeTicks frame_time, + base::TimeDelta vsync_period) { + TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::OnVSync"); + if (!host_) + return; + + if (flush_input_requested_) { + flush_input_requested_ = false; + host_->FlushInput(); + } + + TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::SendBeginFrame"); + base::TimeTicks display_time = frame_time + vsync_period; + + // TODO(brianderson): Use adaptive draw-time estimation. + base::TimeDelta estimated_browser_composite_time = + base::TimeDelta::FromMicroseconds( + (1.0f * base::Time::kMicrosecondsPerSecond) / (3.0f * 60)); + + base::TimeTicks deadline = display_time - estimated_browser_composite_time; + + host_->Send(new ViewMsg_BeginFrame( + host_->GetRoutingID(), + cc::BeginFrameArgs::Create(frame_time, deadline, vsync_period))); + + // TODO(sievers): This should use the LayerTreeHostClient callback + bool needs_animate = Animate(frame_time); + if (needs_begin_frame_ || needs_animate) + content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); +} + void RenderWidgetHostViewAndroid::OnLostResources() { ReleaseLocksOnSurface(); if (layer_.get()) diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h index 978d1f3..dede4c0 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.h +++ b/content/browser/renderer_host/render_widget_host_view_android.h @@ -197,6 +197,8 @@ class RenderWidgetHostViewAndroid virtual void OnAttachCompositor() OVERRIDE {} virtual void OnDetachCompositor() OVERRIDE; virtual void OnWillDestroyWindow() OVERRIDE; + virtual void OnVSync(base::TimeTicks frame_time, + base::TimeDelta vsync_period) OVERRIDE; // ImageTransportFactoryAndroidObserver implementation. virtual void OnLostResources() OVERRIDE; @@ -214,7 +216,6 @@ class RenderWidgetHostViewAndroid void SendMouseEvent(const blink::WebMouseEvent& event); void SendMouseWheelEvent(const blink::WebMouseWheelEvent& event); void SendGestureEvent(const blink::WebGestureEvent& event); - void SendBeginFrame(const cc::BeginFrameArgs& args); void OnTextInputStateChanged(const ViewHostMsg_TextInputState_Params& params); void OnDidChangeBodyBackgroundColor(SkColor color); @@ -242,10 +243,6 @@ class RenderWidgetHostViewAndroid void MoveCaret(const gfx::Point& point); - // Returns true when animation ticks are still needed. This avoids a separate - // round-trip for requesting follow-up animation. - bool Animate(base::TimeTicks frame_time); - void SynchronousFrameMetadata( const cc::CompositorFrameMetadata& frame_metadata); @@ -307,6 +304,10 @@ class RenderWidgetHostViewAndroid void InternalSwapCompositorFrame(uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame); + void SetNeedsAnimate(); + bool Animate(base::TimeTicks frame_time); + + // The model object. RenderWidgetHostImpl* host_; diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java index e59d6f8..5532e06 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java @@ -209,98 +209,6 @@ public class ContentViewCore public void onSmartClipDataExtracted(String result); } - private VSyncManager.Provider mVSyncProvider; - private VSyncManager.Listener mVSyncListener; - private int mVSyncSubscriberCount; - private boolean mVSyncListenerRegistered; - - // To avoid IPC delay we use input events to directly trigger a vsync signal in the renderer. - // When we do this, we also need to avoid sending the real vsync signal for the current - // frame to avoid double-ticking. This flag is used to inhibit the next vsync notification. - private boolean mDidSignalVSyncUsingInputEvent; - - public VSyncManager.Listener getVSyncListener(VSyncManager.Provider vsyncProvider) { - if (mVSyncProvider != null && mVSyncListenerRegistered) { - mVSyncProvider.unregisterVSyncListener(mVSyncListener); - mVSyncListenerRegistered = false; - } - - mVSyncProvider = vsyncProvider; - mVSyncListener = new VSyncManager.Listener() { - @Override - public void updateVSync(long tickTimeMicros, long intervalMicros) { - if (mNativeContentViewCore != 0) { - nativeUpdateVSyncParameters(mNativeContentViewCore, tickTimeMicros, - intervalMicros); - } - } - - @Override - public void onVSync(long frameTimeMicros) { - animateIfNecessary(frameTimeMicros); - - if (mRequestedVSyncForInput) { - mRequestedVSyncForInput = false; - removeVSyncSubscriber(); - } - if (mNativeContentViewCore != 0) { - nativeOnVSync(mNativeContentViewCore, frameTimeMicros); - } - } - }; - - if (mVSyncSubscriberCount > 0) { - // addVSyncSubscriber() is called before getVSyncListener. - vsyncProvider.registerVSyncListener(mVSyncListener); - mVSyncListenerRegistered = true; - } - - return mVSyncListener; - } - - @CalledByNative - void addVSyncSubscriber() { - if (!isVSyncNotificationEnabled()) { - mDidSignalVSyncUsingInputEvent = false; - } - if (mVSyncProvider != null && !mVSyncListenerRegistered) { - mVSyncProvider.registerVSyncListener(mVSyncListener); - mVSyncListenerRegistered = true; - } - mVSyncSubscriberCount++; - } - - @CalledByNative - void removeVSyncSubscriber() { - if (mVSyncProvider != null && mVSyncSubscriberCount == 1) { - assert mVSyncListenerRegistered; - mVSyncProvider.unregisterVSyncListener(mVSyncListener); - mVSyncListenerRegistered = false; - } - mVSyncSubscriberCount--; - assert mVSyncSubscriberCount >= 0; - } - - @CalledByNative - private void resetVSyncNotification() { - while (isVSyncNotificationEnabled()) removeVSyncSubscriber(); - mVSyncSubscriberCount = 0; - mVSyncListenerRegistered = false; - mNeedAnimate = false; - } - - private boolean isVSyncNotificationEnabled() { - return mVSyncProvider != null && mVSyncListenerRegistered; - } - - @CalledByNative - private void setNeedsAnimate() { - if (!mNeedAnimate) { - mNeedAnimate = true; - addVSyncSubscriber(); - } - } - private final Context mContext; private ViewGroup mContainerView; private InternalAccessDelegate mContainerViewInternals; @@ -399,16 +307,6 @@ public class ContentViewCore // Whether we received a new frame since consumePendingRendererFrame() was last called. private boolean mPendingRendererFrame = false; - // Whether we should animate at the next vsync tick. - private boolean mNeedAnimate = false; - - // Whether we requested a proactive vsync event in response to touch input. - // This reduces the latency of responding to input by ensuring the renderer - // is sent a BeginFrame for every touch event we receive. Otherwise the - // renderer's SetNeedsBeginFrame message would get serviced at the next - // vsync. - private boolean mRequestedVSyncForInput = false; - // On single tap this will store the x, y coordinates of the touch. private int mSingleTapX; private int mSingleTapY; @@ -850,8 +748,6 @@ public class ContentViewCore nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore); } mWebContents = null; - resetVSyncNotification(); - mVSyncProvider = null; if (mViewAndroid != null) mViewAndroid.destroy(); mNativeContentViewCore = 0; mContentSettings = null; @@ -1194,11 +1090,6 @@ public class ContentViewCore cancelRequestToScrollFocusedEditableNodeIntoView(); - if (!mRequestedVSyncForInput) { - mRequestedVSyncForInput = true; - addVSyncSubscriber(); - } - final int eventAction = offset.getActionMasked(); // Only these actions have any effect on gesture detection. Other @@ -3119,18 +3010,6 @@ public class ContentViewCore return new Rect(x, y, right, bottom); } - private boolean onAnimate(long frameTimeMicros) { - if (mNativeContentViewCore == 0) return false; - return nativeOnAnimate(mNativeContentViewCore, frameTimeMicros); - } - - private void animateIfNecessary(long frameTimeMicros) { - if (mNeedAnimate) { - mNeedAnimate = onAnimate(frameTimeMicros); - if (!mNeedAnimate) removeVSyncSubscriber(); - } - } - public void extractSmartClipData(int x, int y, int width, int height) { if (mNativeContentViewCore != 0) { nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height); @@ -3340,13 +3219,6 @@ public class ContentViewCore private native String nativeGetOriginalUrlForActiveNavigationEntry( long nativeContentViewCoreImpl); - private native void nativeUpdateVSyncParameters(long nativeContentViewCoreImpl, - long timebaseMicros, long intervalMicros); - - private native void nativeOnVSync(long nativeContentViewCoreImpl, long frameTimeMicros); - - private native boolean nativeOnAnimate(long nativeContentViewCoreImpl, long frameTimeMicros); - private native void nativeWasResized(long nativeContentViewCoreImpl); private native boolean nativeIsRenderWidgetHostViewReady(long nativeContentViewCoreImpl); diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java index d18c7f2..9491258 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java @@ -9,7 +9,6 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PixelFormat; -import android.os.Build; import android.os.Handler; import android.view.Surface; import android.view.SurfaceHolder; @@ -18,8 +17,6 @@ import android.widget.FrameLayout; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; -import org.chromium.base.ObserverList; -import org.chromium.base.ObserverList.RewindableIterator; import org.chromium.base.TraceEvent; import org.chromium.ui.base.WindowAndroid; @@ -30,7 +27,7 @@ import org.chromium.ui.base.WindowAndroid; * Note that only one ContentViewCore can be shown at a time. */ @JNINamespace("content") -public class ContentViewRenderView extends FrameLayout { +public class ContentViewRenderView extends FrameLayout implements WindowAndroid.VSyncClient { private static final int MAX_SWAP_BUFFER_COUNT = 2; // The native side of this object. @@ -38,7 +35,7 @@ public class ContentViewRenderView extends FrameLayout { private final SurfaceHolder.Callback mSurfaceCallback; private final SurfaceView mSurfaceView; - private final VSyncAdapter mVSyncAdapter; + private final WindowAndroid mRootWindow; private int mPendingRenders; private int mPendingSwapBuffers; @@ -64,6 +61,8 @@ public class ContentViewRenderView extends FrameLayout { mNativeContentViewRenderView = nativeInit(rootWindow.getNativePointer()); assert mNativeContentViewRenderView != 0; + mRootWindow = rootWindow; + rootWindow.setVSyncClient(this); mSurfaceView = createSurfaceView(getContext()); mSurfaceView.setZOrderMediaOverlay(true); mSurfaceCallback = new SurfaceHolder.Callback() { @@ -99,86 +98,23 @@ public class ContentViewRenderView extends FrameLayout { }; mSurfaceView.getHolder().addCallback(mSurfaceCallback); - mVSyncAdapter = new VSyncAdapter(getContext()); addView(mSurfaceView, new FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); } - private class VSyncAdapter implements VSyncManager.Provider, VSyncMonitor.Listener { - private final VSyncMonitor mVSyncMonitor; - private boolean mVSyncNotificationEnabled; - private VSyncManager.Listener mVSyncListener; - private final ObserverList<VSyncManager.Listener> mCurrentVSyncListeners; - private final RewindableIterator<VSyncManager.Listener> mCurrentVSyncListenersIterator; - - // The VSyncMonitor gives the timebase for the actual vsync, but we don't want render until - // we have had a chance for input events to propagate to the compositor thread. This takes - // 3 ms typically, so we adjust the vsync timestamps forward by a bit to give input events a - // chance to arrive. - private static final long INPUT_EVENT_LAG_FROM_VSYNC_MICROSECONDS = 3200; - - VSyncAdapter(Context context) { - mVSyncMonitor = new VSyncMonitor(context, this); - mCurrentVSyncListeners = new ObserverList<VSyncManager.Listener>(); - mCurrentVSyncListenersIterator = mCurrentVSyncListeners.rewindableIterator(); - } - - @Override - public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros) { - if (mNeedToRender) { - if (mPendingSwapBuffers + mPendingRenders <= MAX_SWAP_BUFFER_COUNT) { - mNeedToRender = false; - mPendingRenders++; - render(); - } else { - TraceEvent.instant("ContentViewRenderView:bail"); - } - } - - if (mVSyncListener != null) { - if (mVSyncNotificationEnabled) { - for (mCurrentVSyncListenersIterator.rewind(); - mCurrentVSyncListenersIterator.hasNext();) { - mCurrentVSyncListenersIterator.next().onVSync(vsyncTimeMicros); - } - mVSyncMonitor.requestUpdate(); - } else { - // Compensate for input event lag. Input events are delivered immediately on - // pre-JB releases, so this adjustment is only done for later versions. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - vsyncTimeMicros += INPUT_EVENT_LAG_FROM_VSYNC_MICROSECONDS; - } - mVSyncListener.updateVSync(vsyncTimeMicros, - mVSyncMonitor.getVSyncPeriodInMicroseconds()); - } - } - } - - @Override - public void registerVSyncListener(VSyncManager.Listener listener) { - if (!mVSyncNotificationEnabled) mVSyncMonitor.requestUpdate(); - mCurrentVSyncListeners.addObserver(listener); - mVSyncNotificationEnabled = true; - } - - @Override - public void unregisterVSyncListener(VSyncManager.Listener listener) { - mCurrentVSyncListeners.removeObserver(listener); - if (mCurrentVSyncListeners.isEmpty()) { - mVSyncNotificationEnabled = false; + @Override + public void onVSync(long vsyncTimeMicros) { + if (mNeedToRender) { + if (mPendingSwapBuffers + mPendingRenders <= MAX_SWAP_BUFFER_COUNT) { + mNeedToRender = false; + mPendingRenders++; + render(); + } else { + TraceEvent.instant("ContentViewRenderView:bail"); } } - - void setVSyncListener(VSyncManager.Listener listener) { - mVSyncListener = listener; - if (mVSyncListener != null) mVSyncMonitor.requestUpdate(); - } - - void requestUpdate() { - mVSyncMonitor.requestUpdate(); - } } /** @@ -198,6 +134,7 @@ public class ContentViewRenderView extends FrameLayout { * native resource can be freed. */ public void destroy() { + mRootWindow.setVSyncClient(null); mSurfaceView.getHolder().removeCallback(mSurfaceCallback); nativeDestroy(mNativeContentViewRenderView); mNativeContentViewRenderView = 0; @@ -209,7 +146,6 @@ public class ContentViewRenderView extends FrameLayout { if (mContentViewCore != null) { mContentViewCore.onPhysicalBackingSizeChanged(getWidth(), getHeight()); - mVSyncAdapter.setVSyncListener(mContentViewCore.getVSyncListener(mVSyncAdapter)); nativeSetCurrentContentViewCore(mNativeContentViewRenderView, mContentViewCore.getNativeContentViewCore()); } else { @@ -281,12 +217,11 @@ public class ContentViewRenderView extends FrameLayout { } else { post(mRenderRunnable); } - mVSyncAdapter.requestUpdate(); } else if (mPendingRenders <= 0) { assert mPendingRenders == 0; TraceEvent.instant("requestRender:later"); mNeedToRender = true; - mVSyncAdapter.requestUpdate(); + mRootWindow.requestVSyncUpdate(); } } diff --git a/content/public/android/java/src/org/chromium/content/browser/VSyncManager.java b/content/public/android/java/src/org/chromium/content/browser/VSyncManager.java deleted file mode 100644 index a152b41..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/VSyncManager.java +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.content.browser; - -public abstract class VSyncManager { - /** - * Interface for requesting notification of the display vsync signal. The provider will call - * Listener.onVSync() to notify about vsync. The number of registrations and unregistrations of - * a given listener must be balanced. - */ - public static interface Provider { - void registerVSyncListener(Listener listener); - void unregisterVSyncListener(Listener listener); - } - - /** - * Interface for receiving vsync notifications and information about the display refresh - * interval. - */ - public static interface Listener { - /** - * Notification of a vsync event. - * @param frameTimeMicros The latest vsync frame time in microseconds. - */ - void onVSync(long frameTimeMicros); - - /** - * Update with the latest vsync parameters. - * @param tickTimeMicros The latest vsync tick time in microseconds. - * @param intervalMicros The vsync interval in microseconds. - */ - void updateVSync(long tickTimeMicros, long intervalMicros); - } -} diff --git a/content/public/android/javatests/src/org/chromium/content/browser/VSyncMonitorTest.java b/content/public/android/javatests/src/org/chromium/content/browser/VSyncMonitorTest.java index 0b137c7..59e0c62 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/VSyncMonitorTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/VSyncMonitorTest.java @@ -10,6 +10,7 @@ import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.MediumTest; import org.chromium.base.ThreadUtils; +import org.chromium.ui.VSyncMonitor; import java.util.Arrays; import java.util.concurrent.Callable; diff --git a/content/public/android/java/src/org/chromium/content/browser/VSyncMonitor.java b/ui/android/java/src/org/chromium/ui/VSyncMonitor.java index ce9f36df..527b831 100644 --- a/content/public/android/java/src/org/chromium/content/browser/VSyncMonitor.java +++ b/ui/android/java/src/org/chromium/ui/VSyncMonitor.java @@ -1,8 +1,8 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. +// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.content.browser; +package org.chromium.ui; import android.content.Context; import android.os.Build; @@ -61,11 +61,22 @@ public class VSyncMonitor { private final Runnable mSyntheticVSyncRunnable; private long mLastVSyncCpuTimeNano; + /** + * Constructs a VSyncMonitor + * @param context The application context. + * @param listener The listener receiving VSync notifications. + */ public VSyncMonitor(Context context, VSyncMonitor.Listener listener) { this(context, listener, true); } - VSyncMonitor(Context context, VSyncMonitor.Listener listener, boolean enableJBVSync) { + /** + * Constructs a VSyncMonitor + * @param context The application context. + * @param listener The listener receiving VSync notifications. + * @param enableJBVsync Whether to allow Choreographer-based notifications on JB and up. + */ + public VSyncMonitor(Context context, VSyncMonitor.Listener listener, boolean enableJBVSync) { mListener = listener; float refreshRate = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay().getRefreshRate(); @@ -123,7 +134,7 @@ public class VSyncMonitor { /** * Determine whether a true vsync signal is available on this platform. */ - public boolean isVSyncSignalAvailable() { + private boolean isVSyncSignalAvailable() { return mChoreographer != null; } @@ -136,15 +147,6 @@ public class VSyncMonitor { } /** - * Unregister the listener. - * No vsync events will be reported afterwards. - */ - public void unregisterListener() { - stop(); - mListener = null; - } - - /** * Request to be notified of the closest display vsync events. * Listener.onVSync() will be called soon after the upcoming vsync pulses. * It will be called at most MAX_AUTO_ONVSYNC_COUNT times unless requestUpdate() is called. diff --git a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java index 309b735..8be2dd0 100644 --- a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java +++ b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java @@ -17,6 +17,7 @@ import android.widget.Toast; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; +import org.chromium.ui.VSyncMonitor; import java.lang.ref.WeakReference; import java.util.HashMap; @@ -30,6 +31,8 @@ public class WindowAndroid { // Native pointer to the c++ WindowAndroid object. private long mNativeWindowAndroid = 0; + private final VSyncMonitor mVSyncMonitor; + private VSyncClient mVSyncClient = null; // A string used as a key to store intent errors in a bundle static final String WINDOW_CALLBACK_ERRORS = "window_callback_errors"; @@ -45,6 +48,18 @@ public class WindowAndroid { // the Android lint warning "UseSparseArrays". protected HashMap<Integer, String> mIntentErrors; + private final VSyncMonitor.Listener mVSyncListener = new VSyncMonitor.Listener() { + @Override + public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros) { + if (mVSyncClient != null) { + mVSyncClient.onVSync(vsyncTimeMicros); + } + if (mNativeWindowAndroid != 0) { + nativeOnVSync(mNativeWindowAndroid, vsyncTimeMicros); + } + } + }; + /** * @param context The application context. */ @@ -54,6 +69,7 @@ public class WindowAndroid { mApplicationContext = context; mOutstandingIntents = new SparseArray<IntentCallback>(); mIntentErrors = new HashMap<Integer, String>(); + mVSyncMonitor = new VSyncMonitor(context, mVSyncListener); } /** @@ -216,6 +232,36 @@ public class WindowAndroid { } /** + * An interface to receive VSync notifications from the window. + * The one and only client is set with setVSyncClient(client). + */ + public interface VSyncClient { + /** + * Called very soon after the start of the display's vertical sync period. + * @param vsyncTimeMicros Absolute frame time in microseconds. + */ + void onVSync(long vsyncTimeMicros); + } + + /** + * Sets the VSyncClient. + * @param client The client receiving VSync notifications. + */ + public void setVSyncClient(VSyncClient client) { + assert mVSyncClient == null || client == null; + mVSyncClient = client; + } + + /** + * Request a VSync callback. + * VSyncClient.onVSync() will be called at least once. + */ + @CalledByNative + public void requestVSyncUpdate() { + mVSyncMonitor.requestUpdate(); + } + + /** * An interface that intent callback objects have to implement. */ public interface IntentCallback { @@ -226,7 +272,7 @@ public class WindowAndroid { * @param contentResolver An instance of ContentResolver class for accessing returned data. * @param data The data returned by the intent. */ - public void onIntentCompleted(WindowAndroid window, int resultCode, + void onIntentCompleted(WindowAndroid window, int resultCode, ContentResolver contentResolver, Intent data); } @@ -257,7 +303,7 @@ public class WindowAndroid { */ public long getNativePointer() { if (mNativeWindowAndroid == 0) { - mNativeWindowAndroid = nativeInit(); + mNativeWindowAndroid = nativeInit(mVSyncMonitor.getVSyncPeriodInMicroseconds()); } return mNativeWindowAndroid; } @@ -271,7 +317,8 @@ public class WindowAndroid { return null; } - private native long nativeInit(); + private native long nativeInit(long vsyncPeriod); + private native void nativeOnVSync(long nativeWindowAndroid, long vsyncTimeMicros); private native void nativeDestroy(long nativeWindowAndroid); } diff --git a/ui/base/android/window_android.cc b/ui/base/android/window_android.cc index b7a7e13..bbacf9c 100644 --- a/ui/base/android/window_android.cc +++ b/ui/base/android/window_android.cc @@ -17,9 +17,10 @@ namespace ui { using base::android::AttachCurrentThread; using base::android::ScopedJavaLocalRef; -WindowAndroid::WindowAndroid(JNIEnv* env, jobject obj) +WindowAndroid::WindowAndroid(JNIEnv* env, jobject obj, jlong vsync_period) : weak_java_window_(env, obj), - compositor_(NULL) { + compositor_(NULL), + vsync_period_(base::TimeDelta::FromInternalValue(vsync_period)) { } void WindowAndroid::Destroy(JNIEnv* env, jobject obj) { @@ -89,12 +90,24 @@ void WindowAndroid::DetachCompositor() { observer_list_.Clear(); } +void WindowAndroid::RequestVSyncUpdate() { + JNIEnv* env = AttachCurrentThread(); + Java_WindowAndroid_requestVSyncUpdate(env, GetJavaObject().obj()); +} + +void WindowAndroid::OnVSync(JNIEnv* env, jobject obj, jlong time_micros) { + FOR_EACH_OBSERVER( + WindowAndroidObserver, + observer_list_, + OnVSync(base::TimeTicks::FromInternalValue(time_micros), vsync_period_)); +} + // ---------------------------------------------------------------------------- // Native JNI methods // ---------------------------------------------------------------------------- -jlong Init(JNIEnv* env, jobject obj) { - WindowAndroid* window = new WindowAndroid(env, obj); +jlong Init(JNIEnv* env, jobject obj, jlong vsync_period) { + WindowAndroid* window = new WindowAndroid(env, obj, vsync_period); return reinterpret_cast<intptr_t>(window); } diff --git a/ui/base/android/window_android.h b/ui/base/android/window_android.h index 842bc4c..7e39184 100644 --- a/ui/base/android/window_android.h +++ b/ui/base/android/window_android.h @@ -10,6 +10,7 @@ #include "base/android/jni_weak_ref.h" #include "base/android/scoped_java_ref.h" #include "base/observer_list.h" +#include "base/time/time.h" #include "ui/base/ui_base_export.h" #include "ui/gfx/vector2d_f.h" @@ -21,7 +22,7 @@ class WindowAndroidObserver; // Android implementation of the activity window. class UI_BASE_EXPORT WindowAndroid { public: - WindowAndroid(JNIEnv* env, jobject obj); + WindowAndroid(JNIEnv* env, jobject obj, jlong vsync_period); void Destroy(JNIEnv* env, jobject obj); @@ -49,12 +50,16 @@ class UI_BASE_EXPORT WindowAndroid { WindowAndroidCompositor* GetCompositor() { return compositor_; } + void RequestVSyncUpdate(); + void OnVSync(JNIEnv* env, jobject obj, jlong time_micros); + private: ~WindowAndroid(); JavaObjectWeakGlobalRef weak_java_window_; gfx::Vector2dF content_offset_; WindowAndroidCompositor* compositor_; + base::TimeDelta vsync_period_; ObserverList<WindowAndroidObserver> observer_list_; diff --git a/ui/base/android/window_android_observer.h b/ui/base/android/window_android_observer.h index 69e4799..0d0062c 100644 --- a/ui/base/android/window_android_observer.h +++ b/ui/base/android/window_android_observer.h @@ -15,6 +15,8 @@ class UI_BASE_EXPORT WindowAndroidObserver { virtual void OnAttachCompositor() = 0; virtual void OnDetachCompositor() = 0; virtual void OnWillDestroyWindow() = 0; + virtual void OnVSync(base::TimeTicks frame_time, + base::TimeDelta vsync_period) = 0; protected: virtual ~WindowAndroidObserver() {} |