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

Commit 9c137535 authored by chaviw's avatar chaviw
Browse files

Rework blast sync callback model

The previous code waited for a frame complete callback from hwui before
notifying WMS that a draw had occurred. However, frame complete callback
was not guaranteed to get called since it only invokes a callback if a
draw actually occured. VRI really needs a signal that RT has completed,
since it just needs to know that a draw was possible so it can notify
WMS that the RT completed its pass.

Instead, rename frameCompleteCallback to frameCommitCallback since that
API is exposed to a public API when a frame was actually drawn.
Create a new callback, frameCompleteCallback, that is invoked when the
draw has completed, regardless if a frame was actually drawn.

When the frameCompleteCallback is invoked, VRI can check to see if a new
frame actually drew. VRI can call into BBQ to see if the frame acquired
matches the frame that was attempted to draw. If so, VRI can wait on a
transaction callback. If not, it can allow VRI to continue. In either case,
it will notify WMS that the draw has finished.

Test: Split over and over
Bug: 195262673
Change-Id: I24dd19ab2746be3fc33e597761abf8c5249f8b5b
parent b542a663
Loading
Loading
Loading
Loading
+88 −50
Original line number Diff line number Diff line
@@ -758,6 +758,12 @@ public final class ViewRootImpl implements ViewParent,
     */
    private boolean mWaitForBlastSyncComplete = false;

    /**
     * Keeps track of the last frame number that was attempted to draw. Should only be accessed on
     * the RenderThread.
     */
    private long mRtLastAttemptedDrawFrameNum = 0;

    /**
     * Keeps track of whether a traverse was triggered while the UI thread was paused. This can
     * occur when the client is waiting on another process to submit the transaction that
@@ -3974,16 +3980,60 @@ public final class ViewRootImpl implements ViewParent,
    }

    /**
     * The callback will run on the render thread.
     * Only call this on the UI Thread.
     */
    void clearBlastSync() {
        mNextDrawUseBlastSync = false;
        mWaitForBlastSyncComplete = false;
        if (DEBUG_BLAST) {
            Log.d(mTag, "Scheduling a traversal=" + mRequestedTraverseWhilePaused
                    + " due to a previous skipped traversal.");
        }
        if (mRequestedTraverseWhilePaused) {
            mRequestedTraverseWhilePaused = false;
            scheduleTraversals();
        }
    }

    /**
     * @hide
     */
    private HardwareRenderer.FrameCompleteCallback createFrameCompleteCallback(Handler handler,
            boolean reportNextDraw, ArrayList<Runnable> commitCallbacks) {
        return frameNr -> {
    public boolean isHardwareEnabled() {
        return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
    }

    private boolean addFrameCompleteCallbackIfNeeded(boolean reportNextDraw) {
        if (!isHardwareEnabled()) {
            return false;
        }

        if (!mNextDrawUseBlastSync && !reportNextDraw) {
            return false;
        }

        if (DEBUG_BLAST) {
            Log.d(mTag, "Creating frameCompleteCallback");
        }

        mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(() -> {
            long frameNr = mBlastBufferQueue.getLastAcquiredFrameNum();
            if (DEBUG_BLAST) {
                Log.d(mTag, "Received frameCompleteCallback frameNum=" + frameNr);
                Log.d(mTag, "Received frameCompleteCallback "
                        + " lastAcquiredFrameNum=" + frameNr
                        + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum);
            }

            handler.postAtFrontOfQueue(() -> {
            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.setNextTransaction(null);
                mBlastBufferQueue.setTransactionCompleteCallback(mRtLastAttemptedDrawFrameNum,
                        null);
            }

            mHandler.postAtFrontOfQueue(() -> {
                if (mNextDrawUseBlastSync) {
                    // We don't need to synchronize mRtBLASTSyncTransaction here since we're
                    // guaranteed that this is called after onFrameDraw and mNextDrawUseBlastSync
@@ -3993,49 +4043,45 @@ public final class ViewRootImpl implements ViewParent,
                }

                if (reportNextDraw) {
                    // TODO: Use the frame number
                    pendingDrawFinished();
                }
                if (commitCallbacks != null) {
                    for (int i = 0; i < commitCallbacks.size(); i++) {
                        commitCallbacks.get(i).run();
                    }

                if (frameWasNotDrawn) {
                    clearBlastSync();
                }
            });
        };
    }

    /**
     * @hide
     */
    public boolean isHardwareEnabled() {
        return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
        });
        return true;
    }

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

        ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
                .captureFrameCommitCallbacks();
        final boolean needFrameCompleteCallback =
                mNextDrawUseBlastSync || mReportNextDraw
                        || (commitCallbacks != null && commitCallbacks.size() > 0);
        if (needFrameCompleteCallback) {
            if (DEBUG_BLAST) {
                Log.d(mTag, "Creating frameCompleteCallback"
                        + " mNextDrawUseBlastSync=" + mNextDrawUseBlastSync
                        + " mReportNextDraw=" + mReportNextDraw
                        + " commitCallbacks size="
                        + (commitCallbacks == null ? 0 : commitCallbacks.size()));
            }
            mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(
                    createFrameCompleteCallback(mAttachInfo.mHandler, mReportNextDraw,
                            commitCallbacks));
            return true;
        final boolean needFrameCommitCallback =
                (commitCallbacks != null && commitCallbacks.size() > 0);
        if (!needFrameCommitCallback) {
            return;
        }
        return false;

        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();
                }
            });
        });
    }

    private void addFrameCallbackIfNeeded() {
@@ -4065,6 +4111,8 @@ public final class ViewRootImpl implements ViewParent,
                        + " Creating transactionCompleteCallback=" + nextDrawUseBlastSync);
            }

            mRtLastAttemptedDrawFrameNum = frame;

            if (needsCallbackForBlur) {
                mBlurRegionAggregator
                    .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates);
