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

Commit e418a013 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

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
parent b2c48f87
Loading
Loading
Loading
Loading
+91 −0
Original line number Diff line number Diff line
@@ -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.
     */
+96 −0
Original line number Diff line number Diff line
@@ -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;
}