1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/gfx/DrawTimingQueue.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,95 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.gfx; 1.10 + 1.11 +import android.os.SystemClock; 1.12 + 1.13 +/** 1.14 + * A custom-built data structure to assist with measuring draw times. 1.15 + * 1.16 + * This class maintains a fixed-size circular buffer of DisplayPortMetrics 1.17 + * objects and associated timestamps. It provides only three operations, which 1.18 + * is all we require for our purposes of measuring draw times. Note 1.19 + * in particular that the class is designed so that even though it is 1.20 + * accessed from multiple threads, it does not require synchronization; 1.21 + * any concurrency errors that result from this are handled gracefully. 1.22 + * 1.23 + * Assuming an unrolled buffer so that mTail is greater than mHead, the data 1.24 + * stored in the buffer at entries [mHead, mTail) will never be modified, and 1.25 + * so are "safe" to read. If this reading is done on the same thread that 1.26 + * owns mHead, then reading the range [mHead, mTail) is guaranteed to be safe 1.27 + * since the range itself will not shrink. 1.28 + */ 1.29 +final class DrawTimingQueue { 1.30 + private static final String LOGTAG = "GeckoDrawTimingQueue"; 1.31 + private static final int BUFFER_SIZE = 16; 1.32 + 1.33 + private final DisplayPortMetrics[] mMetrics; 1.34 + private final long[] mTimestamps; 1.35 + 1.36 + private int mHead; 1.37 + private int mTail; 1.38 + 1.39 + DrawTimingQueue() { 1.40 + mMetrics = new DisplayPortMetrics[BUFFER_SIZE]; 1.41 + mTimestamps = new long[BUFFER_SIZE]; 1.42 + mHead = BUFFER_SIZE - 1; 1.43 + mTail = 0; 1.44 + } 1.45 + 1.46 + /** 1.47 + * Add a new entry to the tail of the queue. If the buffer is full, 1.48 + * do nothing. This must only be called from the Java UI thread. 1.49 + */ 1.50 + boolean add(DisplayPortMetrics metrics) { 1.51 + if (mHead == mTail) { 1.52 + return false; 1.53 + } 1.54 + mMetrics[mTail] = metrics; 1.55 + mTimestamps[mTail] = SystemClock.uptimeMillis(); 1.56 + mTail = (mTail + 1) % BUFFER_SIZE; 1.57 + return true; 1.58 + } 1.59 + 1.60 + /** 1.61 + * Find the timestamp associated with the given metrics, AND remove 1.62 + * all metrics objects from the start of the queue up to and including 1.63 + * the one provided. Note that because of draw coalescing, the metrics 1.64 + * object passed in here may not be the one at the head of the queue, 1.65 + * and so we must iterate our way through the list to find it. 1.66 + * This must only be called from the compositor thread. 1.67 + */ 1.68 + long findTimeFor(DisplayPortMetrics metrics) { 1.69 + // keep a copy of the tail pointer so that we ignore new items 1.70 + // added to the queue while we are searching. this is fine because 1.71 + // the one we are looking for will either have been added already 1.72 + // or will not be in the queue at all. 1.73 + int tail = mTail; 1.74 + // walk through the "safe" range from mHead to tail; these entries 1.75 + // will not be modified unless we change mHead. 1.76 + int i = (mHead + 1) % BUFFER_SIZE; 1.77 + while (i != tail) { 1.78 + if (mMetrics[i].fuzzyEquals(metrics)) { 1.79 + // found it, copy out the timestamp to a local var BEFORE 1.80 + // changing mHead or add could clobber the timestamp. 1.81 + long timestamp = mTimestamps[i]; 1.82 + mHead = i; 1.83 + return timestamp; 1.84 + } 1.85 + i = (i + 1) % BUFFER_SIZE; 1.86 + } 1.87 + return -1; 1.88 + } 1.89 + 1.90 + /** 1.91 + * Reset the buffer to empty. 1.92 + * This must only be called from the compositor thread. 1.93 + */ 1.94 + void reset() { 1.95 + // we can only modify mHead on this thread. 1.96 + mHead = (mTail + BUFFER_SIZE - 1) % BUFFER_SIZE; 1.97 + } 1.98 +}