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

Commit fa4e4608 authored by Fedor Kudasov's avatar Fedor Kudasov
Browse files

Split frame metrics functionality into a separate unit

Bug: 117921091
Test: all tests should pass
Change-Id: I2b06bf124dc2af9768e8c58af4d7b7b37274f661
parent 19d16c90
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -153,6 +153,7 @@ cc_library_shared {
                "android_view_InputEventReceiver.cpp",
                "android_view_InputEventSender.cpp",
                "android_view_InputQueue.cpp",
                "android_view_FrameMetricsObserver.cpp",
                "android_view_KeyCharacterMap.cpp",
                "android_view_KeyEvent.cpp",
                "android_view_MotionEvent.cpp",
+2 −0
Original line number Diff line number Diff line
@@ -158,6 +158,7 @@ extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
extern int register_android_view_FrameMetricsObserver(JNIEnv* env);
extern int register_android_view_InputApplicationHandle(JNIEnv* env);
extern int register_android_view_InputWindowHandle(JNIEnv* env);
extern int register_android_view_TextureLayer(JNIEnv* env);
@@ -1464,6 +1465,7 @@ static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_view_RenderNode),
    REG_JNI(register_android_view_RenderNodeAnimator),
    REG_JNI(register_android_view_DisplayListCanvas),
    REG_JNI(register_android_view_FrameMetricsObserver),
    REG_JNI(register_android_view_InputApplicationHandle),
    REG_JNI(register_android_view_InputWindowHandle),
    REG_JNI(register_android_view_TextureLayer),
+149 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "android_view_FrameMetricsObserver.h"

namespace android {

struct {
    jfieldID frameMetrics;
    jfieldID timingDataBuffer;
    jfieldID messageQueue;
    jmethodID callback;
} gFrameMetricsObserverClassInfo;

static JNIEnv* getenv(JavaVM* vm) {
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
    }
    return env;
}

static jlongArray get_metrics_buffer(JNIEnv* env, jobject observer) {
    jobject frameMetrics = env->GetObjectField(
            observer, gFrameMetricsObserverClassInfo.frameMetrics);
    LOG_ALWAYS_FATAL_IF(frameMetrics == nullptr, "unable to retrieve data sink object");
    jobject buffer = env->GetObjectField(
            frameMetrics, gFrameMetricsObserverClassInfo.timingDataBuffer);
    LOG_ALWAYS_FATAL_IF(buffer == nullptr, "unable to retrieve data sink buffer");
    return reinterpret_cast<jlongArray>(buffer);
}

class NotifyHandler : public MessageHandler {
public:
    NotifyHandler(JavaVM* vm, FrameMetricsObserverProxy* observer) : mVm(vm), mObserver(observer) {}

    virtual void handleMessage(const Message& message);

private:
    JavaVM* const mVm;
    FrameMetricsObserverProxy* const mObserver;
};

void NotifyHandler::handleMessage(const Message& message) {
    JNIEnv* env = getenv(mVm);

    jobject target = env->NewLocalRef(mObserver->getObserverReference());

    if (target != nullptr) {
        jlongArray javaBuffer = get_metrics_buffer(env, target);
        int dropCount = 0;
        while (mObserver->getNextBuffer(env, javaBuffer, &dropCount)) {
            env->CallVoidMethod(target, gFrameMetricsObserverClassInfo.callback, dropCount);
        }
        env->DeleteLocalRef(target);
    }

    mObserver->decStrong(nullptr);
}

