Loading core/java/android/view/ThreadedRenderer.java +19 −0 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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); } Loading Loading @@ -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(); Loading Loading @@ -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); Loading core/java/android/view/ViewRootImpl.java +30 −10 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -3125,7 +3140,6 @@ public final class ViewRootImpl implements ViewParent, } if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mThreadedRenderer.fence(); mAttachInfo.mThreadedRenderer.setStopped(mStopped); } Loading @@ -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) { Loading Loading @@ -3196,7 +3213,7 @@ public final class ViewRootImpl implements ViewParent, if (animating && mScroller != null) { mScroller.abortAnimation(); } return; return false; } if (fullRedrawNeeded) { Loading Loading @@ -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. Loading Loading @@ -3278,6 +3296,7 @@ public final class ViewRootImpl implements ViewParent, requestDrawWindow(); } useAsyncReport = true; mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, mNextRtFrameCallback); mNextRtFrameCallback = null; } else { Loading @@ -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; } } } Loading @@ -3318,6 +3337,7 @@ public final class ViewRootImpl implements ViewParent, mFullRedrawNeeded = true; scheduleTraversals(); } return useAsyncReport; } /** Loading core/jni/android_view_ThreadedRenderer.cpp +70 −0 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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> Loading Loading @@ -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) { Loading Loading @@ -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() { Loading Loading @@ -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) { Loading Loading @@ -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 }, Loading Loading @@ -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)); } Loading libs/hwui/renderthread/CanvasContext.cpp +16 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ #include <algorithm> #include <cstdlib> #include <functional> #define TRIM_MEMORY_COMPLETE 80 #define TRIM_MEMORY_UI_HIDDEN 20 Loading Loading @@ -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); } Loading Loading @@ -413,6 +420,8 @@ void CanvasContext::draw() { mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo, mRenderNodes, &(profiler())); int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1; waitOnFences(); bool requireSwap = false; Loading Loading @@ -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()); Loading libs/hwui/renderthread/CanvasContext.h +6 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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 */ Loading Loading
core/java/android/view/ThreadedRenderer.java +19 −0 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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); } Loading Loading @@ -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(); Loading Loading @@ -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); Loading
core/java/android/view/ViewRootImpl.java +30 −10 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -3125,7 +3140,6 @@ public final class ViewRootImpl implements ViewParent, } if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mThreadedRenderer.fence(); mAttachInfo.mThreadedRenderer.setStopped(mStopped); } Loading @@ -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) { Loading Loading @@ -3196,7 +3213,7 @@ public final class ViewRootImpl implements ViewParent, if (animating && mScroller != null) { mScroller.abortAnimation(); } return; return false; } if (fullRedrawNeeded) { Loading Loading @@ -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. Loading Loading @@ -3278,6 +3296,7 @@ public final class ViewRootImpl implements ViewParent, requestDrawWindow(); } useAsyncReport = true; mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, mNextRtFrameCallback); mNextRtFrameCallback = null; } else { Loading @@ -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; } } } Loading @@ -3318,6 +3337,7 @@ public final class ViewRootImpl implements ViewParent, mFullRedrawNeeded = true; scheduleTraversals(); } return useAsyncReport; } /** Loading
core/jni/android_view_ThreadedRenderer.cpp +70 −0 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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> Loading Loading @@ -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) { Loading Loading @@ -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() { Loading Loading @@ -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) { Loading Loading @@ -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 }, Loading Loading @@ -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)); } Loading
libs/hwui/renderthread/CanvasContext.cpp +16 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ #include <algorithm> #include <cstdlib> #include <functional> #define TRIM_MEMORY_COMPLETE 80 #define TRIM_MEMORY_UI_HIDDEN 20 Loading Loading @@ -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); } Loading Loading @@ -413,6 +420,8 @@ void CanvasContext::draw() { mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo, mRenderNodes, &(profiler())); int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1; waitOnFences(); bool requireSwap = false; Loading Loading @@ -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()); Loading
libs/hwui/renderthread/CanvasContext.h +6 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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 */ Loading