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

Commit 53f62e57 authored by Chavi Weingarten's avatar Chavi Weingarten Committed by Android (Google) Code Review
Browse files

Merge "Changed setSyncTransaction to syncNextTransaction with callback logic." into tm-dev

parents 8265feec 5ecd6d37
Loading
Loading
Loading
Loading
+22 −11
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import com.android.internal.view.SurfaceCallbackHelper;

import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;

/**
 * Provides a dedicated drawing surface embedded inside of a view hierarchy.
@@ -390,8 +391,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
        }
    }

    private void performDrawFinished(Transaction t) {
    private void performDrawFinished(@Nullable Transaction t) {
        if (t != null) {
            mSyncTransaction.merge(t);
        }

        if (mPendingReportDraws > 0) {
            mDrawFinished = true;
@@ -1038,16 +1041,24 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
                                callbacks = getSurfaceCallbacks();
                            }

                            final Transaction t = new Transaction();
                            if (viewRoot.wasRelayoutRequested()) {
                                mBlastBufferQueue.setSyncTransaction(t,
                                        false /* acquireSingleBuffer */);
                            final boolean wasRelayoutRequested = viewRoot.wasRelayoutRequested();
                            if (wasRelayoutRequested && (mBlastBufferQueue != null)) {
                                mBlastBufferQueue.syncNextTransaction(
                                        false /* acquireSingleBuffer */,
                                        this::onDrawFinished);
                            }
                            mPendingReportDraws++;
                            viewRoot.drawPending();
                            SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> {
                                mBlastBufferQueue.setSyncTransaction(null);
                                onDrawFinished(t);
                                if (mBlastBufferQueue != null) {
                                    mBlastBufferQueue.stopContinuousSyncTransaction();
                                }
                                // If relayout was requested, then a callback from BBQ will
                                // be invoked with the sync transaction. onDrawFinished will be
                                // called in there
                                if (!wasRelayoutRequested) {
                                    onDrawFinished(null);
                                }
                            });
                            sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
                        }
