Split frame metrics functionality into a separate unit

Bug: 117921091
Test: all tests should pass
Change-Id: I2b06bf124dc2af9768e8c58af4d7b7b37274f661
This commit is contained in:
Fedor Kudasov
2019-07-01 14:03:58 +01:00
parent 19d16c9014
commit fa4e46081d
5 changed files with 226 additions and 158 deletions

View File

@@ -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",

View File

@@ -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),

View File

@@ -0,0 +1,149 @@
/*
* 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

View File

@@ -0,0 +1,71 @@
/*
* 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

View File

@@ -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");