FrameMetricsObserverProxy::FrameMetricsObserverProxy(JavaVM *vm, jobject observer) : mVm(vm) {
    JNIEnv* env = getenv(mVm);

    mObserverWeak = env->NewWeakGlobalRef(observer);
    LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr,
            "unable to create frame stats observer reference");

    jlongArray buffer = get_metrics_buffer(env, observer);
    jsize bufferSize = env->GetArrayLength(reinterpret_cast<jarray>(buffer));
    LOG_ALWAYS_FATAL_IF(bufferSize != kBufferSize,
            "Mismatched Java/Native FrameMetrics data format.");

    jobject messageQueueLocal = env->GetObjectField(
            observer, gFrameMetricsObserverClassInfo.messageQueue);
    mMessageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueLocal);
    LOG_ALWAYS_FATAL_IF(mMessageQueue == nullptr, "message queue not available");

    mMessageHandler = new NotifyHandler(mVm, this);
    LOG_ALWAYS_FATAL_IF(mMessageHandler == nullptr,
            "OOM: unable to allocate NotifyHandler");
}

FrameMetricsObserverProxy::~FrameMetricsObserverProxy() {
    JNIEnv* env = getenv(mVm);
    env->DeleteWeakGlobalRef(mObserverWeak);
}

bool FrameMetricsObserverProxy::getNextBuffer(JNIEnv* env, jlongArray sink, int* dropCount) {
    FrameMetricsNotification& elem = mRingBuffer[mNextInQueue];

    if (elem.hasData.load()) {
        env->SetLongArrayRegion(sink, 0, kBufferSize, elem.buffer);
        *dropCount = elem.dropCount;
        mNextInQueue = (mNextInQueue + 1) % kRingSize;
        elem.hasData = false;
        return true;
    }

    return false;
}

void FrameMetricsObserverProxy::notify(const int64_t* stats) {
    FrameMetricsNotification& elem = mRingBuffer[mNextFree];

    if (!elem.hasData.load()) {
        memcpy(elem.buffer, stats, kBufferSize * sizeof(stats[0]));

        elem.dropCount = mDroppedReports;
        mDroppedReports = 0;

        incStrong(nullptr);
        mNextFree = (mNextFree + 1) % kRingSize;
        elem.hasData = true;

        mMessageQueue->getLooper()->sendMessage(mMessageHandler, mMessage);
    } else {
        mDroppedReports++;
    }
}

int register_android_view_FrameMetricsObserver(JNIEnv* env) {
    jclass observerClass = FindClassOrDie(env, "android/view/FrameMetricsObserver");
    gFrameMetricsObserverClassInfo.frameMetrics = GetFieldIDOrDie(
            env, observerClass, "mFrameMetrics", "Landroid/view/FrameMetrics;");
    gFrameMetricsObserverClassInfo.messageQueue = GetFieldIDOrDie(
            env, observerClass, "mMessageQueue", "Landroid/os/MessageQueue;");
    gFrameMetricsObserverClassInfo.callback = GetMethodIDOrDie(
            env, observerClass, "notifyDataAvailable", "(I)V");

    jclass metricsClass = FindClassOrDie(env, "android/view/FrameMetrics");
    gFrameMetricsObserverClassInfo.timingDataBuffer = GetFieldIDOrDie(
            env, metricsClass, "mTimingData", "[J");
    return JNI_OK;
}

} // namespace android
 No newline at end of file
+71 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "jni.h"
#include "core_jni_helpers.h"

#include "android_os_MessageQueue.h"

#include <FrameInfo.h>
#include <FrameMetricsObserver.h>

namespace android {

/*
 * Implements JNI layer for hwui frame metrics reporting.
 */
class FrameMetricsObserverProxy : public uirenderer::FrameMetricsObserver {
public:
    FrameMetricsObserverProxy(JavaVM *vm, jobject observer);

    ~FrameMetricsObserverProxy();

    jweak getObserverReference() {
        return mObserverWeak;
    }

    bool getNextBuffer(JNIEnv* env, jlongArray sink, int* dropCount);

    virtual void notify(const int64_t* stats);

private:
    static const int kBufferSize = static_cast<int>(uirenderer::FrameInfoIndex::NumIndexes);
    static constexpr int kRingSize = 3;

    class FrameMetricsNotification {
    public:
        FrameMetricsNotification() : hasData(false) {}

        std::atomic_bool hasData;
        int64_t buffer[kBufferSize];
        int dropCount = 0;
    };

