From c4ef2c702a744c8d0a918d5dceb8d88bf3713908 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Mon, 9 Dec 2019 12:38:59 -0500 Subject: [PATCH] Move FrameMetricsObserver logic into UI Module. Introduce HardwareRendererObserver to isolate the android.graphics package from the android.view package. The native code that interacts with the observer was also moved from libandroid_runtime to libandroid_graphics to keep it within the confines of the UI Rendering module. Bug: 137655431 Test: CtsUiRenderingTestCases Change-Id: Ibccecbeda0c4d9501d86514a53eb98548233ba6a --- .../android/view/FrameMetricsObserver.java | 61 +++---- core/java/android/view/View.java | 12 +- core/jni/Android.bp | 2 +- .../jni/android/graphics/apex/jni_runtime.cpp | 4 +- ...roid_graphics_HardwareRendererObserver.cpp | 130 +++++++++++++++ ...ndroid_graphics_HardwareRendererObserver.h | 75 +++++++++ .../jni/android_view_FrameMetricsObserver.cpp | 149 ------------------ core/jni/android_view_FrameMetricsObserver.h | 71 --------- core/jni/android_view_ThreadedRenderer.cpp | 29 ++-- .../android/graphics/HardwareRenderer.java | 18 +-- .../graphics/HardwareRendererObserver.java | 103 ++++++++++++ 11 files changed, 356 insertions(+), 298 deletions(-) create mode 100644 core/jni/android_graphics_HardwareRendererObserver.cpp create mode 100644 core/jni/android_graphics_HardwareRendererObserver.h delete mode 100644 core/jni/android_view_FrameMetricsObserver.cpp delete mode 100644 core/jni/android_view_FrameMetricsObserver.h create mode 100644 graphics/java/android/graphics/HardwareRendererObserver.java diff --git a/core/java/android/view/FrameMetricsObserver.java b/core/java/android/view/FrameMetricsObserver.java index 0f38e847f4bd6..41bc9a7427525 100644 --- a/core/java/android/view/FrameMetricsObserver.java +++ b/core/java/android/view/FrameMetricsObserver.java @@ -17,11 +17,8 @@ package android.view; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; -import android.os.Looper; -import android.os.MessageQueue; - -import com.android.internal.util.VirtualRefBasePtr; +import android.graphics.HardwareRendererObserver; +import android.os.Handler; import java.lang.ref.WeakReference; @@ -31,47 +28,39 @@ import java.lang.ref.WeakReference; * * @hide */ -public class FrameMetricsObserver { - @UnsupportedAppUsage - private MessageQueue mMessageQueue; - - private WeakReference mWindow; - - @UnsupportedAppUsage - private FrameMetrics mFrameMetrics; - - /* pacage */ Window.OnFrameMetricsAvailableListener mListener; - /** @hide */ - public VirtualRefBasePtr mNative; +public class FrameMetricsObserver + implements HardwareRendererObserver.OnFrameMetricsAvailableListener { + private final WeakReference mWindow; + private final FrameMetrics mFrameMetrics; + private final HardwareRendererObserver mObserver; + /*package*/ final Window.OnFrameMetricsAvailableListener mListener; /** * Creates a FrameMetricsObserver * - * @param looper the looper to use when invoking callbacks + * @param handler the Handler to use when invoking callbacks */ - FrameMetricsObserver(@NonNull Window window, @NonNull Looper looper, + FrameMetricsObserver(@NonNull Window window, @NonNull Handler handler, @NonNull Window.OnFrameMetricsAvailableListener listener) { - if (looper == null) { - throw new NullPointerException("looper cannot be null"); - } - - mMessageQueue = looper.getQueue(); - if (mMessageQueue == null) { - throw new IllegalStateException("invalid looper, null message queue\n"); - } - - mFrameMetrics = new FrameMetrics(); mWindow = new WeakReference<>(window); mListener = listener; + mFrameMetrics = new FrameMetrics(); + mObserver = new HardwareRendererObserver(this, mFrameMetrics.mTimingData, handler); } - // Called by native on the provided Handler - @SuppressWarnings("unused") - @UnsupportedAppUsage - private void notifyDataAvailable(int dropCount) { - final Window window = mWindow.get(); - if (window != null) { - mListener.onFrameMetricsAvailable(window, mFrameMetrics, dropCount); + /** + * Implementation of OnFrameMetricsAvailableListener + * @param dropCountSinceLastInvocation the number of reports dropped since the last time + * @Override + */ + public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) { + if (mWindow.get() != null) { + mListener.onFrameMetricsAvailable(mWindow.get(), mFrameMetrics, + dropCountSinceLastInvocation); } } + + /*package*/ HardwareRendererObserver getRendererObserver() { + return mObserver; + } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 9d4f387895bbd..515dda6d361d1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7146,10 +7146,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mFrameMetricsObservers = new ArrayList<>(); } - FrameMetricsObserver fmo = new FrameMetricsObserver(window, - handler.getLooper(), listener); + FrameMetricsObserver fmo = new FrameMetricsObserver(window, handler, listener); mFrameMetricsObservers.add(fmo); - mAttachInfo.mThreadedRenderer.addFrameMetricsObserver(fmo); + mAttachInfo.mThreadedRenderer.addObserver(fmo.getRendererObserver()); } else { Log.w(VIEW_LOG_TAG, "View not hardware-accelerated. Unable to observe frame stats"); } @@ -7158,8 +7157,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mFrameMetricsObservers = new ArrayList<>(); } - FrameMetricsObserver fmo = new FrameMetricsObserver(window, - handler.getLooper(), listener); + FrameMetricsObserver fmo = new FrameMetricsObserver(window, handler, listener); mFrameMetricsObservers.add(fmo); } } @@ -7181,7 +7179,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mFrameMetricsObservers != null) { mFrameMetricsObservers.remove(fmo); if (renderer != null) { - renderer.removeFrameMetricsObserver(fmo); + renderer.removeObserver(fmo.getRendererObserver()); } } } @@ -7191,7 +7189,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, ThreadedRenderer renderer = getThreadedRenderer(); if (renderer != null) { for (FrameMetricsObserver fmo : mFrameMetricsObservers) { - renderer.addFrameMetricsObserver(fmo); + renderer.addObserver(fmo.getRendererObserver()); } } else { Log.w(VIEW_LOG_TAG, "View not hardware-accelerated. Unable to observe frame stats"); diff --git a/core/jni/Android.bp b/core/jni/Android.bp index b91d3595fe6aa..ea00519012f8f 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -110,7 +110,6 @@ 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", @@ -353,6 +352,7 @@ cc_library_static { "android_graphics_ColorSpace.cpp", "android_graphics_drawable_AnimatedVectorDrawable.cpp", "android_graphics_drawable_VectorDrawable.cpp", + "android_graphics_HardwareRendererObserver.cpp", "android_graphics_Picture.cpp", "android_nio_utils.cpp", "android_view_DisplayListCanvas.cpp", diff --git a/core/jni/android/graphics/apex/jni_runtime.cpp b/core/jni/android/graphics/apex/jni_runtime.cpp index 7f9bac0df44a5..1f661534ad811 100644 --- a/core/jni/android/graphics/apex/jni_runtime.cpp +++ b/core/jni/android/graphics/apex/jni_runtime.cpp @@ -52,6 +52,7 @@ extern int register_android_graphics_ColorFilter(JNIEnv* env); extern int register_android_graphics_ColorSpace(JNIEnv* env); extern int register_android_graphics_DrawFilter(JNIEnv* env); extern int register_android_graphics_FontFamily(JNIEnv* env); +extern int register_android_graphics_HardwareRendererObserver(JNIEnv* env); extern int register_android_graphics_Matrix(JNIEnv* env); extern int register_android_graphics_Paint(JNIEnv* env); extern int register_android_graphics_Path(JNIEnv* env); @@ -71,7 +72,6 @@ extern int register_android_graphics_text_LineBreaker(JNIEnv *env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_view_DisplayListCanvas(JNIEnv* env); -extern int register_android_view_FrameMetricsObserver(JNIEnv* env); extern int register_android_view_RenderNode(JNIEnv* env); extern int register_android_view_TextureLayer(JNIEnv* env); extern int register_android_view_ThreadedRenderer(JNIEnv* env); @@ -105,6 +105,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_ColorFilter), REG_JNI(register_android_graphics_DrawFilter), REG_JNI(register_android_graphics_FontFamily), + REG_JNI(register_android_graphics_HardwareRendererObserver), REG_JNI(register_android_graphics_ImageDecoder), REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable), REG_JNI(register_android_graphics_Interpolator), @@ -135,7 +136,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_util_PathParser), REG_JNI(register_android_view_RenderNode), REG_JNI(register_android_view_DisplayListCanvas), - REG_JNI(register_android_view_FrameMetricsObserver), REG_JNI(register_android_view_TextureLayer), REG_JNI(register_android_view_ThreadedRenderer), }; diff --git a/core/jni/android_graphics_HardwareRendererObserver.cpp b/core/jni/android_graphics_HardwareRendererObserver.cpp new file mode 100644 index 0000000000000..89b77b0b069a1 --- /dev/null +++ b/core/jni/android_graphics_HardwareRendererObserver.cpp @@ -0,0 +1,130 @@ +/* + * 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_graphics_HardwareRendererObserver.h" + +#include "core_jni_helpers.h" +#include "nativehelper/jni_macros.h" + +#include + +namespace android { + +struct { + jmethodID callback; +} gHardwareRendererObserverClassInfo; + +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; +} + +HardwareRendererObserver::HardwareRendererObserver(JavaVM *vm, jobject observer) : mVm(vm) { + mObserverWeak = getenv(mVm)->NewWeakGlobalRef(observer); + LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr, + "unable to create frame stats observer reference"); +} + +HardwareRendererObserver::~HardwareRendererObserver() { + JNIEnv* env = getenv(mVm); + env->DeleteWeakGlobalRef(mObserverWeak); +} + +bool HardwareRendererObserver::getNextBuffer(JNIEnv* env, jlongArray metrics, int* dropCount) { + jsize bufferSize = env->GetArrayLength(reinterpret_cast(metrics)); + LOG_ALWAYS_FATAL_IF(bufferSize != HardwareRendererObserver::kBufferSize, + "Mismatched Java/Native FrameMetrics data format."); + + FrameMetricsNotification& elem = mRingBuffer[mNextInQueue]; + if (elem.hasData.load()) { + env->SetLongArrayRegion(metrics, 0, kBufferSize, elem.buffer); + *dropCount = elem.dropCount; + mNextInQueue = (mNextInQueue + 1) % kRingSize; + elem.hasData = false; + return true; + } + + return false; +} + +void HardwareRendererObserver::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; + mNextFree = (mNextFree + 1) % kRingSize; + elem.hasData = true; + + JNIEnv* env = getenv(mVm); + jobject target = env->NewLocalRef(mObserverWeak); + if (target != nullptr) { + env->CallVoidMethod(target, gHardwareRendererObserverClassInfo.callback); + env->DeleteLocalRef(target); + } + } else { + mDroppedReports++; + } +} + +static jlong android_graphics_HardwareRendererObserver_createObserver(JNIEnv* env, + jobject observerObj) { + JavaVM* vm = nullptr; + if (env->GetJavaVM(&vm) != JNI_OK) { + LOG_ALWAYS_FATAL("Unable to get Java VM"); + return 0; + } + + HardwareRendererObserver* observer = new HardwareRendererObserver(vm, observerObj); + return reinterpret_cast(observer); +} + +static jint android_graphics_HardwareRendererObserver_getNextBuffer(JNIEnv* env, jobject, + jlong observerPtr, + jlongArray metrics) { + HardwareRendererObserver* observer = reinterpret_cast(observerPtr); + int dropCount = 0; + if (observer->getNextBuffer(env, metrics, &dropCount)) { + return dropCount; + } else { + return -1; + } +} + +static const std::array gMethods = { + MAKE_JNI_NATIVE_METHOD("nCreateObserver", "()J", + android_graphics_HardwareRendererObserver_createObserver), + MAKE_JNI_NATIVE_METHOD("nGetNextBuffer", "(J[J)I", + android_graphics_HardwareRendererObserver_getNextBuffer), +}; + +int register_android_graphics_HardwareRendererObserver(JNIEnv* env) { + + jclass observerClass = FindClassOrDie(env, "android/graphics/HardwareRendererObserver"); + gHardwareRendererObserverClassInfo.callback = GetMethodIDOrDie(env, observerClass, + "notifyDataAvailable", "()V"); + + return RegisterMethodsOrDie(env, "android/graphics/HardwareRendererObserver", + gMethods.data(), gMethods.size()); + +} + +} // namespace android \ No newline at end of file diff --git a/core/jni/android_graphics_HardwareRendererObserver.h b/core/jni/android_graphics_HardwareRendererObserver.h new file mode 100644 index 0000000000000..62111fd7d7a19 --- /dev/null +++ b/core/jni/android_graphics_HardwareRendererObserver.h @@ -0,0 +1,75 @@ +/* + * 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 +#include + +namespace android { + +/* + * Implements JNI layer for hwui frame metrics reporting. + */ +class HardwareRendererObserver : public uirenderer::FrameMetricsObserver { +public: + HardwareRendererObserver(JavaVM *vm, jobject observer); + ~HardwareRendererObserver(); + + /** + * Retrieves frame metrics for the oldest frame that the renderer has retained. The renderer + * will retain a buffer until it has been retrieved, via this method, or its internal storage + * is exhausted at which point it informs the caller of how many frames it has failed to store + * since the last time this method was invoked. + * @param env java env required to populate the provided buffer array + * @param metrics output parameter that represents the buffer of metrics that is to be filled + * @param dropCount output parameter that is updated to reflect the number of buffers that were + discarded since the last successful invocation of this method. + * @return true if there was data to populate the array and false otherwise. If false then + * neither the metrics buffer or dropCount will be modified. + */ + bool getNextBuffer(JNIEnv* env, jlongArray metrics, int* dropCount); + + void notify(const int64_t* stats) override; + +private: + static constexpr int kBufferSize = static_cast(uirenderer::FrameInfoIndex::NumIndexes); + static constexpr int kRingSize = 3; + + class FrameMetricsNotification { + public: + FrameMetricsNotification() {} + + std::atomic_bool hasData = false; + int64_t buffer[kBufferSize]; + int dropCount = 0; + private: + // non-copyable + FrameMetricsNotification(const FrameMetricsNotification&) = delete; + FrameMetricsNotification& operator=(const FrameMetricsNotification& ) = delete; + }; + + JavaVM* const mVm; + jweak mObserverWeak; + + int mNextFree = 0; + int mNextInQueue = 0; + FrameMetricsNotification mRingBuffer[kRingSize]; + + int mDroppedReports = 0; +}; + +} // namespace android diff --git a/core/jni/android_view_FrameMetricsObserver.cpp b/core/jni/android_view_FrameMetricsObserver.cpp deleted file mode 100644 index febcb55bd0cdd..0000000000000 --- a/core/jni/android_view_FrameMetricsObserver.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 deleted file mode 100644 index 647f51c4492d0..0000000000000 --- a/core/jni/android_view_FrameMetricsObserver.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 170e467a72cdb..69ca17c082571 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -30,7 +30,7 @@ #include #include -#include "android_view_FrameMetricsObserver.h" +#include "android_graphics_HardwareRendererObserver.h" #include @@ -580,28 +580,21 @@ static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) { } // ---------------------------------------------------------------------------- -// FrameMetricsObserver +// HardwareRendererObserver // ---------------------------------------------------------------------------- -static jlong android_view_ThreadedRenderer_addFrameMetricsObserver(JNIEnv* env, - jclass clazz, jlong proxyPtr, jobject fso) { - JavaVM* vm = nullptr; - if (env->GetJavaVM(&vm) != JNI_OK) { - LOG_ALWAYS_FATAL("Unable to get Java VM"); - return 0; - } - +static void android_view_ThreadedRenderer_addObserver(JNIEnv* env, jclass clazz, + jlong proxyPtr, jlong observerPtr) { + HardwareRendererObserver* observer = reinterpret_cast(observerPtr); renderthread::RenderProxy* renderProxy = reinterpret_cast(proxyPtr); - FrameMetricsObserver* observer = new FrameMetricsObserverProxy(vm, fso); renderProxy->addFrameMetricsObserver(observer); - return reinterpret_cast(observer); } -static void android_view_ThreadedRenderer_removeFrameMetricsObserver(JNIEnv* env, jclass clazz, +static void android_view_ThreadedRenderer_removeObserver(JNIEnv* env, jclass clazz, jlong proxyPtr, jlong observerPtr) { - FrameMetricsObserver* observer = reinterpret_cast(observerPtr); + HardwareRendererObserver* observer = reinterpret_cast(observerPtr); renderthread::RenderProxy* renderProxy = reinterpret_cast(proxyPtr); @@ -675,12 +668,8 @@ static const JNINativeMethod gMethods[] = { (void*)android_view_ThreadedRenderer_setFrameCallback}, { "nSetFrameCompleteCallback", "(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V", (void*)android_view_ThreadedRenderer_setFrameCompleteCallback }, - { "nAddFrameMetricsObserver", - "(JLandroid/view/FrameMetricsObserver;)J", - (void*)android_view_ThreadedRenderer_addFrameMetricsObserver }, - { "nRemoveFrameMetricsObserver", - "(JJ)V", - (void*)android_view_ThreadedRenderer_removeFrameMetricsObserver }, + { "nAddObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_addObserver }, + { "nRemoveObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeObserver }, { "nCopySurfaceInto", "(Landroid/view/Surface;IIIIJ)I", (void*)android_view_ThreadedRenderer_copySurfaceInto }, { "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;", diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index 3f3ad578e8d77..3b864139cf713 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -28,7 +28,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.util.TimeUtils; -import android.view.FrameMetricsObserver; import android.view.IGraphicsStats; import android.view.IGraphicsStatsCallback; import android.view.NativeVectorDrawableAnimator; @@ -38,8 +37,6 @@ import android.view.SurfaceHolder; import android.view.TextureLayer; import android.view.animation.AnimationUtils; -import com.android.internal.util.VirtualRefBasePtr; - import java.io.File; import java.io.FileDescriptor; import java.lang.annotation.Retention; @@ -598,9 +595,8 @@ public class HardwareRenderer { * * @hide */ - public void addFrameMetricsObserver(FrameMetricsObserver observer) { - long nativeObserver = nAddFrameMetricsObserver(mNativeProxy, observer); - observer.mNative = new VirtualRefBasePtr(nativeObserver); + public void addObserver(HardwareRendererObserver observer) { + nAddObserver(mNativeProxy, observer.getNativeInstance()); } /** @@ -608,9 +604,8 @@ public class HardwareRenderer { * * @hide */ - public void removeFrameMetricsObserver(FrameMetricsObserver observer) { - nRemoveFrameMetricsObserver(mNativeProxy, observer.mNative.get()); - observer.mNative = null; + public void removeObserver(HardwareRendererObserver observer) { + nRemoveObserver(mNativeProxy, observer.getNativeInstance()); } /** @@ -1170,10 +1165,9 @@ public class HardwareRenderer { private static native void nSetFrameCompleteCallback(long nativeProxy, FrameCompleteCallback callback); - private static native long nAddFrameMetricsObserver(long nativeProxy, - FrameMetricsObserver observer); + private static native void nAddObserver(long nativeProxy, long nativeObserver); - private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver); + private static native void nRemoveObserver(long nativeProxy, long nativeObserver); private static native int nCopySurfaceInto(Surface surface, int srcLeft, int srcTop, int srcRight, int srcBottom, long bitmapHandle); diff --git a/graphics/java/android/graphics/HardwareRendererObserver.java b/graphics/java/android/graphics/HardwareRendererObserver.java new file mode 100644 index 0000000000000..da9d03c639f7c --- /dev/null +++ b/graphics/java/android/graphics/HardwareRendererObserver.java @@ -0,0 +1,103 @@ +/* + * 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. + */ + +package android.graphics; + +import android.annotation.NonNull; +import android.os.Handler; + +import com.android.internal.util.VirtualRefBasePtr; + +/** + * Provides streaming access to frame stats information from HardwareRenderer to apps. + * + * @hide + */ +public class HardwareRendererObserver { + private final long[] mFrameMetrics; + private final Handler mHandler; + private final OnFrameMetricsAvailableListener mListener; + private VirtualRefBasePtr mNativePtr; + + /** + * Interface for clients that want frame timing information for each frame rendered. + * @hide + */ + public interface OnFrameMetricsAvailableListener { + /** + * Called when information is available for the previously rendered frame. + * + * Reports can be dropped if this callback takes too long to execute, as the report producer + * cannot wait for the consumer to complete. + * + * It is highly recommended that clients copy the metrics array within this method + * and defer additional computation or storage to another thread to avoid unnecessarily + * dropping reports. + * + * @param dropCountSinceLastInvocation the number of reports dropped since the last time + * this callback was invoked. + */ + void onFrameMetricsAvailable(int dropCountSinceLastInvocation); + } + + /** + * Creates a FrameMetricsObserver + * + * @param frameMetrics the available metrics. This array is reused on every call to the listener + * and thus this reference should only be used within the scope of the listener callback + * as data is not guaranteed to be valid outside the scope of that method. + * @param handler the Handler to use when invoking callbacks + */ + public HardwareRendererObserver(@NonNull OnFrameMetricsAvailableListener listener, + @NonNull long[] frameMetrics, @NonNull Handler handler) { + if (handler == null || handler.getLooper() == null) { + throw new NullPointerException("handler and its looper cannot be null"); + } + + if (handler.getLooper().getQueue() == null) { + throw new IllegalStateException("invalid looper, null message queue\n"); + } + + mFrameMetrics = frameMetrics; + mHandler = handler; + mListener = listener; + mNativePtr = new VirtualRefBasePtr(nCreateObserver()); + } + + /*package*/ long getNativeInstance() { + return mNativePtr.get(); + } + + // Called by native on the provided Handler + @SuppressWarnings("unused") + private void notifyDataAvailable() { + mHandler.post(() -> { + boolean hasMoreData = true; + while (hasMoreData) { + // a drop count of -1 is a sentinel that no more buffers are available + int dropCount = nGetNextBuffer(mNativePtr.get(), mFrameMetrics); + if (dropCount >= 0) { + mListener.onFrameMetricsAvailable(dropCount); + } else { + hasMoreData = false; + } + } + }); + } + + private native long nCreateObserver(); + private static native int nGetNextBuffer(long nativePtr, long[] data); +}