Add Shared timeline jank classification listener (2/2)

Adds the ability to register a listener that gets informed about
SF' jank classifications via the TransactionCompleted interface

Bug: 174755489
Test: FrameTimelineTest
Test: Register listener, ensure data flows back
Change-Id: I3021cd75856b62e579e30f7bb3e5b2c3a61038c5
This commit is contained in:
Jorim Jaggi
2020-12-03 22:41:52 +01:00
parent b2c48f87ec
commit e418a013e3
2 changed files with 187 additions and 0 deletions

View File

@@ -26,6 +26,7 @@ import static android.view.SurfaceControlProto.HASH_CODE;
import static android.view.SurfaceControlProto.NAME;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -54,12 +55,15 @@ import android.util.proto.ProtoOutputStream;
import android.view.Surface.OutOfResourcesException;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.VirtualRefBasePtr;
import dalvik.system.CloseGuard;
import libcore.util.NativeAllocationRegistry;
import java.io.Closeable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -224,6 +228,10 @@ public final class SurfaceControl implements Parcelable {
IBinder focusedToken, int displayId);
private static native void nativeSetFrameTimelineVsync(long transactionObj,
long frameTimelineVsyncId);
private static native void nativeAddJankDataListener(long nativeListener,
long nativeSurfaceControl);
private static native void nativeRemoveJankDataListener(long nativeListener);
private static native long nativeCreateJankDataListenerWrapper(OnJankDataListener listener);
@Nullable
@GuardedBy("mLock")
@@ -249,6 +257,73 @@ public final class SurfaceControl implements Parcelable {
void onReparent(@NonNull Transaction transaction, @Nullable SurfaceControl parent);
}
/**
* Jank information to be fed back via {@link OnJankDataListener}.
* @hide
*/
public static class JankData {
/** @hide */
@IntDef(flag = true, value = {JANK_NONE,
JANK_DISPLAY,
JANK_SURFACEFLINGER_DEADLINE_MISSED,
JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED,
JANK_APP_DEADLINE_MISSED,
JANK_PREDICTION_EXPIRED,
JANK_SURFACEFLINGER_EARLY_LATCH})
@Retention(RetentionPolicy.SOURCE)
public @interface JankType {}
// Needs to be kept in sync with frameworks/native/libs/gui/include/gui/JankInfo.h
// No Jank
public static final int JANK_NONE = 0x0;
// Jank not related to SurfaceFlinger or the App
public static final int JANK_DISPLAY = 0x1;
// SF took too long on the CPU
public static final int JANK_SURFACEFLINGER_DEADLINE_MISSED = 0x2;
// SF took too long on the GPU
public static final int JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED = 0x4;
// Either App or GPU took too long on the frame
public static final int JANK_APP_DEADLINE_MISSED = 0x8;
// Predictions live for 120ms, if prediction is expired for a frame, there is definitely a
// jank
// associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
public static final int JANK_PREDICTION_EXPIRED = 0x10;
// Latching a buffer early might cause an early present of the frame
public static final int JANK_SURFACEFLINGER_EARLY_LATCH = 0x20;
public JankData(long frameVsyncId, @JankType int jankType) {
this.frameVsyncId = frameVsyncId;
this.jankType = jankType;
}
public final long frameVsyncId;
public final @JankType int jankType;
}
/**
* Listener interface to be informed about SurfaceFlinger's jank classification for a specific
* surface.
*
* @see JankData
* @see #addJankDataListener
* @hide
*/
public static abstract class OnJankDataListener {
private final VirtualRefBasePtr mNativePtr;
public OnJankDataListener() {
mNativePtr = new VirtualRefBasePtr(nativeCreateJankDataListenerWrapper(this));
}
/**
* Called when new jank classifications are available.
*/
public abstract void onJankDataAvailable(JankData[] jankStats);
}
private final CloseGuard mCloseGuard = CloseGuard.get();
private String mName;
@@ -2519,6 +2594,22 @@ public final class SurfaceControl implements Parcelable {
nativeSetGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ, lightRadius);
}
/**
* Adds a callback to be informed about SF's jank classification for a specific surface.
* @hide
*/
public static void addJankDataListener(OnJankDataListener listener, SurfaceControl surface) {
nativeAddJankDataListener(listener.mNativePtr.get(), surface.mNativeObject);
}
/**
* Removes a jank callback previously added with {@link #addJankDataListener}
* @hide
*/
public static void removeJankDataListener(OnJankDataListener listener) {
nativeRemoveJankDataListener(listener.mNativePtr.get());
}
/**
* An atomic set of changes to a set of SurfaceControl.
*/

View File

