Coverage Report

Created: 2018-09-25 14:53

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