From 8dbd9ad68248a921637c4b2b866e86854b5860f6 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Fri, 9 Oct 2015 10:06:11 -0700 Subject: [PATCH] Fix missing frame or wrong frame position when resize starts When multi-thread renderer is used, delay the report to draw to the first doFrame in ResizeFrameThread. Otherwise we could unfreeze the window before the frame is drawn. Also when content draw bounds is updated for the first frame, let content draw before ResizeFrameThread so that the bounds get applied. bug: 24715185 Change-Id: I5485dc0be3eae24c555bcc31ee8f71523b68ca8d --- core/java/android/view/ViewRootImpl.java | 55 +++++++++++++++++-- core/java/android/view/WindowCallbacks.java | 11 +++- .../internal/widget/NonClientDecorView.java | 53 +++++++++++++++--- 3 files changed, 103 insertions(+), 16 deletions(-) diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 14e7d6c4e81e6..3d10289ac8e9b 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -88,6 +88,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; import java.util.HashSet; /** @@ -238,6 +239,7 @@ public final class ViewRootImpl implements ViewParent, boolean mNewSurfaceNeeded; boolean mHasHadWindowFocus; boolean mLastWasImTarget; + CountDownLatch mWindowDrawCountDown; boolean mIsDrawing; int mLastSystemUiVisibility; @@ -436,6 +438,12 @@ public final class ViewRootImpl implements ViewParent, } } + public void reportDrawFinish() { + if (mWindowDrawCountDown != null) { + mWindowDrawCountDown.countDown(); + } + } + // FIXME for perf testing only private boolean mProfile = false; @@ -2419,6 +2427,17 @@ public final class ViewRootImpl implements ViewParent, if (mReportNextDraw) { mReportNextDraw = false; + + // if we're using multi-thread renderer, wait for the window frame draws + if (mWindowDrawCountDown != null) { + try { + mWindowDrawCountDown.await(); + } catch (InterruptedException e) { + Log.e(TAG, "Window redraw count down interruped!"); + } + mWindowDrawCountDown = null; + } + if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.fence(); } @@ -2568,13 +2587,13 @@ public final class ViewRootImpl implements ViewParent, // Stage the content drawn size now. It will be transferred to the renderer // shortly before the draw commands get send to the renderer. - synchronized (mWindowCallbacks) { - for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - mWindowCallbacks.get(i).onContentDraw(mWindowAttributes.surfaceInsets.left, - mWindowAttributes.surfaceInsets.top, mWidth, mHeight); - } - } + final boolean updated = updateContentDrawBounds(); + mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this); + + if (updated) { + requestDrawWindow(); + } } else { // If we get here with a disabled & requested hardware renderer, something went // wrong (an invalidate posted right before we destroyed the hardware surface @@ -6796,6 +6815,30 @@ public final class ViewRootImpl implements ViewParent, } } + private boolean updateContentDrawBounds() { + boolean updated = false; + synchronized (mWindowCallbacks) { + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + updated |= mWindowCallbacks.get(i).onContentDrawn( + mWindowAttributes.surfaceInsets.left, + mWindowAttributes.surfaceInsets.top, + mWidth, mHeight); + } + } + return updated | (mDragResizing && mReportNextDraw); + } + + private void requestDrawWindow() { + if (mReportNextDraw) { + mWindowDrawCountDown = new CountDownLatch(mWindowCallbacks.size()); + } + synchronized (mWindowCallbacks) { + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onRequestDraw(mReportNextDraw); + } + } + } + /** * Class for managing the accessibility interaction connection * based on the global accessibility state. diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java index 1aeaec8c6d152..def02365dfed5 100644 --- a/core/java/android/view/WindowCallbacks.java +++ b/core/java/android/view/WindowCallbacks.java @@ -49,7 +49,14 @@ public interface WindowCallbacks { void onWindowDragResizeEnd(); /** - * The content will now be drawn to these bounds. + * The content will now be drawn to these bounds. Returns true if + * a draw should be requested after the next content draw. */ - void onContentDraw(int offsetX, int offsetY, int sizeX, int sizeY); + boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY); + + /** + * Called to request the window to draw one frame. + * @param reportNextDraw Whether it should report when the requested draw finishes. + */ + void onRequestDraw(boolean reportNextDraw); } diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java index 6557ed159a0b6..ed7492aae47be 100644 --- a/core/java/com/android/internal/widget/NonClientDecorView.java +++ b/core/java/com/android/internal/widget/NonClientDecorView.java @@ -355,9 +355,22 @@ public class NonClientDecorView extends LinearLayout } @Override - public void onContentDraw(int xOffset, int yOffset, int xSize, int ySize) { + public boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) { + if (mFrameRendererThread == null) { + return false; + } + return mFrameRendererThread.onContentDrawn(xOffset, yOffset, xSize, ySize); + } + + @Override + public void onRequestDraw(boolean reportNextDraw) { if (mFrameRendererThread != null) { - mFrameRendererThread.onContentDraw(xOffset, yOffset, xSize, ySize); + mFrameRendererThread.onRequsetDraw(reportNextDraw); + } else if (reportNextDraw) { + // If render thread is gone, just report immediately. + if (isAttachedToWindow()) { + getViewRootImpl().reportDrawFinish(); + } } } @@ -423,6 +436,9 @@ public class NonClientDecorView extends LinearLayout private int mLastXOffset; private int mLastYOffset; + // Whether to report when next frame is drawn or not. + private boolean mReportNextDraw; + ResizeFrameThread(ThreadedRenderer renderer, Rect initialBounds) { setName("ResizeFrame"); mRenderer = renderer; @@ -518,12 +534,13 @@ public class NonClientDecorView extends LinearLayout public void doFrame(long frameTimeNanos) { synchronized (this) { if (mRenderer == null) { + reportDrawIfNeeded(); // Tell the looper to stop. We are done. Looper.myLooper().quit(); return; } mNewTargetRect.set(mTargetRect); - if (!mNewTargetRect.equals(mOldTargetRect)) { + if (!mNewTargetRect.equals(mOldTargetRect) || mReportNextDraw) { mOldTargetRect.set(mNewTargetRect); changeWindowSizeLocked(mNewTargetRect); } @@ -538,8 +555,9 @@ public class NonClientDecorView extends LinearLayout * @param yOffset The y offset where the content is drawn to. * @param xSize The width size of the content. This should not be 0. * @param ySize The height of the content. + * @return true if a frame should be requested after the content is drawn; false otherwise. */ - public void onContentDraw(int xOffset, int yOffset, int xSize, int ySize) { + public boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) { synchronized (this) { final boolean firstCall = mLastContentWidth == 0; // The current content buffer is drawn here. @@ -555,10 +573,15 @@ public class NonClientDecorView extends LinearLayout mLastYOffset + mLastCaptionHeight + mLastContentHeight); // If this was the first call and changeWindowSizeLocked got already called prior // to us, we should re-issue a changeWindowSizeLocked now. - if (firstCall && (mLastCaptionHeight != 0 || !mShowDecor)) { - mOldTargetRect.set(0, 0, 0, 0); - pingRenderLocked(); - } + return firstCall && (mLastCaptionHeight != 0 || !mShowDecor); + } + } + + public void onRequsetDraw(boolean reportNextDraw) { + synchronized (this) { + mReportNextDraw = reportNextDraw; + mOldTargetRect.set(0, 0, 0, 0); + pingRenderLocked(); } } @@ -617,6 +640,20 @@ public class NonClientDecorView extends LinearLayout // We need to render both rendered nodes explicitly. mRenderer.drawRenderNode(mFrameNode); mRenderer.drawRenderNode(mBackdropNode); + + reportDrawIfNeeded(); + } + + /** + * Notify view root that a frame has been drawn by us, if it has requested so. + */ + private void reportDrawIfNeeded() { + if (mReportNextDraw) { + if (isAttachedToWindow()) { + getViewRootImpl().reportDrawFinish(); + } + mReportNextDraw = false; + } } /**