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