    JavaVM* const mVm;
    jweak mObserverWeak;

    sp<MessageQueue> mMessageQueue;
    sp<MessageHandler> mMessageHandler;
    Message mMessage;

    int mNextFree = 0;
    int mNextInQueue = 0;
    FrameMetricsNotification mRingBuffer[kRingSize];

    int mDroppedReports = 0;
};

} // namespace android
+3 −158
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@
#include <gui/BufferQueue.h>
#include <gui/Surface.h>

#include "android_view_FrameMetricsObserver.h"

#include <private/EGL/cache.h>

#include <utils/RefBase.h>
@@ -39,10 +41,7 @@
#include <android_runtime/android_view_Surface.h>
#include <system/window.h>

#include "android_os_MessageQueue.h"

#include <FrameInfo.h>
#include <FrameMetricsObserver.h>
#include <IContextFactory.h>
#include <Picture.h>
#include <Properties.h>
@@ -59,13 +58,6 @@ namespace android {
using namespace android::uirenderer;
using namespace android::uirenderer::renderthread;

struct {
    jfieldID frameMetrics;
    jfieldID timingDataBuffer;
    jfieldID messageQueue;
    jmethodID callback;
} gFrameMetricsObserverClassInfo;

struct {
    jclass clazz;
    jmethodID invokePictureCapturedCallback;
@@ -133,142 +125,6 @@ private:
    }
};

class ObserverProxy;

class NotifyHandler : public MessageHandler {
public:
    NotifyHandler(JavaVM* vm, ObserverProxy* observer) : mVm(vm), mObserver(observer) {}

    virtual void handleMessage(const Message& message);

private:
    JavaVM* const mVm;
    ObserverProxy* const mObserver;
};

static jlongArray get_metrics_buffer(JNIEnv* env, jobject observer) {
    jobject frameMetrics = env->GetObjectField(
            observer, gFrameMetricsObserverClassInfo.frameMetrics);
    LOG_ALWAYS_FATAL_IF(frameMetrics == nullptr, "unable to retrieve data sink object");
    jobject buffer = env->GetObjectField(
            frameMetrics, gFrameMetricsObserverClassInfo.timingDataBuffer);
    LOG_ALWAYS_FATAL_IF(buffer == nullptr, "unable to retrieve data sink buffer");
    return reinterpret_cast<jlongArray>(buffer);
}

/*
 * Implements JNI layer for hwui frame metrics reporting.
 */
class ObserverProxy : public FrameMetricsObserver {
public:
    ObserverProxy(JavaVM *vm, jobject observer) : mVm(vm) {
        JNIEnv* env = getenv(mVm);

        mObserverWeak = env->NewWeakGlobalRef(observer);
        LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr,
                "unable to create frame stats observer reference");

        jlongArray buffer = get_metrics_buffer(env, observer);
        jsize bufferSize = env->GetArrayLength(reinterpret_cast<jarray>(buffer));
        LOG_ALWAYS_FATAL_IF(bufferSize != kBufferSize,
                "Mismatched Java/Native FrameMetrics data format.");

        jobject messageQueueLocal = env->GetObjectField(
                observer, gFrameMetricsObserverClassInfo.messageQueue);
        mMessageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueLocal);
        LOG_ALWAYS_FATAL_IF(mMessageQueue == nullptr, "message queue not available");

        mMessageHandler = new NotifyHandler(mVm, this);
        LOG_ALWAYS_FATAL_IF(mMessageHandler == nullptr,
                "OOM: unable to allocate NotifyHandler");
    }

    ~ObserverProxy() {
        JNIEnv* env = getenv(mVm);
        env->DeleteWeakGlobalRef(mObserverWeak);
    }

    jweak getObserverReference() {
        return mObserverWeak;
    }

    bool getNextBuffer(JNIEnv* env, jlongArray sink, int* dropCount) {
        FrameMetricsNotification& elem = mRingBuffer[mNextInQueue];

        if (elem.hasData.load()) {
            env->SetLongArrayRegion(sink, 0, kBufferSize, elem.buffer);
            *dropCount = elem.dropCount;
            mNextInQueue = (mNextInQueue + 1) % kRingSize;
            elem.hasData = false;
            return true;
        }

        return false;
    }

    virtual void notify(const int64_t* stats) {
        FrameMetricsNotification& elem = mRingBuffer[mNextFree];

        if (!elem.hasData.load()) {
            memcpy(elem.buffer, stats, kBufferSize * sizeof(stats[0]));

            elem.dropCount = mDroppedReports;
            mDroppedReports = 0;

            incStrong(nullptr);
            mNextFree = (mNextFree + 1) % kRingSize;
            elem.hasData = true;

            mMessageQueue->getLooper()->sendMessage(mMessageHandler, mMessage);
        } else {
            mDroppedReports++;
        }
    }

