diff options
author | 2016-11-16 13:24:09 -0800 | |
---|---|---|
committer | 2017-01-09 10:44:58 -0800 | |
commit | 25cfa134835e3791bdb6572f5e25cf4599015678 (patch) | |
tree | 11e3a4ef30dca52c893bde1275b639f675453131 | |
parent | 50e0a9a396ec05ed6a73d3c09f95244038f8d8e3 (diff) |
Provide non-blocking SurfaceView draw notification path.
SurfaceView needs to notify the window manager that
drawing has been completed, so that animations and such
can begin. Currently this is implemented through
having the SurfaceView user block in surfaceRedrawNeeded
(called from UI thread) until a frame has been completed.
This blocking can be unnecessary serialization during startup, and
also clumsy to implement for some users.
Test: GLSurfaceView and takeSurface API Demos, android.server.cts.SurfaceViewTests
Bug: 31850030
Change-Id: Idda02098a635f25cf392f2d59a3abbe54a1d64d4
-rw-r--r-- | api/current.txt | 3 | ||||
-rw-r--r-- | api/system-current.txt | 3 | ||||
-rw-r--r-- | api/test-current.txt | 3 | ||||
-rw-r--r-- | core/java/android/view/SurfaceHolder.java | 27 | ||||
-rw-r--r-- | core/java/android/view/SurfaceView.java | 16 | ||||
-rw-r--r-- | core/java/android/view/ViewRootImpl.java | 31 | ||||
-rw-r--r-- | core/java/com/android/internal/view/BaseSurfaceHolder.java | 2 | ||||
-rw-r--r-- | core/java/com/android/internal/view/SurfaceCallbackHelper.java | 77 | ||||
-rw-r--r-- | opengl/java/android/opengl/GLSurfaceView.java | 41 |
9 files changed, 157 insertions, 46 deletions
diff --git a/api/current.txt b/api/current.txt index ec16f5a5f56b..196b5f2483fb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -28553,7 +28553,7 @@ package android.opengl { method public void surfaceChanged(android.view.SurfaceHolder, int, int, int); method public void surfaceCreated(android.view.SurfaceHolder); method public void surfaceDestroyed(android.view.SurfaceHolder); - method public void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public deprecated void surfaceRedrawNeeded(android.view.SurfaceHolder); field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1 field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2 field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1 @@ -43035,6 +43035,7 @@ package android.view { public static abstract interface SurfaceHolder.Callback2 implements android.view.SurfaceHolder.Callback { method public abstract void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, java.lang.Runnable); } public class SurfaceView extends android.view.View { diff --git a/api/system-current.txt b/api/system-current.txt index 4bba111f9392..5a1f88019ad4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -31091,7 +31091,7 @@ package android.opengl { method public void surfaceChanged(android.view.SurfaceHolder, int, int, int); method public void surfaceCreated(android.view.SurfaceHolder); method public void surfaceDestroyed(android.view.SurfaceHolder); - method public void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public deprecated void surfaceRedrawNeeded(android.view.SurfaceHolder); field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1 field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2 field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1 @@ -46227,6 +46227,7 @@ package android.view { public static abstract interface SurfaceHolder.Callback2 implements android.view.SurfaceHolder.Callback { method public abstract void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, java.lang.Runnable); } public class SurfaceView extends android.view.View { diff --git a/api/test-current.txt b/api/test-current.txt index d4fb027bcb4d..e67f2ced13b1 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -28640,7 +28640,7 @@ package android.opengl { method public void surfaceChanged(android.view.SurfaceHolder, int, int, int); method public void surfaceCreated(android.view.SurfaceHolder); method public void surfaceDestroyed(android.view.SurfaceHolder); - method public void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public deprecated void surfaceRedrawNeeded(android.view.SurfaceHolder); field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1 field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2 field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1 @@ -43324,6 +43324,7 @@ package android.view { public static abstract interface SurfaceHolder.Callback2 implements android.view.SurfaceHolder.Callback { method public abstract void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, java.lang.Runnable); } public class SurfaceView extends android.view.View { diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java index a3e8312c0a64..2fd2e966dc38 100644 --- a/core/java/android/view/SurfaceHolder.java +++ b/core/java/android/view/SurfaceHolder.java @@ -116,9 +116,34 @@ public interface SurfaceHolder { * size before it has been correctly drawn that way). This will * typically be preceeded by a call to {@link #surfaceChanged}. * + * As of O, {@link #surfaceRedrawNeededAsync} may be implemented + * to provide a non-blocking implementation. If {@link #surfaceRedrawNeededAsync} + * is not implemented, then this will be called instead. + * * @param holder The SurfaceHolder whose surface has changed. */ - public void surfaceRedrawNeeded(SurfaceHolder holder); + void surfaceRedrawNeeded(SurfaceHolder holder); + + /** + * An alternative to surfaceRedrawNeeded where it is not required to block + * until the redraw is complete. You should initiate the redraw, and return, + * later invoking drawingFinished when your redraw is complete. + * + * This can be useful to avoid blocking your main application thread on rendering. + * + * As of O, if this is implemented {@link #surfaceRedrawNeeded} will not be called. + * However it is still recommended to implement {@link #surfaceRedrawNeeded} for + * compatibility with older versions of the platform. + * + * @param holder The SurfaceHolder which needs redrawing. + * @param drawingFinished A runnable to signal completion. This may be invoked + * from any thread. + * + */ + default void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable drawingFinished) { + surfaceRedrawNeeded(holder); + drawingFinished.run(); + } } /** diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index d46910c2139c..018be866cd8b 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -33,6 +33,7 @@ import android.util.AttributeSet; import android.util.Log; import com.android.internal.view.BaseIWindow; +import com.android.internal.view.SurfaceCallbackHelper; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -639,21 +640,13 @@ public class SurfaceView extends View { if (callbacks == null) { callbacks = getSurfaceCallbacks(); } - for (SurfaceHolder.Callback c : callbacks) { - if (c instanceof SurfaceHolder.Callback2) { - ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( - mSurfaceHolder); - } - } + SurfaceCallbackHelper sch = + new SurfaceCallbackHelper(mSession, mWindow); + sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); } } } finally { mIsCreating = false; - if (redrawNeeded) { - if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " - + "finishedDrawing"); - mSession.finishDrawing(mWindow); - } mSession.performDeferredDestroy(mWindow); } } catch (RemoteException ex) { @@ -876,7 +869,6 @@ public class SurfaceView extends View { } private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() { - private static final String LOG_TAG = "SurfaceHolder"; @Override diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4f2020311ff4..69464fc06e8f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -93,6 +93,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.policy.PhoneFallbackEventHandler; import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.RootViewSurfaceTaker; +import com.android.internal.view.SurfaceCallbackHelper; import java.io.FileDescriptor; import java.io.IOException; @@ -525,11 +526,17 @@ public final class ViewRootImpl implements ViewParent, */ public void notifyChildRebuilt() { if (mView instanceof RootViewSurfaceTaker) { + if (mSurfaceHolderCallback != null) { + mSurfaceHolder.removeCallback(mSurfaceHolderCallback); + } + mSurfaceHolderCallback = ((RootViewSurfaceTaker)mView).willYouTakeTheSurface(); + if (mSurfaceHolderCallback != null) { mSurfaceHolder = new TakenSurfaceHolder(); mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); + mSurfaceHolder.addCallback(mSurfaceHolderCallback); } else { mSurfaceHolder = null; } @@ -578,6 +585,7 @@ public final class ViewRootImpl implements ViewParent, if (mSurfaceHolderCallback != null) { mSurfaceHolder = new TakenSurfaceHolder(); mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); + mSurfaceHolder.addCallback(mSurfaceHolderCallback); } } @@ -1954,7 +1962,6 @@ public final class ViewRootImpl implements ViewParent, mSurfaceHolder.ungetCallbacks(); mIsCreating = true; - mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); if (callbacks != null) { for (SurfaceHolder.Callback c : callbacks) { @@ -1964,8 +1971,6 @@ public final class ViewRootImpl implements ViewParent, surfaceChanged = true; } if (surfaceChanged || surfaceGenerationId != mSurface.getGenerationId()) { - mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder, - lp.format, mWidth, mHeight); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); if (callbacks != null) { for (SurfaceHolder.Callback c : callbacks) { @@ -1978,7 +1983,6 @@ public final class ViewRootImpl implements ViewParent, } else if (hadSurface) { mSurfaceHolder.ungetCallbacks(); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); - mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder); if (callbacks != null) { for (SurfaceHolder.Callback c : callbacks) { c.surfaceDestroyed(mSurfaceHolder); @@ -2643,21 +2647,18 @@ public final class ViewRootImpl implements ViewParent, if (LOCAL_LOGV) { Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle()); } + if (mSurfaceHolder != null && mSurface.isValid()) { - mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder); + SurfaceCallbackHelper sch = new SurfaceCallbackHelper(mWindowSession, mWindow); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); - if (callbacks != null) { - for (SurfaceHolder.Callback c : callbacks) { - if (c instanceof SurfaceHolder.Callback2) { - ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(mSurfaceHolder); - } - } + + sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); + } else { + try { + mWindowSession.finishDrawing(mWindow); + } catch (RemoteException e) { } } - try { - mWindowSession.finishDrawing(mWindow); - } catch (RemoteException e) { - } } } diff --git a/core/java/com/android/internal/view/BaseSurfaceHolder.java b/core/java/com/android/internal/view/BaseSurfaceHolder.java index b41ef297a5be..32ce0fe1282b 100644 --- a/core/java/com/android/internal/view/BaseSurfaceHolder.java +++ b/core/java/com/android/internal/view/BaseSurfaceHolder.java @@ -86,7 +86,7 @@ public abstract class BaseSurfaceHolder implements SurfaceHolder { mCallbacks.remove(callback); } } - + public SurfaceHolder.Callback[] getCallbacks() { if (mHaveGottenCallbacks) { return mGottenCallbacks; diff --git a/core/java/com/android/internal/view/SurfaceCallbackHelper.java b/core/java/com/android/internal/view/SurfaceCallbackHelper.java new file mode 100644 index 000000000000..5b6a82cf1c43 --- /dev/null +++ b/core/java/com/android/internal/view/SurfaceCallbackHelper.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 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 com.android.internal.view; + +import android.os.RemoteException; +import android.view.IWindow; +import android.view.IWindowSession; +import android.view.Surface; +import android.view.SurfaceHolder; + +public class SurfaceCallbackHelper { + IWindowSession mSession; + IWindow.Stub mWindow; + + int mFinishDrawingCollected = 0; + int mFinishDrawingExpected = 0; + + private Runnable mFinishDrawingRunnable = new Runnable() { + @Override + public void run() { + synchronized (SurfaceCallbackHelper.this) { + mFinishDrawingCollected++; + if (mFinishDrawingCollected < mFinishDrawingExpected) { + return; + } + try { + mSession.finishDrawing(mWindow); + } catch (RemoteException e) { + } + } + } + }; + + public SurfaceCallbackHelper(IWindowSession session, + IWindow.Stub window) { + mSession = session; + mWindow = window; + } + + public void dispatchSurfaceRedrawNeededAsync(SurfaceHolder holder, SurfaceHolder.Callback callbacks[]) { + if (callbacks == null || callbacks.length == 0) { + try { + mSession.finishDrawing(mWindow); + } catch (RemoteException e) { + } + return; + } + + synchronized (this) { + mFinishDrawingExpected = callbacks.length; + mFinishDrawingCollected = 0; + } + + for (SurfaceHolder.Callback c : callbacks) { + if (c instanceof SurfaceHolder.Callback2) { + ((SurfaceHolder.Callback2) c).surfaceRedrawNeededAsync( + holder, mFinishDrawingRunnable); + } else { + mFinishDrawingRunnable.run(); + } + } + } +}
\ No newline at end of file diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index 4154ef02772b..329514c0b7fc 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -542,16 +542,27 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * This method is part of the SurfaceHolder.Callback interface, and is + * This method is part of the SurfaceHolder.Callback2 interface, and is * not normally called or subclassed by clients of GLSurfaceView. */ @Override - public void surfaceRedrawNeeded(SurfaceHolder holder) { + public void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable finishDrawing) { if (mGLThread != null) { - mGLThread.requestRenderAndWait(); + mGLThread.requestRenderAndNotify(finishDrawing); } } + /** + * This method is part of the SurfaceHolder.Callback2 interface, and is + * not normally called or subclassed by clients of GLSurfaceView. + */ + @Deprecated + @Override + public void surfaceRedrawNeeded(SurfaceHolder holder) { + // Since we are part of the framework we know only surfaceRedrawNeededAsync + // will be called. + } + /** * Pause the rendering thread, optionally tearing down the EGL context @@ -1305,6 +1316,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback int w = 0; int h = 0; Runnable event = null; + Runnable finishDrawingRunnable = null; while (true) { synchronized (sGLThreadManager) { @@ -1400,6 +1412,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback sGLThreadManager.notifyAll(); } + if (mFinishDrawingRunnable != null) { + finishDrawingRunnable = mFinishDrawingRunnable; + mFinishDrawingRunnable = null; + } + // Ready to draw? if (readyToDraw()) { @@ -1453,7 +1470,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback break; } } - // By design, this is the only place in a GLThread thread where we wait(). if (LOG_THREADS) { Log.i("GLThread", "waiting tid=" + getId() @@ -1546,6 +1562,10 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame"); view.mRenderer.onDrawFrame(gl); + if (finishDrawingRunnable != null) { + finishDrawingRunnable.run(); + finishDrawingRunnable = null; + } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } @@ -1625,7 +1645,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } } - public void requestRenderAndWait() { + public void requestRenderAndNotify(Runnable finishDrawing) { synchronized(sGLThreadManager) { // If we are already on the GL thread, this means a client callback // has caused reentrancy, for example via updating the SurfaceView parameters. @@ -1638,17 +1658,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback mWantRenderNotification = true; mRequestRender = true; mRenderComplete = false; + mFinishDrawingRunnable = finishDrawing; sGLThreadManager.notifyAll(); - - while (!mExited && !mPaused && !mRenderComplete && ableToDraw()) { - try { - sGLThreadManager.wait(); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } } @@ -1821,6 +1833,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private boolean mRenderComplete; private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>(); private boolean mSizeChanged = true; + private Runnable mFinishDrawingRunnable = null; // End of member variables protected by the sGLThreadManager monitor. |