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
This commit is contained in:
@@ -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<Window> mWindow;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
private FrameMetrics mFrameMetrics;
|
||||
|
||||
/* pacage */ Window.OnFrameMetricsAvailableListener mListener;
|
||||
/** @hide */
|
||||
public VirtualRefBasePtr mNative;
|
||||
public class FrameMetricsObserver
|
||||
implements HardwareRendererObserver.OnFrameMetricsAvailableListener {
|
||||
private final WeakReference<Window> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
130
core/jni/android_graphics_HardwareRendererObserver.cpp
Normal file
130
core/jni/android_graphics_HardwareRendererObserver.cpp
Normal file
@@ -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 <array>
|
||||
|
||||
namespace android {
|
||||
|
||||
struct {
|
||||
jmethodID callback;
|
||||
} gHardwareRendererObserverClassInfo;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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<jarray>(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<jlong>(observer);
|
||||
}
|
||||
|
||||
static jint android_graphics_HardwareRendererObserver_getNextBuffer(JNIEnv* env, jobject,
|
||||
jlong observerPtr,
|
||||
jlongArray metrics) {
|
||||
HardwareRendererObserver* observer = reinterpret_cast<HardwareRendererObserver*>(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
|
||||
75
core/jni/android_graphics_HardwareRendererObserver.h
Normal file
75
core/jni/android_graphics_HardwareRendererObserver.h
Normal file
@@ -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 <FrameInfo.h>
|
||||
#include <FrameMetricsObserver.h>
|
||||
|
||||
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<int>(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
|
||||
@@ -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<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
|
||||
@@ -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 <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
|
||||
@@ -30,7 +30,7 @@
|
||||
#include <gui/BufferQueue.h>
|
||||
#include <gui/Surface.h>
|
||||
|
||||
#include "android_view_FrameMetricsObserver.h"
|
||||
#include "android_graphics_HardwareRendererObserver.h"
|
||||
|
||||
#include <private/EGL/cache.h>
|
||||
|
||||
@@ -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<HardwareRendererObserver*>(observerPtr);
|
||||
renderthread::RenderProxy* renderProxy =
|
||||
reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
|
||||
|
||||
FrameMetricsObserver* observer = new FrameMetricsObserverProxy(vm, fso);
|
||||
renderProxy->addFrameMetricsObserver(observer);
|
||||
return reinterpret_cast<jlong>(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<FrameMetricsObserver*>(observerPtr);
|
||||
HardwareRendererObserver* observer = reinterpret_cast<HardwareRendererObserver*>(observerPtr);
|
||||
renderthread::RenderProxy* renderProxy =
|
||||
reinterpret_cast<renderthread::RenderProxy*>(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;",
|
||||
|
||||
@@ -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);
|
||||
|
||||
103
graphics/java/android/graphics/HardwareRendererObserver.java
Normal file
103
graphics/java/android/graphics/HardwareRendererObserver.java
Normal file
@@ -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 <strong>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</strong>.
|
||||
* @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);
|
||||
}
|
||||
Reference in New Issue
Block a user