Fix quickReject logic to account for setClipChildren() setting
The rendering code optimizes by rejecting drawing operations that lie outside of the bounds of their views. This works in most situations, but breaks down when containers have called setClipChildren(false), because we reject drawing that is outside of that container, but which should be drawn anyway. Fix is to pass in the value of that flag to the DisplayList drawing routines which take that flag into account when deciding whether to quickReject any particular operation. Issue #8659277 animation clipping Change-Id: Ief568e4db01b533a97b3c5ea5ad777c03c0eea71
This commit is contained in:
@@ -285,9 +285,9 @@ public abstract class DisplayList {
|
||||
* Set whether the display list should clip itself to its bounds. This property is controlled by
|
||||
* the view's parent.
|
||||
*
|
||||
* @param clipChildren true if the display list should clip to its bounds
|
||||
* @param clipToBounds true if the display list should clip to its bounds
|
||||
*/
|
||||
public abstract void setClipChildren(boolean clipChildren);
|
||||
public abstract void setClipToBounds(boolean clipToBounds);
|
||||
|
||||
/**
|
||||
* Set the static matrix on the display list. The specified matrix is combined with other
|
||||
|
||||
@@ -137,9 +137,9 @@ class GLES20DisplayList extends DisplayList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClipChildren(boolean clipChildren) {
|
||||
public void setClipToBounds(boolean clipToBounds) {
|
||||
if (hasNativeDisplayList()) {
|
||||
nSetClipChildren(mFinalizer.mNativeDisplayList, clipChildren);
|
||||
nSetClipToBounds(mFinalizer.mNativeDisplayList, clipToBounds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ class GLES20DisplayList extends DisplayList {
|
||||
private static native void nSetPivotY(int displayList, float pivotY);
|
||||
private static native void nSetPivotX(int displayList, float pivotX);
|
||||
private static native void nSetCaching(int displayList, boolean caching);
|
||||
private static native void nSetClipChildren(int displayList, boolean clipChildren);
|
||||
private static native void nSetClipToBounds(int displayList, boolean clipToBounds);
|
||||
private static native void nSetAlpha(int displayList, float alpha);
|
||||
private static native void nSetHasOverlappingRendering(int displayList,
|
||||
boolean hasOverlappingRendering);
|
||||
|
||||
@@ -13489,8 +13489,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
displayList.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
|
||||
displayList.setHasOverlappingRendering(hasOverlappingRendering());
|
||||
if (mParent instanceof ViewGroup) {
|
||||
displayList.setClipChildren(
|
||||
(((ViewGroup)mParent).mGroupFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0);
|
||||
displayList.setClipToBounds(
|
||||
(((ViewGroup) mParent).mGroupFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0);
|
||||
}
|
||||
float alpha = 1;
|
||||
if (mParent instanceof ViewGroup && (((ViewGroup) mParent).mGroupFlags &
|
||||
|
||||
@@ -3109,7 +3109,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
for (int i = 0; i < mChildrenCount; ++i) {
|
||||
View child = getChildAt(i);
|
||||
if (child.mDisplayList != null) {
|
||||
child.mDisplayList.setClipChildren(clipChildren);
|
||||
child.mDisplayList.setClipToBounds(clipChildren);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1351,7 +1351,7 @@ public class Editor {
|
||||
} finally {
|
||||
blockDisplayList.end();
|
||||
// Same as drawDisplayList below, handled by our TextView's parent
|
||||
blockDisplayList.setClipChildren(false);
|
||||
blockDisplayList.setClipToBounds(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,9 +84,9 @@ static void android_view_GLES20DisplayList_setAnimationMatrix(JNIEnv* env,
|
||||
displayList->setAnimationMatrix(matrix);
|
||||
}
|
||||
|
||||
static void android_view_GLES20DisplayList_setClipChildren(JNIEnv* env,
|
||||
jobject clazz, DisplayList* displayList, jboolean clipChildren) {
|
||||
displayList->setClipChildren(clipChildren);
|
||||
static void android_view_GLES20DisplayList_setClipToBounds(JNIEnv* env,
|
||||
jobject clazz, DisplayList* displayList, jboolean clipToBounds) {
|
||||
displayList->setClipToBounds(clipToBounds);
|
||||
}
|
||||
|
||||
static void android_view_GLES20DisplayList_setAlpha(JNIEnv* env,
|
||||
@@ -308,7 +308,7 @@ static JNINativeMethod gMethods[] = {
|
||||
{ "nSetCaching", "(IZ)V", (void*) android_view_GLES20DisplayList_setCaching },
|
||||
{ "nSetStaticMatrix", "(II)V", (void*) android_view_GLES20DisplayList_setStaticMatrix },
|
||||
{ "nSetAnimationMatrix", "(II)V", (void*) android_view_GLES20DisplayList_setAnimationMatrix },
|
||||
{ "nSetClipChildren", "(IZ)V", (void*) android_view_GLES20DisplayList_setClipChildren },
|
||||
{ "nSetClipToBounds", "(IZ)V", (void*) android_view_GLES20DisplayList_setClipToBounds },
|
||||
{ "nSetAlpha", "(IF)V", (void*) android_view_GLES20DisplayList_setAlpha },
|
||||
{ "nSetHasOverlappingRendering", "(IZ)V",
|
||||
(void*) android_view_GLES20DisplayList_setHasOverlappingRendering },
|
||||
|
||||
@@ -231,7 +231,7 @@ void DisplayList::init() {
|
||||
mTop = 0;
|
||||
mRight = 0;
|
||||
mBottom = 0;
|
||||
mClipChildren = true;
|
||||
mClipToBounds = true;
|
||||
mAlpha = 1;
|
||||
mHasOverlappingRendering = true;
|
||||
mTranslationX = 0;
|
||||
@@ -358,7 +358,7 @@ void DisplayList::outputViewProperties(const int level) {
|
||||
ALOGD("%*sScaleAlpha %.2f", level * 2, "", mAlpha);
|
||||
} else {
|
||||
int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
|
||||
if (mClipChildren) {
|
||||
if (mClipToBounds) {
|
||||
flags |= SkCanvas::kClipToLayer_SaveFlag;
|
||||
}
|
||||
ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
|
||||
@@ -366,7 +366,7 @@ void DisplayList::outputViewProperties(const int level) {
|
||||
(int)(mAlpha * 255), flags);
|
||||
}
|
||||
}
|
||||
if (mClipChildren && !mCaching) {
|
||||
if (mClipToBounds && !mCaching) {
|
||||
ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
|
||||
(float) mRight - mLeft, (float) mBottom - mTop);
|
||||
}
|
||||
@@ -411,16 +411,17 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler,
|
||||
// have to pass it into this call. In fact, this information might be in the
|
||||
// location/size info that we store with the new native transform data.
|
||||
int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
|
||||
if (mClipChildren) {
|
||||
if (mClipToBounds) {
|
||||
saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
|
||||
}
|
||||
handler(mSaveLayerOp->reinit(0, 0, mRight - mLeft, mBottom - mTop,
|
||||
mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT);
|
||||
mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT,
|
||||
mClipToBounds);
|
||||
}
|
||||
}
|
||||
if (mClipChildren && !mCaching) {
|
||||
if (mClipToBounds && !mCaching) {
|
||||
handler(mClipRectOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op),
|
||||
PROPERTY_SAVECOUNT);
|
||||
PROPERTY_SAVECOUNT, mClipToBounds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,8 +429,8 @@ class DeferOperationHandler {
|
||||
public:
|
||||
DeferOperationHandler(DeferStateStruct& deferStruct, int level)
|
||||
: mDeferStruct(deferStruct), mLevel(level) {}
|
||||
inline void operator()(DisplayListOp* operation, int saveCount) {
|
||||
operation->defer(mDeferStruct, saveCount, mLevel);
|
||||
inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
|
||||
operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
|
||||
}
|
||||
private:
|
||||
DeferStateStruct& mDeferStruct;
|
||||
@@ -445,11 +446,11 @@ class ReplayOperationHandler {
|
||||
public:
|
||||
ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
|
||||
: mReplayStruct(replayStruct), mLevel(level) {}
|
||||
inline void operator()(DisplayListOp* operation, int saveCount) {
|
||||
inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
|
||||
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
|
||||
mReplayStruct.mRenderer.eventMark(operation->name());
|
||||
#endif
|
||||
operation->replay(mReplayStruct, saveCount, mLevel);
|
||||
operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
|
||||
}
|
||||
private:
|
||||
ReplayStateStruct& mReplayStruct;
|
||||
@@ -492,16 +493,16 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level)
|
||||
|
||||
int restoreTo = renderer.getSaveCount();
|
||||
handler(mSaveOp->reinit(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
|
||||
PROPERTY_SAVECOUNT);
|
||||
PROPERTY_SAVECOUNT, mClipToBounds);
|
||||
|
||||
DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
|
||||
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
|
||||
|
||||
setViewProperties<T>(renderer, handler, level + 1);
|
||||
|
||||
if (renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
|
||||
if (mClipToBounds && renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
|
||||
DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo);
|
||||
handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT);
|
||||
handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -510,12 +511,12 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level)
|
||||
for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
|
||||
DisplayListOp *op = mDisplayListData->displayListOps[i];
|
||||
|
||||
handler(op, saveCount);
|
||||
handler(op, saveCount, mClipToBounds);
|
||||
logBuffer.writeCommand(level, op->name());
|
||||
}
|
||||
|
||||
DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
|
||||
handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT);
|
||||
handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds);
|
||||
renderer.restoreToCount(restoreTo);
|
||||
renderer.setOverrideLayerAlpha(1.0f);
|
||||
}
|
||||
|
||||
@@ -137,8 +137,8 @@ public:
|
||||
return mName.string();
|
||||
}
|
||||
|
||||
void setClipChildren(bool clipChildren) {
|
||||
mClipChildren = clipChildren;
|
||||
void setClipToBounds(bool clipToBounds) {
|
||||
mClipToBounds = clipToBounds;
|
||||
}
|
||||
|
||||
void setStaticMatrix(SkMatrix* matrix) {
|
||||
@@ -498,7 +498,7 @@ private:
|
||||
String8 mName;
|
||||
|
||||
// View properties
|
||||
bool mClipChildren;
|
||||
bool mClipToBounds;
|
||||
float mAlpha;
|
||||
bool mHasOverlappingRendering;
|
||||
float mTranslationX, mTranslationY;
|
||||
|
||||
@@ -78,9 +78,11 @@ public:
|
||||
kOpLogFlag_JSON = 0x2 // TODO: add?
|
||||
};
|
||||
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) = 0;
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
|
||||
bool useQuickReject) = 0;
|
||||
|
||||
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) = 0;
|
||||
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
|
||||
bool useQuickReject) = 0;
|
||||
|
||||
virtual void output(int level, uint32_t logFlags = 0) = 0;
|
||||
|
||||
@@ -104,7 +106,8 @@ public:
|
||||
|
||||
virtual ~StateOp() {}
|
||||
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
|
||||
bool useQuickReject) {
|
||||
// default behavior only affects immediate, deferrable state, issue directly to renderer
|
||||
applyState(deferStruct.mRenderer, saveCount);
|
||||
}
|
||||
@@ -113,7 +116,8 @@ public:
|
||||
* State operations are applied directly to the renderer, but can cause the deferred drawing op
|
||||
* list to flush
|
||||
*/
|
||||
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) {
|
||||
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
|
||||
bool useQuickReject) {
|
||||
applyState(replayStruct.mRenderer, saveCount);
|
||||
}
|
||||
|
||||
@@ -126,9 +130,9 @@ public:
|
||||
DrawOp(SkPaint* paint)
|
||||
: mPaint(paint), mQuickRejected(false) {}
|
||||
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
|
||||
if (mQuickRejected &&
|
||||
CC_LIKELY(deferStruct.mReplayFlags & DisplayList::kReplayFlag_ClipChildren)) {
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
|
||||
bool useQuickReject) {
|
||||
if (mQuickRejected && CC_LIKELY(useQuickReject)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -140,9 +144,9 @@ public:
|
||||
deferStruct.mDeferredList.addDrawOp(deferStruct.mRenderer, this);
|
||||
}
|
||||
|
||||
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) {
|
||||
if (mQuickRejected &&
|
||||
CC_LIKELY(replayStruct.mReplayFlags & DisplayList::kReplayFlag_ClipChildren)) {
|
||||
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
|
||||
bool useQuickReject) {
|
||||
if (mQuickRejected && CC_LIKELY(useQuickReject)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -261,7 +265,8 @@ public:
|
||||
SaveOp(int flags)
|
||||
: mFlags(flags) {}
|
||||
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
|
||||
bool useQuickReject) {
|
||||
int newSaveCount = deferStruct.mRenderer.save(mFlags);
|
||||
deferStruct.mDeferredList.addSave(deferStruct.mRenderer, this, newSaveCount);
|
||||
}
|
||||
@@ -293,7 +298,8 @@ public:
|
||||
RestoreToCountOp(int count)
|
||||
: mCount(count) {}
|
||||
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
|
||||
bool useQuickReject) {
|
||||
deferStruct.mDeferredList.addRestoreToCount(deferStruct.mRenderer,
|
||||
this, saveCount + mCount);
|
||||
deferStruct.mRenderer.restoreToCount(saveCount + mCount);
|
||||
@@ -326,7 +332,8 @@ public:
|
||||
int alpha, SkXfermode::Mode mode, int flags)
|
||||
: mArea(left, top, right, bottom), mAlpha(alpha), mMode(mode), mFlags(flags) {}
|
||||
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
|
||||
bool useQuickReject) {
|
||||
// NOTE: don't bother with actual saveLayer, instead issuing it at flush time
|
||||
int newSaveCount = deferStruct.mRenderer.getSaveCount();
|
||||
deferStruct.mDeferredList.addSaveLayer(deferStruct.mRenderer, this, newSaveCount);
|
||||
@@ -490,7 +497,8 @@ class ClipOp : public StateOp {
|
||||
public:
|
||||
ClipOp(SkRegion::Op op) : mOp(op) {}
|
||||
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
|
||||
bool useQuickReject) {
|
||||
// NOTE: must defer op BEFORE applying state, since it may read clip
|
||||
deferStruct.mDeferredList.addClip(deferStruct.mRenderer, this);
|
||||
|
||||
@@ -1371,12 +1379,14 @@ public:
|
||||
: DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0),
|
||||
mDisplayList(displayList), mFlags(flags) {}
|
||||
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
|
||||
bool useQuickReject) {
|
||||
if (mDisplayList && mDisplayList->isRenderable()) {
|
||||
mDisplayList->defer(deferStruct, level + 1);
|
||||
}
|
||||
}
|
||||
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) {
|
||||
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
|
||||
bool useQuickReject) {
|
||||
if (mDisplayList && mDisplayList->isRenderable()) {
|
||||
mDisplayList->replay(replayStruct, level + 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user