Refactor DrawView to avoid extra allocates.

Summary: Previously, the first time we collected a draw view, we would make a clone, even though the draw view had never been mutated.  This refactors draw view to avoid this extra allocate.

Reviewed By: ahmedre

Differential Revision: D3719056
This commit is contained in:
Seth Kirby 2016-08-15 18:56:36 -07:00 committed by Ahmed El-Helw
parent cc216b5ec3
commit b2f41e2921
3 changed files with 70 additions and 42 deletions

View File

@ -42,7 +42,7 @@ import android.graphics.Color;
&& mClipRight == clipRight && mClipBottom == clipBottom;
}
public final void setClipBounds(
protected final void setClipBounds(
float clipLeft,
float clipTop,
float clipRight,
@ -185,6 +185,13 @@ import android.graphics.Color;
return mFrozen;
}
/**
* Mark this object as frozen, indicating that it should not be mutated.
*/
public final void freeze() {
mFrozen = true;
}
/**
* Left position of this DrawCommand relative to the hosting View.
*/
@ -226,7 +233,7 @@ import android.graphics.Color;
/**
* Updates boundaries of this DrawCommand.
*/
private void setBounds(float left, float top, float right, float bottom) {
protected final void setBounds(float left, float top, float right, float bottom) {
mLeft = left;
mTop = top;
mRight = right;
@ -238,7 +245,7 @@ import android.graphics.Color;
/**
* Returns true if boundaries match and don't need to be updated. False otherwise.
*/
private boolean boundsMatch(float left, float top, float right, float bottom) {
protected final boolean boundsMatch(float left, float top, float right, float bottom) {
return mLeft == left && mTop == top && mRight == right && mBottom == bottom;
}
}

View File

@ -72,48 +72,53 @@ import android.graphics.RectF;
float clipRight,
float clipBottom,
float clipRadius) {
DrawView drawView = (DrawView)
updateBoundsAndFreeze(
left,
top,
right,
bottom,
clipLeft,
clipTop,
clipRight,
clipBottom);
boolean clipRadiusChanged = Math.abs(mClipRadius - clipRadius) > 0.001f;
boolean logicalBoundsChanged =
!logicalBoundsMatch(logicalLeft, logicalTop, logicalRight, logicalBottom);
if (drawView == this && (clipRadiusChanged || logicalBoundsChanged)) {
// everything matches except the clip radius, so we clone the old one so that we can update
// the clip radius in the block below.
try {
drawView = (DrawView) clone();
} catch (CloneNotSupportedException e) {
// This should not happen since AbstractDrawCommand implements Cloneable
throw new RuntimeException(e);
}
if (!isFrozen()) {
// We haven't collected this draw view yet, so we can just set everything.
setBounds(left, top, right, bottom);
setClipBounds(clipLeft, clipTop, clipRight, clipBottom);
setClipRadius(clipRadius);
setLogicalBounds(logicalLeft, logicalTop, logicalRight, logicalBottom);
freeze();
return this;
}
if (drawView != this) {
drawView.mClipRadius = clipRadius;
if (clipRadius > MINIMUM_ROUNDED_CLIPPING_VALUE) {
// update the path that we'll clip based on
drawView.updateClipPath();
} else {
drawView.mPath = null;
}
boolean boundsMatch = boundsMatch(left, top, right, bottom);
boolean clipBoundsMatch = clipBoundsMatch(clipLeft, clipTop, clipRight, clipBottom);
boolean clipRadiusMatch = mClipRadius == clipRadius;
boolean logicalBoundsMatch =
logicalBoundsMatch(logicalLeft, logicalTop, logicalRight, logicalBottom);
if (logicalBoundsChanged) {
drawView.setLogicalBounds(logicalLeft, logicalTop, logicalRight, logicalBottom);
}
// It is very important that we unset this, as our spec is that newly created DrawViews are
// handled differently by the FlatViewGroup. This is needed because updateBoundsAndFreeze
// uses .clone(), so we maintain the previous state.
drawView.mWasMounted = false;
// See if we can reuse the draw view.
if (boundsMatch && clipBoundsMatch && clipRadiusMatch && logicalBoundsMatch) {
return this;
}
DrawView drawView = (DrawView) mutableCopy();
if (!boundsMatch) {
drawView.setBounds(left, top, right, bottom);
}
if (!clipBoundsMatch) {
drawView.setClipBounds(clipLeft, clipTop, clipRight, clipBottom);
}
if (!logicalBoundsMatch) {
drawView.setLogicalBounds(logicalLeft, logicalTop, logicalRight, logicalBottom);
}
if (!clipRadiusMatch || !boundsMatch) {
// If the bounds change, we need to update the clip path.
drawView.setClipRadius(clipRadius);
}
// It is very important that we unset this, as our spec is that newly created DrawViews
// are handled differently by the FlatViewGroup. This is needed because clone() maintains
// the previous state.
drawView.mWasMounted = false;
drawView.freeze();
return drawView;
}
@ -143,6 +148,22 @@ import android.graphics.RectF;
}
}
/**
* Set the clip radius. Should only be called when the clip radius is first set or when it
* changes, in order to avoid extra work.
*
* @param clipRadius The new clip radius.
*/
void setClipRadius(float clipRadius) {
mClipRadius = clipRadius;
if (clipRadius > MINIMUM_ROUNDED_CLIPPING_VALUE) {
// update the path that we'll clip based on
updateClipPath();
} else {
mPath = null;
}
}
/**
* Update the path with which we'll clip this view
*/

View File

@ -503,7 +503,7 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
float clipRight,
float clipBottom) {
Assertions.assumeNotNull(mDrawView);
if (mDrawView.reactTag == 0) {
if (mDrawView == EMPTY_DRAW_VIEW) {
// This is the first time we have collected this DrawView, but we have to create a new
// DrawView anyway, as reactTag is final, and our DrawView instance is the static copy.
mDrawView = new DrawView(getReactTag());