Attempting to fix the black flicker
Bug: 79231206 Test: Repro steps from bug Change-Id: I4c2810c42a7a4358f64584da3ab0cdf1499e71b6
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 @@ 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() {
|
||||
@@ -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<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 @@ 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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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 @@ 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());
|
||||
|
||||
@@ -186,6 +186,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);
|
||||
@@ -263,6 +267,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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<void(int64_t)>&& 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<void(int64_t)> mFrameCallback;
|
||||
std::function<void(int64_t)> mFrameCompleteCallback;
|
||||
};
|
||||
|
||||
} /* namespace renderthread */
|
||||
|
||||
@@ -275,6 +275,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::addFrameMetricsObserver(FrameMetricsObserver* observerPtr) {
|
||||
mRenderThread.queue().post([ this, observer = sp{observerPtr} ]() {
|
||||
mContext->addFrameMetricsObserver(observer.get());
|
||||
|
||||
@@ -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<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);
|
||||
|
||||
Reference in New Issue
Block a user