diff options
author | Chet Haase <chet@google.com> | 2011-01-10 14:10:36 -0800 |
---|---|---|
committer | Chet Haase <chet@google.com> | 2011-01-24 08:43:20 -0800 |
commit | daf98e941e140e8739458126640183b9f296a2ab (patch) | |
tree | e338ad021139d706004b70a38fbbe539ccfbbacf /core | |
parent | 57ffc00239edcfe733832771e1429fca20182207 (diff) | |
download | frameworks_base-daf98e941e140e8739458126640183b9f296a2ab.zip frameworks_base-daf98e941e140e8739458126640183b9f296a2ab.tar.gz frameworks_base-daf98e941e140e8739458126640183b9f296a2ab.tar.bz2 |
Use optimized display lists for all hwaccelerated rendering
Previously, display lists were used only if hardware acceleration
was enabled for an application (hardwareAccelerated=true) *and* if
setDrawingCacheEnabled(true) was called. This change makes the framework
use display lists for all views in an application if hardware acceleration
is enabled.
In addition, display list renderering has been optimized so that
any view's recreation of its own display list (which is necessary whenever
the visuals of that view change) will not cause any other display list
in its parent hierarchy to change. Instead, when there are any visual
changes in the hierarchy, only those views which need to have new
display list content will recreate their display lists.
This optimization works by caching display list references in each
parent display list (so the container of some child will refer to its
child's display list by a reference to the child's display list). Then when
a view needs to recreate its display list, it will do so inside the same
display list object. This will cause the content to get refreshed, but not
the reference to that content. Then when the view hierarchy is redrawn,
it will automatically pick up the new content from the old reference.
This optimization will not necessarily improve performance when applications
need to update the entire view hierarchy or redraw the entire screen, but it does
show significant improvements when redrawing only a portion of the screen,
especially when the regions that are not refreshed are complex and time-
consuming to redraw.
Change-Id: I68d21cac6a224a05703070ec85253220cb001eb4
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/animation/LayoutTransition.java | 19 | ||||
-rw-r--r-- | core/java/android/view/DisplayList.java | 6 | ||||
-rw-r--r-- | core/java/android/view/GLES20Canvas.java | 15 | ||||
-rw-r--r-- | core/java/android/view/GLES20DisplayList.java | 14 | ||||
-rw-r--r-- | core/java/android/view/HardwareCanvas.java | 23 | ||||
-rw-r--r-- | core/java/android/view/HardwareRenderer.java | 29 | ||||
-rw-r--r-- | core/java/android/view/View.java | 157 | ||||
-rw-r--r-- | core/java/android/view/ViewDebug.java | 17 | ||||
-rw-r--r-- | core/java/android/view/ViewGroup.java | 66 | ||||
-rw-r--r-- | core/java/android/view/ViewRoot.java | 2 | ||||
-rw-r--r-- | core/java/android/webkit/WebView.java | 34 | ||||
-rw-r--r-- | core/java/android/widget/AbsListView.java | 4 | ||||
-rw-r--r-- | core/java/android/widget/HorizontalScrollView.java | 1 | ||||
-rw-r--r-- | core/java/android/widget/ScrollView.java | 1 | ||||
-rw-r--r-- | core/java/android/widget/TextView.java | 1 | ||||
-rw-r--r-- | core/jni/android_view_GLES20Canvas.cpp | 13 |
16 files changed, 320 insertions, 82 deletions
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index e405df5..f13d940 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -34,15 +34,16 @@ import java.util.List; * custom animations, use the {@link LayoutTransition#setAnimator(int, Animator) * setAnimator()} method. * - * <p>One of the core concepts of these transition animations is that there are two core + * <p>One of the core concepts of these transition animations is that there are two types of * changes that cause the transition and four different animations that run because of * those changes. The changes that trigger the transition are items being added to a container * (referred to as an "appearing" transition) or removed from a container (also known as - * "disappearing"). The animations that run due to those events are one that animates + * "disappearing"). Setting the visibility of views (between GONE and VISIBLE) will trigger + * the same add/remove logic. The animations that run due to those events are one that animates * items being added, one that animates items being removed, and two that animate the other * items in the container that change due to the add/remove occurrence. Users of * the transition may want different animations for the changing items depending on whether - * they are changing due to anappearing or disappearing event, so there is one animation for + * they are changing due to an appearing or disappearing event, so there is one animation for * each of these variations of the changing event. Most of the API of this class is concerned * with setting up the basic properties of the animations used in these four situations, * or with setting up custom animations for any or all of the four.</p> @@ -62,6 +63,18 @@ import java.util.List; * values when the transition begins. Custom animations will be similarly populated with * the target and values being animated, assuming they use ObjectAnimator objects with * property names that are known on the target object.</p> + * + * <p>This class, and the associated XML flag for containers, animateLayoutChanges="true", + * provides a simple utility meant for automating changes in straightforward situations. + * Using LayoutTransition at multiple levels of a nested view hierarchy may not work due to the + * interrelationship of the various levels of layout. Also, a container that is being scrolled + * at the same time as items are being added or removed is probably not a good candidate for + * this utility, because the before/after locations calculated by LayoutTransition + * may not match the actual locations when the animations finish due to the container + * being scrolled as the animations are running. You can work around that + * particular issue by disabling the 'changing' animations by setting the CHANGE_APPEARING + * and CHANGE_DISAPPEARING animations to null, and setting the startDelay of the + * other animations appropriately.</p> */ public class LayoutTransition { diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java index 959fae4..4484d59 100644 --- a/core/java/android/view/DisplayList.java +++ b/core/java/android/view/DisplayList.java @@ -21,9 +21,11 @@ package android.view; * them later. Display lists are usually built by recording operations on a * {@link android.graphics.Canvas}. Replaying the operations from a display list * avoids executing views drawing code on every frame, and is thus much more - * efficient. + * efficient. + * + * @hide */ -abstract class DisplayList { +public abstract class DisplayList { /** * Starts recording the display list. All operations performed on the * returned canvas are recorded and stored in this display list. diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index f6c5e0b..dce1a6c 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -220,6 +220,13 @@ class GLES20Canvas extends HardwareCanvas { private native void nAcquireContext(int renderer); @Override + public boolean callDrawGLFunction(int drawGLFunction) { + return nCallDrawGLFunction(mRenderer, drawGLFunction); + } + + private native boolean nCallDrawGLFunction(int renderer, int drawGLFunction); + + @Override public void releaseContext() { if (mContextLocked) { nReleaseContext(mRenderer); @@ -246,11 +253,11 @@ class GLES20Canvas extends HardwareCanvas { private static native void nDestroyDisplayList(int displayList); @Override - public void drawDisplayList(DisplayList displayList) { - nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).mNativeDisplayList); + public boolean drawDisplayList(DisplayList displayList) { + return nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).mNativeDisplayList); } - private native void nDrawDisplayList(int renderer, int displayList); + private native boolean nDrawDisplayList(int renderer, int displayList); /////////////////////////////////////////////////////////////////////////// // Hardware layer @@ -306,7 +313,7 @@ class GLES20Canvas extends HardwareCanvas { @Override public boolean clipRect(int left, int top, int right, int bottom) { - return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); + return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); } private native boolean nClipRect(int renderer, int left, int top, int right, int bottom, int op); diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java index e813bc9..262eb81 100644 --- a/core/java/android/view/GLES20DisplayList.java +++ b/core/java/android/view/GLES20DisplayList.java @@ -16,6 +16,8 @@ package android.view; +import java.lang.ref.WeakReference; + /** * An implementation of display list for OpenGL ES 2.0. */ @@ -27,12 +29,24 @@ class GLES20DisplayList extends DisplayList { private boolean mValid = false; int mNativeDisplayList; + WeakReference<View> hostView; // The native display list will be destroyed when this object dies. // DO NOT overwrite this reference once it is set. @SuppressWarnings("unused") private DisplayListFinalizer mFinalizer; + public GLES20DisplayList(View view) { + hostView = new WeakReference<View>(view); + } + + public void invalidateView() { + View v = hostView.get(); + if (v != null) { + v.invalidate(); + } + } + @Override HardwareCanvas start() { if (mStarted) { diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java index 2273238..a4d36b7 100644 --- a/core/java/android/view/HardwareCanvas.java +++ b/core/java/android/view/HardwareCanvas.java @@ -21,9 +21,11 @@ import android.graphics.Canvas; import android.graphics.Paint; /** - * Hardware accelerated canvas. + * Hardware accelerated canvas. + * + * @hide */ -abstract class HardwareCanvas extends Canvas { +public abstract class HardwareCanvas extends Canvas { @Override public boolean isHardwareAccelerated() { return true; @@ -49,7 +51,7 @@ abstract class HardwareCanvas extends Canvas { * * @param displayList The display list to replay. */ - abstract void drawDisplayList(DisplayList displayList); + abstract boolean drawDisplayList(DisplayList displayList); /** * Draws the specified layer onto this canvas. @@ -59,5 +61,18 @@ abstract class HardwareCanvas extends Canvas { * @param y The top coordinate of the layer * @param paint The paint used to draw the layer */ - abstract void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint); + abstract void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint); + + /** + * Calls the function specified with the drawGLFunction function pointer. This is + * functionality used by webkit for calling into their renderer from our display lists. + * This function may return true if an invalidation is needed after the call. + * + * @param drawGLFunction A native function pointer + * @return true if an invalidate is needed after the call, false otherwise + */ + public boolean callDrawGLFunction(int drawGLFunction) { + // Noop - this is done in the display list recorder subclass + return false; + } } diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index addd1b3..c82184a 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -120,7 +120,7 @@ public abstract class HardwareRenderer { * * @return A new display list. */ - abstract DisplayList createDisplayList(); + abstract DisplayList createDisplayList(View v); /** * Creates a new hardware layer. @@ -506,19 +506,32 @@ public abstract class HardwareRenderer { if (checkCurrent()) { onPreDraw(); - Canvas canvas = mCanvas; + HardwareCanvas canvas = mCanvas; + attachInfo.mHardwareCanvas = canvas; int saveCount = canvas.save(); callbacks.onHardwarePreDraw(canvas); - + try { - view.draw(canvas); + view.mRecreateDisplayList = + (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED; + view.mPrivateFlags &= ~View.INVALIDATED; + DisplayList displayList = view.getDisplayList(); + if (displayList != null) { + if (canvas.drawDisplayList(displayList)) { + view.invalidate(); + } + } else { + // Shouldn't reach here + view.draw(canvas); + } } finally { callbacks.onHardwarePostDraw(canvas); canvas.restoreToCount(saveCount); + view.mRecreateDisplayList = false; } - + onPostDraw(); - + if (ViewDebug.DEBUG_PROFILE_DRAWING) { EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime); } @@ -704,8 +717,8 @@ public abstract class HardwareRenderer { } @Override - DisplayList createDisplayList() { - return new GLES20DisplayList(); + DisplayList createDisplayList(View v) { + return new GLES20DisplayList(v); } @Override diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6d5fd2c..5c6dd29 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1509,6 +1509,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /*package*/ int mMeasuredHeight; /** + * Flag to indicate that this view was marked INVALIDATED, or had its display list + * invalidated, prior to the current drawing iteration. If true, the view must re-draw + * its display list. This flag, used only when hw accelerated, allows us to clear the + * flag while retaining this information until it's needed (at getDisplayList() time and + * in drawChild(), when we decide to draw a view's children's display lists into our own). + * + * {@hide} + */ + boolean mRecreateDisplayList = false; + + /** * The view's identifier. * {@hide} * @@ -1672,6 +1683,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility static final int ACTIVATED = 0x40000000; /** + * Indicates that this view was specifically invalidated, not just dirtied because some + * child view was invalidated. The flag is used to determine when we need to recreate + * a view's display list (as opposed to just returning a reference to its existing + * display list). + * + * @hide + */ + static final int INVALIDATED = 0x80000000; + + /** * Always allow a user to over-scroll this view, provided it is a * view that can scroll. * @@ -5295,6 +5316,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if ((changed & VISIBILITY_MASK) != 0) { if (mParent instanceof ViewGroup) { ((ViewGroup)mParent).onChildVisibilityChanged(this, (flags & VISIBILITY_MASK)); + ((View) mParent).invalidate(); } dispatchVisibilityChanged(this, (flags & VISIBILITY_MASK)); } @@ -5306,6 +5328,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if ((changed & DRAWING_CACHE_ENABLED) != 0) { destroyDrawingCache(); mPrivateFlags &= ~DRAWING_CACHE_VALID; + invalidateParentIfAccelerated(); } if ((changed & DRAWING_CACHE_QUALITY_MASK) != 0) { @@ -5666,6 +5689,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mMatrixDirty = true; mPrivateFlags |= DRAWN; // force another invalidation with the new orientation invalidate(false); + invalidateParentIfAccelerated(); } } @@ -5699,6 +5723,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mMatrixDirty = true; mPrivateFlags |= DRAWN; // force another invalidation with the new orientation invalidate(false); + invalidateParentIfAccelerated(); } } @@ -5732,6 +5757,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mMatrixDirty = true; mPrivateFlags |= DRAWN; // force another invalidation with the new orientation invalidate(false); + invalidateParentIfAccelerated(); } } @@ -5767,6 +5793,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mMatrixDirty = true; mPrivateFlags |= DRAWN; // force another invalidation with the new orientation invalidate(false); + invalidateParentIfAccelerated(); } } @@ -5802,6 +5829,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mMatrixDirty = true; mPrivateFlags |= DRAWN; // force another invalidation with the new orientation invalidate(false); + invalidateParentIfAccelerated(); } } @@ -5843,6 +5871,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mMatrixDirty = true; mPrivateFlags |= DRAWN; // force another invalidation with the new orientation invalidate(false); + invalidateParentIfAccelerated(); } } @@ -5883,6 +5912,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mMatrixDirty = true; mPrivateFlags |= DRAWN; // force another invalidation with the new orientation invalidate(false); + invalidateParentIfAccelerated(); } } @@ -5922,6 +5952,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mPrivateFlags &= ~ALPHA_SET; invalidate(false); } + invalidateParentIfAccelerated(); } /** @@ -6241,6 +6272,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mMatrixDirty = true; mPrivateFlags |= DRAWN; // force another invalidation with the new orientation invalidate(false); + invalidateParentIfAccelerated(); } } @@ -6274,6 +6306,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mMatrixDirty = true; mPrivateFlags |= DRAWN; // force another invalidation with the new orientation invalidate(false); + invalidateParentIfAccelerated(); } } @@ -6490,6 +6523,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility int oldY = mScrollY; mScrollX = x; mScrollY = y; + invalidateParentIfAccelerated(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (!awakenScrollBars()) { invalidate(); @@ -6690,8 +6724,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) || - (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) { + (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID || + (mPrivateFlags & INVALIDATED) != INVALIDATED) { mPrivateFlags &= ~DRAWING_CACHE_VALID; + mPrivateFlags |= INVALIDATED; final ViewParent p = mParent; final AttachInfo ai = mAttachInfo; if (p != null && ai != null && ai.mHardwareAccelerated) { @@ -6728,8 +6764,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) || - (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) { + (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID || + (mPrivateFlags & INVALIDATED) != INVALIDATED) { mPrivateFlags &= ~DRAWING_CACHE_VALID; + mPrivateFlags |= INVALIDATED; final ViewParent p = mParent; final AttachInfo ai = mAttachInfo; if (p != null && ai != null && ai.mHardwareAccelerated) { @@ -6776,10 +6814,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility boolean opaque = isOpaque(); if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) || - opaque != mLastIsOpaque) { + opaque != mLastIsOpaque || (mPrivateFlags & INVALIDATED) != INVALIDATED) { mLastIsOpaque = opaque; mPrivateFlags &= ~DRAWN; if (invalidateCache) { + mPrivateFlags |= INVALIDATED; mPrivateFlags &= ~DRAWING_CACHE_VALID; } final AttachInfo ai = mAttachInfo; @@ -6802,6 +6841,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Used to indicate that the parent of this view should be invalidated. This functionality + * is used to force the parent to rebuild its display list (when hardware-accelerated), + * which is necessary when various parent-managed properties of the view change, such as + * alpha, translationX/Y, scrollX/Y, scaleX/Y, and rotation/X/Y. + * + * @hide + */ + protected void invalidateParentIfAccelerated() { + if (isHardwareAccelerated() && mParent instanceof View) { + ((View) mParent).invalidate(); + } + } + + /** * Indicates whether this View is opaque. An opaque View guarantees that it will * draw all the pixels overlapping its bounds using a fully opaque color. * @@ -7630,6 +7683,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mHardwareLayer = null; } + if (mDisplayList != null) { + mDisplayList.invalidate(); + } + if (mAttachInfo != null) { mAttachInfo.mHandler.removeMessages(AttachInfo.INVALIDATE_MSG, this); mAttachInfo.mHandler.removeMessages(AttachInfo.INVALIDATE_RECT_MSG, this); @@ -7953,7 +8010,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, " + "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE"); } - + if (layerType == mLayerType) { if (layerType != LAYER_TYPE_NONE && paint != mLayerPaint) { mLayerPaint = paint == null ? new Paint() : paint; @@ -8041,7 +8098,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mHardwareLayer.resize(width, height); } - final HardwareCanvas canvas = mHardwareLayer.start(currentCanvas); + final HardwareCanvas canvas = mHardwareLayer.start(mAttachInfo.mHardwareCanvas); try { canvas.setViewport(width, height); canvas.onPreDraw(); @@ -8064,7 +8121,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility canvas.restoreToCount(restoreCount); } finally { canvas.onPostDraw(); - mHardwareLayer.end(currentCanvas); + mHardwareLayer.end(mAttachInfo.mHardwareCanvas); } } @@ -8081,9 +8138,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * * <p>Enabling the drawing cache is similar to * {@link #setLayerType(int, android.graphics.Paint) setting a layer} when hardware - * acceleration is turned off. When hardware acceleration is turned on enabling the - * drawing cache has either no effect or the cache used at drawing time is not a bitmap. - * This API can however be used to manually generate a bitmap copy of this view.</p> + * acceleration is turned off. When hardware acceleration is turned on, enabling the + * drawing cache has no effect on rendering because the system uses a different mechanism + * for acceleration which ignores the flag. If you want to use a Bitmap for the view, even + * when hardware acceleration is enabled, see {@link #setLayerType(int, android.graphics.Paint)} + * for information on how to enable software and hardware layers.</p> + * + * <p>This API can be used to manually generate + * a bitmap copy of this view, by setting the flag to <code>true</code> and calling + * {@link #getDrawingCache()}.</p> * * @param enabled true to enable the drawing cache, false otherwise * @@ -8110,25 +8173,76 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Debugging utility which recursively outputs the dirty state of a view and its + * descendants. + * + * @hide + */ + public void outputDirtyFlags(String indent, boolean clear, int clearMask) { + Log.d("View", indent + this + " DIRTY(" + (mPrivateFlags & View.DIRTY_MASK) + + ") DRAWN(" + (mPrivateFlags & DRAWN) + ")" + " CACHE_VALID(" + + (mPrivateFlags & View.DRAWING_CACHE_VALID) + + ") INVALIDATED(" + (mPrivateFlags & INVALIDATED) + ")"); + if (clear) { + mPrivateFlags &= clearMask; + } + if (this instanceof ViewGroup) { + ViewGroup parent = (ViewGroup) this; + final int count = parent.getChildCount(); + for (int i = 0; i < count; i++) { + final View child = (View) parent.getChildAt(i); + child.outputDirtyFlags(indent + " ", clear, clearMask); + } + } + } + + /** + * This method is used by ViewGroup to cause its children to restore or recreate their + * display lists. It is called by getDisplayList() when the parent ViewGroup does not need + * to recreate its own display list, which would happen if it went through the normal + * draw/dispatchDraw mechanisms. + * + * @hide + */ + protected void dispatchGetDisplayList() {} + + /** * <p>Returns a display list that can be used to draw this view again * without executing its draw method.</p> * * @return A DisplayList ready to replay, or null if caching is not enabled. + * + * @hide */ - DisplayList getDisplayList() { - if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) { - return null; - } + public DisplayList getDisplayList() { if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { return null; } - if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED && - ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || - mDisplayList == null || !mDisplayList.isValid())) { + if (((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || + mDisplayList == null || !mDisplayList.isValid() || + mRecreateDisplayList)) { + // Don't need to recreate the display list, just need to tell our + // children to restore/recreate theirs + if (mDisplayList != null && mDisplayList.isValid() && + !mRecreateDisplayList) { + mPrivateFlags |= DRAWN | DRAWING_CACHE_VALID; + mPrivateFlags &= ~DIRTY_MASK; + dispatchGetDisplayList(); + + return mDisplayList; + } + + // If we got here, we're recreating it. Mark it as such to ensure that + // we copy in child display lists into ours in drawChild() + mRecreateDisplayList = true; if (mDisplayList == null) { - mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList(); + mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList(this); + // If we're creating a new display list, make sure our parent gets invalidated + // since they will need to recreate their display list to account for this + // new child display list. + invalidateParentIfAccelerated(); } final HardwareCanvas canvas = mDisplayList.start(); @@ -8141,6 +8255,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility final int restoreCount = canvas.save(); + computeScroll(); + canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= DRAWN | DRAWING_CACHE_VALID; // Fast path for layouts with no backgrounds @@ -8229,9 +8345,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mUnscaledDrawingCache.recycle(); mUnscaledDrawingCache = null; } - if (mDisplayList != null) { - mDisplayList.invalidate(); - } } /** @@ -10480,6 +10593,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility animation.setStartTime(Animation.START_ON_FIRST_FRAME); setAnimation(animation); invalidate(); + invalidateParentIfAccelerated(); } /** @@ -10490,6 +10604,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mCurrentAnimation.detach(); } mCurrentAnimation = null; + invalidateParentIfAccelerated(); } /** @@ -11495,6 +11610,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility final Callbacks mRootCallbacks; + Canvas mHardwareCanvas; + /** * The top view of the hierarchy. */ diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index d5c440c..c19a107 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -998,22 +998,27 @@ public class ViewDebug { new ViewOperation<Object>() { public Object[] pre() { final DisplayMetrics metrics = - view.getResources().getDisplayMetrics(); - final Bitmap bitmap = + (view != null && view.getResources() != null) ? + view.getResources().getDisplayMetrics() : null; + final Bitmap bitmap = metrics != null ? Bitmap.createBitmap(metrics.widthPixels, - metrics.heightPixels, Bitmap.Config.RGB_565); - final Canvas canvas = new Canvas(bitmap); + metrics.heightPixels, Bitmap.Config.RGB_565) : null; + final Canvas canvas = bitmap != null ? new Canvas(bitmap) : null; return new Object[] { bitmap, canvas }; } public void run(Object... data) { - view.draw((Canvas) data[1]); + if (data[1] != null) { + view.draw((Canvas) data[1]); + } } public void post(Object... data) { - ((Bitmap) data[0]).recycle(); + if (data[0] != null) { + ((Bitmap) data[0]).recycle(); + } } }) : 0; out.write(String.valueOf(durationMeasure)); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index d6c8ad6..c73cbe6 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2206,6 +2206,27 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * This method is used to cause children of this ViewGroup to restore or recreate their + * display lists. It is called by getDisplayList() when the parent ViewGroup does not need + * to recreate its own display list, which would happen if it went through the normal + * draw/dispatchDraw mechanisms. + * + * @hide + */ + @Override + protected void dispatchGetDisplayList() { + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + final View child = children[i]; + child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED; + child.mPrivateFlags &= ~INVALIDATED; + child.getDisplayList(); + child.mRecreateDisplayList = false; + } + } + + /** * Draw one child of this View Group. This method is responsible for getting * the canvas in the right state. This includes clipping, translating so * that the child's scrolled origin is at 0, 0, and applying any animation @@ -2247,7 +2268,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager caching = true; if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired; } else { - caching = layerType != LAYER_TYPE_NONE; + caching = (layerType != LAYER_TYPE_NONE) || canvas.isHardwareAccelerated(); } if (a != null) { @@ -2329,6 +2350,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return more; } + if (canvas.isHardwareAccelerated()) { + // Clear INVALIDATED flag to allow invalidation to occur during rendering, but + // retain the flag's value temporarily in the mRecreateDisplayList flag + child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED; + child.mPrivateFlags &= ~INVALIDATED; + } + child.computeScroll(); final int sx = child.mScrollX; @@ -2347,7 +2375,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (layerType == LAYER_TYPE_SOFTWARE) { child.buildDrawingCache(true); cache = child.getDrawingCache(true); - } else { + } else if (layerType == LAYER_TYPE_NONE) { displayList = child.getDisplayList(); } } @@ -2357,7 +2385,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final boolean hasNoCache = cache == null || hasDisplayList; final int restoreTo = canvas.save(); - if (hasNoCache) { + if (cache == null && !hasDisplayList) { canvas.translate(cl - sx, ct - sy); } else { canvas.translate(cl, ct); @@ -2375,7 +2403,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager int transX = 0; int transY = 0; - if (hasNoCache) { + if (cache == null && !hasDisplayList) { transX = -sx; transY = -sy; } @@ -2435,10 +2463,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { - if (hasNoCache) { + if (cache == null && !hasDisplayList) { canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct)); } else { - if (!scalingRequired) { + if (!scalingRequired || cache == null) { canvas.clipRect(0, 0, cr - cl, cb - ct); } else { canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight()); @@ -2473,7 +2501,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } else { child.mPrivateFlags &= ~DIRTY_MASK; - ((HardwareCanvas) canvas).drawDisplayList(displayList); + // Skip drawing the display list into ours if we were just refreshing + // it's content; we already have a reference to it in our display list + if (mRecreateDisplayList || mLayerType != LAYER_TYPE_NONE) { + ((HardwareCanvas) canvas).drawDisplayList(displayList); + } } } } else if (cache != null) { @@ -2503,6 +2535,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager finishAnimatingView(child, a); } + if (more && canvas.isHardwareAccelerated()) { + // invalidation is the trigger to recreate display lists, so if we're using + // display lists to render, force an invalidate to allow the animation to + // continue drawing another frame + invalidate(); + } + + child.mRecreateDisplayList = false; + return more; } @@ -2743,7 +2784,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // addViewInner() will call child.requestLayout() when setting the new LayoutParams // therefore, we call requestLayout() on ourselves before, so that the child's request // will be blocked at our level - child.mPrivateFlags &= ~DIRTY_MASK; requestLayout(); invalidate(); addViewInner(child, index, params, false); @@ -3425,10 +3465,20 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; if (dirty == null) { + if (child.mLayerType != LAYER_TYPE_NONE) { + mPrivateFlags |= INVALIDATED; + mPrivateFlags &= ~DRAWING_CACHE_VALID; + } do { View view = null; if (parent instanceof View) { view = (View) parent; + if (view.mLayerType != LAYER_TYPE_NONE && + view.getParent() instanceof View) { + final View grandParent = (View) view.getParent(); + grandParent.mPrivateFlags |= INVALIDATED; + grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID; + } if ((view.mPrivateFlags & DIRTY_MASK) != 0) { // already marked dirty - we're done break; diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index d932141..ba671c0 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -597,7 +597,7 @@ public final class ViewRoot extends Handler implements ViewParent, dirty.inset(-1, -1); } } - if (!mDirty.isEmpty()) { + if (!mDirty.isEmpty() && !mDirty.contains(dirty)) { mAttachInfo.mIgnoreDirtyState = true; } mDirty.union(dirty); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 3102ee9..83061ec 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -16,6 +16,7 @@ package android.webkit; +import android.view.HardwareCanvas; import com.android.internal.R; import android.annotation.Widget; @@ -353,7 +354,7 @@ public class WebView extends AbsoluteLayout private ZoomManager mZoomManager; - private Rect mGLRectViewport; + private Rect mGLRectViewport = new Rect(); /** * Transportation object for returning WebView across thread boundaries. @@ -4079,20 +4080,8 @@ public class WebView extends AbsoluteLayout } if (canvas.isHardwareAccelerated()) { - try { - if (canvas.acquireContext()) { - Rect rect = new Rect(mGLRectViewport.left, - mGLRectViewport.top, - mGLRectViewport.right, - mGLRectViewport.bottom - - getVisibleTitleHeight()); - if (nativeDrawGL(rect, getScale(), extras)) { - invalidate(); - } - } - } finally { - canvas.releaseContext(); - } + int functor = nativeGetDrawGLFunction(mGLRectViewport, getScale(), extras); + ((HardwareCanvas) canvas).callDrawGLFunction(functor); } else { DrawFilter df = null; if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) { @@ -5163,7 +5152,6 @@ public class WebView extends AbsoluteLayout if (mNativeClass != 0) { nativeRecordButtons(false, false, true); } - setFocusControllerActive(false); } mGotKeyDown = false; } @@ -5173,18 +5161,16 @@ public class WebView extends AbsoluteLayout void setGLRectViewport() { // Use the getGlobalVisibleRect() to get the intersection among the parents - Rect webViewRect = new Rect(); - boolean visible = getGlobalVisibleRect(webViewRect); + getGlobalVisibleRect(mGLRectViewport); // Then need to invert the Y axis, just for GL View rootView = getRootView(); int rootViewHeight = rootView.getHeight(); - int savedWebViewBottom = webViewRect.bottom; - webViewRect.bottom = rootViewHeight - webViewRect.top; - webViewRect.top = rootViewHeight - savedWebViewBottom; + int savedWebViewBottom = mGLRectViewport.bottom; + mGLRectViewport.bottom = rootViewHeight - mGLRectViewport.top - getVisibleTitleHeight(); + mGLRectViewport.top = rootViewHeight - savedWebViewBottom; - // Store the viewport - mGLRectViewport = webViewRect; + nativeUpdateDrawGLFunction(mGLRectViewport); } /** @@ -8239,6 +8225,8 @@ public class WebView extends AbsoluteLayout boolean splitIfNeeded); private native void nativeDumpDisplayTree(String urlOrNull); private native boolean nativeEvaluateLayersAnimations(); + private native int nativeGetDrawGLFunction(Rect rect, float scale, int extras); + private native void nativeUpdateDrawGLFunction(Rect rect); private native boolean nativeDrawGL(Rect rect, float scale, int extras); private native void nativeExtendSelection(int x, int y); private native int nativeFindAll(String findLower, String findUpper, diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 665f9e7..fc5c893 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -2357,6 +2357,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } if (mScrollY != 0) { mScrollY = 0; + invalidateParentIfAccelerated(); finishGlows(); invalidate(); } @@ -2733,6 +2734,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (mScrollY != 0) { mScrollY = 0; + invalidateParentIfAccelerated(); finishGlows(); invalidate(); } @@ -2951,6 +2953,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // Coming back to 'real' list scrolling incrementalDeltaY = -newScroll; mScrollY = 0; + invalidateParentIfAccelerated(); // No need to do all this work if we're not going to move anyway if (incrementalDeltaY != 0) { @@ -3244,6 +3247,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { mScrollY = scrollY; + invalidateParentIfAccelerated(); if (clampedY) { // Velocity is broken by hitting the limit; don't start a fling off of this. diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index db22a0c..ff6677f 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -647,6 +647,7 @@ public class HorizontalScrollView extends FrameLayout { if (!mScroller.isFinished()) { mScrollX = scrollX; mScrollY = scrollY; + invalidateParentIfAccelerated(); if (clampedX) { mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0); } diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index ce6da72..fc049cc 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -644,6 +644,7 @@ public class ScrollView extends FrameLayout { if (!mScroller.isFinished()) { mScrollX = scrollX; mScrollY = scrollY; + invalidateParentIfAccelerated(); if (clampedY) { mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange()); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index aac57ed..a0a615a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -6272,6 +6272,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mScroller.computeScrollOffset()) { mScrollX = mScroller.getCurrX(); mScrollY = mScroller.getCurrY(); + invalidateParentIfAccelerated(); postInvalidate(); // So we draw again } } diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 8d074af..e4a89d7 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -117,6 +117,11 @@ static void android_view_GLES20Canvas_acquireContext(JNIEnv* env, jobject canvas renderer->acquireContext(); } +static bool android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, Functor *functor) { + return renderer->callDrawGLFunction(functor); +} + static void android_view_GLES20Canvas_releaseContext(JNIEnv* env, jobject canvas, OpenGLRenderer* renderer) { renderer->releaseContext(); @@ -482,9 +487,9 @@ static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env, delete displayList; } -static void android_view_GLES20Canvas_drawDisplayList(JNIEnv* env, +static bool android_view_GLES20Canvas_drawDisplayList(JNIEnv* env, jobject canvas, OpenGLRenderer* renderer, DisplayList* displayList) { - renderer->drawDisplayList(displayList); + return renderer->drawDisplayList(displayList); } // ---------------------------------------------------------------------------- @@ -577,6 +582,8 @@ static JNINativeMethod gMethods[] = { { "nPrepare", "(IZ)V", (void*) android_view_GLES20Canvas_prepare }, { "nFinish", "(I)V", (void*) android_view_GLES20Canvas_finish }, { "nAcquireContext", "(I)V", (void*) android_view_GLES20Canvas_acquireContext }, + { "nCallDrawGLFunction", "(II)Z", + (void*) android_view_GLES20Canvas_callDrawGLFunction }, { "nReleaseContext", "(I)V", (void*) android_view_GLES20Canvas_releaseContext }, { "nSave", "(II)I", (void*) android_view_GLES20Canvas_save }, @@ -639,7 +646,7 @@ static JNINativeMethod gMethods[] = { { "nGetDisplayList", "(I)I", (void*) android_view_GLES20Canvas_getDisplayList }, { "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20Canvas_destroyDisplayList }, { "nGetDisplayListRenderer", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListRenderer }, - { "nDrawDisplayList", "(II)V", (void*) android_view_GLES20Canvas_drawDisplayList }, + { "nDrawDisplayList", "(II)Z", (void*) android_view_GLES20Canvas_drawDisplayList }, { "nInterrupt", "(I)V", (void*) android_view_GLES20Canvas_interrupt }, { "nResume", "(I)V", (void*) android_view_GLES20Canvas_resume }, |