Merge "Attempting to fix the black flicker"
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 0252807..aa1e407 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -329,6 +329,7 @@
// 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 @@
}
}
+ void setFrameCompleteCallback(FrameCompleteCallback callback) {
+ nSetFrameCompleteCallback(mNativeProxy, callback);
+ }
+
static void invokeFunctor(long functor, boolean waitForCompletion) {
nInvokeFunctor(functor, waitForCompletion);
}
@@ -1059,6 +1064,18 @@
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 @@
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 d34b92b..d839d75 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3089,13 +3089,28 @@
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 @@
}
if (mAttachInfo.mThreadedRenderer != null) {
- mAttachInfo.mThreadedRenderer.fence();
mAttachInfo.mThreadedRenderer.setStopped(mStopped);
}
@@ -3138,16 +3152,19 @@
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 @@
if (animating && mScroller != null) {
mScroller.abortAnimation();
}
- return;
+ return false;
}
if (fullRedrawNeeded) {
@@ -3242,6 +3259,7 @@
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 @@
requestDrawWindow();
}
+ useAsyncReport = true;
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, mNextRtFrameCallback);
mNextRtFrameCallback = null;
} else {
@@ -3299,17 +3318,17 @@
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 @@
mFullRedrawNeeded = true;
scheduleTraversals();
}
+ return useAsyncReport;
}
/**
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index a73939c..497b289 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 <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 @@
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 @@
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() {
@@ -885,6 +935,19 @@
}
}
+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) {
@@ -1084,6 +1147,8 @@
{ "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 @@
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 23bbbef..9e70a89 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -42,6 +42,7 @@
#include <algorithm>
#include <cstdlib>
+#include <functional>
#define TRIM_MEMORY_COMPLETE 80
#define TRIM_MEMORY_UI_HIDDEN 20
@@ -347,6 +348,12 @@
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 @@
mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo,
mRenderNodes, &(profiler()));
+ int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1;
+
waitOnFences();
bool requireSwap = false;
@@ -473,6 +482,13 @@
}
#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 056a706..8ca54af 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -186,6 +186,10 @@
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);
@@ -263,6 +267,8 @@
std::vector<sp<FuncTask>> mFrameFences;
sp<TaskProcessor<bool>> mFrameWorkProcessor;
std::unique_ptr<IRenderPipeline> mRenderPipeline;
+
+ std::vector<std::function<void(int64_t)>> mFrameCompleteCallbacks;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 60df514..51eeab7 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -90,6 +90,11 @@
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 @@
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 0037a0f..696cfaef 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -45,6 +45,7 @@
UIRedrawRequired = 1 << 0,
LostSurfaceRewardIfFound = 1 << 1,
ContextIsStopped = 1 << 2,
+ FrameDropped = 1 << 3,
};
}
@@ -77,6 +78,10 @@
mFrameCallback = std::move(callback);
}
+ void setFrameCompleteCallback(std::function<void(int64_t)>&& callback) {
+ mFrameCompleteCallback = std::move(callback);
+ }
+
private:
void postAndWait();
bool syncFrameState(TreeInfo& info);
@@ -101,6 +106,7 @@
int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
std::function<void(int64_t)> mFrameCallback;
+ std::function<void(int64_t)> mFrameCompleteCallback;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 12220a6..6eca8d2 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -275,6 +275,10 @@
mDrawFrameTask.setFrameCallback(std::move(callback));
}
+void RenderProxy::setFrameCompleteCallback(std::function<void(int64_t)>&& 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 e7cd091..5668484 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -114,6 +114,7 @@
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);