Merge "Pass actual present time to ViewRootImpl" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
6387cdfb91
@@ -45,7 +45,8 @@ public class FrameMetricsObserver
|
||||
mWindow = new WeakReference<>(window);
|
||||
mListener = listener;
|
||||
mFrameMetrics = new FrameMetrics();
|
||||
mObserver = new HardwareRendererObserver(this, mFrameMetrics.mTimingData, handler);
|
||||
mObserver = new HardwareRendererObserver(this, mFrameMetrics.mTimingData, handler,
|
||||
false /*waitForPresentTime*/);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.MessageQueue;
|
||||
import android.os.Trace;
|
||||
import android.util.Log;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
@@ -197,6 +198,15 @@ public abstract class InputEventReceiver {
|
||||
event.recycleIfNeededAfterDispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the latency information for a specific input event.
|
||||
*/
|
||||
public final void reportLatencyInfo(int inputEventId, long gpuCompletedTime, long presentTime) {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "reportLatencyInfo");
|
||||
// TODO(b/169866723) : send this data to InputDispatcher via InputChannel
|
||||
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes all pending batched input events.
|
||||
* Must be called on the same Looper thread to which the receiver is attached.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.Display.INVALID_DISPLAY;
|
||||
import static android.view.InputDevice.SOURCE_CLASS_NONE;
|
||||
@@ -105,6 +106,7 @@ import android.graphics.Color;
|
||||
import android.graphics.FrameInfo;
|
||||
import android.graphics.HardwareRenderer;
|
||||
import android.graphics.HardwareRenderer.FrameDrawingCallback;
|
||||
import android.graphics.HardwareRendererObserver;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.PixelFormat;
|
||||
@@ -1191,6 +1193,14 @@ public final class ViewRootImpl implements ViewParent,
|
||||
}
|
||||
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
|
||||
Looper.myLooper());
|
||||
|
||||
if (mAttachInfo.mThreadedRenderer != null) {
|
||||
InputMetricsListener listener =
|
||||
new InputMetricsListener(mInputEventReceiver);
|
||||
mHardwareRendererObserver = new HardwareRendererObserver(
|
||||
listener, listener.data, mHandler, true /*waitForPresentTime*/);
|
||||
mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver);
|
||||
}
|
||||
}
|
||||
|
||||
view.assignParent(this);
|
||||
@@ -8569,6 +8579,34 @@ public final class ViewRootImpl implements ViewParent,
|
||||
}
|
||||
WindowInputEventReceiver mInputEventReceiver;
|
||||
|
||||
final class InputMetricsListener
|
||||
implements HardwareRendererObserver.OnFrameMetricsAvailableListener {
|
||||
public long[] data = new long[FrameMetrics.Index.FRAME_STATS_COUNT];
|
||||
|
||||
private InputEventReceiver mReceiver;
|
||||
|
||||
InputMetricsListener(InputEventReceiver receiver) {
|
||||
mReceiver = receiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
|
||||
final int inputEventId = (int) data[FrameMetrics.Index.INPUT_EVENT_ID];
|
||||
if (inputEventId == INVALID_INPUT_EVENT_ID) {
|
||||
return;
|
||||
}
|
||||
final long presentTime = data[FrameMetrics.Index.DISPLAY_PRESENT_TIME];
|
||||
if (presentTime <= 0) {
|
||||
// Present time is not available for this frame. If the present time is not
|
||||
// available, we cannot compute end-to-end input latency metrics.
|
||||
return;
|
||||
}
|
||||
final long gpuCompletedTime = data[FrameMetrics.Index.GPU_COMPLETED];
|
||||
mReceiver.reportLatencyInfo(inputEventId, gpuCompletedTime, presentTime);
|
||||
}
|
||||
}
|
||||
HardwareRendererObserver mHardwareRendererObserver;
|
||||
|
||||
final class ConsumeBatchedInputRunnable implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -121,7 +121,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
|
||||
mChoreographer = choreographer;
|
||||
mSurfaceControlWrapper = surfaceControlWrapper;
|
||||
mHandler = handler;
|
||||
mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
|
||||
mObserver = new HardwareRendererObserver(
|
||||
this, mMetricsWrapper.getTiming(), handler, false /*waitForPresentTime*/);
|
||||
mTraceThresholdMissedFrames = traceThresholdMissedFrames;
|
||||
mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
|
||||
mListener = listener;
|
||||
|
||||
@@ -62,7 +62,7 @@ public class HardwareRendererObserver {
|
||||
* @param handler the Handler to use when invoking callbacks
|
||||
*/
|
||||
public HardwareRendererObserver(@NonNull OnFrameMetricsAvailableListener listener,
|
||||
@NonNull long[] frameMetrics, @NonNull Handler handler) {
|
||||
@NonNull long[] frameMetrics, @NonNull Handler handler, boolean waitForPresentTime) {
|
||||
if (handler == null || handler.getLooper() == null) {
|
||||
throw new NullPointerException("handler and its looper cannot be null");
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public class HardwareRendererObserver {
|
||||
mFrameMetrics = frameMetrics;
|
||||
mHandler = handler;
|
||||
mListener = listener;
|
||||
mNativePtr = new VirtualRefBasePtr(nCreateObserver());
|
||||
mNativePtr = new VirtualRefBasePtr(nCreateObserver(waitForPresentTime));
|
||||
}
|
||||
|
||||
/*package*/ long getNativeInstance() {
|
||||
@@ -98,6 +98,6 @@ public class HardwareRendererObserver {
|
||||
});
|
||||
}
|
||||
|
||||
private native long nCreateObserver();
|
||||
private native long nCreateObserver(boolean waitForPresentTime);
|
||||
private static native int nGetNextBuffer(long nativePtr, long[] data);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,24 @@ namespace uirenderer {
|
||||
class FrameMetricsObserver : public VirtualLightRefBase {
|
||||
public:
|
||||
virtual void notify(const int64_t* buffer) = 0;
|
||||
bool waitForPresentTime() const { return mWaitForPresentTime; };
|
||||
|
||||
/**
|
||||
* Create a new metrics observer. An observer that watches present time gets notified at a
|
||||
* different time than the observer that doesn't.
|
||||
*
|
||||
* The observer that doesn't want present time is notified about metrics just after the frame
|
||||
* is completed. This is the default behaviour that's used by public API's.
|
||||
*
|
||||
* An observer that watches present time is notified about metrics after the actual display
|
||||
* present time is known.
|
||||
* WARNING! This observer may not receive metrics for the last several frames that the app
|
||||
* produces.
|
||||
*/
|
||||
FrameMetricsObserver(bool waitForPresentTime) : mWaitForPresentTime(waitForPresentTime) {}
|
||||
|
||||
private:
|
||||
const bool mWaitForPresentTime;
|
||||
};
|
||||
|
||||
} // namespace uirenderer
|
||||
|
||||
@@ -55,13 +55,24 @@ public:
|
||||
return mObservers.size() > 0;
|
||||
}
|
||||
|
||||
void reportFrameMetrics(const int64_t* stats) {
|
||||
/**
|
||||
* Notify observers about the metrics contained in 'stats'.
|
||||
* If an observer is waiting for present time, notify when 'stats' has present time.
|
||||
*
|
||||
* If an observer does not want present time, only notify when 'hasPresentTime' is false.
|
||||
* Never notify both types of observers from the same callback, because the callback with
|
||||
* 'hasPresentTime' is sent at a different time than the one without.
|
||||
*/
|
||||
void reportFrameMetrics(const int64_t* stats, bool hasPresentTime) {
|
||||
FatVector<sp<FrameMetricsObserver>, 10> copy;
|
||||
{
|
||||
std::lock_guard lock(mObserversLock);
|
||||
copy.reserve(mObservers.size());
|
||||
for (size_t i = 0; i < mObservers.size(); i++) {
|
||||
copy.push_back(mObservers[i]);
|
||||
const bool wantsPresentTime = mObservers[i]->waitForPresentTime();
|
||||
if (hasPresentTime == wantsPresentTime) {
|
||||
copy.push_back(mObservers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < copy.size(); i++) {
|
||||
|
||||
@@ -35,7 +35,9 @@ static JNIEnv* getenv(JavaVM* vm) {
|
||||
return env;
|
||||
}
|
||||
|
||||
HardwareRendererObserver::HardwareRendererObserver(JavaVM *vm, jobject observer) : mVm(vm) {
|
||||
HardwareRendererObserver::HardwareRendererObserver(JavaVM* vm, jobject observer,
|
||||
bool waitForPresentTime)
|
||||
: uirenderer::FrameMetricsObserver(waitForPresentTime), mVm(vm) {
|
||||
mObserverWeak = getenv(mVm)->NewWeakGlobalRef(observer);
|
||||
LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr,
|
||||
"unable to create frame stats observer reference");
|
||||
@@ -86,14 +88,16 @@ void HardwareRendererObserver::notify(const int64_t* stats) {
|
||||
}
|
||||
|
||||
static jlong android_graphics_HardwareRendererObserver_createObserver(JNIEnv* env,
|
||||
jobject observerObj) {
|
||||
jobject observerObj,
|
||||
jboolean waitForPresentTime) {
|
||||
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);
|
||||
HardwareRendererObserver* observer =
|
||||
new HardwareRendererObserver(vm, observerObj, waitForPresentTime);
|
||||
return reinterpret_cast<jlong>(observer);
|
||||
}
|
||||
|
||||
@@ -110,10 +114,10 @@ static jint android_graphics_HardwareRendererObserver_getNextBuffer(JNIEnv* env,
|
||||
}
|
||||
|
||||
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),
|
||||
MAKE_JNI_NATIVE_METHOD("nCreateObserver", "(Z)J",
|
||||
android_graphics_HardwareRendererObserver_createObserver),
|
||||
MAKE_JNI_NATIVE_METHOD("nGetNextBuffer", "(J[J)I",
|
||||
android_graphics_HardwareRendererObserver_getNextBuffer),
|
||||
};
|
||||
|
||||
int register_android_graphics_HardwareRendererObserver(JNIEnv* env) {
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace android {
|
||||
*/
|
||||
class HardwareRendererObserver : public uirenderer::FrameMetricsObserver {
|
||||
public:
|
||||
HardwareRendererObserver(JavaVM *vm, jobject observer);
|
||||
HardwareRendererObserver(JavaVM* vm, jobject observer, bool waitForPresentTime);
|
||||
~HardwareRendererObserver();
|
||||
|
||||
/**
|
||||
|
||||
@@ -599,10 +599,41 @@ void CanvasContext::finishFrame(FrameInfo* frameInfo) {
|
||||
// TODO (b/169858044): Move this into JankTracker to adjust deadline when queue is
|
||||
// double-stuffed.
|
||||
if (CC_UNLIKELY(mFrameMetricsReporter.get() != nullptr)) {
|
||||
mFrameMetricsReporter->reportFrameMetrics(frameInfo->data());
|
||||
mFrameMetricsReporter->reportFrameMetrics(frameInfo->data(), false /*hasPresentTime*/);
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasContext::reportMetricsWithPresentTime() {
|
||||
if (mFrameMetricsReporter == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (mNativeSurface == nullptr) {
|
||||
return;
|
||||
}
|
||||
FrameInfo* forthBehind;
|
||||
int64_t frameNumber;
|
||||
{ // acquire lock
|
||||
std::scoped_lock lock(mLast4FrameInfosMutex);
|
||||
if (mLast4FrameInfos.size() != mLast4FrameInfos.capacity()) {
|
||||
// Not enough frames yet
|
||||
return;
|
||||
}
|
||||
// Surface object keeps stats for the last 8 frames.
|
||||
std::tie(forthBehind, frameNumber) = mLast4FrameInfos.front();
|
||||
} // release lock
|
||||
|
||||
nsecs_t presentTime = 0;
|
||||
native_window_get_frame_timestamps(
|
||||
mNativeSurface->getNativeWindow(), frameNumber, nullptr /*outRequestedPresentTime*/,
|
||||
nullptr /*outAcquireTime*/, nullptr /*outLatchTime*/,
|
||||
nullptr /*outFirstRefreshStartTime*/, nullptr /*outLastRefreshStartTime*/,
|
||||
nullptr /*outGpuCompositionDoneTime*/, &presentTime, nullptr /*outDequeueReadyTime*/,
|
||||
nullptr /*outReleaseTime*/);
|
||||
|
||||
forthBehind->set(FrameInfoIndex::DisplayPresentTime) = presentTime;
|
||||
mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/);
|
||||
}
|
||||
|
||||
void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
|
||||
ASurfaceControlStats* stats) {
|
||||
|
||||
@@ -624,6 +655,9 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* cont
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instance->reportMetricsWithPresentTime();
|
||||
|
||||
if (frameInfo != nullptr) {
|
||||
if (gpuCompleteTime == -1) {
|
||||
gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
|
||||
|
||||
@@ -219,6 +219,12 @@ private:
|
||||
SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
|
||||
void finishFrame(FrameInfo* frameInfo);
|
||||
|
||||
/**
|
||||
* Invoke 'reportFrameMetrics' on the last frame stored in 'mLast4FrameInfos'.
|
||||
* Populate the 'presentTime' field before calling.
|
||||
*/
|
||||
void reportMetricsWithPresentTime();
|
||||
|
||||
// The same type as Frame.mWidth and Frame.mHeight
|
||||
int32_t mLastFrameWidth = 0;
|
||||
int32_t mLastFrameHeight = 0;
|
||||
|
||||
Reference in New Issue
Block a user