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
This commit is contained in:
@@ -1200,8 +1200,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
|
||||
}
|
||||
mTransformHint = viewRoot.getBufferTransformHint();
|
||||
mBlastSurfaceControl.setTransformHint(mTransformHint);
|
||||
|
||||
mBlastBufferQueue = new BLASTBufferQueue(name, false /* updateDestinationFrame */);
|
||||
mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat);
|
||||
mBlastBufferQueue.setTransactionHangCallback(ViewRootImpl.sTransactionHangCallback);
|
||||
}
|
||||
|
||||
private void onDrawFinished() {
|
||||
|
||||
@@ -851,6 +851,28 @@ public final class ViewRootImpl implements ViewParent,
|
||||
*/
|
||||
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;
|
||||
|
||||
public ViewRootImpl(Context context, Display display) {
|
||||
@@ -2086,6 +2108,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
}
|
||||
mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
|
||||
mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
|
||||
mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
|
||||
Surface blastSurface = mBlastBufferQueue.createSurface();
|
||||
// Only call transferFrom if the surface has changed to prevent inc the generation ID and
|
||||
// causing EGL resources to be recreated.
|
||||
|
||||
@@ -47,6 +47,43 @@ static JNIEnv* getenv(JavaVM* vm) {
|
||||
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,
|
||||
jboolean updateDestinationFrame) {
|
||||
ScopedUtfChars name(env, jName);
|
||||
@@ -141,6 +178,20 @@ static bool nativeIsSameSurfaceControl(JNIEnv* env, jclass clazz, jlong ptr, jlo
|
||||
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
|
||||
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,
|
||||
jlong frameNum) {
|
||||
@@ -163,7 +214,10 @@ static const JNINativeMethod gMethods[] = {
|
||||
{"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
|
||||
{"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions},
|
||||
{"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
|
||||
};
|
||||
|
||||
@@ -180,6 +234,11 @@ int register_android_graphics_BLASTBufferQueue(JNIEnv* env) {
|
||||
jclass consumer = FindClassOrDie(env, "java/util/function/Consumer");
|
||||
gTransactionConsumer.accept =
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,12 @@ public final class BLASTBufferQueue {
|
||||
private static native boolean nativeIsSameSurfaceControl(long ptr, long surfaceControlPtr);
|
||||
private static native SurfaceControl.Transaction nativeGatherPendingTransactions(long ptr,
|
||||
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. */
|
||||
public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
|
||||
@@ -184,4 +190,8 @@ public final class BLASTBufferQueue {
|
||||
public SurfaceControl.Transaction gatherPendingTransactions(long frameNumber) {
|
||||
return nativeGatherPendingTransactions(mNativeObject, frameNumber);
|
||||
}
|
||||
|
||||
public void setTransactionHangCallback(TransactionHangCallback hangCallback) {
|
||||
nativeSetTransactionHangCallback(mNativeObject, hangCallback);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user