gecko-dev/gfx/layers/composite/FPSCounter.cpp
Nicholas Nethercote 18fae65f38 Bug 1563139 - Remove StaticPrefs.h. r=glandium
This requires replacing inclusions of it with inclusions of more specific prefs
files.

The exception is that StaticPrefsAll.h, which is equivalent to StaticPrefs.h,
and is used in `Codegen.py` because doing something smarter is tricky and
suitable for a follow-up. As a result, any change to StaticPrefList.yaml will
still trigger recompilation of all the generated DOM bindings files, but that's
still a big improvement over trigger recompilation of every file that uses
static prefs.

Most of the changes in this commit are very boring. The only changes that are
not boring are modules/libpref/*, Codegen.py, and ServoBindings.toml.

Differential Revision: https://phabricator.services.mozilla.com/D39138

--HG--
extra : moz-landing-system : lando
2019-07-26 01:10:23 +00:00

344 lines
10 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <stddef.h> // for size_t
#include "Units.h" // for ScreenIntRect
#include "gfxRect.h" // for gfxRect
#include "mozilla/gfx/Point.h" // for IntSize, Point
#include "mozilla/gfx/Rect.h" // for Rect
#include "mozilla/gfx/Types.h" // for Color, SurfaceFormat
#include "mozilla/layers/Compositor.h" // for Compositor
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
#include "mozilla/StaticPrefs_layers.h"
#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
#include "nsPoint.h" // for nsIntPoint
#include "nsRect.h" // for mozilla::gfx::IntRect
#include "nsIFile.h" // for nsIFile
#include "nsDirectoryServiceDefs.h" // for NS_OS_TMP_DIR
#include "mozilla/Sprintf.h"
#include "FPSCounter.h"
namespace mozilla {
namespace layers {
using namespace mozilla::gfx;
FPSCounter::FPSCounter(const char* aName)
: mWriteIndex(0), mIteratorIndex(-1), mFPSName(aName) {
Init();
}
FPSCounter::~FPSCounter() {}
void FPSCounter::Init() {
for (int i = 0; i < kMaxFrames; i++) {
mFrameTimestamps.AppendElement(TimeStamp());
}
mLastInterval = TimeStamp::Now();
}
// Returns true if we captured a full interval of data
bool FPSCounter::CapturedFullInterval(TimeStamp aTimestamp) {
TimeDuration duration = aTimestamp - mLastInterval;
return duration.ToSeconds() >= kFpsDumpInterval;
}
void FPSCounter::AddFrame(TimeStamp aTimestamp) {
NS_ASSERTION(mWriteIndex < kMaxFrames,
"We probably have a bug with the circular buffer");
NS_ASSERTION(mWriteIndex >= 0,
"Circular Buffer index should never be negative");
int index = mWriteIndex++;
if (mWriteIndex == kMaxFrames) {
mWriteIndex = 0;
}
mFrameTimestamps[index] = aTimestamp;
if (CapturedFullInterval(aTimestamp)) {
PrintFPS();
WriteFrameTimeStamps();
mLastInterval = aTimestamp;
}
}
double FPSCounter::AddFrameAndGetFps(TimeStamp aTimestamp) {
AddFrame(aTimestamp);
return GetFPS(aTimestamp);
}
int FPSCounter::GetLatestReadIndex() {
if (mWriteIndex == 0) {
return kMaxFrames - 1;
}
return mWriteIndex - 1;
}
TimeStamp FPSCounter::GetLatestTimeStamp() {
TimeStamp timestamp = mFrameTimestamps[GetLatestReadIndex()];
MOZ_ASSERT(!timestamp.IsNull(), "Cannot use null timestamps");
return timestamp;
}
// Returns true if we iterated over a full interval of data
bool FPSCounter::IteratedFullInterval(TimeStamp aTimestamp, double aDuration) {
MOZ_ASSERT(mIteratorIndex >= 0, "Cannot be negative");
MOZ_ASSERT(mIteratorIndex < kMaxFrames,
"Iterator index cannot be greater than kMaxFrames");
TimeStamp currentStamp = mFrameTimestamps[mIteratorIndex];
TimeDuration duration = aTimestamp - currentStamp;
return duration.ToSeconds() >= aDuration;
}
void FPSCounter::ResetReverseIterator() {
mIteratorIndex = GetLatestReadIndex();
}
/***
* Returns true if we have another timestamp that is valid and
* is within the given duration that we're interested in.
* Duration is in seconds
*/
bool FPSCounter::HasNext(TimeStamp aTimestamp, double aDuration) {
// Order of evaluation here has to stay the same
// otherwise IteratedFullInterval reads from mFrameTimestamps which cannot
// be null
return (mIteratorIndex != mWriteIndex) // Didn't loop around the buffer
&& !mFrameTimestamps[mIteratorIndex].IsNull() // valid data
&& !IteratedFullInterval(aTimestamp, aDuration);
}
TimeStamp FPSCounter::GetNextTimeStamp() {
TimeStamp timestamp = mFrameTimestamps[mIteratorIndex--];
MOZ_ASSERT(!timestamp.IsNull(), "Reading Invalid Timestamp Data");
if (mIteratorIndex == -1) {
mIteratorIndex = kMaxFrames - 1;
}
return timestamp;
}
/**
* GetFPS calculates how many frames we've already composited from the current
* frame timestamp and we iterate from the latest timestamp we recorded,
* going back in time. When we hit a frame that is longer than the 1 second
* from the current composited frame, we return how many frames we've counted.
* Just a visualization:
*
* aTimestamp
* Frames: 1 2 3 4 5 6 7 8 9 10 11 12
* Time -------------------------->
*
* GetFPS iterates from aTimestamp, which is the current frame.
* Then starting at frame 12, going back to frame 11, 10, etc, we calculate
* the duration of the recorded frame timestamp from aTimestamp.
* Once duration is greater than 1 second, we return how many frames
* we composited.
*/
double FPSCounter::GetFPS(TimeStamp aTimestamp) {
int frameCount = 0;
int duration = 1.0; // Only care about the last 1s of data
ResetReverseIterator();
while (HasNext(aTimestamp, duration)) {
GetNextTimeStamp();
frameCount++;
}
return frameCount;
}
// Iterate the same way we do in GetFPS()
int FPSCounter::BuildHistogram(std::map<int, int>& aFpsData) {
TimeStamp currentIntervalStart = GetLatestTimeStamp();
TimeStamp currentTimeStamp = GetLatestTimeStamp();
TimeStamp startTimeStamp = GetLatestTimeStamp();
int frameCount = 0;
int totalFrameCount = 0;
ResetReverseIterator();
while (HasNext(startTimeStamp)) {
currentTimeStamp = GetNextTimeStamp();
TimeDuration interval = currentIntervalStart - currentTimeStamp;
if (interval.ToSeconds() >= 1.0) {
currentIntervalStart = currentTimeStamp;
aFpsData[frameCount]++;
frameCount = 0;
}
frameCount++;
totalFrameCount++;
}
TimeDuration totalTime = currentIntervalStart - currentTimeStamp;
printf_stderr("Discarded %d frames over %f ms in histogram for %s\n",
frameCount, totalTime.ToMilliseconds(), mFPSName);
return totalFrameCount;
}
// Iterate the same way we do in GetFPS()
void FPSCounter::WriteFrameTimeStamps(PRFileDesc* fd) {
const int bufferSize = 256;
char buffer[bufferSize];
int writtenCount = SprintfLiteral(buffer, "FPS Data for: %s\n", mFPSName);
MOZ_ASSERT(writtenCount < bufferSize);
if (writtenCount >= bufferSize) {
return;
}
PR_Write(fd, buffer, writtenCount);
ResetReverseIterator();
TimeStamp startTimeStamp = GetLatestTimeStamp();
MOZ_ASSERT(HasNext(startTimeStamp));
TimeStamp previousSample = GetNextTimeStamp();
MOZ_ASSERT(HasNext(startTimeStamp));
TimeStamp nextTimeStamp = GetNextTimeStamp();
while (HasNext(startTimeStamp)) {
TimeDuration duration = previousSample - nextTimeStamp;
writtenCount = SprintfLiteral(buffer, "%f,\n", duration.ToMilliseconds());
MOZ_ASSERT(writtenCount < bufferSize);
if (writtenCount >= bufferSize) {
continue;
}
PR_Write(fd, buffer, writtenCount);
previousSample = nextTimeStamp;
nextTimeStamp = GetNextTimeStamp();
}
}
double FPSCounter::GetMean(std::map<int, int> aHistogram) {
double average = 0.0;
double samples = 0.0;
for (std::map<int, int>::iterator iter = aHistogram.begin();
iter != aHistogram.end(); ++iter) {
int fps = iter->first;
int count = iter->second;
average += fps * count;
samples += count;
}
return average / samples;
}
double FPSCounter::GetStdDev(std::map<int, int> aHistogram) {
double sumOfDifferences = 0;
double average = GetMean(aHistogram);
double samples = 0.0;
for (std::map<int, int>::iterator iter = aHistogram.begin();
iter != aHistogram.end(); ++iter) {
int fps = iter->first;
int count = iter->second;
double diff = ((double)fps) - average;
diff *= diff;
for (int i = 0; i < count; i++) {
sumOfDifferences += diff;
}
samples += count;
}
double stdDev = sumOfDifferences / samples;
return sqrt(stdDev);
}
void FPSCounter::PrintFPS() {
if (!StaticPrefs::layers_acceleration_draw_fps_print_histogram()) {
return;
}
std::map<int, int> histogram;
int totalFrames = BuildHistogram(histogram);
TimeDuration measurementInterval =
mFrameTimestamps[GetLatestReadIndex()] - mLastInterval;
printf_stderr("FPS for %s. Total Frames: %d Time Interval: %f seconds\n",
mFPSName, totalFrames,
measurementInterval.ToSecondsSigDigits());
PrintHistogram(histogram);
}
void FPSCounter::PrintHistogram(std::map<int, int>& aHistogram) {
if (aHistogram.size() == 0) {
return;
}
int length = 0;
const int kBufferLength = 512;
int availableSpace = kBufferLength;
char buffer[kBufferLength];
for (std::map<int, int>::iterator iter = aHistogram.begin();
iter != aHistogram.end(); iter++) {
int fps = iter->first;
int count = iter->second;
int lengthRequired =
snprintf(buffer + length, availableSpace, "FPS: %d = %d. ", fps, count);
// Ran out of buffer space. Oh well - just print what we have.
if (lengthRequired > availableSpace) {
break;
}
length += lengthRequired;
availableSpace -= lengthRequired;
}
printf_stderr("%s\n", buffer);
printf_stderr("Mean: %f , std dev %f\n", GetMean(aHistogram),
GetStdDev(aHistogram));
}
// Write FPS timestamp data to a file only if
// draw-fps.write-to-file is true
nsresult FPSCounter::WriteFrameTimeStamps() {
if (!StaticPrefs::layers_acceleration_draw_fps_write_to_file()) {
return NS_OK;
}
MOZ_ASSERT(mWriteIndex == 0);
nsCOMPtr<nsIFile> resultFile;
nsresult rv =
NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(resultFile));
NS_ENSURE_SUCCESS(rv, rv);
if (!strncmp(mFPSName, "Compositor", strlen(mFPSName))) {
resultFile->Append(NS_LITERAL_STRING("fps.txt"));
} else {
resultFile->Append(NS_LITERAL_STRING("txn.txt"));
}
PRFileDesc* fd = nullptr;
int mode = 644;
int openFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
rv = resultFile->OpenNSPRFileDesc(openFlags, mode, &fd);
NS_ENSURE_SUCCESS(rv, rv);
WriteFrameTimeStamps(fd);
PR_Close(fd);
printf_stderr("Wrote FPS data to file: %s\n",
resultFile->HumanReadablePath().get());
return NS_OK;
}
} // end namespace layers
} // end namespace mozilla