Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include <stddef.h> // for size_t
7 : #include "Units.h" // for ScreenIntRect
8 : #include "gfxRect.h" // for gfxRect
9 : #include "gfxPrefs.h" // for gfxPrefs
10 : #include "mozilla/gfx/Point.h" // for IntSize, Point
11 : #include "mozilla/gfx/Rect.h" // for Rect
12 : #include "mozilla/gfx/Types.h" // for Color, SurfaceFormat
13 : #include "mozilla/layers/Compositor.h" // for Compositor
14 : #include "mozilla/layers/CompositorTypes.h"
15 : #include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
16 : #include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
17 : #include "nsPoint.h" // for nsIntPoint
18 : #include "nsRect.h" // for mozilla::gfx::IntRect
19 : #include "nsIFile.h" // for nsIFile
20 : #include "nsDirectoryServiceDefs.h" // for NS_OS_TMP_DIR
21 : #include "mozilla/Sprintf.h"
22 : #include "FPSCounter.h"
23 :
24 : namespace mozilla {
25 : namespace layers {
26 :
27 : using namespace mozilla::gfx;
28 :
29 2 : FPSCounter::FPSCounter(const char* aName)
30 : : mWriteIndex(0)
31 : , mIteratorIndex(-1)
32 2 : , mFPSName(aName)
33 : {
34 2 : Init();
35 2 : }
36 :
37 0 : FPSCounter::~FPSCounter() { }
38 :
39 : void
40 2 : FPSCounter::Init()
41 : {
42 4802 : for (int i = 0; i < kMaxFrames; i++) {
43 4800 : mFrameTimestamps.AppendElement(TimeStamp());
44 : }
45 2 : mLastInterval = TimeStamp::Now();
46 2 : }
47 :
48 : // Returns true if we captured a full interval of data
49 : bool
50 0 : FPSCounter::CapturedFullInterval(TimeStamp aTimestamp) {
51 0 : TimeDuration duration = aTimestamp - mLastInterval;
52 0 : return duration.ToSeconds() >= kFpsDumpInterval;
53 : }
54 :
55 : void
56 0 : FPSCounter::AddFrame(TimeStamp aTimestamp) {
57 0 : NS_ASSERTION(mWriteIndex < kMaxFrames, "We probably have a bug with the circular buffer");
58 0 : NS_ASSERTION(mWriteIndex >= 0, "Circular Buffer index should never be negative");
59 :
60 0 : int index = mWriteIndex++;
61 0 : if (mWriteIndex == kMaxFrames) {
62 0 : mWriteIndex = 0;
63 : }
64 :
65 0 : mFrameTimestamps[index] = aTimestamp;
66 :
67 0 : if (CapturedFullInterval(aTimestamp)) {
68 0 : PrintFPS();
69 0 : WriteFrameTimeStamps();
70 0 : mLastInterval = aTimestamp;
71 : }
72 0 : }
73 :
74 : double
75 0 : FPSCounter::AddFrameAndGetFps(TimeStamp aTimestamp) {
76 0 : AddFrame(aTimestamp);
77 0 : return GetFPS(aTimestamp);
78 : }
79 :
80 : int
81 0 : FPSCounter::GetLatestReadIndex()
82 : {
83 0 : if (mWriteIndex == 0) {
84 0 : return kMaxFrames - 1;
85 : }
86 :
87 0 : return mWriteIndex - 1;
88 : }
89 :
90 : TimeStamp
91 0 : FPSCounter::GetLatestTimeStamp()
92 : {
93 0 : TimeStamp timestamp = mFrameTimestamps[GetLatestReadIndex()];
94 0 : MOZ_ASSERT(!timestamp.IsNull(), "Cannot use null timestamps");
95 0 : return timestamp;
96 : }
97 :
98 : // Returns true if we iterated over a full interval of data
99 : bool
100 0 : FPSCounter::IteratedFullInterval(TimeStamp aTimestamp, double aDuration) {
101 0 : MOZ_ASSERT(mIteratorIndex >= 0, "Cannot be negative");
102 0 : MOZ_ASSERT(mIteratorIndex < kMaxFrames, "Iterator index cannot be greater than kMaxFrames");
103 :
104 0 : TimeStamp currentStamp = mFrameTimestamps[mIteratorIndex];
105 0 : TimeDuration duration = aTimestamp - currentStamp;
106 0 : return duration.ToSeconds() >= aDuration;
107 : }
108 :
109 : void
110 0 : FPSCounter::ResetReverseIterator()
111 : {
112 0 : mIteratorIndex = GetLatestReadIndex();
113 0 : }
114 :
115 : /***
116 : * Returns true if we have another timestamp that is valid and
117 : * is within the given duration that we're interested in.
118 : * Duration is in seconds
119 : */
120 0 : bool FPSCounter::HasNext(TimeStamp aTimestamp, double aDuration)
121 : {
122 : // Order of evaluation here has to stay the same
123 : // otherwise IteratedFullInterval reads from mFrameTimestamps which cannot
124 : // be null
125 0 : return (mIteratorIndex != mWriteIndex) // Didn't loop around the buffer
126 0 : && !mFrameTimestamps[mIteratorIndex].IsNull() // valid data
127 0 : && !IteratedFullInterval(aTimestamp, aDuration);
128 : }
129 :
130 : TimeStamp
131 0 : FPSCounter::GetNextTimeStamp()
132 : {
133 0 : TimeStamp timestamp = mFrameTimestamps[mIteratorIndex--];
134 0 : MOZ_ASSERT(!timestamp.IsNull(), "Reading Invalid Timestamp Data");
135 :
136 0 : if (mIteratorIndex == -1) {
137 0 : mIteratorIndex = kMaxFrames - 1;
138 : }
139 0 : return timestamp;
140 : }
141 :
142 : /**
143 : * GetFPS calculates how many frames we've already composited from the current
144 : * frame timestamp and we iterate from the latest timestamp we recorded,
145 : * going back in time. When we hit a frame that is longer than the 1 second
146 : * from the current composited frame, we return how many frames we've counted.
147 : * Just a visualization:
148 : *
149 : * aTimestamp
150 : * Frames: 1 2 3 4 5 6 7 8 9 10 11 12
151 : * Time -------------------------->
152 : *
153 : * GetFPS iterates from aTimestamp, which is the current frame.
154 : * Then starting at frame 12, going back to frame 11, 10, etc, we calculate
155 : * the duration of the recorded frame timestamp from aTimestamp.
156 : * Once duration is greater than 1 second, we return how many frames
157 : * we composited.
158 : */
159 : double
160 0 : FPSCounter::GetFPS(TimeStamp aTimestamp)
161 : {
162 0 : int frameCount = 0;
163 0 : int duration = 1.0; // Only care about the last 1s of data
164 :
165 0 : ResetReverseIterator();
166 0 : while (HasNext(aTimestamp, duration)) {
167 0 : GetNextTimeStamp();
168 0 : frameCount++;
169 : }
170 :
171 0 : return frameCount;
172 : }
173 :
174 : // Iterate the same way we do in GetFPS()
175 : int
176 0 : FPSCounter::BuildHistogram(std::map<int, int>& aFpsData)
177 : {
178 0 : TimeStamp currentIntervalStart = GetLatestTimeStamp();
179 0 : TimeStamp currentTimeStamp = GetLatestTimeStamp();
180 0 : TimeStamp startTimeStamp = GetLatestTimeStamp();
181 :
182 0 : int frameCount = 0;
183 0 : int totalFrameCount = 0;
184 :
185 0 : ResetReverseIterator();
186 0 : while (HasNext(startTimeStamp)) {
187 0 : currentTimeStamp = GetNextTimeStamp();
188 0 : TimeDuration interval = currentIntervalStart - currentTimeStamp;
189 :
190 0 : if (interval.ToSeconds() >= 1.0 ) {
191 0 : currentIntervalStart = currentTimeStamp;
192 0 : aFpsData[frameCount]++;
193 0 : frameCount = 0;
194 : }
195 :
196 0 : frameCount++;
197 0 : totalFrameCount++;
198 : }
199 :
200 0 : TimeDuration totalTime = currentIntervalStart - currentTimeStamp;
201 0 : printf_stderr("Discarded %d frames over %f ms in histogram for %s\n",
202 0 : frameCount, totalTime.ToMilliseconds(), mFPSName);
203 0 : return totalFrameCount;
204 : }
205 :
206 : // Iterate the same way we do in GetFPS()
207 : void
208 0 : FPSCounter::WriteFrameTimeStamps(PRFileDesc* fd)
209 : {
210 0 : const int bufferSize = 256;
211 : char buffer[bufferSize];
212 0 : int writtenCount = SprintfLiteral(buffer, "FPS Data for: %s\n", mFPSName);
213 0 : MOZ_ASSERT(writtenCount >= 0);
214 0 : PR_Write(fd, buffer, writtenCount);
215 :
216 0 : ResetReverseIterator();
217 0 : TimeStamp startTimeStamp = GetLatestTimeStamp();
218 :
219 0 : MOZ_ASSERT(HasNext(startTimeStamp));
220 0 : TimeStamp previousSample = GetNextTimeStamp();
221 :
222 0 : MOZ_ASSERT(HasNext(startTimeStamp));
223 0 : TimeStamp nextTimeStamp = GetNextTimeStamp();
224 :
225 0 : while (HasNext(startTimeStamp)) {
226 0 : TimeDuration duration = previousSample - nextTimeStamp;
227 0 : writtenCount = SprintfLiteral(buffer, "%f,\n", duration.ToMilliseconds());
228 :
229 0 : MOZ_ASSERT(writtenCount >= 0);
230 0 : PR_Write(fd, buffer, writtenCount);
231 :
232 0 : previousSample = nextTimeStamp;
233 0 : nextTimeStamp = GetNextTimeStamp();
234 : }
235 0 : }
236 :
237 : double
238 0 : FPSCounter::GetMean(std::map<int, int> aHistogram)
239 : {
240 0 : double average = 0.0;
241 0 : double samples = 0.0;
242 :
243 0 : for (std::map<int, int>::iterator iter = aHistogram.begin();
244 0 : iter != aHistogram.end(); ++iter)
245 : {
246 0 : int fps = iter->first;
247 0 : int count = iter->second;
248 :
249 0 : average += fps * count;
250 0 : samples += count;
251 : }
252 :
253 0 : return average / samples;
254 : }
255 :
256 : double
257 0 : FPSCounter::GetStdDev(std::map<int, int> aHistogram)
258 : {
259 0 : double sumOfDifferences = 0;
260 0 : double average = GetMean(aHistogram);
261 0 : double samples = 0.0;
262 :
263 0 : for (std::map<int, int>::iterator iter = aHistogram.begin();
264 0 : iter != aHistogram.end(); ++iter)
265 : {
266 0 : int fps = iter->first;
267 0 : int count = iter->second;
268 :
269 0 : double diff = ((double) fps) - average;
270 0 : diff *= diff;
271 :
272 0 : for (int i = 0; i < count; i++) {
273 0 : sumOfDifferences += diff;
274 : }
275 0 : samples += count;
276 : }
277 :
278 0 : double stdDev = sumOfDifferences / samples;
279 0 : return sqrt(stdDev);
280 : }
281 :
282 : void
283 0 : FPSCounter::PrintFPS()
284 : {
285 0 : if (!gfxPrefs::FPSPrintHistogram()) {
286 0 : return;
287 : }
288 :
289 0 : std::map<int, int> histogram;
290 0 : int totalFrames = BuildHistogram(histogram);
291 :
292 0 : TimeDuration measurementInterval = mFrameTimestamps[GetLatestReadIndex()] - mLastInterval;
293 0 : printf_stderr("FPS for %s. Total Frames: %d Time Interval: %f seconds\n",
294 0 : mFPSName, totalFrames, measurementInterval.ToSecondsSigDigits());
295 :
296 0 : PrintHistogram(histogram);
297 : }
298 :
299 : void
300 0 : FPSCounter::PrintHistogram(std::map<int, int>& aHistogram)
301 : {
302 0 : int length = 0;
303 0 : const int kBufferLength = 512;
304 : char buffer[kBufferLength];
305 :
306 0 : for (std::map<int, int>::iterator iter = aHistogram.begin();
307 0 : iter != aHistogram.end(); iter++)
308 : {
309 0 : int fps = iter->first;
310 0 : int count = iter->second;
311 :
312 0 : length += snprintf(buffer + length, kBufferLength - length,
313 : "FPS: %d = %d. ", fps, count);
314 0 : NS_ASSERTION(length >= kBufferLength, "Buffer overrun while printing FPS histogram.");
315 : }
316 :
317 0 : printf_stderr("%s\n", buffer);
318 0 : printf_stderr("Mean: %f , std dev %f\n", GetMean(aHistogram), GetStdDev(aHistogram));
319 0 : }
320 :
321 : // Write FPS timestamp data to a file only if
322 : // draw-fps.write-to-file is true
323 : nsresult
324 0 : FPSCounter::WriteFrameTimeStamps()
325 : {
326 0 : if (!gfxPrefs::WriteFPSToFile()) {
327 0 : return NS_OK;
328 : }
329 :
330 0 : MOZ_ASSERT(mWriteIndex == 0);
331 :
332 0 : nsCOMPtr<nsIFile> resultFile;
333 0 : nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(resultFile));
334 0 : NS_ENSURE_SUCCESS(rv, rv);
335 :
336 0 : if (!strncmp(mFPSName, "Compositor", strlen(mFPSName))) {
337 0 : resultFile->Append(NS_LITERAL_STRING("fps.txt"));
338 : } else {
339 0 : resultFile->Append(NS_LITERAL_STRING("txn.txt"));
340 : }
341 :
342 0 : PRFileDesc* fd = nullptr;
343 0 : int mode = 644;
344 0 : int openFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
345 0 : rv = resultFile->OpenNSPRFileDesc(openFlags, mode, &fd);
346 0 : NS_ENSURE_SUCCESS(rv, rv);
347 :
348 0 : WriteFrameTimeStamps(fd);
349 0 : PR_Close(fd);
350 :
351 0 : nsAutoCString path;
352 0 : rv = resultFile->GetNativePath(path);
353 0 : NS_ENSURE_SUCCESS(rv, rv);
354 :
355 0 : printf_stderr("Wrote FPS data to file: %s\n", path.get());
356 0 : return NS_OK;
357 : }
358 :
359 : } // end namespace layers
360 : } // end namespace mozilla
|