summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorChet Haase <chet@google.com>2011-01-10 14:10:36 -0800
committerChet Haase <chet@google.com>2011-01-24 08:43:20 -0800
commitdaf98e941e140e8739458126640183b9f296a2ab (patch)
treee338ad021139d706004b70a38fbbe539ccfbbacf /core
parent57ffc00239edcfe733832771e1429fca20182207 (diff)
downloadframeworks_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.java19
-rw-r--r--core/java/android/view/DisplayList.java6
-rw-r--r--core/java/android/view/GLES20Canvas.java15
-rw-r--r--core/java/android/view/GLES20DisplayList.java14
-rw-r--r--core/java/android/view/HardwareCanvas.java23
-rw-r--r--core/java/android/view/HardwareRenderer.java29
-rw-r--r--core/java/android/view/View.java157
-rw-r--r--core/java/android/view/ViewDebug.java17
-rw-r--r--core/java/android/view/ViewGroup.java66
-rw-r--r--core/java/android/view/ViewRoot.java2
-rw-r--r--core/java/android/webkit/WebView.java34
-rw-r--r--core/java/android/widget/AbsListView.java4
-rw-r--r--core/java/android/widget/HorizontalScrollView.java1
-rw-r--r--core/java/android/widget/ScrollView.java1
-rw-r--r--core/java/android/widget/TextView.java1
-rw-r--r--core/jni/android_view_GLES20Canvas.cpp13
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 },