Merge "Pass actual present time to ViewRootImpl" into sc-dev

This commit is contained in:
Siarhei Vishniakou
2021-03-08 19:44:12 +00:00
committed by Android (Google) Code Review
11 changed files with 139 additions and 16 deletions

View File

@@ -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*/);
}
/**

View File

@@ -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.

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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++) {

View File

@@ -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) {

View File

@@ -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();
/**

View File

@@ -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);

View File

@@ -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;