private:
    static const int kBufferSize = static_cast<int>(FrameInfoIndex::NumIndexes);
    static constexpr int kRingSize = 3;

    class FrameMetricsNotification {
    public:
        FrameMetricsNotification() : hasData(false) {}

        std::atomic_bool hasData;
        int64_t buffer[kBufferSize];
        int dropCount = 0;
    };

    JavaVM* const mVm;
    jweak mObserverWeak;

    sp<MessageQueue> mMessageQueue;
    sp<NotifyHandler> mMessageHandler;
    Message mMessage;

    int mNextFree = 0;
    int mNextInQueue = 0;
    FrameMetricsNotification mRingBuffer[kRingSize];

    int mDroppedReports = 0;
};

void NotifyHandler::handleMessage(const Message& message) {
    JNIEnv* env = getenv(mVm);

    jobject target = env->NewLocalRef(mObserver->getObserverReference());

    if (target != nullptr) {
        jlongArray javaBuffer = get_metrics_buffer(env, target);
        int dropCount = 0;
        while (mObserver->getNextBuffer(env, javaBuffer, &dropCount)) {
            env->CallVoidMethod(target, gFrameMetricsObserverClassInfo.callback, dropCount);
        }
        env->DeleteLocalRef(target);
    }

    mObserver->decStrong(nullptr);
}

static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) {
    RenderProxy::rotateProcessStatsBuffer();
}
@@ -734,7 +590,7 @@ static jlong android_view_ThreadedRenderer_addFrameMetricsObserver(JNIEnv* env,
    renderthread::RenderProxy* renderProxy =
            reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);

    FrameMetricsObserver* observer = new ObserverProxy(vm, fso);
    FrameMetricsObserver* observer = new FrameMetricsObserverProxy(vm, fso);
    renderProxy->addFrameMetricsObserver(observer);
    return reinterpret_cast<jlong>(observer);
}
@@ -853,17 +709,6 @@ static void attachRenderThreadToJvm(const char* name) {
int register_android_view_ThreadedRenderer(JNIEnv* env) {
    env->GetJavaVM(&mJvm);
    RenderThread::setOnStartHook(&attachRenderThreadToJvm);
    jclass observerClass = FindClassOrDie(env, "android/view/FrameMetricsObserver");
    gFrameMetricsObserverClassInfo.frameMetrics = GetFieldIDOrDie(
            env, observerClass, "mFrameMetrics", "Landroid/view/FrameMetrics;");
    gFrameMetricsObserverClassInfo.messageQueue = GetFieldIDOrDie(
            env, observerClass, "mMessageQueue", "Landroid/os/MessageQueue;");
    gFrameMetricsObserverClassInfo.callback = GetMethodIDOrDie(
            env, observerClass, "notifyDataAvailable", "(I)V");

    jclass metricsClass = FindClassOrDie(env, "android/view/FrameMetrics");
    gFrameMetricsObserverClassInfo.timingDataBuffer = GetFieldIDOrDie(
            env, metricsClass, "mTimingData", "[J");

    jclass hardwareRenderer = FindClassOrDie(env,
            "android/graphics/HardwareRenderer");