From 1f19e1141e3f40f2f5af0c6b750bde7f13047eaf Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Mon, 20 Sep 2021 08:52:50 -0700 Subject: [PATCH] Expose getSurfaceTransformHint API The transform hint can be used by a buffer producer to pre-rotate the rendering such that the final transformation in the system composer is identity. This can be very useful when used in conjunction with the h/w composer HAL in situations where it cannot handle rotations or handle them with an additional power cost. Bug: 196167822 Test: atest AttachedSurfaceControlTest Change-Id: Idb7fcf58ecbcc22cd43c226ee4faad958c265e72 --- core/api/current.txt | 7 ++ .../android/view/AttachedSurfaceControl.java | 68 +++++++++++++++++++ core/java/android/view/ViewRootImpl.java | 42 +++++++++++- 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/core/api/current.txt b/core/api/current.txt index ec8dce0837cd4..3a499f1bd8c4e 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -46890,8 +46890,15 @@ package android.view { } @UiThread public interface AttachedSurfaceControl { + method public default void addOnSurfaceTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnSurfaceTransformHintChangedListener); method public boolean applyTransactionOnDraw(@NonNull android.view.SurfaceControl.Transaction); method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl); + method public default int getSurfaceTransformHint(); + method public default void removeOnSurfaceTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnSurfaceTransformHintChangedListener); + } + + @UiThread public static interface AttachedSurfaceControl.OnSurfaceTransformHintChangedListener { + method public void onSurfaceTransformHintChanged(int); } public final class Choreographer { diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java index bcc5b56459bbd..b2fc9a0c85fc5 100644 --- a/core/java/android/view/AttachedSurfaceControl.java +++ b/core/java/android/view/AttachedSurfaceControl.java @@ -53,4 +53,72 @@ public interface AttachedSurfaceControl { * to the View hierarchy you may need to call {@link android.view.View#invalidate} */ boolean applyTransactionOnDraw(@NonNull SurfaceControl.Transaction t); + + /** + * The transform hint can be used by a buffer producer to pre-rotate the rendering such that the + * final transformation in the system composer is identity. This can be very useful when used in + * conjunction with the h/w composer HAL in situations where it cannot handle rotations or + * handle them with an additional power cost. + * + * The transform hint should be used with ASurfaceControl APIs when submitting buffers. + * Example usage: + * + * 1. After a configuration change, before dequeuing a buffer, the buffer producer queries the + * function for the transform hint. + * + * 2. The desired buffer width and height is rotated by the transform hint. + * + * 3. The producer dequeues a buffer of the new pre-rotated size. + * + * 4. The producer renders to the buffer such that the image is already transformed, that is + * applying the transform hint to the rendering. + * + * 5. The producer applies the inverse transform hint to the buffer it just rendered. + * + * 6. The producer queues the pre-transformed buffer with the buffer transform. + * + * 7. The composer combines the buffer transform with the display transform. If the buffer + * transform happens to cancel out the display transform then no rotation is needed and there + * will be no performance penalties. + * + * Note, when using ANativeWindow APIs in conjunction with a NativeActivity Surface or + * SurfaceView Surface, the buffer producer will already have access to the transform hint and + * no additional work is needed. + */ + default @Surface.Rotation int getSurfaceTransformHint() { + return Surface.ROTATION_0; + } + + /** + * Surface transform hint change listener. + * @see #getSurfaceTransformHint + */ + @UiThread + interface OnSurfaceTransformHintChangedListener { + /** + * @param hint new surface transform hint + * @see #getSurfaceTransformHint + */ + void onSurfaceTransformHintChanged(@Surface.Rotation int hint); + } + + /** + * Registers a surface transform hint changed listener to receive notifications about when + * the transform hint changes. + * + * @see #getSurfaceTransformHint + * @see #removeOnSurfaceTransformHintChangedListener + */ + default void addOnSurfaceTransformHintChangedListener( + @NonNull OnSurfaceTransformHintChangedListener listener) { + } + + /** + * Unregisters a surface transform hint changed listener. + * + * @see #addOnSurfaceTransformHintChangedListener + */ + default void removeOnSurfaceTransformHintChangedListener( + @NonNull OnSurfaceTransformHintChangedListener listener) { + } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4c36fc939c34c..5fb675885da9d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -312,6 +312,9 @@ public final class ViewRootImpl implements ViewParent, static final ArrayList sFirstDrawHandlers = new ArrayList<>(); static boolean sFirstDrawComplete = false; + private ArrayList mTransformHintListeners = + new ArrayList<>(); + private @Surface.Rotation int mPreviousTransformHint = Surface.ROTATION_0; /** * Callback for notifying about global configuration changes. */ @@ -7824,6 +7827,11 @@ public final class ViewRootImpl implements ViewParent, } mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl); } + int transformHint = mSurfaceControl.getTransformHint(); + if (mPreviousTransformHint != transformHint) { + mPreviousTransformHint = transformHint; + dispatchTransformHintChanged(transformHint); + } } else { destroySurface(); } @@ -10447,7 +10455,39 @@ public final class ViewRootImpl implements ViewParent, return true; } - int getSurfaceTransformHint() { + @Override + public @Surface.Rotation int getSurfaceTransformHint() { return mSurfaceControl.getTransformHint(); } + + @Override + public void addOnSurfaceTransformHintChangedListener( + OnSurfaceTransformHintChangedListener listener) { + Objects.requireNonNull(listener); + if (mTransformHintListeners.contains(listener)) { + throw new IllegalArgumentException( + "attempt to call addOnSurfaceTransformHintChangedListener() " + + "with a previously registered listener"); + } + mTransformHintListeners.add(listener); + } + + @Override + public void removeOnSurfaceTransformHintChangedListener( + OnSurfaceTransformHintChangedListener listener) { + Objects.requireNonNull(listener); + mTransformHintListeners.remove(listener); + } + + private void dispatchTransformHintChanged(@Surface.Rotation int hint) { + if (mTransformHintListeners.isEmpty()) { + return; + } + ArrayList listeners = + (ArrayList) mTransformHintListeners.clone(); + for (int i = 0; i < listeners.size(); i++) { + OnSurfaceTransformHintChangedListener listener = listeners.get(i); + listener.onSurfaceTransformHintChanged(hint); + } + } }