Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
michael@0 | 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 4 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | package org.mozilla.gecko.gfx; |
michael@0 | 7 | |
michael@0 | 8 | import android.os.SystemClock; |
michael@0 | 9 | |
michael@0 | 10 | /** |
michael@0 | 11 | * A custom-built data structure to assist with measuring draw times. |
michael@0 | 12 | * |
michael@0 | 13 | * This class maintains a fixed-size circular buffer of DisplayPortMetrics |
michael@0 | 14 | * objects and associated timestamps. It provides only three operations, which |
michael@0 | 15 | * is all we require for our purposes of measuring draw times. Note |
michael@0 | 16 | * in particular that the class is designed so that even though it is |
michael@0 | 17 | * accessed from multiple threads, it does not require synchronization; |
michael@0 | 18 | * any concurrency errors that result from this are handled gracefully. |
michael@0 | 19 | * |
michael@0 | 20 | * Assuming an unrolled buffer so that mTail is greater than mHead, the data |
michael@0 | 21 | * stored in the buffer at entries [mHead, mTail) will never be modified, and |
michael@0 | 22 | * so are "safe" to read. If this reading is done on the same thread that |
michael@0 | 23 | * owns mHead, then reading the range [mHead, mTail) is guaranteed to be safe |
michael@0 | 24 | * since the range itself will not shrink. |
michael@0 | 25 | */ |
michael@0 | 26 | final class DrawTimingQueue { |
michael@0 | 27 | private static final String LOGTAG = "GeckoDrawTimingQueue"; |
michael@0 | 28 | private static final int BUFFER_SIZE = 16; |
michael@0 | 29 | |
michael@0 | 30 | private final DisplayPortMetrics[] mMetrics; |
michael@0 | 31 | private final long[] mTimestamps; |
michael@0 | 32 | |
michael@0 | 33 | private int mHead; |
michael@0 | 34 | private int mTail; |
michael@0 | 35 | |
michael@0 | 36 | DrawTimingQueue() { |
michael@0 | 37 | mMetrics = new DisplayPortMetrics[BUFFER_SIZE]; |
michael@0 | 38 | mTimestamps = new long[BUFFER_SIZE]; |
michael@0 | 39 | mHead = BUFFER_SIZE - 1; |
michael@0 | 40 | mTail = 0; |
michael@0 | 41 | } |
michael@0 | 42 | |
michael@0 | 43 | /** |
michael@0 | 44 | * Add a new entry to the tail of the queue. If the buffer is full, |
michael@0 | 45 | * do nothing. This must only be called from the Java UI thread. |
michael@0 | 46 | */ |
michael@0 | 47 | boolean add(DisplayPortMetrics metrics) { |
michael@0 | 48 | if (mHead == mTail) { |
michael@0 | 49 | return false; |
michael@0 | 50 | } |
michael@0 | 51 | mMetrics[mTail] = metrics; |
michael@0 | 52 | mTimestamps[mTail] = SystemClock.uptimeMillis(); |
michael@0 | 53 | mTail = (mTail + 1) % BUFFER_SIZE; |
michael@0 | 54 | return true; |
michael@0 | 55 | } |
michael@0 | 56 | |
michael@0 | 57 | /** |
michael@0 | 58 | * Find the timestamp associated with the given metrics, AND remove |
michael@0 | 59 | * all metrics objects from the start of the queue up to and including |
michael@0 | 60 | * the one provided. Note that because of draw coalescing, the metrics |
michael@0 | 61 | * object passed in here may not be the one at the head of the queue, |
michael@0 | 62 | * and so we must iterate our way through the list to find it. |
michael@0 | 63 | * This must only be called from the compositor thread. |
michael@0 | 64 | */ |
michael@0 | 65 | long findTimeFor(DisplayPortMetrics metrics) { |
michael@0 | 66 | // keep a copy of the tail pointer so that we ignore new items |
michael@0 | 67 | // added to the queue while we are searching. this is fine because |
michael@0 | 68 | // the one we are looking for will either have been added already |
michael@0 | 69 | // or will not be in the queue at all. |
michael@0 | 70 | int tail = mTail; |
michael@0 | 71 | // walk through the "safe" range from mHead to tail; these entries |
michael@0 | 72 | // will not be modified unless we change mHead. |
michael@0 | 73 | int i = (mHead + 1) % BUFFER_SIZE; |
michael@0 | 74 | while (i != tail) { |
michael@0 | 75 | if (mMetrics[i].fuzzyEquals(metrics)) { |
michael@0 | 76 | // found it, copy out the timestamp to a local var BEFORE |
michael@0 | 77 | // changing mHead or add could clobber the timestamp. |
michael@0 | 78 | long timestamp = mTimestamps[i]; |
michael@0 | 79 | mHead = i; |
michael@0 | 80 | return timestamp; |
michael@0 | 81 | } |
michael@0 | 82 | i = (i + 1) % BUFFER_SIZE; |
michael@0 | 83 | } |
michael@0 | 84 | return -1; |
michael@0 | 85 | } |
michael@0 | 86 | |
michael@0 | 87 | /** |
michael@0 | 88 | * Reset the buffer to empty. |
michael@0 | 89 | * This must only be called from the compositor thread. |
michael@0 | 90 | */ |
michael@0 | 91 | void reset() { |
michael@0 | 92 | // we can only modify mHead on this thread. |
michael@0 | 93 | mHead = (mTail + BUFFER_SIZE - 1) % BUFFER_SIZE; |
michael@0 | 94 | } |
michael@0 | 95 | } |