@@ -4087,18 +4135,7 @@ public final class ViewRootImpl implements ViewParent,
                    if (DEBUG_BLAST) {
                        Log.d(mTag, "Received transactionCompleteCallback frameNum=" + frame);
                    }
                    mHandler.postAtFrontOfQueue(() -> {
                        mNextDrawUseBlastSync = false;
                        mWaitForBlastSyncComplete = false;
                        if (DEBUG_BLAST) {
                            Log.d(mTag, "Scheduling a traversal=" + mRequestedTraverseWhilePaused
                                    + " due to a previous skipped traversal.");
                        }
                        if (mRequestedTraverseWhilePaused) {
                            mRequestedTraverseWhilePaused = false;
                            scheduleTraversals();
                        }
                    });
                    mHandler.postAtFrontOfQueue(this::clearBlastSync);
                });
            } else if (reportNextDraw) {
                // If we need to report next draw, wait for adapter to flush its shadow queue
@@ -4124,8 +4161,9 @@ public final class ViewRootImpl implements ViewParent,
        mIsDrawing = true;
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");

        boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded();
        addFrameCallbackIfNeeded();
        addFrameCommitCallbackIfNeeded();
        boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded(mReportNextDraw);

        try {
            boolean canUseAsync = draw(fullRedrawNeeded);
+7 −1
Original line number Diff line number Diff line
@@ -139,6 +139,11 @@ static void nativeSetTransactionCompleteCallback(JNIEnv* env, jclass clazz, jlon
    }
}

static jlong nativeGetLastAcquiredFrameNum(JNIEnv* env, jclass clazz, jlong ptr) {
    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
    return queue->getLastAcquiredFrameNum();
}

static const JNINativeMethod gMethods[] = {
        /* name, signature, funcPtr */
        // clang-format off
@@ -151,7 +156,8 @@ static const JNINativeMethod gMethods[] = {
        {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
        {"nativeSetTransactionCompleteCallback",
                "(JJLandroid/graphics/BLASTBufferQueue$TransactionCompleteCallback;)V",
                (void*)nativeSetTransactionCompleteCallback}
                (void*)nativeSetTransactionCompleteCallback},
        {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
        // clang-format on
};

+4 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ public final class BLASTBufferQueue {
                                                              long frameNumber);
    private static native void nativeSetTransactionCompleteCallback(long ptr, long frameNumber,
            TransactionCompleteCallback callback);
    private static native long nativeGetLastAcquiredFrameNum(long ptr);

    /**
     * Callback sent to {@link #setTransactionCompleteCallback(long, TransactionCompleteCallback)}
@@ -145,4 +146,7 @@ public final class BLASTBufferQueue {
        nativeMergeWithNextTransaction(mNativeObject, nativeTransaction, frameNumber);
    }

    public long getLastAcquiredFrameNum() {
        return nativeGetLastAcquiredFrameNum(mNativeObject);
    }
}
+28 −5
Original line number Diff line number Diff line
@@ -388,7 +388,8 @@ public class HardwareRenderer {
         */
        public @NonNull FrameRenderRequest setFrameCommitCallback(@NonNull Executor executor,
                @NonNull Runnable frameCommitCallback) {
            setFrameCompleteCallback(frameNr -> executor.execute(frameCommitCallback));
            nSetFrameCommitCallback(mNativeProxy,
                    didProduceBuffer -> executor.execute(frameCommitCallback));
            return this;
        }

@@ -608,6 +609,11 @@ public class HardwareRenderer {
        return mOpaque;
    }

    /** @hide */
    public void setFrameCommitCallback(FrameCommitCallback callback) {
        nSetFrameCommitCallback(mNativeProxy, callback);
    }

    /** @hide */
    public void setFrameCompleteCallback(FrameCompleteCallback callback) {
        nSetFrameCompleteCallback(mNativeProxy, callback);
@@ -896,13 +902,27 @@ public class HardwareRenderer {
     *
     * @hide
     */
    public interface FrameCompleteCallback {
    public interface FrameCommitCallback {
        /**
         * Invoked after a new frame was drawn
         *
         * @param didProduceBuffer The draw successfully produced a new buffer.
         */
        void onFrameCommit(boolean didProduceBuffer);
    }

    /**
         * Invoked after a frame draw
     * Interface used to be notified when RenderThread has finished an attempt to draw. This doesn't
     * mean a new frame has drawn, specifically if there's nothing new to draw, but only that
     * RenderThread had a chance to draw a frame.
     *
         * @param frameNr The id of the frame that was drawn.
     * @hide
     */
        void onFrameComplete(long frameNr);
    public interface FrameCompleteCallback {
        /**
         * Invoked after a frame draw was attempted.
         */
        void onFrameComplete();
    }

    /**
@@ -1391,6 +1411,9 @@ public class HardwareRenderer {

    private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);

    private static native void nSetFrameCommitCallback(long nativeProxy,
            FrameCommitCallback callback);

    private static native void nSetFrameCompleteCallback(long nativeProxy,
            FrameCompleteCallback callback);

+41 −13
Original line number Diff line number Diff line
@@ -72,6 +72,10 @@ struct {
    jmethodID onFrameDraw;
} gFrameDrawingCallback;

struct {
    jmethodID onFrameCommit;
} gFrameCommitCallback;

struct {
    jmethodID onFrameComplete;
} gFrameCompleteCallback;
@@ -101,22 +105,21 @@ private:
    JavaVM* mVm;
};

class FrameCompleteWrapper : public LightRefBase<FrameCompleteWrapper> {
class FrameCommitWrapper : public LightRefBase<FrameCommitWrapper> {
public:
    explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
    explicit FrameCommitWrapper(JNIEnv* env, jobject jobject) {
        env->GetJavaVM(&mVm);
        mObject = env->NewGlobalRef(jobject);
        LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref");
    }

    ~FrameCompleteWrapper() {
        releaseObject();
    }
    ~FrameCommitWrapper() { releaseObject(); }

    void onFrameComplete(int64_t frameNr) {
    void onFrameCommit(bool didProduceBuffer) {
        if (mObject) {
            ATRACE_FORMAT("frameComplete %" PRId64, frameNr);
            getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr);
            ATRACE_FORMAT("frameCommit success=%d", didProduceBuffer);
            getenv(mVm)->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit,
                                        didProduceBuffer);
            releaseObject();
        }
    }
@@ -607,15 +610,33 @@ static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env,
    }
}

static void android_view_ThreadedRenderer_setFrameCommitCallback(JNIEnv* env, jobject clazz,
                                                                 jlong proxyPtr, jobject callback) {
    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
    if (!callback) {
        proxy->setFrameCommitCallback(nullptr);
    } else {
        sp<FrameCommitWrapper> wrapper = new FrameCommitWrapper{env, callback};
        proxy->setFrameCommitCallback(
                [wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); });
    }
}

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->onFrameComplete(frameNr);
        RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
        JavaVM* vm = nullptr;
        LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
        auto globalCallbackRef =
                std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
        proxy->setFrameCompleteCallback([globalCallbackRef]() {
            JNIEnv* env = getenv(globalCallbackRef->vm());
            env->CallVoidMethod(globalCallbackRef->object(),
                                gFrameCompleteCallback.onFrameComplete);
        });
    }
}
@@ -907,6 +928,8 @@ static const JNINativeMethod gMethods[] = {
         (void*)android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCallback},
        {"nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V",
         (void*)android_view_ThreadedRenderer_setFrameCallback},
        {"nSetFrameCommitCallback", "(JLandroid/graphics/HardwareRenderer$FrameCommitCallback;)V",
         (void*)android_view_ThreadedRenderer_setFrameCommitCallback},
        {"nSetFrameCompleteCallback",
         "(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V",
         (void*)android_view_ThreadedRenderer_setFrameCompleteCallback},
@@ -974,10 +997,15 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) {
    gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass,
            "onFrameDraw", "(J)V");

    jclass frameCommitClass =
            FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameCommitCallback");
    gFrameCommitCallback.onFrameCommit =
            GetMethodIDOrDie(env, frameCommitClass, "onFrameCommit", "(Z)V");

    jclass frameCompleteClass = FindClassOrDie(env,
            "android/graphics/HardwareRenderer$FrameCompleteCallback");
    gFrameCompleteCallback.onFrameComplete = GetMethodIDOrDie(env, frameCompleteClass,
            "onFrameComplete", "(J)V");
    gFrameCompleteCallback.onFrameComplete =
            GetMethodIDOrDie(env, frameCompleteClass, "onFrameComplete", "()V");

    void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
    fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface");
Loading