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

Commit 7e53d0e9 authored by Robert Carr's avatar Robert Carr
Browse files

ViewRootImpl/SurfaceView: Listen for queue stalls

Native machinery now reports queue stalls from native layer
up to java layer, which can more appropriately handle errors.
The first case we handle is the case of "stuck fences", generally
indicating GPU hangs. In this case we trigger a bespoke ANR rather
than waiting for an ANR in dequeueBuffers later. dequeueBuffers
ANR could have any number of causes, and this large cluster
is difficult to debug.

Bug: 216160569
Test: Existing tests pass
Change-Id: I7b4429ce96d0bbfa1b74534ddf2b447facb22d10
parent 73a728ed
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -1200,8 +1200,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
        }
        }
        mTransformHint = viewRoot.getBufferTransformHint();
        mTransformHint = viewRoot.getBufferTransformHint();
        mBlastSurfaceControl.setTransformHint(mTransformHint);
        mBlastSurfaceControl.setTransformHint(mTransformHint);

        mBlastBufferQueue = new BLASTBufferQueue(name, false /* updateDestinationFrame */);
        mBlastBufferQueue = new BLASTBufferQueue(name, false /* updateDestinationFrame */);
        mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat);
        mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat);
        mBlastBufferQueue.setTransactionHangCallback(ViewRootImpl.sTransactionHangCallback);
    }
    }


    private void onDrawFinished() {
    private void onDrawFinished() {
+23 −0
Original line number Original line Diff line number Diff line
@@ -851,6 +851,28 @@ public final class ViewRootImpl implements ViewParent,
     */
     */
    private Bundle mRelayoutBundle = new Bundle();
    private Bundle mRelayoutBundle = new Bundle();


    private static volatile boolean sAnrReported = false;
    static BLASTBufferQueue.TransactionHangCallback sTransactionHangCallback =
        new BLASTBufferQueue.TransactionHangCallback() {
            @Override
            public void onTransactionHang(boolean isGPUHang) {
                if (isGPUHang && !sAnrReported) {
                    sAnrReported = true;
                    try {
                        ActivityManager.getService().appNotResponding(
                            "Buffer processing hung up due to stuck fence. Indicates GPU hang");
                    } catch (RemoteException e) {
                        // We asked the system to crash us, but the system
                        // already crashed. Unfortunately things may be
                        // out of control.
                    }
                } else {
                    // TODO: Do something with this later. For now we just ANR
                    // in dequeue buffer later like we always have.
                }
            }
        };

    private String mTag = TAG;
    private String mTag = TAG;


    public ViewRootImpl(Context context, Display display) {
    public ViewRootImpl(Context context, Display display) {
@@ -2086,6 +2108,7 @@ public final class ViewRootImpl implements ViewParent,
        }
        }
        mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
        mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
                mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
                mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
        mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
        Surface blastSurface = mBlastBufferQueue.createSurface();
        Surface blastSurface = mBlastBufferQueue.createSurface();
        // Only call transferFrom if the surface has changed to prevent inc the generation ID and
        // Only call transferFrom if the surface has changed to prevent inc the generation ID and
        // causing EGL resources to be recreated.
        // causing EGL resources to be recreated.
+60 −1
Original line number Original line Diff line number Diff line
@@ -47,6 +47,43 @@ static JNIEnv* getenv(JavaVM* vm) {
    return env;
    return env;
}
}


  struct {
    jmethodID onTransactionHang;
} gTransactionHangCallback;

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

    ~TransactionHangCallbackWrapper() {
        if (mTransactionHangObject) {
            getenv()->DeleteGlobalRef(mTransactionHangObject);
            mTransactionHangObject = nullptr;
        }
    }

    void onTransactionHang(bool isGpuHang) {
        if (mTransactionHangObject) {
            getenv()->CallVoidMethod(mTransactionHangObject,
                                     gTransactionHangCallback.onTransactionHang, isGpuHang);
        }
    }

private:
    JavaVM* mVm;
    jobject mTransactionHangObject;

    JNIEnv* getenv() {
        JNIEnv* env;
        mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
        return env;
    }
};

