Loading core/java/android/view/FrameStatsObserver.java 0 → 100644 +122 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 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.view; import android.annotation.NonNull; import android.util.Log; import android.os.Looper; import android.os.MessageQueue; import com.android.internal.util.VirtualRefBasePtr; import java.lang.NullPointerException; import java.lang.ref.WeakReference; import java.lang.SuppressWarnings; /** * Provides streaming access to frame stats information from the rendering * subsystem to apps. * * @hide */ public abstract class FrameStatsObserver { private static final String TAG = "FrameStatsObserver"; private MessageQueue mMessageQueue; private long[] mBuffer; private FrameStats mFrameStats; /* package */ ThreadedRenderer mRenderer; /* package */ VirtualRefBasePtr mNative; /** * Containing class for frame statistics reported * by the rendering subsystem. */ public static class FrameStats { /** * Precise timing data for various milestones in a frame * lifecycle. * * This data is exactly the same as what is returned by * `adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats` * * The fields reported may change from release to release. * * @see {@link http://developer.android.com/training/testing/performance.html} * for a description of the fields present. */ public long[] mTimingData; } /** * Creates a FrameStatsObserver * * @param looper the looper to use when invoking callbacks */ public FrameStatsObserver(@NonNull Looper looper) { 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"); } mFrameStats = new FrameStats(); } /** * Called on provided looper when frame stats data is available * for the previous frame. * * Clients of this class must do as little work as possible within * this callback, as the buffer is shared between the producer and consumer. * * If the consumer is still executing within this method when there is new * data available that data will be dropped. The producer cannot * wait on the consumer. * * @param data the newly available data */ public abstract void onDataAvailable(FrameStats data); /** * Returns the number of reports dropped as a result of a slow * consumer. */ public long getDroppedReportCount() { if (mRenderer == null) { return 0; } return mRenderer.getDroppedFrameReportCount(); } public boolean isRegistered() { return mRenderer != null && mNative != null; } // === called by native === // @SuppressWarnings("unused") private void notifyDataAvailable() { mFrameStats.mTimingData = mBuffer; onDataAvailable(mFrameStats); } } core/java/android/view/ThreadedRenderer.java +35 −0 Original line number Diff line number Diff line Loading @@ -24,7 +24,9 @@ import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; Loading @@ -34,12 +36,14 @@ import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; import com.android.internal.R; import com.android.internal.util.VirtualRefBasePtr; import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.HashSet; /** * Hardware renderer that proxies the rendering to a render thread. Most calls Loading Loading @@ -339,6 +343,8 @@ public final class ThreadedRenderer { private boolean mEnabled; private boolean mRequested = true; private HashSet<FrameStatsObserver> mFrameStatsObservers; ThreadedRenderer(Context context, boolean translucent) { final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0); mLightY = a.getDimension(R.styleable.Lighting_lightY, 0); Loading Loading @@ -947,6 +953,31 @@ public final class ThreadedRenderer { } } void addFrameStatsObserver(FrameStatsObserver fso) { if (mFrameStatsObservers == null) { mFrameStatsObservers = new HashSet<>(); } long nativeFso = nAddFrameStatsObserver(mNativeProxy, fso); fso.mRenderer = this; fso.mNative = new VirtualRefBasePtr(nativeFso); mFrameStatsObservers.add(fso); } void removeFrameStatsObserver(FrameStatsObserver fso) { if (!mFrameStatsObservers.remove(fso)) { throw new IllegalArgumentException("attempt to remove FrameStatsObserver that was never added"); } nRemoveFrameStatsObserver(mNativeProxy, fso.mNative.get()); fso.mRenderer = null; fso.mNative = null; } long getDroppedFrameReportCount() { return nGetDroppedFrameReportCount(mNativeProxy); } static native void setupShadersDiskCache(String cacheFile); private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map); Loading Loading @@ -1000,4 +1031,8 @@ public final class ThreadedRenderer { private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode); private static native void nSetContentDrawBounds(long nativeProxy, int left, int top, int right, int bottom); private static native long nAddFrameStatsObserver(long nativeProxy, FrameStatsObserver fso); private static native void nRemoveFrameStatsObserver(long nativeProxy, long nativeFso); private static native long nGetDroppedFrameReportCount(long nativeProxy); } core/java/android/view/View.java +31 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,7 @@ import com.android.internal.view.menu.MenuBuilder; import com.google.android.collect.Lists; import com.google.android.collect.Maps; import java.lang.NullPointerException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; Loading Loading @@ -5380,6 +5381,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback, getListenerInfo().mOnCreateContextMenuListener = l; } /** * Set an observer to collect stats for each frame rendered for this view. * * @hide */ public void addFrameStatsObserver(FrameStatsObserver fso) { if (mAttachInfo != null) { if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.addFrameStatsObserver(fso); } else { throw new IllegalStateException("View must be hardware-accelerated"); } } else { // TODO: store as pending registration and merge when we are attached to a surface throw new IllegalStateException("View not yet attached"); } } /** * Remove observer configured to collect frame stats for this view. * * @hide */ public void removeFrameStatsObserver(FrameStatsObserver fso) { ThreadedRenderer renderer = getHardwareRenderer(); if (renderer != null) { renderer.removeFrameStatsObserver(fso); } } /** * Call this view's OnClickListener, if it is defined. Performs all normal * actions associated with clicking: reporting accessibility event, playing Loading core/java/android/view/Window.java +35 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.graphics.drawable.Drawable; import android.media.session.MediaController; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; Loading Loading @@ -794,6 +795,40 @@ public abstract class Window { return mCallback; } /** * Set an observer to collect frame stats for each frame rendererd in this window. * * Must be in hardware rendering mode. * @hide */ public final void addFrameStatsObserver(@NonNull FrameStatsObserver fso) { final View decorView = getDecorView(); if (decorView == null) { throw new IllegalStateException("can't observe a Window without an attached view"); } if (fso == null) { throw new NullPointerException("FrameStatsObserver cannot be null"); } if (fso.isRegistered()) { throw new IllegalStateException("FrameStatsObserver already registered on a Window."); } decorView.addFrameStatsObserver(fso); } /** * Remove observer and stop listening to frame stats for this window. * @hide */ public final void removeFrameStatsObserver(FrameStatsObserver fso) { final View decorView = getDecorView(); if (decorView != null) { getDecorView().removeFrameStatsObserver(fso); } } /** @hide */ public final void setOnWindowDismissedCallback(OnWindowDismissedCallback dcb) { mOnWindowDismissedCallback = dcb; Loading core/jni/android_view_ThreadedRenderer.cpp +156 −0 Original line number Diff line number Diff line Loading @@ -28,14 +28,18 @@ #include <EGL/eglext.h> #include <EGL/egl_cache.h> #include <utils/Looper.h> #include <utils/RefBase.h> #include <utils/StrongPointer.h> #include <android_runtime/android_view_Surface.h> #include <system/window.h> #include "android_view_GraphicBuffer.h" #include "android_os_MessageQueue.h" #include <Animator.h> #include <AnimationContext.h> #include <FrameInfo.h> #include <IContextFactory.h> #include <JankTracker.h> #include <RenderNode.h> Loading @@ -50,6 +54,12 @@ namespace android { using namespace android::uirenderer; using namespace android::uirenderer::renderthread; struct { jfieldID buffer; jfieldID messageQueue; jmethodID notifyData; } gFrameStatsObserverClassInfo; static JNIEnv* getenv(JavaVM* vm) { JNIEnv* env; if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { Loading Loading @@ -207,6 +217,99 @@ private: RootRenderNode* mRootNode; }; class ObserverProxy; class NotifyHandler : public MessageHandler { public: NotifyHandler(JavaVM* vm) : mVm(vm) {} void setObserver(ObserverProxy* observer) { mObserver = observer; } void setBuffer(BufferPool::Buffer* buffer) { mBuffer = buffer; } virtual void handleMessage(const Message& message); private: JavaVM* mVm; sp<ObserverProxy> mObserver; BufferPool::Buffer* mBuffer; }; class ObserverProxy : public FrameStatsObserver { public: ObserverProxy(JavaVM *vm, jobject fso) : mVm(vm) { JNIEnv* env = getenv(mVm); jlongArray longArrayLocal = env->NewLongArray(kBufferSize); LOG_ALWAYS_FATAL_IF(longArrayLocal == nullptr, "OOM: can't allocate frame stats buffer"); env->SetObjectField(fso, gFrameStatsObserverClassInfo.buffer, longArrayLocal); mFsoWeak = env->NewWeakGlobalRef(fso); LOG_ALWAYS_FATAL_IF(mFsoWeak == nullptr, "unable to create frame stats observer reference"); jobject messageQueueLocal = env->GetObjectField(fso, gFrameStatsObserverClassInfo.messageQueue); mMessageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueLocal); LOG_ALWAYS_FATAL_IF(mMessageQueue == nullptr, "message queue not available"); mMessageHandler = new NotifyHandler(mVm); LOG_ALWAYS_FATAL_IF(mMessageHandler == nullptr, "OOM: unable to allocate NotifyHandler"); } ~ObserverProxy() { JNIEnv* env = getenv(mVm); env->DeleteWeakGlobalRef(mFsoWeak); } jweak getJavaObjectRef() { return mFsoWeak; } virtual void notify(BufferPool::Buffer* buffer) { buffer->incRef(); mMessageHandler->setBuffer(buffer); mMessageHandler->setObserver(this); mMessageQueue->getLooper()->sendMessage(mMessageHandler, mMessage); } private: static const int kBufferSize = static_cast<int>(FrameInfoIndex::NumIndexes); JavaVM* mVm; jweak mFsoWeak; sp<MessageQueue> mMessageQueue; sp<NotifyHandler> mMessageHandler; Message mMessage; }; void NotifyHandler::handleMessage(const Message& message) { JNIEnv* env = getenv(mVm); jobject target = env->NewLocalRef(mObserver->getJavaObjectRef()); if (target != nullptr) { jobject javaBuffer = env->GetObjectField(target, gFrameStatsObserverClassInfo.buffer); if (javaBuffer != nullptr) { env->SetLongArrayRegion(reinterpret_cast<jlongArray>(javaBuffer), 0, mBuffer->getSize(), mBuffer->getBuffer()); env->CallVoidMethod(target, gFrameStatsObserverClassInfo.notifyData); env->DeleteLocalRef(target); } } mBuffer->release(); mObserver.clear(); } static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz, jlong proxyPtr, jobject graphicBuffer, jlongArray atlasMapArray) { sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer); Loading Loading @@ -467,6 +570,42 @@ static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env, proxy->setContentDrawBounds(left, top, right, bottom); } // ---------------------------------------------------------------------------- // FrameStatsObserver // ---------------------------------------------------------------------------- static jlong android_view_ThreadedRenderer_addFrameStatsObserver(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; } renderthread::RenderProxy* renderProxy = reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); FrameStatsObserver* observer = new ObserverProxy(vm, fso); renderProxy->addFrameStatsObserver(observer); return reinterpret_cast<jlong>(observer); } static void android_view_ThreadedRenderer_removeFrameStatsObserver(JNIEnv* env, jclass clazz, jlong proxyPtr, jlong observerPtr) { FrameStatsObserver* observer = reinterpret_cast<FrameStatsObserver*>(observerPtr); renderthread::RenderProxy* renderProxy = reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); renderProxy->removeFrameStatsObserver(observer); } static jint android_view_ThreadedRenderer_getDroppedFrameReportCount(JNIEnv* env, jclass clazz, jlong proxyPtr) { renderthread::RenderProxy* renderProxy = reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); return renderProxy->getDroppedFrameReportCount(); } // ---------------------------------------------------------------------------- // Shaders // ---------------------------------------------------------------------------- Loading Loading @@ -523,9 +662,26 @@ static const JNINativeMethod gMethods[] = { { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode}, { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode}, { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds}, { "nAddFrameStatsObserver", "(JLandroid/view/FrameStatsObserver;)J", (void*)android_view_ThreadedRenderer_addFrameStatsObserver }, { "nRemoveFrameStatsObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeFrameStatsObserver }, { "nGetDroppedFrameReportCount", "(J)J", (void*)android_view_ThreadedRenderer_getDroppedFrameReportCount }, }; int register_android_view_ThreadedRenderer(JNIEnv* env) { jclass clazz = FindClassOrDie(env, "android/view/FrameStatsObserver"); gFrameStatsObserverClassInfo.messageQueue = GetFieldIDOrDie(env, clazz, "mMessageQueue", "Landroid/os/MessageQueue;"); gFrameStatsObserverClassInfo.buffer = GetFieldIDOrDie(env, clazz, "mBuffer", "[J"); gFrameStatsObserverClassInfo.notifyData = GetMethodIDOrDie(env, clazz, "notifyDataAvailable", "()V"); return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } Loading Loading
core/java/android/view/FrameStatsObserver.java 0 → 100644 +122 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 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.view; import android.annotation.NonNull; import android.util.Log; import android.os.Looper; import android.os.MessageQueue; import com.android.internal.util.VirtualRefBasePtr; import java.lang.NullPointerException; import java.lang.ref.WeakReference; import java.lang.SuppressWarnings; /** * Provides streaming access to frame stats information from the rendering * subsystem to apps. * * @hide */ public abstract class FrameStatsObserver { private static final String TAG = "FrameStatsObserver"; private MessageQueue mMessageQueue; private long[] mBuffer; private FrameStats mFrameStats; /* package */ ThreadedRenderer mRenderer; /* package */ VirtualRefBasePtr mNative; /** * Containing class for frame statistics reported * by the rendering subsystem. */ public static class FrameStats { /** * Precise timing data for various milestones in a frame * lifecycle. * * This data is exactly the same as what is returned by * `adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats` * * The fields reported may change from release to release. * * @see {@link http://developer.android.com/training/testing/performance.html} * for a description of the fields present. */ public long[] mTimingData; } /** * Creates a FrameStatsObserver * * @param looper the looper to use when invoking callbacks */ public FrameStatsObserver(@NonNull Looper looper) { 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"); } mFrameStats = new FrameStats(); } /** * Called on provided looper when frame stats data is available * for the previous frame. * * Clients of this class must do as little work as possible within * this callback, as the buffer is shared between the producer and consumer. * * If the consumer is still executing within this method when there is new * data available that data will be dropped. The producer cannot * wait on the consumer. * * @param data the newly available data */ public abstract void onDataAvailable(FrameStats data); /** * Returns the number of reports dropped as a result of a slow * consumer. */ public long getDroppedReportCount() { if (mRenderer == null) { return 0; } return mRenderer.getDroppedFrameReportCount(); } public boolean isRegistered() { return mRenderer != null && mNative != null; } // === called by native === // @SuppressWarnings("unused") private void notifyDataAvailable() { mFrameStats.mTimingData = mBuffer; onDataAvailable(mFrameStats); } }
core/java/android/view/ThreadedRenderer.java +35 −0 Original line number Diff line number Diff line Loading @@ -24,7 +24,9 @@ import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; Loading @@ -34,12 +36,14 @@ import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; import com.android.internal.R; import com.android.internal.util.VirtualRefBasePtr; import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.HashSet; /** * Hardware renderer that proxies the rendering to a render thread. Most calls Loading Loading @@ -339,6 +343,8 @@ public final class ThreadedRenderer { private boolean mEnabled; private boolean mRequested = true; private HashSet<FrameStatsObserver> mFrameStatsObservers; ThreadedRenderer(Context context, boolean translucent) { final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0); mLightY = a.getDimension(R.styleable.Lighting_lightY, 0); Loading Loading @@ -947,6 +953,31 @@ public final class ThreadedRenderer { } } void addFrameStatsObserver(FrameStatsObserver fso) { if (mFrameStatsObservers == null) { mFrameStatsObservers = new HashSet<>(); } long nativeFso = nAddFrameStatsObserver(mNativeProxy, fso); fso.mRenderer = this; fso.mNative = new VirtualRefBasePtr(nativeFso); mFrameStatsObservers.add(fso); } void removeFrameStatsObserver(FrameStatsObserver fso) { if (!mFrameStatsObservers.remove(fso)) { throw new IllegalArgumentException("attempt to remove FrameStatsObserver that was never added"); } nRemoveFrameStatsObserver(mNativeProxy, fso.mNative.get()); fso.mRenderer = null; fso.mNative = null; } long getDroppedFrameReportCount() { return nGetDroppedFrameReportCount(mNativeProxy); } static native void setupShadersDiskCache(String cacheFile); private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map); Loading Loading @@ -1000,4 +1031,8 @@ public final class ThreadedRenderer { private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode); private static native void nSetContentDrawBounds(long nativeProxy, int left, int top, int right, int bottom); private static native long nAddFrameStatsObserver(long nativeProxy, FrameStatsObserver fso); private static native void nRemoveFrameStatsObserver(long nativeProxy, long nativeFso); private static native long nGetDroppedFrameReportCount(long nativeProxy); }
core/java/android/view/View.java +31 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,7 @@ import com.android.internal.view.menu.MenuBuilder; import com.google.android.collect.Lists; import com.google.android.collect.Maps; import java.lang.NullPointerException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; Loading Loading @@ -5380,6 +5381,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback, getListenerInfo().mOnCreateContextMenuListener = l; } /** * Set an observer to collect stats for each frame rendered for this view. * * @hide */ public void addFrameStatsObserver(FrameStatsObserver fso) { if (mAttachInfo != null) { if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.addFrameStatsObserver(fso); } else { throw new IllegalStateException("View must be hardware-accelerated"); } } else { // TODO: store as pending registration and merge when we are attached to a surface throw new IllegalStateException("View not yet attached"); } } /** * Remove observer configured to collect frame stats for this view. * * @hide */ public void removeFrameStatsObserver(FrameStatsObserver fso) { ThreadedRenderer renderer = getHardwareRenderer(); if (renderer != null) { renderer.removeFrameStatsObserver(fso); } } /** * Call this view's OnClickListener, if it is defined. Performs all normal * actions associated with clicking: reporting accessibility event, playing Loading
core/java/android/view/Window.java +35 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.graphics.drawable.Drawable; import android.media.session.MediaController; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; Loading Loading @@ -794,6 +795,40 @@ public abstract class Window { return mCallback; } /** * Set an observer to collect frame stats for each frame rendererd in this window. * * Must be in hardware rendering mode. * @hide */ public final void addFrameStatsObserver(@NonNull FrameStatsObserver fso) { final View decorView = getDecorView(); if (decorView == null) { throw new IllegalStateException("can't observe a Window without an attached view"); } if (fso == null) { throw new NullPointerException("FrameStatsObserver cannot be null"); } if (fso.isRegistered()) { throw new IllegalStateException("FrameStatsObserver already registered on a Window."); } decorView.addFrameStatsObserver(fso); } /** * Remove observer and stop listening to frame stats for this window. * @hide */ public final void removeFrameStatsObserver(FrameStatsObserver fso) { final View decorView = getDecorView(); if (decorView != null) { getDecorView().removeFrameStatsObserver(fso); } } /** @hide */ public final void setOnWindowDismissedCallback(OnWindowDismissedCallback dcb) { mOnWindowDismissedCallback = dcb; Loading
core/jni/android_view_ThreadedRenderer.cpp +156 −0 Original line number Diff line number Diff line Loading @@ -28,14 +28,18 @@ #include <EGL/eglext.h> #include <EGL/egl_cache.h> #include <utils/Looper.h> #include <utils/RefBase.h> #include <utils/StrongPointer.h> #include <android_runtime/android_view_Surface.h> #include <system/window.h> #include "android_view_GraphicBuffer.h" #include "android_os_MessageQueue.h" #include <Animator.h> #include <AnimationContext.h> #include <FrameInfo.h> #include <IContextFactory.h> #include <JankTracker.h> #include <RenderNode.h> Loading @@ -50,6 +54,12 @@ namespace android { using namespace android::uirenderer; using namespace android::uirenderer::renderthread; struct { jfieldID buffer; jfieldID messageQueue; jmethodID notifyData; } gFrameStatsObserverClassInfo; static JNIEnv* getenv(JavaVM* vm) { JNIEnv* env; if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { Loading Loading @@ -207,6 +217,99 @@ private: RootRenderNode* mRootNode; }; class ObserverProxy; class NotifyHandler : public MessageHandler { public: NotifyHandler(JavaVM* vm) : mVm(vm) {} void setObserver(ObserverProxy* observer) { mObserver = observer; } void setBuffer(BufferPool::Buffer* buffer) { mBuffer = buffer; } virtual void handleMessage(const Message& message); private: JavaVM* mVm; sp<ObserverProxy> mObserver; BufferPool::Buffer* mBuffer; }; class ObserverProxy : public FrameStatsObserver { public: ObserverProxy(JavaVM *vm, jobject fso) : mVm(vm) { JNIEnv* env = getenv(mVm); jlongArray longArrayLocal = env->NewLongArray(kBufferSize); LOG_ALWAYS_FATAL_IF(longArrayLocal == nullptr, "OOM: can't allocate frame stats buffer"); env->SetObjectField(fso, gFrameStatsObserverClassInfo.buffer, longArrayLocal); mFsoWeak = env->NewWeakGlobalRef(fso); LOG_ALWAYS_FATAL_IF(mFsoWeak == nullptr, "unable to create frame stats observer reference"); jobject messageQueueLocal = env->GetObjectField(fso, gFrameStatsObserverClassInfo.messageQueue); mMessageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueLocal); LOG_ALWAYS_FATAL_IF(mMessageQueue == nullptr, "message queue not available"); mMessageHandler = new NotifyHandler(mVm); LOG_ALWAYS_FATAL_IF(mMessageHandler == nullptr, "OOM: unable to allocate NotifyHandler"); } ~ObserverProxy() { JNIEnv* env = getenv(mVm); env->DeleteWeakGlobalRef(mFsoWeak); } jweak getJavaObjectRef() { return mFsoWeak; } virtual void notify(BufferPool::Buffer* buffer) { buffer->incRef(); mMessageHandler->setBuffer(buffer); mMessageHandler->setObserver(this); mMessageQueue->getLooper()->sendMessage(mMessageHandler, mMessage); } private: static const int kBufferSize = static_cast<int>(FrameInfoIndex::NumIndexes); JavaVM* mVm; jweak mFsoWeak; sp<MessageQueue> mMessageQueue; sp<NotifyHandler> mMessageHandler; Message mMessage; }; void NotifyHandler::handleMessage(const Message& message) { JNIEnv* env = getenv(mVm); jobject target = env->NewLocalRef(mObserver->getJavaObjectRef()); if (target != nullptr) { jobject javaBuffer = env->GetObjectField(target, gFrameStatsObserverClassInfo.buffer); if (javaBuffer != nullptr) { env->SetLongArrayRegion(reinterpret_cast<jlongArray>(javaBuffer), 0, mBuffer->getSize(), mBuffer->getBuffer()); env->CallVoidMethod(target, gFrameStatsObserverClassInfo.notifyData); env->DeleteLocalRef(target); } } mBuffer->release(); mObserver.clear(); } static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz, jlong proxyPtr, jobject graphicBuffer, jlongArray atlasMapArray) { sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer); Loading Loading @@ -467,6 +570,42 @@ static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env, proxy->setContentDrawBounds(left, top, right, bottom); } // ---------------------------------------------------------------------------- // FrameStatsObserver // ---------------------------------------------------------------------------- static jlong android_view_ThreadedRenderer_addFrameStatsObserver(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; } renderthread::RenderProxy* renderProxy = reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); FrameStatsObserver* observer = new ObserverProxy(vm, fso); renderProxy->addFrameStatsObserver(observer); return reinterpret_cast<jlong>(observer); } static void android_view_ThreadedRenderer_removeFrameStatsObserver(JNIEnv* env, jclass clazz, jlong proxyPtr, jlong observerPtr) { FrameStatsObserver* observer = reinterpret_cast<FrameStatsObserver*>(observerPtr); renderthread::RenderProxy* renderProxy = reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); renderProxy->removeFrameStatsObserver(observer); } static jint android_view_ThreadedRenderer_getDroppedFrameReportCount(JNIEnv* env, jclass clazz, jlong proxyPtr) { renderthread::RenderProxy* renderProxy = reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); return renderProxy->getDroppedFrameReportCount(); } // ---------------------------------------------------------------------------- // Shaders // ---------------------------------------------------------------------------- Loading Loading @@ -523,9 +662,26 @@ static const JNINativeMethod gMethods[] = { { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode}, { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode}, { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds}, { "nAddFrameStatsObserver", "(JLandroid/view/FrameStatsObserver;)J", (void*)android_view_ThreadedRenderer_addFrameStatsObserver }, { "nRemoveFrameStatsObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeFrameStatsObserver }, { "nGetDroppedFrameReportCount", "(J)J", (void*)android_view_ThreadedRenderer_getDroppedFrameReportCount }, }; int register_android_view_ThreadedRenderer(JNIEnv* env) { jclass clazz = FindClassOrDie(env, "android/view/FrameStatsObserver"); gFrameStatsObserverClassInfo.messageQueue = GetFieldIDOrDie(env, clazz, "mMessageQueue", "Landroid/os/MessageQueue;"); gFrameStatsObserverClassInfo.buffer = GetFieldIDOrDie(env, clazz, "mBuffer", "[J"); gFrameStatsObserverClassInfo.notifyData = GetMethodIDOrDie(env, clazz, "notifyDataAvailable", "()V"); return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } Loading