@@ -1178,7 +1189,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
        mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat);
    }

    private void onDrawFinished(Transaction t) {
    private void onDrawFinished(@Nullable Transaction t) {
        if (DEBUG) {
            Log.i(TAG, System.identityHashCode(this) + " "
                    + "finishedDrawing");
@@ -1792,7 +1803,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
    /**
     * @hide
     */
    public void syncNextFrame(Transaction t) {
        mBlastBufferQueue.setSyncTransaction(t);
    public void syncNextFrame(Consumer<Transaction> t) {
        mBlastBufferQueue.syncNextTransaction(t);
    }
}
+40 −33
Original line number Diff line number Diff line
@@ -803,15 +803,6 @@ public final class ViewRootImpl implements ViewParent,
        return mHandwritingInitiator;
    }

    /**
     * This is only used on the RenderThread when handling a blast sync. Specifically, it's only
     * used when calling {@link BLASTBufferQueue#setSyncTransaction(Transaction)} and then merged
     * with a tmp transaction on the Render Thread. The tmp transaction is then merged into
     * {@link #mSurfaceChangedTransaction} on the UI Thread, avoiding any threading issues.
     */
    private final SurfaceControl.Transaction mRtBLASTSyncTransaction =
            new SurfaceControl.Transaction();

    /**
     * Keeps track of the last frame number that was attempted to draw. Should only be accessed on
     * the RenderThread.
@@ -4175,43 +4166,49 @@ public final class ViewRootImpl implements ViewParent,
                        + " didProduceBuffer=" + didProduceBuffer);
            }

            final Transaction tmpTransaction = new Transaction();
            tmpTransaction.merge(mRtBLASTSyncTransaction);

            // 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.
            final Transaction pendingTransactions;
            if (!didProduceBuffer) {
                mBlastBufferQueue.setSyncTransaction(null);
                mBlastBufferQueue.syncNextTransaction(null);
                // Get 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.
                Transaction pendingTransactions = mBlastBufferQueue.gatherPendingTransactions(
                pendingTransactions = mBlastBufferQueue.gatherPendingTransactions(
                        mRtLastAttemptedDrawFrameNum);
                tmpTransaction.merge(pendingTransactions);
            }
                if (!useBlastSync && !reportNextDraw) {
                tmpTransaction.apply();
                    pendingTransactions.apply();
                }
            } else {
                pendingTransactions = null;
            }

            // 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 (!didProduceBuffer && useBlastSync) {
                    mSurfaceChangedTransaction.merge(pendingTransactions);
                    if (blastSyncConsumer != null) {
                        blastSyncConsumer.accept(mSurfaceChangedTransaction);
                    }
                }
                tmpTransaction.close();

                if (reportNextDraw) {
                // This is to ensure pendingDrawFinished is only called exactly one time per draw
                // attempt when reportNextDraw is true. Since, we sometimes create a sync
                // transaction callback, the callback will handle calling pendingDrawFinished.
                // However, there are cases where the transaction callback may not be called.
                // 1. If useBlastSync is false, then we know that a sync transaction callback was
                // not created so we won't invoke pendingDrawFinished there.
                // 2. If the draw didn't produce a frame, didProduceBuffer == false, then we know
                // the sync transaction callback will not be invoked even if one was set up.
                if (reportNextDraw && (!didProduceBuffer || !useBlastSync)) {
                    pendingDrawFinished();
                }
            });

        };
    }

@@ -4294,7 +4291,19 @@ public final class ViewRootImpl implements ViewParent,
                    // 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);
                    mBlastBufferQueue.syncNextTransaction(
                            t -> {
                                mHandler.postAtFrontOfQueue(() -> {
                                    mSurfaceChangedTransaction.merge(t);
                                    if (blastSyncConsumer != null) {
                                        blastSyncConsumer.accept(mSurfaceChangedTransaction);
                                    }

                                    if (reportNextDraw) {
                                        pendingDrawFinished();
                                    }
                                });
                            });
                }

                return createFrameCommitCallbackForSync(useBlastSync, reportNextDraw,
@@ -10928,24 +10937,23 @@ public final class ViewRootImpl implements ViewParent,
                                    + frame + ".");
                }

                final Transaction t = new Transaction();

                // 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) {
                    t.merge(mBlastBufferQueue.gatherPendingTransactions(frame));
                    syncBufferCallback.onBufferReady(t);
                    syncBufferCallback.onBufferReady(
                            mBlastBufferQueue.gatherPendingTransactions(frame));
                    return null;
                }

                mBlastBufferQueue.setSyncTransaction(t);
                if (DEBUG_BLAST) {
                    Log.d(mTag, "Setting up sync and frameCommitCallback");
                }

                mBlastBufferQueue.syncNextTransaction(t -> syncBufferCallback.onBufferReady(t));

                return didProduceBuffer -> {
                    if (DEBUG_BLAST) {
                        Log.d(mTag, "Received frameCommittedCallback"
@@ -10957,15 +10965,14 @@ public final class ViewRootImpl implements ViewParent,
                    // the next draw attempt. The next transaction and transaction complete callback
                    // were only set for the current draw attempt.
                    if (!didProduceBuffer) {
                        mBlastBufferQueue.setSyncTransaction(null);
                        mBlastBufferQueue.syncNextTransaction(null);
                        // Gather 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.
                        t.merge(mBlastBufferQueue.gatherPendingTransactions(frame));
                        syncBufferCallback.onBufferReady(
                                mBlastBufferQueue.gatherPendingTransactions(frame));
                    }

                    syncBufferCallback.onBufferReady(t);
                };
            }
        });
+4 −19
Original line number Diff line number Diff line
@@ -343,35 +343,20 @@ public class SurfaceSyncer {

        @Override
        public void onReadyToSync(SyncBufferCallback syncBufferCallback) {
            Transaction mTransaction = sTransactionFactory.get();
            mFrameCallbackConsumer.accept(new SurfaceViewFrameCallback() {
                @Override
                public void onFrameStarted() {
                    mSurfaceView.syncNextFrame(mTransaction);
                }

                @Override
                public void onFrameComplete() {
                    syncBufferCallback.onBufferReady(mTransaction);
                }
            });
            mFrameCallbackConsumer.accept(
                    () -> mSurfaceView.syncNextFrame(syncBufferCallback::onBufferReady));
        }
    }

    /**
     * A frame callback that is used to synchronize SurfaceViews. The owner of the SurfaceView must
     * implement onFrameStarted and onFrameComplete when trying to sync the SurfaceView. This is to
     * ensure the sync knows when the frame is ready to add to the sync.
     * implement onFrameStarted when trying to sync the SurfaceView. This is to ensure the sync
     * knows when the frame is ready to add to the sync.
     */
    public interface SurfaceViewFrameCallback {
        /**
         * Called when the SurfaceView is going to render a frame
         */
        void onFrameStarted();

        /**
         * Called when the SurfaceView has finished rendering a frame.
         */
        void onFrameComplete();
    }
}
+62 −5
Original line number Diff line number Diff line
@@ -35,6 +35,18 @@ static struct {
    jmethodID ctor;
} gTransactionClassInfo;

struct {
    jmethodID accept;
} gTransactionConsumer;

static JNIEnv* getenv(JavaVM* vm) {
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
    }
    return env;
}

