gecko/mobile/android/base/gfx/DrawTimingQueue.java

96 lines
3.7 KiB
Java

/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
import android.os.SystemClock;
/**
* A custom-built data structure to assist with measuring draw times.
*
* This class maintains a fixed-size circular buffer of DisplayPortMetrics
* objects and associated timestamps. It provides only three operations, which
* is all we require for our purposes of measuring draw times. Note
* in particular that the class is designed so that even though it is
* accessed from multiple threads, it does not require synchronization;
* any concurrency errors that result from this are handled gracefully.
*
* Assuming an unrolled buffer so that mTail is greater than mHead, the data
* stored in the buffer at entries [mHead, mTail) will never be modified, and
* so are "safe" to read. If this reading is done on the same thread that
* owns mHead, then reading the range [mHead, mTail) is guaranteed to be safe
* since the range itself will not shrink.
*/
final class DrawTimingQueue {
private static final String LOGTAG = "GeckoDrawTimingQueue";
private static final int BUFFER_SIZE = 16;
private final DisplayPortMetrics[] mMetrics;
private final long[] mTimestamps;
private int mHead;
private int mTail;
DrawTimingQueue() {
mMetrics = new DisplayPortMetrics[BUFFER_SIZE];
mTimestamps = new long[BUFFER_SIZE];
mHead = BUFFER_SIZE - 1;
mTail = 0;
}
/**
* Add a new entry to the tail of the queue. If the buffer is full,
* do nothing. This must only be called from the Java UI thread.
*/
boolean add(DisplayPortMetrics metrics) {
if (mHead == mTail) {
return false;
}
mMetrics[mTail] = metrics;
mTimestamps[mTail] = SystemClock.uptimeMillis();
mTail = (mTail + 1) % BUFFER_SIZE;
return true;
}
/**
* Find the timestamp associated with the given metrics, AND remove
* all metrics objects from the start of the queue up to and including
* the one provided. Note that because of draw coalescing, the metrics
* object passed in here may not be the one at the head of the queue,
* and so we must iterate our way through the list to find it.
* This must only be called from the compositor thread.
*/
long findTimeFor(DisplayPortMetrics metrics) {
// keep a copy of the tail pointer so that we ignore new items
// added to the queue while we are searching. this is fine because
// the one we are looking for will either have been added already
// or will not be in the queue at all.
int tail = mTail;
// walk through the "safe" range from mHead to tail; these entries
// will not be modified unless we change mHead.
int i = (mHead + 1) % BUFFER_SIZE;
while (i != tail) {
if (mMetrics[i].fuzzyEquals(metrics)) {
// found it, copy out the timestamp to a local var BEFORE
// changing mHead or add could clobber the timestamp.
long timestamp = mTimestamps[i];
mHead = i;
return timestamp;
}
i = (i + 1) % BUFFER_SIZE;
}
return -1;
}
/**
* Reset the buffer to empty.
* This must only be called from the compositor thread.
*/
void reset() {
// we can only modify mHead on this thread.
mHead = (mTail + BUFFER_SIZE - 1) % BUFFER_SIZE;
}
}