From fa4e46081daa7921befff50528c10d53fb66332f Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Mon, 1 Jul 2019 14:03:58 +0100 Subject: [PATCH] Split frame metrics functionality into a separate unit Bug: 117921091 Test: all tests should pass Change-Id: I2b06bf124dc2af9768e8c58af4d7b7b37274f661 --- core/jni/Android.bp | 1 + core/jni/AndroidRuntime.cpp | 2 + .../jni/android_view_FrameMetricsObserver.cpp | 149 ++++++++++++++++ core/jni/android_view_FrameMetricsObserver.h | 71 ++++++++ core/jni/android_view_ThreadedRenderer.cpp | 161 +----------------- 5 files changed, 226 insertions(+), 158 deletions(-) create mode 100644 core/jni/android_view_FrameMetricsObserver.cpp create mode 100644 core/jni/android_view_FrameMetricsObserver.h diff --git a/core/jni/Android.bp b/core/jni/Android.bp index b50b8248ee8d8..5eb840879e963 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -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", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index dad4a28c4fcbe..533f403034a92 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -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), diff --git a/core/jni/android_view_FrameMetricsObserver.cpp b/core/jni/android_view_FrameMetricsObserver.cpp new file mode 100644 index 0000000000000..febcb55bd0cdd --- /dev/null +++ b/core/jni/android_view_FrameMetricsObserver.cpp @@ -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(&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(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(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 diff --git a/core/jni/android_view_FrameMetricsObserver.h b/core/jni/android_view_FrameMetricsObserver.h new file mode 100644 index 0000000000000..647f51c4492d0 --- /dev/null +++ b/core/jni/android_view_FrameMetricsObserver.h @@ -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 +#include + +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(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 mMessageQueue; + sp mMessageHandler; + Message mMessage; + + int mNextFree = 0; + int mNextInQueue = 0; + FrameMetricsNotification mRingBuffer[kRingSize]; + + int mDroppedReports = 0; +}; + +} // namespace android diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 7dd0e935a08f2..ccb840d122df5 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -30,6 +30,8 @@ #include #include +#include "android_view_FrameMetricsObserver.h" + #include #include @@ -39,10 +41,7 @@ #include #include -#include "android_os_MessageQueue.h" - #include -#include #include #include #include @@ -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(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(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(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 mMessageQueue; - sp 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(proxyPtr); - FrameMetricsObserver* observer = new ObserverProxy(vm, fso); + FrameMetricsObserver* observer = new FrameMetricsObserverProxy(vm, fso); renderProxy->addFrameMetricsObserver(observer); return reinterpret_cast(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");