OSDN Git Service

Attempting to fix the black flicker
authorJohn Reck <jreck@google.com>
Thu, 17 May 2018 17:44:00 +0000 (10:44 -0700)
committerJohn Reck <jreck@google.com>
Thu, 31 May 2018 19:56:28 +0000 (12:56 -0700)
Bug: 79231206
Test: Repro steps from bug

Merged-In: I4c2810c42a7a4358f64584da3ab0cdf1499e71b6
Change-Id: I4c2810c42a7a4358f64584da3ab0cdf1499e71b6

core/java/android/view/ThreadedRenderer.java
core/java/android/view/ViewRootImpl.java
core/jni/android_view_ThreadedRenderer.cpp
libs/hwui/renderthread/CanvasContext.cpp
libs/hwui/renderthread/CanvasContext.h
libs/hwui/renderthread/DrawFrameTask.cpp
libs/hwui/renderthread/DrawFrameTask.h
libs/hwui/renderthread/RenderProxy.cpp
libs/hwui/renderthread/RenderProxy.h

index d19cc9c..5b61015 100644 (file)
@@ -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,
@@ -832,6 +833,10 @@ public final class ThreadedRenderer {
         }
     }
 
+    void setFrameCompleteCallback(FrameCompleteCallback callback) {
+        nSetFrameCompleteCallback(mNativeProxy, callback);
+    }
+
     static void invokeFunctor(long functor, boolean waitForCompletion) {
         nInvokeFunctor(functor, waitForCompletion);
     }
@@ -1067,6 +1072,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();
 
@@ -1218,6 +1235,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);
index 48f380d..0506f30 100644 (file)
@@ -3096,13 +3096,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);
@@ -3132,7 +3147,6 @@ public final class ViewRootImpl implements ViewParent,
             }
 
             if (mAttachInfo.mThreadedRenderer != null) {
-                mAttachInfo.mThreadedRenderer.fence();
                 mAttachInfo.mThreadedRenderer.setStopped(mStopped);
             }
 
@@ -3145,16 +3159,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) {
@@ -3203,7 +3220,7 @@ public final class ViewRootImpl implements ViewParent,
             if (animating && mScroller != null) {
                 mScroller.abortAnimation();
             }
-            return;
+            return false;
         }
 
         if (fullRedrawNeeded) {
@@ -3250,6 +3267,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.
@@ -3286,6 +3304,7 @@ public final class ViewRootImpl implements ViewParent,
                     requestDrawWindow();
                 }
 
+                useAsyncReport = true;
                 mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, mNextRtFrameCallback);
                 mNextRtFrameCallback = null;
             } else {
@@ -3307,17 +3326,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;
                 }
             }
         }
@@ -3326,6 +3345,7 @@ public final class ViewRootImpl implements ViewParent,
             mFullRedrawNeeded = true;
             scheduleTraversals();
         }
+        return useAsyncReport;
     }
 
     /**
index 7481f1c..ee9a123 100644 (file)
  */
 
 #define LOG_TAG "ThreadedRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
 
 #include <algorithm>
 #include <atomic>
+#include <inttypes.h>
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
@@ -37,6 +39,7 @@
 #include <utils/RefBase.h>
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
+#include <utils/TraceUtils.h>
 #include <android_runtime/android_view_Surface.h>
 #include <system/window.h>
 
@@ -72,6 +75,10 @@ struct {
     jmethodID onFrameDraw;
 } gFrameDrawingCallback;
 
+struct {
+    jmethodID onFrameComplete;
+} gFrameCompleteCallback;
+
 static JNIEnv* getenv(JavaVM* vm) {
     JNIEnv* env;
     if (vm->GetEnv(reinterpret_cast<void**>(&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<Looper> 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() {
@@ -891,6 +941,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<RenderProxy*>(proxyPtr);
+    if (!callback) {
+        proxy->setFrameCompleteCallback(nullptr);
+    } else {
+        sp<FrameCompleteWrapper> 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) {
@@ -1091,6 +1154,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 },
@@ -1143,6 +1208,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));
 }
 
index 2ddf55b..aed0620 100644 (file)
@@ -46,6 +46,7 @@
 #include <algorithm>
 
 #include <cstdlib>
+#include <functional>
 
 #define TRIM_MEMORY_COMPLETE 80
 #define TRIM_MEMORY_UI_HIDDEN 20
@@ -383,6 +384,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);
     }
@@ -449,6 +456,8 @@ void CanvasContext::draw() {
                                       mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo,
                                       mRenderNodes, &(profiler()));
 
+    int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1;
+
     waitOnFences();
 
     bool requireSwap = false;
@@ -509,6 +518,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());
index e52b644..1c4e02d 100644 (file)
@@ -190,6 +190,10 @@ public:
 
     IRenderPipeline* getRenderPipeline() { return mRenderPipeline.get(); }
 
+    void addFrameCompleteListener(std::function<void(int64_t)>&& func) {
+        mFrameCompleteCallbacks.push_back(std::move(func));
+    }
+
 private:
     CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
                   IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
@@ -267,6 +271,8 @@ private:
     std::vector<sp<FuncTask>> mFrameFences;
     sp<TaskProcessor<bool>> mFrameWorkProcessor;
     std::unique_ptr<IRenderPipeline> mRenderPipeline;
+
+    std::vector<std::function<void(int64_t)>> mFrameCompleteCallbacks;
 };
 
 } /* namespace renderthread */
index 60df514..51eeab7 100644 (file)
@@ -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;
 }
index d8c43e0..2c46762 100644 (file)
@@ -46,6 +46,7 @@ enum {
     UIRedrawRequired = 1 << 0,
     LostSurfaceRewardIfFound = 1 << 1,
     ContextIsStopped = 1 << 2,
+    FrameDropped = 1 << 3,
 };
 }
 
@@ -78,6 +79,10 @@ public:
         mFrameCallback = std::move(callback);
     }
 
+    void setFrameCompleteCallback(std::function<void(int64_t)>&& callback) {
+        mFrameCompleteCallback = std::move(callback);
+    }
+
 private:
     void postAndWait();
     bool syncFrameState(TreeInfo& info);
@@ -102,6 +107,7 @@ private:
     int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
 
     std::function<void(int64_t)> mFrameCallback;
+    std::function<void(int64_t)> mFrameCompleteCallback;
 };
 
 } /* namespace renderthread */
index c6a9b55..59048be 100644 (file)
@@ -274,6 +274,10 @@ void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) {
     mDrawFrameTask.setFrameCallback(std::move(callback));
 }
 
+void RenderProxy::setFrameCompleteCallback(std::function<void(int64_t)>&& callback) {
+    mDrawFrameTask.setFrameCompleteCallback(std::move(callback));
+}
+
 void RenderProxy::serializeDisplayListTree() {
     mRenderThread.queue().post([=]() { mContext->serializeDisplayListTree(); });
 }
index 3425c5c..ad534f0 100644 (file)
@@ -116,6 +116,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<void(int64_t)>&& callback);
+    ANDROID_API void setFrameCompleteCallback(std::function<void(int64_t)>&& callback);
 
     ANDROID_API void addFrameMetricsObserver(FrameMetricsObserver* observer);
     ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);