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:
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user