Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b680371f authored by chaviw's avatar chaviw
Browse files

Send syncStatus and return FrameCommitCallback in FrameCallback

FrameCallback sends back the syncStatus and allows the caller to return
a FrameCommitCallback. This is so the caller can evaluate the results of
the sync and determine if it wants to continue waiting for a commit
callback. In cases, where the sync has failed, there will never be a
commit callback so the caller can avoid waiting.

This is partically helpful for VRI because it wants to sync with a frame
that will draw, but also doesn't want to get stuck indefinitely waiting
on the callback. VRI can check the syncStatus and only handle blast sync
and/or reportDraw when its determined that a draw will happen.

This allows VRI to remove frameCompleteCallback since it can now wait
until a buffer has drawn instead of when the vsync has completed. This makes
sure it waits on FrameDropped since that result indicates HWUI will draw on
the next vsync

Test: BlastSync
Bug: 200284684
Change-Id: Ic6e2f08ea21ac8a1634a3389c927fcb68ede3f7b
parent 6d2adf42
Loading
Loading
Loading
Loading
+38 −3
Original line number Diff line number Diff line
@@ -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<FrameDrawingCallback> frameCallbacks = mNextRtFrameCallbacks;
            mNextRtFrameCallbacks = null;
            setFrameCallback(frame -> {
            setFrameCallback(new FrameDrawingCallback() {
                @Override
                public void onFrameDraw(long frame) {
                }

                @Override
                public FrameCommitCallback onFrameDraw(int syncResult, long frame) {
                    ArrayList<FrameCommitCallback> frameCommitCallbacks = new ArrayList<>();
                    for (int i = 0; i < frameCallbacks.size(); ++i) {
                    frameCallbacks.get(i).onFrameDraw(frame);
                        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);
                        }
                    };
                }
            });
        }
+119 −78
Original line number Diff line number Diff line
@@ -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;
@@ -1395,12 +1397,21 @@ public final class ViewRootImpl implements ViewParent,
     */
    public void registerRtFrameCallback(@NonNull FrameDrawingCallback callback) {
        if (mAttachInfo.mThreadedRenderer != null) {
            mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frame -> {
            mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
                @Override
                public void onFrameDraw(long frame) {
                }

                @Override
                public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult,
                        long frame) {
                    try {
                    callback.onFrameDraw(frame);
                        return callback.onFrameDraw(syncResult, frame);
                    } catch (Exception e) {
                        Log.e(TAG, "Exception while executing onFrameDraw", e);
                    }
                    return null;
                }
            });
        }
    }