static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName,
                          jboolean updateDestinationFrame) {
    ScopedUtfChars name(env, jName);
@@ -55,11 +67,51 @@ static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr,
                                                  queue->getSurface(includeSurfaceControlHandle));
}

static void nativeSetSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr,
class JGlobalRefHolder {
public:
    JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {}

    virtual ~JGlobalRefHolder() {
        getenv(mVm)->DeleteGlobalRef(mObject);
        mObject = nullptr;
    }

    jobject object() { return mObject; }
    JavaVM* vm() { return mVm; }

private:
    JGlobalRefHolder(const JGlobalRefHolder&) = delete;
    void operator=(const JGlobalRefHolder&) = delete;

    JavaVM* mVm;
    jobject mObject;
};

static void nativeSyncNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jobject callback,
                                      jboolean acquireSingleBuffer) {
    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
    queue->setSyncTransaction(transaction, acquireSingleBuffer);
    JavaVM* vm = nullptr;
    LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
    if (!callback) {
        queue->syncNextTransaction(nullptr, acquireSingleBuffer);
    } else {
        auto globalCallbackRef =
                std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
        queue->syncNextTransaction(
                [globalCallbackRef](SurfaceComposerClient::Transaction* t) {
                    JNIEnv* env = getenv(globalCallbackRef->vm());
                    env->CallVoidMethod(globalCallbackRef->object(), gTransactionConsumer.accept,
                                        env->NewObject(gTransactionClassInfo.clazz,
                                                       gTransactionClassInfo.ctor,
                                                       reinterpret_cast<jlong>(t)));
                },
                acquireSingleBuffer);
    }
}

static void nativeStopContinuousSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr) {
    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
    queue->stopContinuousSyncTransaction();
}

