diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 00754af6a853f..1566f9e50f661 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -477,6 +477,19 @@ public final class ThreadedRenderer extends HardwareRenderer { mNextRtFrameCallbacks.add(callback); } + /** + * Remove a frame drawing callback that was added via + * {@link #registerRtFrameCallback(FrameDrawingCallback)} + * + * @param callback The callback to unregister. + */ + void unregisterRtFrameCallback(@NonNull FrameDrawingCallback callback) { + if (mNextRtFrameCallbacks == null) { + return; + } + mNextRtFrameCallbacks.remove(callback); + } + /** * Destroys all hardware rendering resources associated with the specified * view hierarchy. @@ -679,9 +692,31 @@ public final class ThreadedRenderer extends HardwareRenderer { if (mNextRtFrameCallbacks != null) { final ArrayList frameCallbacks = mNextRtFrameCallbacks; mNextRtFrameCallbacks = null; - setFrameCallback(frame -> { - for (int i = 0; i < frameCallbacks.size(); ++i) { - frameCallbacks.get(i).onFrameDraw(frame); + setFrameCallback(new FrameDrawingCallback() { + @Override + public void onFrameDraw(long frame) { + } + + @Override + public FrameCommitCallback onFrameDraw(int syncResult, long frame) { + ArrayList frameCommitCallbacks = new ArrayList<>(); + for (int i = 0; i < frameCallbacks.size(); ++i) { + FrameCommitCallback frameCommitCallback = frameCallbacks.get(i) + .onFrameDraw(syncResult, frame); + if (frameCommitCallback != null) { + frameCommitCallbacks.add(frameCommitCallback); + } + } + + if (frameCommitCallbacks.isEmpty()) { + return null; + } + + return didProduceBuffer -> { + for (int i = 0; i < frameCommitCallbacks.size(); ++i) { + frameCommitCallbacks.get(i).onFrameCommit(didProduceBuffer); + } + }; } }); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index af496dcea8e3d..1fdbf0e204da8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -16,6 +16,8 @@ package android.view; +import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED; +import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND; import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -1392,11 +1394,20 @@ public final class ViewRootImpl implements ViewParent, */ public void registerRtFrameCallback(@NonNull FrameDrawingCallback callback) { if (mAttachInfo.mThreadedRenderer != null) { - mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frame -> { - try { - callback.onFrameDraw(frame); - } catch (Exception e) { - Log.e(TAG, "Exception while executing onFrameDraw", e); + mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() { + @Override + public void onFrameDraw(long frame) { + } + + @Override + public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, + long frame) { + try { + return callback.onFrameDraw(syncResult, frame); + } catch (Exception e) { + Log.e(TAG, "Exception while executing onFrameDraw", e); + } + return null; } }); } @@ -4020,61 +4031,6 @@ public final class ViewRootImpl implements ViewParent, return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled(); } - private boolean addFrameCompleteCallbackIfNeeded(boolean useBlastSync, - boolean reportNextDraw) { - if (!isHardwareEnabled()) { - return false; - } - - if (!useBlastSync && !reportNextDraw) { - return false; - } - - if (DEBUG_BLAST) { - Log.d(mTag, "Creating frameCompleteCallback"); - } - - final Consumer blastSyncConsumer = mBLASTDrawConsumer; - mBLASTDrawConsumer = null; - - mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(() -> { - long frameNr = mBlastBufferQueue.getLastAcquiredFrameNum(); - if (DEBUG_BLAST) { - Log.d(mTag, "Received frameCompleteCallback " - + " lastAcquiredFrameNum=" + frameNr - + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum); - } - - boolean frameWasNotDrawn = frameNr != mRtLastAttemptedDrawFrameNum; - // If frame wasn't drawn, clear out the next transaction so it doesn't affect the next - // draw attempt. The next transaction and transaction complete callback were only set - // for the current draw attempt. - if (frameWasNotDrawn) { - mBlastBufferQueue.setSyncTransaction(null); - // Apply the transactions that were sent to mergeWithNextTransaction since the - // frame didn't draw on this vsync. It's possible the frame will draw later, but - // it's better to not be sync than to block on a frame that may never come. - mBlastBufferQueue.applyPendingTransactions(mRtLastAttemptedDrawFrameNum); - } - - Transaction tmpTransaction = new Transaction(); - tmpTransaction.merge(mRtBLASTSyncTransaction); - mHandler.postAtFrontOfQueue(() -> { - if (useBlastSync) { - mSurfaceChangedTransaction.merge(tmpTransaction); - if (blastSyncConsumer != null) { - blastSyncConsumer.accept(mSurfaceChangedTransaction); - } - } - - if (reportNextDraw) { - pendingDrawFinished(); - } - }); - }); - return true; - } - private void addFrameCommitCallbackIfNeeded() { if (!isHardwareEnabled()) { return; @@ -4105,51 +4061,131 @@ public final class ViewRootImpl implements ViewParent, }); } - private void addFrameCallbackIfNeeded(boolean useBlastSync) { + private HardwareRenderer.FrameCommitCallback createFrameCommitCallbackForSync( + boolean useBlastSync, boolean reportNextDraw, Consumer blastSyncConsumer) { + return didProduceBuffer -> { + if (DEBUG_BLAST) { + Log.d(mTag, "Received frameCommittedCallback " + + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum + + " didProduceBuffer=" + didProduceBuffer); + } + + // If frame wasn't drawn, clear out the next transaction so it doesn't affect the next + // draw attempt. The next transaction and transaction complete callback were only set + // for the current draw attempt. + if (!didProduceBuffer) { + mBlastBufferQueue.setSyncTransaction(null); + // Apply the transactions that were sent to mergeWithNextTransaction since the + // frame didn't draw on this vsync. It's possible the frame will draw later, but + // it's better to not be sync than to block on a frame that may never come. + mBlastBufferQueue.applyPendingTransactions(mRtLastAttemptedDrawFrameNum); + } + + Transaction tmpTransaction = new Transaction(); + tmpTransaction.merge(mRtBLASTSyncTransaction); + // Post at front of queue so the buffer can be processed immediately and allow RT + // to continue processing new buffers. If RT tries to process buffers before the sync + // buffer is applied, the new buffers will not get acquired and could result in a + // deadlock. UI thread would wait on RT, but RT would be blocked waiting for a free + // buffer. + mHandler.postAtFrontOfQueue(() -> { + if (useBlastSync) { + mSurfaceChangedTransaction.merge(tmpTransaction); + if (blastSyncConsumer != null) { + blastSyncConsumer.accept(mSurfaceChangedTransaction); + } + } + + if (reportNextDraw) { + pendingDrawFinished(); + } + }); + }; + } + + @Nullable + private FrameDrawingCallback createFrameDrawingCallbackIfNeeded(boolean useBlastSync, + boolean reportNextDraw) { + if (!isHardwareEnabled()) { + return null; + } final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates(); final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions(); - if (!useBlastSync && !needsCallbackForBlur) { - return; + if (!useBlastSync && !needsCallbackForBlur && !reportNextDraw) { + return null; } + final Consumer blastSyncConsumer = mBLASTDrawConsumer; + mBLASTDrawConsumer = null; + if (DEBUG_BLAST) { Log.d(mTag, "Creating frameDrawingCallback" + " nextDrawUseBlastSync=" + useBlastSync - + " hasBlurUpdates=" + hasBlurUpdates); + + " reportNextDraw=" + reportNextDraw + + " hasBlurUpdates=" + hasBlurUpdates + + " hasBlastSyncConsumer=" + (blastSyncConsumer != null)); } + final BackgroundBlurDrawable.BlurRegion[] blurRegionsForFrame = needsCallbackForBlur ? mBlurRegionAggregator.getBlurRegionsCopyForRT() : null; // The callback will run on the render thread. - HardwareRenderer.FrameDrawingCallback frameDrawingCallback = frame -> { - if (DEBUG_BLAST) { - Log.d(mTag, "Received frameDrawingCallback frameNum=" + frame + "." - + " Creating transactionCompleteCallback=" + useBlastSync); + return new FrameDrawingCallback() { + @Override + public void onFrameDraw(long frame) { } - mRtLastAttemptedDrawFrameNum = frame; + @Override + public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) { + if (DEBUG_BLAST) { + Log.d(mTag, + "Received frameDrawingCallback syncResult=" + syncResult + " frameNum=" + + frame + "."); + } - if (needsCallbackForBlur) { - mBlurRegionAggregator - .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates); - } + mRtLastAttemptedDrawFrameNum = frame; - if (mBlastBufferQueue == null) { - return; - } + if (needsCallbackForBlur) { + mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frame, + blurRegionsForFrame, hasBlurUpdates); + } - if (useBlastSync) { - // Frame callbacks will always occur after submitting draw requests and before - // the draw actually occurs. This will ensure that we set the next transaction - // for the frame that's about to get drawn and not on a previous frame that. + if (mBlastBufferQueue == null) { + return null; + } - // We don't need to synchronize mRtBLASTSyncTransaction here since it's not - // being modified and only sent to BlastBufferQueue. - mBlastBufferQueue.setSyncTransaction(mRtBLASTSyncTransaction); + if (!useBlastSync && !reportNextDraw) { + return null; + } + + // If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or + // SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up + // any blast sync or commit callback, and the code should directly call + // pendingDrawFinished. + if ((syncResult + & (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) { + if (reportNextDraw) { + mHandler.postAtFrontOfQueue(() -> pendingDrawFinished()); + } + return null; + } + + if (DEBUG_BLAST) { + Log.d(mTag, "Setting up sync and frameCommitCallback"); + } + + if (useBlastSync) { + // Frame callbacks will always occur after submitting draw requests and before + // the draw actually occurs. This will ensure that we set the next transaction + // for the frame that's about to get drawn and not on a previous frame. + mBlastBufferQueue.setSyncTransaction(mRtBLASTSyncTransaction); + } + + return createFrameCommitCallbackForSync(useBlastSync, reportNextDraw, + blastSyncConsumer); } }; - registerRtFrameCallback(frameDrawingCallback); } private void performDraw(boolean useBlastSync) { @@ -4165,15 +4201,20 @@ public final class ViewRootImpl implements ViewParent, mIsDrawing = true; Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw"); - addFrameCallbackIfNeeded(useBlastSync); + FrameDrawingCallback frameDrawingCallback = createFrameDrawingCallbackIfNeeded(useBlastSync, + mReportNextDraw); + if (frameDrawingCallback != null) { + mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frameDrawingCallback); + } addFrameCommitCallbackIfNeeded(); - boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded(useBlastSync, mReportNextDraw); + boolean usingAsyncReport = isHardwareEnabled() && (useBlastSync || mReportNextDraw); try { boolean canUseAsync = draw(fullRedrawNeeded); if (usingAsyncReport && !canUseAsync) { - mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null); + mAttachInfo.mThreadedRenderer.setFrameCallback(null); usingAsyncReport = false; + mAttachInfo.mThreadedRenderer.unregisterRtFrameCallback(frameDrawingCallback); } } finally { mIsDrawing = false; diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index 2b18350460726..fd4bed19e927c 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -902,6 +902,20 @@ public class HardwareRenderer { * @param frame The id of the frame being drawn. */ void onFrameDraw(long frame); + + /** + * Invoked during a frame drawing. + * + * @param syncResult The result of the draw. Should be a value or a combination of values + * from {@link SyncAndDrawResult} + * @param frame The id of the frame being drawn. + * + * @return A {@link FrameCommitCallback} that will report back if the current vsync draws. + */ + default FrameCommitCallback onFrameDraw(@SyncAndDrawResult int syncResult, long frame) { + onFrameDraw(frame); + return null; + } } /** diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index bd93a4f9b8a5c..27865b3972d7e 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -609,10 +609,19 @@ static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env, LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); auto globalCallbackRef = std::make_shared(vm, env->NewGlobalRef(frameCallback)); - proxy->setFrameCallback([globalCallbackRef](int64_t frameNr) { + proxy->setFrameCallback([globalCallbackRef](int32_t syncResult, + int64_t frameNr) -> std::function { JNIEnv* env = getenv(globalCallbackRef->vm()); - env->CallVoidMethod(globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw, - static_cast(frameNr)); + ScopedLocalRef frameCommitCallback( + env, env->CallObjectMethod( + globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw, + static_cast(syncResult), static_cast(frameNr))); + if (frameCommitCallback == nullptr) { + return nullptr; + } + sp wrapper = + sp::make(env, frameCommitCallback.get()); + return [wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); }; }); } } @@ -623,7 +632,7 @@ static void android_view_ThreadedRenderer_setFrameCommitCallback(JNIEnv* env, jo if (!callback) { proxy->setFrameCommitCallback(nullptr); } else { - sp wrapper = new FrameCommitWrapper{env, callback}; + sp wrapper = sp::make(env, callback); proxy->setFrameCommitCallback( [wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); }); } @@ -1003,8 +1012,9 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) { jclass frameCallbackClass = FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameDrawingCallback"); - gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass, - "onFrameDraw", "(J)V"); + gFrameDrawingCallback.onFrameDraw = + GetMethodIDOrDie(env, frameCallbackClass, "onFrameDraw", + "(IJ)Landroid/graphics/HardwareRenderer$FrameCommitCallback;"); jclass frameCommitClass = FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameCommitCallback"); diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 94aedd0f43be3..8c98c723dbb35 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -158,7 +158,8 @@ void DrawFrameTask::run() { // Grab a copy of everything we need CanvasContext* context = mContext; - std::function frameCallback = std::move(mFrameCallback); + std::function(int32_t, int64_t)> frameCallback = + std::move(mFrameCallback); std::function frameCompleteCallback = std::move(mFrameCompleteCallback); mFrameCallback = nullptr; mFrameCompleteCallback = nullptr; @@ -173,8 +174,13 @@ void DrawFrameTask::run() { // Even if we aren't drawing this vsync pulse the next frame number will still be accurate if (CC_UNLIKELY(frameCallback)) { - context->enqueueFrameWork( - [frameCallback, frameNr = context->getFrameNumber()]() { frameCallback(frameNr); }); + context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult, + frameNr = context->getFrameNumber()]() { + auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr)); + if (frameCommitCallback) { + context->addFrameCommitListener(std::move(frameCommitCallback)); + } + }); } nsecs_t dequeueBufferDuration = 0; diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index e3ea802b07b9c..8ad8abcff2edd 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -77,7 +77,7 @@ public: void run(); - void setFrameCallback(std::function&& callback) { + void setFrameCallback(std::function(int32_t, int64_t)>&& callback) { mFrameCallback = std::move(callback); } @@ -126,7 +126,7 @@ private: int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE]; - std::function mFrameCallback; + std::function(int32_t, int64_t)> mFrameCallback; std::function mFrameCommitCallback; std::function mFrameCompleteCallback; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 430c4d3321b93..026699c63e165 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -327,7 +327,8 @@ void RenderProxy::setPrepareSurfaceControlForWebviewCallback( [this, cb = callback]() { mContext->setPrepareSurfaceControlForWebviewCallback(cb); }); } -void RenderProxy::setFrameCallback(std::function&& callback) { +void RenderProxy::setFrameCallback( + std::function(int32_t, int64_t)>&& callback) { mDrawFrameTask.setFrameCallback(std::move(callback)); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 6d46be4b07397..491dbd72a14d6 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -124,7 +124,7 @@ public: void setASurfaceTransactionCallback( const std::function& callback); void setPrepareSurfaceControlForWebviewCallback(const std::function& callback); - void setFrameCallback(std::function&& callback); + void setFrameCallback(std::function(int32_t, int64_t)>&& callback); void setFrameCommitCallback(std::function&& callback); void setFrameCompleteCallback(std::function&& callback);