From cc2eee89c81a9b348b7283000678562f43c061c7 Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 17 May 2018 10:44:00 -0700 Subject: [PATCH] Attempting to fix the black flicker Bug: 79231206 Test: Repro steps from bug Change-Id: I4c2810c42a7a4358f64584da3ab0cdf1499e71b6 --- core/java/android/view/ThreadedRenderer.java | 19 ++++++ core/java/android/view/ViewRootImpl.java | 40 ++++++++--- core/jni/android_view_ThreadedRenderer.cpp | 70 ++++++++++++++++++++ libs/hwui/renderthread/CanvasContext.cpp | 16 +++++ libs/hwui/renderthread/CanvasContext.h | 6 ++ libs/hwui/renderthread/DrawFrameTask.cpp | 8 +++ libs/hwui/renderthread/DrawFrameTask.h | 6 ++ libs/hwui/renderthread/RenderProxy.cpp | 4 ++ libs/hwui/renderthread/RenderProxy.h | 1 + 9 files changed, 160 insertions(+), 10 deletions(-) diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 0252807a76aa1..aa1e4079f91ff 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -329,6 +329,7 @@ public final class ThreadedRenderer { // in response, so it really just exists to differentiate from LOST_SURFACE // but possibly both can just be deleted. private static final int SYNC_CONTEXT_IS_STOPPED = 1 << 2; + private static final int SYNC_FRAME_DROPPED = 1 << 3; private static final String[] VISUALIZERS = { PROFILE_PROPERTY_VISUALIZE_BARS, @@ -828,6 +829,10 @@ public final class ThreadedRenderer { } } + void setFrameCompleteCallback(FrameCompleteCallback callback) { + nSetFrameCompleteCallback(mNativeProxy, callback); + } + static void invokeFunctor(long functor, boolean waitForCompletion) { nInvokeFunctor(functor, waitForCompletion); } @@ -1059,6 +1064,18 @@ public final class ThreadedRenderer { void onFrameDraw(long frame); } + /** + * Interface used to be notified when a frame has finished rendering + */ + public interface FrameCompleteCallback { + /** + * Invoked after a frame draw + * + * @param frameNr The id of the frame that was drawn. + */ + void onFrameComplete(long frameNr); + } + private static class ProcessInitializer { static ProcessInitializer sInstance = new ProcessInitializer(); @@ -1208,6 +1225,8 @@ public final class ThreadedRenderer { private static native void nSetContentDrawBounds(long nativeProxy, int left, int top, int right, int bottom); private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback); + private static native void nSetFrameCompleteCallback(long nativeProxy, + FrameCompleteCallback callback); private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer); private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d34b92ba9a489..d839d759b52e3 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3089,13 +3089,28 @@ public final class ViewRootImpl implements ViewParent, return; } - final boolean fullRedrawNeeded = mFullRedrawNeeded; + final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw; mFullRedrawNeeded = false; mIsDrawing = true; Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw"); + + boolean usingAsyncReport = false; + if (mReportNextDraw && mAttachInfo.mThreadedRenderer != null + && mAttachInfo.mThreadedRenderer.isEnabled()) { + usingAsyncReport = true; + mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> { + // TODO: Use the frame number + pendingDrawFinished(); + }); + } + try { - draw(fullRedrawNeeded); + boolean canUseAsync = draw(fullRedrawNeeded); + if (usingAsyncReport && !canUseAsync) { + mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null); + usingAsyncReport = false; + } } finally { mIsDrawing = false; Trace.traceEnd(Trace.TRACE_TAG_VIEW); @@ -3125,7 +3140,6 @@ public final class ViewRootImpl implements ViewParent, } if (mAttachInfo.mThreadedRenderer != null) { - mAttachInfo.mThreadedRenderer.fence(); mAttachInfo.mThreadedRenderer.setStopped(mStopped); } @@ -3138,16 +3152,19 @@ public final class ViewRootImpl implements ViewParent, SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); - } else { + } else if (!usingAsyncReport) { + if (mAttachInfo.mThreadedRenderer != null) { + mAttachInfo.mThreadedRenderer.fence(); + } pendingDrawFinished(); } } } - private void draw(boolean fullRedrawNeeded) { + private boolean draw(boolean fullRedrawNeeded) { Surface surface = mSurface; if (!surface.isValid()) { - return; + return false; } if (DEBUG_FPS) { @@ -3196,7 +3213,7 @@ public final class ViewRootImpl implements ViewParent, if (animating && mScroller != null) { mScroller.abortAnimation(); } - return; + return false; } if (fullRedrawNeeded) { @@ -3242,6 +3259,7 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mDrawingTime = mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS; + boolean useAsyncReport = false; if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) { // If accessibility focus moved, always invalidate the root. @@ -3278,6 +3296,7 @@ public final class ViewRootImpl implements ViewParent, requestDrawWindow(); } + useAsyncReport = true; mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, mNextRtFrameCallback); mNextRtFrameCallback = null; } else { @@ -3299,17 +3318,17 @@ public final class ViewRootImpl implements ViewParent, mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets); } catch (OutOfResourcesException e) { handleOutOfResourcesException(e); - return; + return false; } mFullRedrawNeeded = true; scheduleTraversals(); - return; + return false; } if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty, surfaceInsets)) { - return; + return false; } } } @@ -3318,6 +3337,7 @@ public final class ViewRootImpl implements ViewParent, mFullRedrawNeeded = true; scheduleTraversals(); } + return useAsyncReport; } /** diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index a73939c7b52e1..497b2894d4059 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -15,9 +15,11 @@ */ #define LOG_TAG "ThreadedRenderer" +#define ATRACE_TAG ATRACE_TAG_VIEW #include #include +#include #include "jni.h" #include @@ -37,6 +39,7 @@ #include #include #include +#include #include #include @@ -72,6 +75,10 @@ struct { jmethodID onFrameDraw; } gFrameDrawingCallback; +struct { + jmethodID onFrameComplete; +} gFrameCompleteCallback; + static JNIEnv* getenv(JavaVM* vm) { JNIEnv* env; if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { @@ -153,6 +160,49 @@ private: std::string mMessage; }; +class FrameCompleteWrapper : public MessageHandler { +public: + FrameCompleteWrapper(JNIEnv* env, jobject jobject) { + mLooper = Looper::getForThread(); + LOG_ALWAYS_FATAL_IF(!mLooper.get(), "Must create runnable on a Looper thread!"); + env->GetJavaVM(&mVm); + mObject = env->NewGlobalRef(jobject); + LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref"); + } + + virtual ~FrameCompleteWrapper() { + releaseObject(); + } + + void postFrameComplete(int64_t frameNr) { + if (mObject) { + mFrameNr = frameNr; + mLooper->sendMessage(this, 0); + } + } + + virtual void handleMessage(const Message&) { + if (mObject) { + ATRACE_FORMAT("frameComplete %" PRId64, mFrameNr); + getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, mFrameNr); + releaseObject(); + } + } + +private: + JavaVM* mVm; + jobject mObject; + sp mLooper; + int64_t mFrameNr = -1; + + void releaseObject() { + if (mObject) { + getenv(mVm)->DeleteGlobalRef(mObject); + mObject = nullptr; + } + } +}; + class RootRenderNode : public RenderNode, ErrorHandler { public: explicit RootRenderNode(JNIEnv* env) : RenderNode() { @@ -885,6 +935,19 @@ static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env, } } +static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env, + jobject clazz, jlong proxyPtr, jobject callback) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + if (!callback) { + proxy->setFrameCompleteCallback(nullptr); + } else { + sp wrapper = new FrameCompleteWrapper{env, callback}; + proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) { + wrapper->postFrameComplete(frameNr); + }); + } +} + static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, jobject clazz, jobject jsurface, jint left, jint top, jint right, jint bottom, jobject jbitmap) { @@ -1084,6 +1147,8 @@ static const JNINativeMethod gMethods[] = { { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds}, { "nSetFrameCallback", "(JLandroid/view/ThreadedRenderer$FrameDrawingCallback;)V", (void*)android_view_ThreadedRenderer_setFrameCallback}, + { "nSetFrameCompleteCallback", "(JLandroid/view/ThreadedRenderer$FrameCompleteCallback;)V", + (void*)android_view_ThreadedRenderer_setFrameCompleteCallback }, { "nAddFrameMetricsObserver", "(JLandroid/view/FrameMetricsObserver;)J", (void*)android_view_ThreadedRenderer_addFrameMetricsObserver }, @@ -1136,6 +1201,11 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) { gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass, "onFrameDraw", "(J)V"); + jclass frameCompleteClass = FindClassOrDie(env, + "android/view/ThreadedRenderer$FrameCompleteCallback"); + gFrameCompleteCallback.onFrameComplete = GetMethodIDOrDie(env, frameCompleteClass, + "onFrameComplete", "(J)V"); + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 23bbbefa0b69e..9e70a8942e6c6 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -42,6 +42,7 @@ #include #include +#include #define TRIM_MEMORY_COMPLETE 80 #define TRIM_MEMORY_UI_HIDDEN 20 @@ -347,6 +348,12 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy info.out.canDrawThisFrame = true; } + // TODO: Do we need to abort out if the backdrop is added but not ready? Should that even + // be an allowable combination? + if (mRenderNodes.size() > 2 && !mRenderNodes[1]->isRenderable()) { + info.out.canDrawThisFrame = false; + } + if (!info.out.canDrawThisFrame) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); } @@ -413,6 +420,8 @@ void CanvasContext::draw() { mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo, mRenderNodes, &(profiler())); + int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1; + waitOnFences(); bool requireSwap = false; @@ -473,6 +482,13 @@ void CanvasContext::draw() { } #endif + if (didSwap) { + for (auto& func : mFrameCompleteCallbacks) { + std::invoke(func, frameCompleteNr); + } + mFrameCompleteCallbacks.clear(); + } + mJankTracker.finishFrame(*mCurrentFrameInfo); if (CC_UNLIKELY(mFrameMetricsReporter.get() != nullptr)) { mFrameMetricsReporter->reportFrameMetrics(mCurrentFrameInfo->data()); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 056a70621befa..8ca54af13baad 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -186,6 +186,10 @@ public: IRenderPipeline* getRenderPipeline() { return mRenderPipeline.get(); } + void addFrameCompleteListener(std::function&& func) { + mFrameCompleteCallbacks.push_back(std::move(func)); + } + private: CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory, std::unique_ptr renderPipeline); @@ -263,6 +267,8 @@ private: std::vector> mFrameFences; sp> mFrameWorkProcessor; std::unique_ptr mRenderPipeline; + + std::vector> mFrameCompleteCallbacks; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 60df514ecc2bd..51eeab7e46ce7 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -90,6 +90,11 @@ void DrawFrameTask::run() { TreeInfo info(TreeInfo::MODE_FULL, *mContext); canUnblockUiThread = syncFrameState(info); canDrawThisFrame = info.out.canDrawThisFrame; + + if (mFrameCompleteCallback) { + mContext->addFrameCompleteListener(std::move(mFrameCompleteCallback)); + mFrameCompleteCallback = nullptr; + } } // Grab a copy of everything we need @@ -152,6 +157,9 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { mSyncResult |= SyncResult::UIRedrawRequired; } } + if (!info.out.canDrawThisFrame) { + mSyncResult |= SyncResult::FrameDropped; + } // If prepareTextures is false, we ran out of texture cache space return info.prepareTextures; } diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 0037a0f4306d0..696cfaef3cd7b 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -45,6 +45,7 @@ enum { UIRedrawRequired = 1 << 0, LostSurfaceRewardIfFound = 1 << 1, ContextIsStopped = 1 << 2, + FrameDropped = 1 << 3, }; } @@ -77,6 +78,10 @@ public: mFrameCallback = std::move(callback); } + void setFrameCompleteCallback(std::function&& callback) { + mFrameCompleteCallback = std::move(callback); + } + private: void postAndWait(); bool syncFrameState(TreeInfo& info); @@ -101,6 +106,7 @@ private: int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE]; std::function mFrameCallback; + std::function mFrameCompleteCallback; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 12220a6b3776f..6eca8d2f346f3 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -275,6 +275,10 @@ void RenderProxy::setFrameCallback(std::function&& callback) { mDrawFrameTask.setFrameCallback(std::move(callback)); } +void RenderProxy::setFrameCompleteCallback(std::function&& callback) { + mDrawFrameTask.setFrameCompleteCallback(std::move(callback)); +} + void RenderProxy::addFrameMetricsObserver(FrameMetricsObserver* observerPtr) { mRenderThread.queue().post([ this, observer = sp{observerPtr} ]() { mContext->addFrameMetricsObserver(observer.get()); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index e7cd0913231d5..5668484673fb1 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -114,6 +114,7 @@ public: ANDROID_API void drawRenderNode(RenderNode* node); ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom); ANDROID_API void setFrameCallback(std::function&& callback); + ANDROID_API void setFrameCompleteCallback(std::function&& callback); ANDROID_API void addFrameMetricsObserver(FrameMetricsObserver* observer); ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);