static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,
@@ -104,7 +156,8 @@ static const JNINativeMethod gMethods[] = {
        {"nativeCreate", "(Ljava/lang/String;Z)J", (void*)nativeCreate},
        {"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
        {"nativeDestroy", "(J)V", (void*)nativeDestroy},
        {"nativeSetSyncTransaction", "(JJZ)V", (void*)nativeSetSyncTransaction},
        {"nativeSyncNextTransaction", "(JLjava/util/function/Consumer;Z)V", (void*)nativeSyncNextTransaction},
        {"nativeStopContinuousSyncTransaction", "(J)V", (void*)nativeStopContinuousSyncTransaction},
        {"nativeUpdate", "(JJJJI)V", (void*)nativeUpdate},
        {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
        {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
@@ -123,6 +176,10 @@ int register_android_graphics_BLASTBufferQueue(JNIEnv* env) {
    gTransactionClassInfo.clazz = MakeGlobalRefOrDie(env, transactionClazz);
    gTransactionClassInfo.ctor =
            GetMethodIDOrDie(env, gTransactionClassInfo.clazz, "<init>", "(J)V");

    jclass consumer = FindClassOrDie(env, "java/util/function/Consumer");
    gTransactionConsumer.accept =
            GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V");
    return 0;
}

+33 −17
Original line number Diff line number Diff line
@@ -16,10 +16,11 @@

package android.graphics;

import android.annotation.Nullable;
import android.view.Surface;
import android.view.SurfaceControl;

import java.util.function.Consumer;

/**
 * @hide
 */
@@ -30,8 +31,9 @@ public final class BLASTBufferQueue {
    private static native long nativeCreate(String name, boolean updateDestinationFrame);
    private static native void nativeDestroy(long ptr);
    private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
    private static native void nativeSetSyncTransaction(long ptr, long transactionPtr,
            boolean acquireSingleBuffer);
    private static native void nativeSyncNextTransaction(long ptr,
            Consumer<SurfaceControl.Transaction> callback, boolean acquireSingleBuffer);
    private static native void nativeStopContinuousSyncTransaction(long ptr);
    private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height,
            int format);
    private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr,
@@ -74,25 +76,39 @@ public final class BLASTBufferQueue {
    }

    /**
     * Send the transaction to BBQ so the next frame can be added and not applied immediately. This
     * gives the caller a chance to apply the transaction when it's ready.
     *
     * @param t                   The transaction to add the frame to. This can be null to clear the
     *                            transaction.
     * Send a callback that accepts a transaction to BBQ. BBQ will acquire buffers into the a
     * transaction it created and will eventually send the transaction into the callback
     * when it is ready.
     * @param callback The callback invoked when the buffer has been added to the transaction. The
     *                 callback will contain the transaction with the buffer.
     * @param acquireSingleBuffer If true, only acquire a single buffer when processing frames. The
     *                            transaction will be cleared once a single buffer has been
     *                            callback will be cleared once a single buffer has been
     *                            acquired. If false, continue to acquire all buffers into the
     *                            transaction until setSyncTransaction is called again with a null
     *                            transaction.
     *                            transaction until stopContinuousSyncTransaction is called.
     */
    public void syncNextTransaction(boolean acquireSingleBuffer,
            Consumer<SurfaceControl.Transaction> callback) {
        nativeSyncNextTransaction(mNativeObject, callback, acquireSingleBuffer);
    }

    /**
     * Send a callback that accepts a transaction to BBQ. BBQ will acquire buffers into the a
     * transaction it created and will eventually send the transaction into the callback
     * when it is ready.
     * @param callback The callback invoked when the buffer has been added to the transaction. The
     *                 callback will contain the transaction with the buffer.
     */
    public void setSyncTransaction(@Nullable SurfaceControl.Transaction t,
            boolean acquireSingleBuffer) {
        nativeSetSyncTransaction(mNativeObject, t == null ? 0 : t.mNativeObject,
                acquireSingleBuffer);
    public void syncNextTransaction(Consumer<SurfaceControl.Transaction> callback) {
        syncNextTransaction(true /* acquireSingleBuffer */, callback);
    }

    public void setSyncTransaction(@Nullable SurfaceControl.Transaction t) {
        setSyncTransaction(t, true /* acquireSingleBuffer */);
    /**
     * Tell BBQ to stop acquiring buffers into a single transaction. BBQ will send the sync
     * transaction callback after this has been called. This should only be used when
     * syncNextTransaction was called with acquireSingleBuffer set to false.
     */
    public void stopContinuousSyncTransaction() {
        nativeStopContinuousSyncTransaction(mNativeObject);
    }

    /**
Loading