diff options
| -rw-r--r-- | core/java/android/view/ThreadedRenderer.java | 19 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 40 | ||||
| -rw-r--r-- | core/jni/android_view_ThreadedRenderer.cpp | 70 | ||||
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 16 | ||||
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 6 | ||||
| -rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.cpp | 8 | ||||
| -rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.h | 6 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 4 | ||||
| -rw-r--r-- | 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 d19cc9c8fa92..5b61015e9d42 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, @@ -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); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 48f380d87dcc..0506f30d04b8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -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; } /** diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 7481f1c1fe53..ee9a123ab674 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 @@ 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)); } diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 2ddf55be7574..aed0620166dd 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -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()); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index e52b6447a611..1c4e02d7df70 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -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 */ diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 60df514ecc2b..51eeab7e46ce 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 d8c43e0d8ca8..2c46762fee5c 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -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 */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index c6a9b55f8ac1..59048be768e9 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -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(); }); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 3425c5c68a72..ad534f0d96b5 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -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); |