@@ -52,6 +52,7 @@
#include <ui/Rect.h>
#include <ui/Region.h>
#include <utils/Log.h>
#include <utils/LightRefBase.h>
// ----------------------------------------------------------------------------
@@ -206,6 +207,16 @@ static struct {
jfieldID appRequestRefreshRateMax;
} gDesiredDisplayConfigSpecsClassInfo;
static struct {
jclass clazz;
jmethodID onJankDataAvailable;
} gJankDataListenerClassInfo;
static struct {
jclass clazz;
jmethodID ctor;
} gJankDataClassInfo;
class JNamedColorSpace {
public:
// ColorSpace.Named.SRGB.ordinal() = 0;
@@ -1601,6 +1612,73 @@ static void nativeSetFrameTimelineVsync(JNIEnv* env, jclass clazz, jlong transac
transaction->setFrameTimelineVsync(frameTimelineVsyncId);
}
class JankDataListenerWrapper : public JankDataListener {
public:
JankDataListenerWrapper(JNIEnv* env, jobject onJankDataListenerObject) {
mOnJankDataListenerWeak = env->NewWeakGlobalRef(onJankDataListenerObject);
env->GetJavaVM(&mVm);
}
~JankDataListenerWrapper() {
JNIEnv* env = getEnv();
env->DeleteWeakGlobalRef(mOnJankDataListenerWeak);
}
void onJankDataAvailable(const std::vector<JankData>& jankData) {
JNIEnv* env = getEnv();
jobject target = env->NewLocalRef(mOnJankDataListenerWeak);
if (target == nullptr) return;
jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(),
gJankDataClassInfo.clazz, nullptr);
for (int i = 0; i < jankData.size(); i++) {
jobject jJankData = env->NewObject(gJankDataClassInfo.clazz,
gJankDataClassInfo.ctor, jankData[i].frameVsyncId, jankData[i].jankType);
env->SetObjectArrayElement(jJankDataArray, i, jJankData);
}
env->CallVoidMethod(target,
gJankDataListenerClassInfo.onJankDataAvailable,
jJankDataArray);
env->DeleteLocalRef(target);
}
private:
JNIEnv* getEnv() {
JNIEnv* env;
mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
return env;
}
JavaVM* mVm;
jobject mOnJankDataListenerWeak;
};
static void nativeAddJankDataListener(JNIEnv* env, jclass clazz,
jlong jankDataCallbackListenerPtr,
jlong nativeSurfaceControl) {
sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl *>(nativeSurfaceControl));
if (surface == nullptr) {
return;
}
JankDataListenerWrapper* wrapper =
reinterpret_cast<JankDataListenerWrapper*>(jankDataCallbackListenerPtr);
TransactionCompletedListener::getInstance()->addJankListener(wrapper, surface);
}
static void nativeRemoveJankDataListener(JNIEnv* env, jclass clazz,
jlong jankDataCallbackListenerPtr) {
JankDataListenerWrapper* wrapper =
reinterpret_cast<JankDataListenerWrapper*>(jankDataCallbackListenerPtr);
TransactionCompletedListener::getInstance()->removeJankListener(wrapper);
}
static jlong nativeCreateJankDataListenerWrapper(JNIEnv* env, jclass clazz,
jobject jankDataListenerObject) {
return reinterpret_cast<jlong>(new JankDataListenerWrapper(env, jankDataListenerObject));
}
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -1785,6 +1863,12 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetFocusedWindow},
{"nativeSetFrameTimelineVsync", "(JJ)V",
(void*)nativeSetFrameTimelineVsync },
{"nativeAddJankDataListener", "(JJ)V",
(void*)nativeAddJankDataListener },
{"nativeRemoveJankDataListener", "(J)V",
(void*)nativeRemoveJankDataListener },
{"nativeCreateJankDataListenerWrapper", "(Landroid/view/SurfaceControl$OnJankDataListener;)J",
(void*)nativeCreateJankDataListenerWrapper },
// clang-format on
};
@@ -1966,6 +2050,18 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gScreenCaptureListenerClassInfo.onScreenCaptureComplete =
GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete",
"(Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;)V");
jclass jankDataClazz =
FindClassOrDie(env, "android/view/SurfaceControl$JankData");
gJankDataClassInfo.clazz = MakeGlobalRefOrDie(env, jankDataClazz);
gJankDataClassInfo.ctor =
GetMethodIDOrDie(env, gJankDataClassInfo.clazz, "<init>", "(JI)V");
jclass onJankDataListenerClazz =
FindClassOrDie(env, "android/view/SurfaceControl$OnJankDataListener");
gJankDataListenerClassInfo.clazz = MakeGlobalRefOrDie(env, onJankDataListenerClazz);
gJankDataListenerClassInfo.onJankDataAvailable =
GetMethodIDOrDie(env, onJankDataListenerClazz, "onJankDataAvailable",
"([Landroid/view/SurfaceControl$JankData;)V");
return err;
}