static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName,
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName,
                          jboolean updateDestinationFrame) {
                          jboolean updateDestinationFrame) {
    ScopedUtfChars name(env, jName);
    ScopedUtfChars name(env, jName);
@@ -142,6 +179,20 @@ static bool nativeIsSameSurfaceControl(JNIEnv* env, jclass clazz, jlong ptr, jlo
    return queue->isSameSurfaceControl(reinterpret_cast<SurfaceControl*>(surfaceControl));
    return queue->isSameSurfaceControl(reinterpret_cast<SurfaceControl*>(surfaceControl));
}
}
  
  
static void nativeSetTransactionHangCallback(JNIEnv* env, jclass clazz, jlong ptr,
                                             jobject transactionHangCallback) {
    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
    if (transactionHangCallback == nullptr) {
        queue->setTransactionHangCallback(nullptr);
    } else {
        sp<TransactionHangCallbackWrapper> wrapper =
                new TransactionHangCallbackWrapper{env, transactionHangCallback};
        queue->setTransactionHangCallback([wrapper](bool isGpuHang) {
            wrapper->onTransactionHang(isGpuHang);
        });
    }
}

static jobject nativeGatherPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr,
static jobject nativeGatherPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr,
                                               jlong frameNum) {
                                               jlong frameNum) {
    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
@@ -163,7 +214,10 @@ static const JNINativeMethod gMethods[] = {
        {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
        {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
        {"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions},
        {"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions},
        {"nativeIsSameSurfaceControl", "(JJ)Z", (void*)nativeIsSameSurfaceControl},
        {"nativeIsSameSurfaceControl", "(JJ)Z", (void*)nativeIsSameSurfaceControl},
        {"nativeGatherPendingTransactions", "(JJ)Landroid/view/SurfaceControl$Transaction;", (void*)nativeGatherPendingTransactions}
        {"nativeGatherPendingTransactions", "(JJ)Landroid/view/SurfaceControl$Transaction;", (void*)nativeGatherPendingTransactions},
        {"nativeSetTransactionHangCallback",
         "(JLandroid/graphics/BLASTBufferQueue$TransactionHangCallback;)V",
         (void*)nativeSetTransactionHangCallback},
        // clang-format on
        // clang-format on
};
};


@@ -180,6 +234,11 @@ int register_android_graphics_BLASTBufferQueue(JNIEnv* env) {
    jclass consumer = FindClassOrDie(env, "java/util/function/Consumer");
    jclass consumer = FindClassOrDie(env, "java/util/function/Consumer");
    gTransactionConsumer.accept =
    gTransactionConsumer.accept =
            GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V");
            GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V");
    jclass transactionHangClass =
            FindClassOrDie(env, "android/graphics/BLASTBufferQueue$TransactionHangCallback");
    gTransactionHangCallback.onTransactionHang =
            GetMethodIDOrDie(env, transactionHangClass, "onTransactionHang", "(Z)V");

    return 0;
    return 0;
}
}


+10 −0
Original line number Original line Diff line number Diff line
@@ -43,6 +43,12 @@ public final class BLASTBufferQueue {
    private static native boolean nativeIsSameSurfaceControl(long ptr, long surfaceControlPtr);
    private static native boolean nativeIsSameSurfaceControl(long ptr, long surfaceControlPtr);
    private static native SurfaceControl.Transaction nativeGatherPendingTransactions(long ptr,
    private static native SurfaceControl.Transaction nativeGatherPendingTransactions(long ptr,
            long frameNumber);
            long frameNumber);
    private static native void nativeSetTransactionHangCallback(long ptr,
            TransactionHangCallback callback);

    public interface TransactionHangCallback {
        void onTransactionHang(boolean isGpuHang);
    }


    /** Create a new connection with the surface flinger. */
    /** Create a new connection with the surface flinger. */
    public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
    public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
@@ -184,4 +190,8 @@ public final class BLASTBufferQueue {
    public SurfaceControl.Transaction gatherPendingTransactions(long frameNumber) {
    public SurfaceControl.Transaction gatherPendingTransactions(long frameNumber) {
        return nativeGatherPendingTransactions(mNativeObject, frameNumber);
        return nativeGatherPendingTransactions(mNativeObject, frameNumber);
    }
    }

    public void setTransactionHangCallback(TransactionHangCallback hangCallback) {
        nativeSetTransactionHangCallback(mNativeObject, hangCallback);
    }
}
}