diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index d7a913d6c380f..181f77e2c8869 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -598,6 +598,42 @@ public final class Display { } } + /** + * Gets the app VSYNC offset, in nanoseconds. This is a positive value indicating + * the phase offset of the VSYNC events provided by Choreographer relative to the + * display refresh. For example, if Choreographer reports that the refresh occurred + * at time N, it actually occurred at (N - appVsyncOffset). + *

+ * Apps generally do not need to be aware of this. It's only useful for fine-grained + * A/V synchronization. + * @hide + */ + public long getAppVsyncOffsetNanos() { + synchronized (this) { + updateDisplayInfoLocked(); + return mDisplayInfo.appVsyncOffsetNanos; + } + } + + /** + * This is how far in advance a buffer must be queued for presentation at + * a given time. If you want a buffer to appear on the screen at + * time N, you must submit the buffer before (N - presentationDeadline). + *

+ * The desired presentation time for GLES rendering may be set with + * {@link android.opengl.EGLExt#eglPresentationTimeANDROID}. For video decoding, use + * {@link android.media.MediaCodec#releaseOutputBuffer(int, long)}. Times are + * expressed in nanoseconds, using the system monotonic clock + * ({@link System#nanoTime}). + * @hide + */ + public long getPresentationDeadlineNanos() { + synchronized (this) { + updateDisplayInfoLocked(); + return mDisplayInfo.presentationDeadlineNanos; + } + } + /** * Gets display metrics that describe the size and density of this display. *

diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index b0fe0faf72f60..98696c7e54ece 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -179,6 +179,20 @@ public final class DisplayInfo implements Parcelable { */ public float physicalYDpi; + /** + * This is a positive value indicating the phase offset of the VSYNC events provided by + * Choreographer relative to the display refresh. For example, if Choreographer reports + * that the refresh occurred at time N, it actually occurred at (N - appVsyncOffsetNanos). + */ + public long appVsyncOffsetNanos; + + /** + * This is how far in advance a buffer must be queued for presentation at + * a given time. If you want a buffer to appear on the screen at + * time N, you must submit the buffer before (N - bufferDeadlineNanos). + */ + public long presentationDeadlineNanos; + /** * The state of the display, such as {@link android.view.Display#STATE_ON}. */ @@ -253,6 +267,8 @@ public final class DisplayInfo implements Parcelable { && logicalDensityDpi == other.logicalDensityDpi && physicalXDpi == other.physicalXDpi && physicalYDpi == other.physicalYDpi + && appVsyncOffsetNanos == other.appVsyncOffsetNanos + && presentationDeadlineNanos == other.presentationDeadlineNanos && state == other.state && ownerUid == other.ownerUid && Objects.equal(ownerPackageName, other.ownerPackageName); @@ -286,6 +302,8 @@ public final class DisplayInfo implements Parcelable { logicalDensityDpi = other.logicalDensityDpi; physicalXDpi = other.physicalXDpi; physicalYDpi = other.physicalYDpi; + appVsyncOffsetNanos = other.appVsyncOffsetNanos; + presentationDeadlineNanos = other.presentationDeadlineNanos; state = other.state; ownerUid = other.ownerUid; ownerPackageName = other.ownerPackageName; @@ -314,6 +332,8 @@ public final class DisplayInfo implements Parcelable { logicalDensityDpi = source.readInt(); physicalXDpi = source.readFloat(); physicalYDpi = source.readFloat(); + appVsyncOffsetNanos = source.readLong(); + presentationDeadlineNanos = source.readLong(); state = source.readInt(); ownerUid = source.readInt(); ownerPackageName = source.readString(); @@ -343,6 +363,8 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(logicalDensityDpi); dest.writeFloat(physicalXDpi); dest.writeFloat(physicalYDpi); + dest.writeLong(appVsyncOffsetNanos); + dest.writeLong(presentationDeadlineNanos); dest.writeInt(state); dest.writeInt(ownerUid); dest.writeString(ownerPackageName); @@ -450,6 +472,10 @@ public final class DisplayInfo implements Parcelable { sb.append(physicalYDpi); sb.append(") dpi, layerStack "); sb.append(layerStack); + sb.append(", appVsyncOff "); + sb.append(appVsyncOffsetNanos); + sb.append(", presDeadline "); + sb.append(presentationDeadlineNanos); sb.append(", type "); sb.append(Display.typeToString(type)); if (address != null) { diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 79f19b583bc58..94d8f703c53b6 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -458,6 +458,8 @@ public class SurfaceControl { public float xDpi; public float yDpi; public boolean secure; + public long appVsyncOffsetNanos; + public long presentationDeadlineNanos; public PhysicalDisplayInfo() { } @@ -479,7 +481,9 @@ public class SurfaceControl { && density == other.density && xDpi == other.xDpi && yDpi == other.yDpi - && secure == other.secure; + && secure == other.secure + && appVsyncOffsetNanos == other.appVsyncOffsetNanos + && presentationDeadlineNanos == other.presentationDeadlineNanos; } @Override @@ -495,6 +499,8 @@ public class SurfaceControl { xDpi = other.xDpi; yDpi = other.yDpi; secure = other.secure; + appVsyncOffsetNanos = other.appVsyncOffsetNanos; + presentationDeadlineNanos = other.presentationDeadlineNanos; } // For debugging purposes @@ -502,7 +508,8 @@ public class SurfaceControl { public String toString() { return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, " + "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure - + "}"; + + ", appVsyncOffset " + appVsyncOffsetNanos + + ", bufferDeadline " + presentationDeadlineNanos + "}"; } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index c0d5221911475..cfc8eb89fceb8 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -61,6 +61,8 @@ static struct { jfieldID xDpi; jfieldID yDpi; jfieldID secure; + jfieldID appVsyncOffsetNanos; + jfieldID presentationDeadlineNanos; } gPhysicalDisplayInfoClassInfo; static struct { @@ -392,6 +394,10 @@ static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz, env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.xDpi, info.xdpi); env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.yDpi, info.ydpi); env->SetBooleanField(infoObj, gPhysicalDisplayInfoClassInfo.secure, info.secure); + env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.appVsyncOffsetNanos, + info.appVsyncOffset); + env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos, + info.presentationDeadline); env->SetObjectArrayElement(configArray, static_cast(c), infoObj); env->DeleteLocalRef(infoObj); } @@ -648,6 +654,10 @@ int register_android_view_SurfaceControl(JNIEnv* env) gPhysicalDisplayInfoClassInfo.xDpi = env->GetFieldID(clazz, "xDpi", "F"); gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F"); gPhysicalDisplayInfoClassInfo.secure = env->GetFieldID(clazz, "secure", "Z"); + gPhysicalDisplayInfoClassInfo.appVsyncOffsetNanos = env->GetFieldID(clazz, + "appVsyncOffsetNanos", "J"); + gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos = env->GetFieldID(clazz, + "presentationDeadlineNanos", "J"); jclass rectClazz = env->FindClass("android/graphics/Rect"); gRectClassInfo.bottom = env->GetFieldID(rectClazz, "bottom", "I"); diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index a77443dbc1cff..c7f4f6a61e598 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -119,7 +119,7 @@ final class DisplayDeviceInfo { public int height; /** - * The refresh rate of the display. + * The refresh rate of the display, in frames per second. */ public float refreshRate; @@ -143,6 +143,20 @@ final class DisplayDeviceInfo { */ public float yDpi; + /** + * This is a positive value indicating the phase offset of the VSYNC events provided by + * Choreographer relative to the display refresh. For example, if Choreographer reports + * that the refresh occurred at time N, it actually occurred at (N - appVsyncOffsetNanos). + */ + public long appVsyncOffsetNanos; + + /** + * This is how far in advance a buffer must be queued for presentation at + * a given time. If you want a buffer to appear on the screen at + * time N, you must submit the buffer before (N - bufferDeadlineNanos). + */ + public long presentationDeadlineNanos; + /** * Display flags. */ @@ -219,6 +233,8 @@ final class DisplayDeviceInfo { && densityDpi == other.densityDpi && xDpi == other.xDpi && yDpi == other.yDpi + && appVsyncOffsetNanos == other.appVsyncOffsetNanos + && presentationDeadlineNanos == other.presentationDeadlineNanos && flags == other.flags && touch == other.touch && rotation == other.rotation @@ -242,6 +258,8 @@ final class DisplayDeviceInfo { densityDpi = other.densityDpi; xDpi = other.xDpi; yDpi = other.yDpi; + appVsyncOffsetNanos = other.appVsyncOffsetNanos; + presentationDeadlineNanos = other.presentationDeadlineNanos; flags = other.flags; touch = other.touch; rotation = other.rotation; @@ -261,6 +279,8 @@ final class DisplayDeviceInfo { sb.append(", ").append(refreshRate).append(" fps, "); sb.append("density ").append(densityDpi); sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi"); + sb.append(", appVsyncOff ").append(appVsyncOffsetNanos); + sb.append(", presDeadline ").append(presentationDeadlineNanos); sb.append(", touch ").append(touchToString(touch)); sb.append(", rotation ").append(rotation); sb.append(", type ").append(Display.typeToString(type)); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index e80aecdcfb44a..098537cd56bbe 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -161,6 +161,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.width = mPhys.width; mInfo.height = mPhys.height; mInfo.refreshRate = mPhys.refreshRate; + mInfo.appVsyncOffsetNanos = mPhys.appVsyncOffsetNanos; + mInfo.presentationDeadlineNanos = mPhys.presentationDeadlineNanos; mInfo.state = mState; // Assume that all built-in displays that have secure output (eg. HDCP) also diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index d61a35b77ffde..ed619d9c60cb3 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -213,6 +213,8 @@ final class LogicalDisplay { mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi; mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi; mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi; + mBaseDisplayInfo.appVsyncOffsetNanos = deviceInfo.appVsyncOffsetNanos; + mBaseDisplayInfo.presentationDeadlineNanos = deviceInfo.presentationDeadlineNanos; mBaseDisplayInfo.state = deviceInfo.state; mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width; mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height; diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index bfd8372c52e38..3b23b6add8a04 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -191,6 +191,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { private final int mWidth; private final int mHeight; private final float mRefreshRate; + private final long mDisplayPresentationDeadlineNanos; private final int mDensityDpi; private final boolean mSecure; @@ -200,7 +201,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { private DisplayDeviceInfo mInfo; public OverlayDisplayDevice(IBinder displayToken, String name, - int width, int height, float refreshRate, + int width, int height, float refreshRate, long presentationDeadlineNanos, int densityDpi, boolean secure, int state, SurfaceTexture surfaceTexture) { super(OverlayDisplayAdapter.this, displayToken); @@ -208,6 +209,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { mWidth = width; mHeight = height; mRefreshRate = refreshRate; + mDisplayPresentationDeadlineNanos = presentationDeadlineNanos; mDensityDpi = densityDpi; mSecure = secure; mState = state; @@ -249,6 +251,8 @@ final class OverlayDisplayAdapter extends DisplayAdapter { mInfo.densityDpi = mDensityDpi; mInfo.xDpi = mDensityDpi; mInfo.yDpi = mDensityDpi; + mInfo.presentationDeadlineNanos = mDisplayPresentationDeadlineNanos + + 1000000000L / (int) mRefreshRate; // display's deadline + 1 frame mInfo.flags = DisplayDeviceInfo.FLAG_PRESENTATION; if (mSecure) { mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE; @@ -297,12 +301,13 @@ final class OverlayDisplayAdapter extends DisplayAdapter { // Called on the UI thread. @Override - public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate, int state) { + public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate, + long presentationDeadlineNanos, int state) { synchronized (getSyncRoot()) { IBinder displayToken = SurfaceControl.createDisplay(mName, mSecure); mDevice = new OverlayDisplayDevice(displayToken, mName, - mWidth, mHeight, refreshRate, mDensityDpi, mSecure, - state, surfaceTexture); + mWidth, mHeight, refreshRate, presentationDeadlineNanos, + mDensityDpi, mSecure, state, surfaceTexture); sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED); } diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java index 06891f37a6eab..9ca5fda608b28 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java +++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java @@ -303,7 +303,7 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { mListener.onWindowCreated(surfaceTexture, mDefaultDisplayInfo.refreshRate, - mDefaultDisplayInfo.state); + mDefaultDisplayInfo.presentationDeadlineNanos, mDefaultDisplayInfo.state); } @Override @@ -373,7 +373,7 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { */ public interface Listener { public void onWindowCreated(SurfaceTexture surfaceTexture, - float refreshRate, int state); + float refreshRate, long presentationDeadlineNanos, int state); public void onWindowDestroyed(); public void onStateChanged(int state); } diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index 14ef5a9162b4d..ec14cf1e1d10d 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -171,6 +171,7 @@ final class VirtualDisplayAdapter extends DisplayAdapter { mInfo.densityDpi = mDensityDpi; mInfo.xDpi = mDensityDpi; mInfo.yDpi = mDensityDpi; + mInfo.presentationDeadlineNanos = 1000000000L / (int) mInfo.refreshRate; // 1 frame mInfo.flags = 0; if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) { mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java index cd57941bcc3ed..a05bf2c84dcd0 100644 --- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java @@ -127,7 +127,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast); pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate); pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers); - + // Try to dump the controller state. if (mDisplayController == null) { pw.println("mDisplayController=null"); @@ -729,6 +729,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { mInfo.width = mWidth; mInfo.height = mHeight; mInfo.refreshRate = mRefreshRate; + mInfo.presentationDeadlineNanos = 1000000000L / (int) mRefreshRate; // 1 frame mInfo.flags = mFlags; mInfo.type = Display.TYPE_WIFI; mInfo.address = mAddress;