@@ -4026,36 +4037,49 @@ public final class ViewRootImpl implements ViewParent,
        return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
    }

    private boolean addFrameCompleteCallbackIfNeeded(boolean useBlastSync,
            boolean reportNextDraw) {
    private void addFrameCommitCallbackIfNeeded() {
        if (!isHardwareEnabled()) {
            return false;
            return;
        }

        if (!useBlastSync && !reportNextDraw) {
            return false;
        ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
                .captureFrameCommitCallbacks();
        final boolean needFrameCommitCallback =
                (commitCallbacks != null && commitCallbacks.size() > 0);
        if (!needFrameCommitCallback) {
            return;
        }

        if (DEBUG_BLAST) {
            Log.d(mTag, "Creating frameCompleteCallback");
        if (DEBUG_DRAW) {
            Log.d(mTag, "Creating frameCommitCallback"
                    + " commitCallbacks size=" + commitCallbacks.size());
        }
        mAttachInfo.mThreadedRenderer.setFrameCommitCallback(didProduceBuffer -> {
            if (DEBUG_DRAW) {
                Log.d(mTag, "Received frameCommitCallback didProduceBuffer=" + didProduceBuffer);
            }

        final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer;
        mBLASTDrawConsumer = null;
            mHandler.postAtFrontOfQueue(() -> {
                for (int i = 0; i < commitCallbacks.size(); i++) {
                    commitCallbacks.get(i).run();
                }
            });
        });
    }

        mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(() -> {
            long frameNr = mBlastBufferQueue.getLastAcquiredFrameNum();
    private HardwareRenderer.FrameCommitCallback createFrameCommitCallbackForSync(
            boolean useBlastSync, boolean reportNextDraw, Consumer<Transaction> blastSyncConsumer) {
        return didProduceBuffer -> {
            if (DEBUG_BLAST) {
                Log.d(mTag, "Received frameCompleteCallback "
                        + " lastAcquiredFrameNum=" + frameNr
                        + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum);
                Log.d(mTag, "Received frameCommittedCallback "
                        + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum
                        + " didProduceBuffer=" + didProduceBuffer);
            }

            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) {
            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
@@ -4065,6 +4089,11 @@ public final class ViewRootImpl implements ViewParent,

            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);
@@ -4077,85 +4106,92 @@ public final class ViewRootImpl implements ViewParent,
                    pendingDrawFinished();
                }
            });
        });
        return true;
        };
    }

    private void addFrameCommitCallbackIfNeeded() {
    @Nullable
    private FrameDrawingCallback createFrameDrawingCallbackIfNeeded(boolean useBlastSync,
            boolean reportNextDraw) {
        if (!isHardwareEnabled()) {
            return;
        }

        ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
                .captureFrameCommitCallbacks();
        final boolean needFrameCommitCallback =
                (commitCallbacks != null && commitCallbacks.size() > 0);
        if (!needFrameCommitCallback) {
            return;
        }

        if (DEBUG_DRAW) {
            Log.d(mTag, "Creating frameCommitCallback"
                    + " commitCallbacks size=" + commitCallbacks.size());
        }
        mAttachInfo.mThreadedRenderer.setFrameCommitCallback(didProduceBuffer -> {
            if (DEBUG_DRAW) {
                Log.d(mTag, "Received frameCommitCallback didProduceBuffer=" + didProduceBuffer);
            }

            mHandler.postAtFrontOfQueue(() -> {
                for (int i = 0; i < commitCallbacks.size(); i++) {
                    commitCallbacks.get(i).run();
                }
            });
        });
            return null;
        }

    private void addFrameCallbackIfNeeded(boolean useBlastSync) {
        final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates();
        final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions();

        if (!useBlastSync && !needsCallbackForBlur) {
            return;
        if (!useBlastSync && !needsCallbackForBlur && !reportNextDraw) {
            return null;
        }

        final Consumer<SurfaceControl.Transaction> 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 -> {
        return new FrameDrawingCallback() {
            @Override
            public void onFrameDraw(long frame) {
            }

            @Override
            public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) {
                if (DEBUG_BLAST) {
                Log.d(mTag, "Received frameDrawingCallback frameNum=" + frame + "."
                        + " Creating transactionCompleteCallback=" + useBlastSync);
                    Log.d(mTag,
                            "Received frameDrawingCallback syncResult=" + syncResult + " frameNum="
                                    + frame + ".");
                }

                mRtLastAttemptedDrawFrameNum = frame;

                if (needsCallbackForBlur) {
                mBlurRegionAggregator
                    .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates);
                    mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frame,
                            blurRegionsForFrame, hasBlurUpdates);
                }

                if (mBlastBufferQueue == null) {
                return;
                    return null;
                }

                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 that.

                // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
                // being modified and only sent to BlastBufferQueue.
                    // 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) {
@@ -4171,15 +4207,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;
+14 −0
Original line number Diff line number Diff line
@@ -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;
        }
    }

    /**
+16 −6
Original line number Diff line number Diff line
@@ -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<JGlobalRefHolder>(vm,
                env->NewGlobalRef(frameCallback));
        proxy->setFrameCallback([globalCallbackRef](int64_t frameNr) {
        proxy->setFrameCallback([globalCallbackRef](int32_t syncResult,
                                                    int64_t frameNr) -> std::function<void(bool)> {
            JNIEnv* env = getenv(globalCallbackRef->vm());
            env->CallVoidMethod(globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
                    static_cast<jlong>(frameNr));
            ScopedLocalRef<jobject> frameCommitCallback(
                    env, env->CallObjectMethod(
                                 globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
                                 static_cast<jint>(syncResult), static_cast<jlong>(frameNr)));
            if (frameCommitCallback == nullptr) {
                return nullptr;
            }
            sp<FrameCommitWrapper> wrapper =
                    sp<FrameCommitWrapper>::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<FrameCommitWrapper> wrapper = new FrameCommitWrapper{env, callback};
        sp<FrameCommitWrapper> wrapper = sp<FrameCommitWrapper>::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");
+9 −3
Original line number Diff line number Diff line
@@ -158,7 +158,8 @@ void DrawFrameTask::run() {

    // Grab a copy of everything we need
    CanvasContext* context = mContext;
    std::function<void(int64_t)> frameCallback = std::move(mFrameCallback);
    std::function<std::function<void(bool)>(int32_t, int64_t)> frameCallback =
            std::move(mFrameCallback);
    std::function<void()> 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;
Loading