/* * 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; import java.lang.ref.WeakReference; /** * Provides streaming access to frame stats information from HardwareRenderer to apps. * * @hide */ public final 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, boolean waitForPresentTime) { 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( new WeakReference<>(this), waitForPresentTime)); } /*package*/ long getNativeInstance() { return mNativePtr.get(); } 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; } } }); } /** * called by native * @hide * @return true to keep listening, false if this is a dead observer */ static boolean invokeDataAvailable(WeakReference weakObserver) { HardwareRendererObserver observer = weakObserver.get(); if (observer != null) { observer.notifyDataAvailable(); return true; } return false; } private static native long nCreateObserver(WeakReference observer, boolean waitForPresentTime); private static native int nGetNextBuffer(long nativePtr, long[] data); }