Merge "Optimize VelocityTracker to run in linear time." into gingerbread

This commit is contained in:
Jeff Brown
2010-08-10 16:26:56 -07:00
committed by Android (Google) Code Review
2 changed files with 160 additions and 84 deletions

View File

@@ -132,9 +132,9 @@ public final class InputQueue {
synchronized (sLock) {
FinishedCallback callback = sRecycleHead;
if (callback != null) {
callback.mRecycleNext = null;
sRecycleHead = callback.mRecycleNext;
sRecycleCount -= 1;
callback.mRecycleNext = null;
} else {
callback = new FinishedCallback();
}

View File

@@ -33,14 +33,15 @@ import android.util.PoolableManager;
* and {@link #getXVelocity()}.
*/
public final class VelocityTracker implements Poolable<VelocityTracker> {
static final String TAG = "VelocityTracker";
static final boolean DEBUG = false;
static final boolean localLOGV = DEBUG || Config.LOGV;
private static final String TAG = "VelocityTracker";
private static final boolean DEBUG = false;
private static final boolean localLOGV = DEBUG || Config.LOGV;
static final int NUM_PAST = 10;
static final int MAX_AGE_MILLISECONDS = 200;
private static final int NUM_PAST = 10;
private static final int MAX_AGE_MILLISECONDS = 200;
private static final int POINTER_POOL_CAPACITY = 20;
static final VelocityTracker[] mPool = new VelocityTracker[1];
private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
Pools.finitePool(new PoolableManager<VelocityTracker>() {
public VelocityTracker newInstance() {
@@ -48,16 +49,19 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
}
public void onAcquired(VelocityTracker element) {
element.clear();
}
public void onReleased(VelocityTracker element) {
element.clear();
}
}, 2));
private static final int INITIAL_POINTERS = 5;
private static Pointer sRecycledPointerListHead;
private static int sRecycledPointerCount;
private static final class PointerData {
private static final class Pointer {
public Pointer next;
public int id;
public float xVelocity;
public float yVelocity;
@@ -65,11 +69,13 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
public final float[] pastX = new float[NUM_PAST];
public final float[] pastY = new float[NUM_PAST];
public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel
public int generation;
}
private PointerData[] mPointers = new PointerData[INITIAL_POINTERS];
private int mNumPointers;
private Pointer mPointerListHead; // sorted by id in increasing order
private int mLastTouchIndex;
private int mGeneration;
private VelocityTracker mNext;
@@ -115,7 +121,9 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* Reset the velocity tracker back to its initial state.
*/
public void clear() {
mNumPointers = 0;
releasePointerList(mPointerListHead);
mPointerListHead = null;
mLastTouchIndex = 0;
}
@@ -134,56 +142,62 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
final int lastTouchIndex = mLastTouchIndex;
final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST;
final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST;
final int generation = mGeneration++;
if (pointerCount < mNumPointers) {
final PointerData[] pointers = mPointers;
int i = mNumPointers;
while (--i >= 0) {
final PointerData pointerData = pointers[i];
if (ev.findPointerIndex(pointerData.id) == -1) {
// Pointer went up.
// Shuffle pointers down to fill the hole. Place the old pointer data at
// the end so we can recycle it if more pointers are added later.
mNumPointers -= 1;
final int remaining = mNumPointers - i;
if (remaining != 0) {
System.arraycopy(pointers, i + 1, pointers, i, remaining);
pointers[mNumPointers] = pointerData;
}
}
}
}
mLastTouchIndex = finalTouchIndex;
// Update pointer data.
Pointer previousPointer = null;
for (int i = 0; i < pointerCount; i++){
final int pointerId = ev.getPointerId(i);
PointerData pointerData = getPointerData(pointerId);
if (pointerData == null) {
// Pointer went down.
// Add a new entry. Write a sentinel at the end of the pastTime trace so we
// will be able to tell where the trace started.
final PointerData[] oldPointers = mPointers;
final int newPointerIndex = mNumPointers;
if (newPointerIndex < oldPointers.length) {
pointerData = oldPointers[newPointerIndex];
if (pointerData == null) {
pointerData = new PointerData();
oldPointers[newPointerIndex] = pointerData;
}
} else {
final PointerData[] newPointers = new PointerData[newPointerIndex * 2];
System.arraycopy(oldPointers, 0, newPointers, 0, newPointerIndex);
mPointers = newPointers;
pointerData = new PointerData();
newPointers[newPointerIndex] = pointerData;
}
pointerData.id = pointerId;
pointerData.pastTime[lastTouchIndex] = Long.MIN_VALUE;
mNumPointers += 1;
// Find the pointer data for this pointer id.
// This loop is optimized for the common case where pointer ids in the event
// are in sorted order. However, we check for this case explicitly and
// perform a full linear scan from the start if needed.
Pointer nextPointer;
if (previousPointer == null || pointerId < previousPointer.id) {
previousPointer = null;
nextPointer = mPointerListHead;
} else {
nextPointer = previousPointer.next;
}
final float[] pastX = pointerData.pastX;
final float[] pastY = pointerData.pastY;
final long[] pastTime = pointerData.pastTime;
final Pointer pointer;
for (;;) {
if (nextPointer != null) {
final int nextPointerId = nextPointer.id;
if (nextPointerId == pointerId) {
pointer = nextPointer;
break;
}
if (nextPointerId < pointerId) {
nextPointer = nextPointer.next;
continue;
}
}
// Pointer went down. Add it to the list.
// Write a sentinel at the end of the pastTime trace so we will be able to
// tell when the trace started.
pointer = obtainPointer();
pointer.id = pointerId;
pointer.pastTime[lastTouchIndex] = Long.MIN_VALUE;
pointer.next = nextPointer;
if (previousPointer == null) {
mPointerListHead = pointer;
} else {
previousPointer.next = pointer;
}
break;
}
pointer.generation = generation;
previousPointer = pointer;
final float[] pastX = pointer.pastX;
final float[] pastY = pointer.pastY;
final long[] pastTime = pointer.pastTime;
for (int j = 0; j < historySize; j++) {
final int touchIndex = (nextTouchIndex + j) % NUM_PAST;
@@ -196,7 +210,23 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
pastTime[finalTouchIndex] = ev.getEventTime();
}
mLastTouchIndex = finalTouchIndex;
// Find removed pointers.
previousPointer = null;
for (Pointer pointer = mPointerListHead; pointer != null; ) {
final Pointer nextPointer = pointer.next;
if (pointer.generation != generation) {
// Pointer went up. Remove it from the list.
if (previousPointer == null) {
mPointerListHead = nextPointer;
} else {
previousPointer.next = nextPointer;
}
releasePointer(pointer);
} else {
previousPointer = pointer;
}
pointer = nextPointer;
}
}
/**
@@ -223,13 +253,10 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* must be positive.
*/
public void computeCurrentVelocity(int units, float maxVelocity) {
final int numPointers = mNumPointers;
final PointerData[] pointers = mPointers;
final int lastTouchIndex = mLastTouchIndex;
for (int p = 0; p < numPointers; p++) {
final PointerData pointerData = pointers[p];
final long[] pastTime = pointerData.pastTime;
for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
final long[] pastTime = pointer.pastTime;
// Search backwards in time for oldest acceptable time.
// Stop at the beginning of the trace as indicated by the sentinel time Long.MIN_VALUE.
@@ -253,8 +280,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
}
// Kind-of stupid.
final float[] pastX = pointerData.pastX;
final float[] pastY = pointerData.pastY;
final float[] pastX = pointer.pastX;
final float[] pastY = pointer.pastY;
final float oldestX = pastX[oldestTouchIndex];
final float oldestY = pastY[oldestTouchIndex];
@@ -290,11 +317,11 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
accumY = maxVelocity;
}
pointerData.xVelocity = accumX;
pointerData.yVelocity = accumY;
pointer.xVelocity = accumX;
pointer.yVelocity = accumY;
if (localLOGV) {
Log.v(TAG, "[" + p + "] Pointer " + pointerData.id
Log.v(TAG, "Pointer " + pointer.id
+ ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches);
}
}
@@ -307,8 +334,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return The previously computed X velocity.
*/
public float getXVelocity() {
PointerData pointerData = getPointerData(0);
return pointerData != null ? pointerData.xVelocity : 0;
Pointer pointer = getPointer(0);
return pointer != null ? pointer.xVelocity : 0;
}
/**
@@ -318,8 +345,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return The previously computed Y velocity.
*/
public float getYVelocity() {
PointerData pointerData = getPointerData(0);
return pointerData != null ? pointerData.yVelocity : 0;
Pointer pointer = getPointer(0);
return pointer != null ? pointer.yVelocity : 0;
}
/**
@@ -330,8 +357,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return The previously computed X velocity.
*/
public float getXVelocity(int id) {
PointerData pointerData = getPointerData(id);
return pointerData != null ? pointerData.xVelocity : 0;
Pointer pointer = getPointer(id);
return pointer != null ? pointer.xVelocity : 0;
}
/**
@@ -342,19 +369,68 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return The previously computed Y velocity.
*/
public float getYVelocity(int id) {
PointerData pointerData = getPointerData(id);
return pointerData != null ? pointerData.yVelocity : 0;
Pointer pointer = getPointer(id);
return pointer != null ? pointer.yVelocity : 0;
}
private final PointerData getPointerData(int id) {
final PointerData[] pointers = mPointers;
final int numPointers = mNumPointers;
for (int p = 0; p < numPointers; p++) {
PointerData pointerData = pointers[p];
if (pointerData.id == id) {
return pointerData;
private final Pointer getPointer(int id) {
for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
if (pointer.id == id) {
return pointer;
}
}
return null;
}
private static final Pointer obtainPointer() {
synchronized (sPool) {
if (sRecycledPointerCount != 0) {
Pointer element = sRecycledPointerListHead;
sRecycledPointerCount -= 1;
sRecycledPointerListHead = element.next;
element.next = null;
return element;
}
}
return new Pointer();
}
private static final void releasePointer(Pointer pointer) {
synchronized (sPool) {
if (sRecycledPointerCount < POINTER_POOL_CAPACITY) {
pointer.next = sRecycledPointerListHead;
sRecycledPointerCount += 1;
sRecycledPointerListHead = pointer;
}
}
}
private static final void releasePointerList(Pointer pointer) {
if (pointer != null) {
synchronized (sPool) {
int count = sRecycledPointerCount;
if (count >= POINTER_POOL_CAPACITY) {
return;
}
Pointer tail = pointer;
for (;;) {
count += 1;
if (count >= POINTER_POOL_CAPACITY) {
break;
}
Pointer next = tail.next;
if (next == null) {
break;
}
tail = next;
}
tail.next = sRecycledPointerListHead;
sRecycledPointerCount = count;
sRecycledPointerListHead = pointer;
}
}
}
}