Skip to content

Commit db8c9a6

Browse files
committed
Optimization of alpha with DisplayList properties
Some views (such as ImageView and TextView) handle non-opaque alpha values directly. This was originally an optimization, but we can handle it faster in many cases without this optimization when DisplayList properties are enabled. Basically, if a view has non-overlapping rendering, we set the alpha value directly on the renderer (the equivalent of setting it on the Paint object) and draw each primitive with that alpha value. Doing it this way avoids re-creating DisplayLists while getting the same speedup that onSetAlpha() used to get pre-DisplayList properties. Change-Id: I0f7827f075d3b35093a882d4adbb300a1063c288
1 parent b3fa396 commit db8c9a6

18 files changed

Lines changed: 368 additions & 35 deletions

api/current.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23371,6 +23371,7 @@ package android.view {
2337123371
method public boolean hasFocus();
2337223372
method public boolean hasFocusable();
2337323373
method public boolean hasOnClickListeners();
23374+
method public boolean hasOverlappingRendering();
2337423375
method public boolean hasTransientState();
2337523376
method public boolean hasWindowFocus();
2337623377
method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);

core/java/android/view/DisplayList.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,15 @@ public abstract class DisplayList {
148148
*/
149149
public abstract void setAlpha(float alpha);
150150

151+
/**
152+
* Sets whether the DisplayList renders content which overlaps. Non-overlapping rendering
153+
* can use a fast path for alpha that avoids rendering to an offscreen buffer.
154+
*
155+
* @param hasOverlappingRendering
156+
* @see android.view.View#hasOverlappingRendering()
157+
*/
158+
public abstract void setHasOverlappingRendering(boolean hasOverlappingRendering);
159+
151160
/**
152161
* Sets the translationX value for the DisplayList
153162
*

core/java/android/view/GLES20DisplayList.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,15 @@ public void setAlpha(float alpha) {
146146
}
147147
}
148148

149+
@Override
150+
public void setHasOverlappingRendering(boolean hasOverlappingRendering) {
151+
try {
152+
nSetHasOverlappingRendering(getNativeDisplayList(), hasOverlappingRendering);
153+
} catch (IllegalStateException e) {
154+
// invalid DisplayList okay: we'll set current values the next time we render to it
155+
}
156+
}
157+
149158
@Override
150159
public void setTranslationX(float translationX) {
151160
try {
@@ -335,6 +344,8 @@ private static native void nSetLeftTopRightBottom(int displayList, int left, int
335344
private static native void nSetClipChildren(int displayList, boolean clipChildren);
336345
private static native void nSetApplicationScale(int displayList, float scale);
337346
private static native void nSetAlpha(int displayList, float alpha);
347+
private static native void nSetHasOverlappingRendering(int displayList,
348+
boolean hasOverlappingRendering);
338349
private static native void nSetTranslationX(int displayList, float translationX);
339350
private static native void nSetTranslationY(int displayList, float translationY);
340351
private static native void nSetRotation(int displayList, float rotation);

core/java/android/view/HardwareRenderer.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public abstract class HardwareRenderer {
134134
/**
135135
* Number of frames to profile.
136136
*/
137-
private static final int PROFILE_MAX_FRAMES = 64;
137+
private static final int PROFILE_MAX_FRAMES = 120;
138138

139139
/**
140140
* Number of floats per profiled frame.
@@ -1046,10 +1046,6 @@ boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callba
10461046
Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- getDisplayList() took " +
10471047
total + "ms");
10481048
}
1049-
if (View.USE_DISPLAY_LIST_PROPERTIES) {
1050-
Log.d("DLProperties", "getDisplayList():\t" +
1051-
mProfileData[mProfileCurrentFrame]);
1052-
}
10531049
}
10541050

10551051
if (displayList != null) {

core/java/android/view/View.java

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2829,19 +2829,6 @@ static class ListenerInfo {
28292829
*/
28302830
private boolean mSendingHoverAccessibilityEvents;
28312831

2832-
/**
2833-
* Delegate for injecting accessiblity functionality.
2834-
*/
2835-
AccessibilityDelegate mAccessibilityDelegate;
2836-
2837-
/**
2838-
* Consistency verifier for debugging purposes.
2839-
* @hide
2840-
*/
2841-
protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
2842-
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
2843-
new InputEventConsistencyVerifier(this, 0) : null;
2844-
28452832
/**
28462833
* Simple constructor to use when creating a view from code.
28472834
*
@@ -2862,6 +2849,19 @@ public View(Context context) {
28622849
mUserPaddingRelative = false;
28632850
}
28642851

2852+
/**
2853+
* Delegate for injecting accessiblity functionality.
2854+
*/
2855+
AccessibilityDelegate mAccessibilityDelegate;
2856+
2857+
/**
2858+
* Consistency verifier for debugging purposes.
2859+
* @hide
2860+
*/
2861+
protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
2862+
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
2863+
new InputEventConsistencyVerifier(this, 0) : null;
2864+
28652865
/**
28662866
* Constructor that is called when inflating a view from XML. This is called
28672867
* when a view is being constructed from an XML file, supplying attributes
@@ -7854,6 +7854,23 @@ public float getAlpha() {
78547854
return mTransformationInfo != null ? mTransformationInfo.mAlpha : 1;
78557855
}
78567856

7857+
/**
7858+
* Returns whether this View has content which overlaps. This function, intended to be
7859+
* overridden by specific View types, is an optimization when alpha is set on a view. If
7860+
* rendering overlaps in a view with alpha < 1, that view is drawn to an offscreen buffer
7861+
* and then composited it into place, which can be expensive. If the view has no overlapping
7862+
* rendering, the view can draw each primitive with the appropriate alpha value directly.
7863+
* An example of overlapping rendering is a TextView with a background image, such as a
7864+
* Button. An example of non-overlapping rendering is a TextView with no background, or
7865+
* an ImageView with only the foreground image. The default implementation returns true;
7866+
* subclasses should override if they have cases which can be optimized.
7867+
*
7868+
* @return true if the content in this view might overlap, false otherwise.
7869+
*/
7870+
public boolean hasOverlappingRendering() {
7871+
return true;
7872+
}
7873+
78577874
/**
78587875
* <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is
78597876
* completely transparent and 1 means the view is completely opaque.</p>
@@ -11525,6 +11542,7 @@ void setDisplayListProperties() {
1152511542
void setDisplayListProperties(DisplayList displayList) {
1152611543
if (USE_DISPLAY_LIST_PROPERTIES && displayList != null) {
1152711544
displayList.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
11545+
displayList.setHasOverlappingRendering(hasOverlappingRendering());
1152811546
if (mParent instanceof ViewGroup) {
1152911547
displayList.setClipChildren(
1153011548
(((ViewGroup)mParent).mGroupFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0);

core/java/android/view/ViewPropertyAnimator.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -834,40 +834,49 @@ private void animatePropertyBy(int constantName, float startValue, float byValue
834834
*/
835835
private void setValue(int propertyConstant, float value) {
836836
final View.TransformationInfo info = mView.mTransformationInfo;
837+
DisplayList displayList = View.USE_DISPLAY_LIST_PROPERTIES ? mView.mDisplayList : null;
837838
switch (propertyConstant) {
838839
case TRANSLATION_X:
839840
info.mTranslationX = value;
841+
if (displayList != null) displayList.setTranslationX(value);
840842
break;
841843
case TRANSLATION_Y:
842844
info.mTranslationY = value;
845+
if (displayList != null) displayList.setTranslationY(value);
843846
break;
844847
case ROTATION:
845848
info.mRotation = value;
849+
if (displayList != null) displayList.setRotation(value);
846850
break;
847851
case ROTATION_X:
848852
info.mRotationX = value;
853+
if (displayList != null) displayList.setRotationX(value);
849854
break;
850855
case ROTATION_Y:
851856
info.mRotationY = value;
857+
if (displayList != null) displayList.setRotationY(value);
852858
break;
853859
case SCALE_X:
854860
info.mScaleX = value;
861+
if (displayList != null) displayList.setScaleX(value);
855862
break;
856863
case SCALE_Y:
857864
info.mScaleY = value;
865+
if (displayList != null) displayList.setScaleY(value);
858866
break;
859867
case X:
860868
info.mTranslationX = value - mView.mLeft;
869+
if (displayList != null) displayList.setTranslationX(value - mView.mLeft);
861870
break;
862871
case Y:
863872
info.mTranslationY = value - mView.mTop;
873+
if (displayList != null) displayList.setTranslationY(value - mView.mTop);
864874
break;
865875
case ALPHA:
866876
info.mAlpha = value;
877+
if (displayList != null) displayList.setAlpha(value);
867878
break;
868879
}
869-
// TODO: optimize to set only the properties that have changed
870-
mView.setDisplayListProperties();
871880
}
872881

873882
/**

core/java/android/widget/ImageView.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ public int getResolvedLayoutDirection(Drawable dr) {
201201

202202
@Override
203203
protected boolean onSetAlpha(int alpha) {
204-
if (getBackground() == null) {
204+
if (!USE_DISPLAY_LIST_PROPERTIES && getBackground() == null) {
205205
int scale = alpha + (alpha >> 7);
206206
if (mViewAlphaScale != scale) {
207207
mViewAlphaScale = scale;
@@ -213,6 +213,15 @@ protected boolean onSetAlpha(int alpha) {
213213
return false;
214214
}
215215

216+
@Override
217+
public boolean hasOverlappingRendering() {
218+
if (!USE_DISPLAY_LIST_PROPERTIES) {
219+
return super.hasOverlappingRendering();
220+
} else {
221+
return (getBackground() != null);
222+
}
223+
}
224+
216225
@Override
217226
public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
218227
super.onPopulateAccessibilityEvent(event);

core/java/android/widget/TextView.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4268,7 +4268,8 @@ public int getResolvedLayoutDirection(Drawable who) {
42684268
protected boolean onSetAlpha(int alpha) {
42694269
// Alpha is supported if and only if the drawing can be done in one pass.
42704270
// TODO text with spans with a background color currently do not respect this alpha.
4271-
if (getBackground() == null) {
4271+
if (!USE_DISPLAY_LIST_PROPERTIES &&
4272+
(getBackground() != null || mText instanceof Spannable || hasSelection())) {
42724273
if (mCurrentAlpha != alpha) {
42734274
mCurrentAlpha = alpha;
42744275
final Drawables dr = mDrawables;
@@ -4292,6 +4293,15 @@ protected boolean onSetAlpha(int alpha) {
42924293
return false;
42934294
}
42944295

4296+
@Override
4297+
public boolean hasOverlappingRendering() {
4298+
if (!USE_DISPLAY_LIST_PROPERTIES) {
4299+
return super.hasOverlappingRendering();
4300+
} else {
4301+
return (getBackground() != null || mText instanceof Spannable || hasSelection());
4302+
}
4303+
}
4304+
42954305
/**
42964306
* When a TextView is used to display a useful piece of information to the user (such as a
42974307
* contact's address), it should be made selectable, so that the user can select and copy this

core/jni/android_view_GLES20DisplayList.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ static void android_view_GLES20DisplayList_setAlpha(JNIEnv* env,
6565
displayList->setAlpha(alpha);
6666
}
6767

68+
static void android_view_GLES20DisplayList_setHasOverlappingRendering(JNIEnv* env,
69+
jobject clazz, DisplayList* displayList, bool hasOverlappingRendering) {
70+
displayList->setHasOverlappingRendering(hasOverlappingRendering);
71+
}
72+
6873
static void android_view_GLES20DisplayList_setTranslationX(JNIEnv* env,
6974
jobject clazz, DisplayList* displayList, float tx) {
7075
displayList->setTranslationX(tx);
@@ -185,6 +190,8 @@ static JNINativeMethod gMethods[] = {
185190
{ "nSetAnimationMatrix", "(II)V", (void*) android_view_GLES20DisplayList_setAnimationMatrix },
186191
{ "nSetClipChildren", "(IZ)V", (void*) android_view_GLES20DisplayList_setClipChildren },
187192
{ "nSetAlpha", "(IF)V", (void*) android_view_GLES20DisplayList_setAlpha },
193+
{ "nSetHasOverlappingRendering", "(IZ)V",
194+
(void*) android_view_GLES20DisplayList_setHasOverlappingRendering },
188195
{ "nSetTranslationX", "(IF)V", (void*) android_view_GLES20DisplayList_setTranslationX },
189196
{ "nSetTranslationY", "(IF)V", (void*) android_view_GLES20DisplayList_setTranslationY },
190197
{ "nSetRotation", "(IF)V", (void*) android_view_GLES20DisplayList_setRotation },

libs/hwui/DisplayListRenderer.cpp

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ void DisplayList::initProperties() {
112112
mClipChildren = true;
113113
mAlpha = 1;
114114
mMultipliedAlpha = 255;
115+
mHasOverlappingRendering = true;
115116
mTranslationX = 0;
116117
mTranslationY = 0;
117118
mRotation = 0;
@@ -772,18 +773,23 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, uint32_t width, ui
772773
}
773774
}
774775
if (mAlpha < 1 && !mCaching) {
775-
// TODO: should be able to store the size of a DL at record time and not
776-
// have to pass it into this call. In fact, this information might be in the
777-
// location/size info that we store with the new native transform data.
778-
int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
779-
if (mClipChildren) {
780-
flags |= SkCanvas::kClipToLayer_SaveFlag;
776+
if (!mHasOverlappingRendering) {
777+
DISPLAY_LIST_LOGD("%s%s %.2f", indent, "SetAlpha", mAlpha);
778+
renderer.setAlpha(mAlpha);
779+
} else {
780+
// TODO: should be able to store the size of a DL at record time and not
781+
// have to pass it into this call. In fact, this information might be in the
782+
// location/size info that we store with the new native transform data.
783+
int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
784+
if (mClipChildren) {
785+
flags |= SkCanvas::kClipToLayer_SaveFlag;
786+
}
787+
DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", indent, "SaveLayerAlpha",
788+
(float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
789+
mMultipliedAlpha, flags);
790+
renderer.saveLayerAlpha(0, 0, mRight - mLeft, mBottom - mTop,
791+
mMultipliedAlpha, flags);
781792
}
782-
DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", indent, "SaveLayerAlpha",
783-
(float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
784-
mMultipliedAlpha, flags);
785-
renderer.saveLayerAlpha(0, 0, mRight - mLeft, mBottom - mTop,
786-
mMultipliedAlpha, flags);
787793
}
788794
if (mClipChildren) {
789795
DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f", indent, "ClipRect", 0.0f, 0.0f,

0 commit comments

Comments
 (0)