diff -r 000000000000 -r 6474c204b198 mobile/android/base/gfx/DrawTimingQueue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mobile/android/base/gfx/DrawTimingQueue.java Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,95 @@ +/* -*- 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; + } +}