mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 990835 - Change Compositor FPS to be Histogram based. r=benwa
This commit is contained in:
parent
6e413161bb
commit
572664685b
449
gfx/layers/composite/FPSCounter.cpp
Normal file
449
gfx/layers/composite/FPSCounter.cpp
Normal file
@ -0,0 +1,449 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* 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 "gfxPrefs.h" // for gfxPrefs
|
||||
#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/TimeStamp.h" // for TimeStamp, TimeDuration
|
||||
#include "nsPoint.h" // for nsIntPoint
|
||||
#include "nsRect.h" // for nsIntRect
|
||||
#include "nsIFile.h" // for nsIFile
|
||||
#include "nsDirectoryServiceDefs.h" // for NS_OS_TMP_DIR
|
||||
#include "prprf.h" // for PR_snprintf
|
||||
#include "FPSCounter.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::gl;
|
||||
|
||||
FPSCounter::FPSCounter(const char* aName)
|
||||
: mWriteIndex(0)
|
||||
, 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.ToSecondsSigDigits() >= 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.ToSecondsSigDigits() >= 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.ToSecondsSigDigits() >= 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 = PR_snprintf(buffer, bufferSize, "FPS Data for: %s\n", mFPSName);
|
||||
MOZ_ASSERT(writtenCount >= 0);
|
||||
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 = PR_snprintf(buffer, bufferSize, "%f,\n", duration.ToMilliseconds());
|
||||
|
||||
MOZ_ASSERT(writtenCount >= 0);
|
||||
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 (!gfxPrefs::FPSPrintHistogram()) {
|
||||
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)
|
||||
{
|
||||
int length = 0;
|
||||
const int kBufferLength = 512;
|
||||
char buffer[kBufferLength];
|
||||
|
||||
for (std::map<int, int>::iterator iter = aHistogram.begin();
|
||||
iter != aHistogram.end(); iter++)
|
||||
{
|
||||
int fps = iter->first;
|
||||
int count = iter->second;
|
||||
|
||||
length += PR_snprintf(buffer + length, kBufferLength - length,
|
||||
"FPS: %d = %d. ", fps, count);
|
||||
NS_ASSERTION(length >= kBufferLength, "Buffer overrun while printing FPS histogram.");
|
||||
}
|
||||
|
||||
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 (!gfxPrefs::WriteFPSToFile()) {
|
||||
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);
|
||||
|
||||
nsAutoCString path;
|
||||
rv = resultFile->GetNativePath(path);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
printf_stderr("Wrote FPS data to file: %s\n", path.get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
FPSState::FPSState()
|
||||
: mCompositionFps("Compositor")
|
||||
, mTransactionFps("LayerTransactions")
|
||||
{
|
||||
}
|
||||
|
||||
// Size of the builtin font.
|
||||
static const float FontHeight = 7.f;
|
||||
static const float FontWidth = 4.f;
|
||||
static const float FontStride = 4.f;
|
||||
|
||||
// Scale the font when drawing it to the viewport for better readability.
|
||||
static const float FontScaleX = 2.f;
|
||||
static const float FontScaleY = 3.f;
|
||||
|
||||
static void DrawDigits(unsigned int aValue,
|
||||
int aOffsetX, int aOffsetY,
|
||||
Compositor* aCompositor,
|
||||
EffectChain& aEffectChain)
|
||||
{
|
||||
if (aValue > 999) {
|
||||
aValue = 999;
|
||||
}
|
||||
|
||||
unsigned int divisor = 100;
|
||||
float textureWidth = FontWidth * 10;
|
||||
gfx::Float opacity = 1;
|
||||
gfx::Matrix4x4 transform;
|
||||
transform.Scale(FontScaleX, FontScaleY, 1);
|
||||
|
||||
for (size_t n = 0; n < 3; ++n) {
|
||||
unsigned int digit = aValue % (divisor * 10) / divisor;
|
||||
divisor /= 10;
|
||||
|
||||
RefPtr<TexturedEffect> texturedEffect = static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
|
||||
texturedEffect->mTextureCoords = Rect(float(digit * FontWidth) / textureWidth, 0, FontWidth / textureWidth, 1.0f);
|
||||
|
||||
Rect drawRect = Rect(aOffsetX + n * FontWidth, aOffsetY, FontWidth, FontHeight);
|
||||
Rect clipRect = Rect(0, 0, 300, 100);
|
||||
aCompositor->DrawQuad(drawRect, clipRect,
|
||||
aEffectChain, opacity, transform);
|
||||
}
|
||||
}
|
||||
|
||||
void FPSState::DrawFPS(TimeStamp aNow,
|
||||
int aOffsetX, int aOffsetY,
|
||||
unsigned int aFillRatio,
|
||||
Compositor* aCompositor)
|
||||
{
|
||||
if (!mFPSTextureSource) {
|
||||
const char *text =
|
||||
" "
|
||||
" XXX XX XXX XXX X X XXX XXX XXX XXX XXX"
|
||||
" X X X X X X X X X X X X X X"
|
||||
" X X X XXX XXX XXX XXX XXX X XXX XXX"
|
||||
" X X X X X X X X X X X X X"
|
||||
" XXX XXX XXX XXX X XXX XXX X XXX X"
|
||||
" ";
|
||||
|
||||
// Convert the text encoding above to RGBA.
|
||||
int w = FontWidth * 10;
|
||||
int h = FontHeight;
|
||||
uint32_t* buf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
|
||||
for (int i = 0; i < h; i++) {
|
||||
for (int j = 0; j < w; j++) {
|
||||
uint32_t purple = 0xfff000ff;
|
||||
uint32_t white = 0xffffffff;
|
||||
buf[i * w + j] = (text[i * w + j] == ' ') ? purple : white;
|
||||
}
|
||||
}
|
||||
|
||||
int bytesPerPixel = 4;
|
||||
RefPtr<DataSourceSurface> fpsSurface = Factory::CreateWrappingDataSourceSurface(
|
||||
reinterpret_cast<uint8_t*>(buf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
|
||||
mFPSTextureSource = aCompositor->CreateDataTextureSource();
|
||||
mFPSTextureSource->Update(fpsSurface);
|
||||
}
|
||||
|
||||
EffectChain effectChain;
|
||||
effectChain.mPrimaryEffect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mFPSTextureSource, Filter::POINT);
|
||||
|
||||
unsigned int fps = unsigned(mCompositionFps.AddFrameAndGetFps(aNow));
|
||||
unsigned int txnFps = unsigned(mTransactionFps.GetFPS(aNow));
|
||||
|
||||
DrawDigits(fps, aOffsetX + 0, aOffsetY, aCompositor, effectChain);
|
||||
DrawDigits(txnFps, aOffsetX + FontWidth * 4, aOffsetY, aCompositor, effectChain);
|
||||
DrawDigits(aFillRatio, aOffsetX + FontWidth * 8, aOffsetY, aCompositor, effectChain);
|
||||
}
|
||||
|
||||
} // end namespace layers
|
||||
} // end namespace mozilla
|
@ -6,12 +6,15 @@
|
||||
#ifndef mozilla_layers_opengl_FPSCounter_h_
|
||||
#define mozilla_layers_opengl_FPSCounter_h_
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for min
|
||||
#include <stddef.h> // for size_t
|
||||
#include <map> // for std::map
|
||||
#include "GLDefs.h" // for GLuint
|
||||
#include "mozilla/RefPtr.h" // for TemporaryRef, RefCounted
|
||||
#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
|
||||
#include "nsTArray.h" // for nsAutoTArray, nsTArray_Impl, etc
|
||||
#include "VBOArena.h" // for gl::VBOArena
|
||||
#include "prio.h" // for NSPR file i/o
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
@ -21,68 +24,91 @@ namespace layers {
|
||||
|
||||
class DataTextureSource;
|
||||
class ShaderProgramOGL;
|
||||
class Compositor;
|
||||
|
||||
const double kFpsWindowMs = 250.0;
|
||||
const size_t kNumFrameTimeStamps = 16;
|
||||
struct FPSCounter {
|
||||
FPSCounter() : mCurrentFrameIndex(0) {
|
||||
mFrames.SetLength(kNumFrameTimeStamps);
|
||||
}
|
||||
// Dump the FPS histogram every 10 seconds or kMaxFrameFPS
|
||||
const int kFpsDumpInterval = 10;
|
||||
|
||||
// We keep a circular buffer of the time points at which the last K
|
||||
// frames were drawn. To estimate FPS, we count the number of
|
||||
// frames we've drawn within the last kFPSWindowMs milliseconds and
|
||||
// divide by the amount time since the first of those frames.
|
||||
nsAutoTArray<TimeStamp, kNumFrameTimeStamps> mFrames;
|
||||
size_t mCurrentFrameIndex;
|
||||
// On desktop, we can have 240 hz monitors, so 10 seconds
|
||||
// times 240 frames = 2400
|
||||
const int kMaxFrames = 2400;
|
||||
|
||||
void AddFrame(TimeStamp aNewFrame) {
|
||||
mFrames[mCurrentFrameIndex] = aNewFrame;
|
||||
mCurrentFrameIndex = (mCurrentFrameIndex + 1) % kNumFrameTimeStamps;
|
||||
}
|
||||
/**
|
||||
* The FPSCounter tracks how often we composite or have a layer transaction.
|
||||
* At each composite / layer transaction, we record the timestamp.
|
||||
* After kFpsDumpInterval number of composites / transactions, we calculate
|
||||
* the average and standard deviation of frames composited. We dump a histogram,
|
||||
* which allows for more statistically significant measurements. We also dump
|
||||
* absolute frame composite times to a file on the device.
|
||||
* The FPS counters displayed on screen are based on how many frames we
|
||||
* composited within the last ~1 second. The more accurate measurement is to
|
||||
* grab the histogram from stderr or grab the FPS timestamp dumps written to file.
|
||||
*
|
||||
* To enable dumping to file, enable
|
||||
* layers.acceleration.draw-fps.write-to-file pref.
|
||||
|
||||
double AddFrameAndGetFps(TimeStamp aCurrentFrame) {
|
||||
AddFrame(aCurrentFrame);
|
||||
return EstimateFps(aCurrentFrame);
|
||||
}
|
||||
* To enable printing histogram data to logcat,
|
||||
* enable layers.acceleration.draw-fps.print-histogram
|
||||
*
|
||||
* Use the HasNext(), GetNextTimeStamp() like an iterator to read the data,
|
||||
* backwards in time. This abstracts away the mechanics of reading the data.
|
||||
*/
|
||||
class FPSCounter {
|
||||
public:
|
||||
FPSCounter(const char* aName);
|
||||
~FPSCounter();
|
||||
|
||||
double GetFpsAt(TimeStamp aNow) {
|
||||
return EstimateFps(aNow);
|
||||
}
|
||||
void AddFrame(TimeStamp aTimestamp);
|
||||
double AddFrameAndGetFps(TimeStamp aTimestamp);
|
||||
double GetFPS(TimeStamp aTimestamp);
|
||||
|
||||
private:
|
||||
double EstimateFps(TimeStamp aNow) {
|
||||
TimeStamp beginningOfWindow =
|
||||
(aNow - TimeDuration::FromMilliseconds(kFpsWindowMs));
|
||||
TimeStamp earliestFrameInWindow = aNow;
|
||||
size_t numFramesDrawnInWindow = 0;
|
||||
for (size_t i = 0; i < kNumFrameTimeStamps; ++i) {
|
||||
const TimeStamp& frame = mFrames[i];
|
||||
if (!frame.IsNull() && frame > beginningOfWindow) {
|
||||
++numFramesDrawnInWindow;
|
||||
earliestFrameInWindow = std::min(earliestFrameInWindow, frame);
|
||||
}
|
||||
}
|
||||
double realWindowSecs = (aNow - earliestFrameInWindow).ToSeconds();
|
||||
if (realWindowSecs == 0.0 || numFramesDrawnInWindow == 1) {
|
||||
return 0.0;
|
||||
}
|
||||
return double(numFramesDrawnInWindow - 1) / realWindowSecs;
|
||||
}
|
||||
void Init();
|
||||
bool CapturedFullInterval(TimeStamp aTimestamp);
|
||||
|
||||
// Used while iterating backwards over the data
|
||||
void ResetReverseIterator();
|
||||
bool HasNext(TimeStamp aTimestamp, double aDuration = kFpsDumpInterval);
|
||||
TimeStamp GetNextTimeStamp();
|
||||
int GetLatestReadIndex();
|
||||
TimeStamp GetLatestTimeStamp();
|
||||
void WriteFrameTimeStamps(PRFileDesc* fd);
|
||||
bool IteratedFullInterval(TimeStamp aTimestamp, double aDuration);
|
||||
|
||||
void PrintFPS();
|
||||
int BuildHistogram(std::map<int, int>& aHistogram);
|
||||
void PrintHistogram(std::map<int, int>& aHistogram);
|
||||
double GetMean(std::map<int,int> aHistogram);
|
||||
double GetStdDev(std::map<int, int> aHistogram);
|
||||
nsresult WriteFrameTimeStamps();
|
||||
|
||||
/***
|
||||
* mFrameTimestamps is a psuedo circular buffer
|
||||
* Since we have a constant write time and don't
|
||||
* read at an offset except our latest write
|
||||
* we don't need an explicit read pointer.
|
||||
*/
|
||||
nsAutoTArray<TimeStamp, kMaxFrames> mFrameTimestamps;
|
||||
int mWriteIndex; // points to next open write slot
|
||||
int mIteratorIndex; // used only when iterating
|
||||
const char* mFPSName;
|
||||
TimeStamp mLastInterval;
|
||||
};
|
||||
|
||||
struct FPSState {
|
||||
FPSCounter mCompositionFps;
|
||||
FPSCounter mTransactionFps;
|
||||
|
||||
FPSState() {}
|
||||
|
||||
FPSState();
|
||||
void DrawFPS(TimeStamp, int offsetX, int offsetY, unsigned, Compositor* aCompositor);
|
||||
|
||||
void NotifyShadowTreeTransaction() {
|
||||
mTransactionFps.AddFrame(TimeStamp::Now());
|
||||
}
|
||||
|
||||
FPSCounter mCompositionFps;
|
||||
FPSCounter mTransactionFps;
|
||||
|
||||
private:
|
||||
RefPtr<DataTextureSource> mFPSTextureSource;
|
||||
};
|
||||
|
@ -311,89 +311,6 @@ LayerManagerComposite::RootLayer() const
|
||||
return ToLayerComposite(mRoot);
|
||||
}
|
||||
|
||||
// Size of the builtin font.
|
||||
static const float FontHeight = 7.f;
|
||||
static const float FontWidth = 4.f;
|
||||
static const float FontStride = 4.f;
|
||||
|
||||
// Scale the font when drawing it to the viewport for better readability.
|
||||
static const float FontScaleX = 2.f;
|
||||
static const float FontScaleY = 3.f;
|
||||
|
||||
static void DrawDigits(unsigned int aValue,
|
||||
int aOffsetX, int aOffsetY,
|
||||
Compositor* aCompositor,
|
||||
EffectChain& aEffectChain)
|
||||
{
|
||||
if (aValue > 999) {
|
||||
aValue = 999;
|
||||
}
|
||||
|
||||
unsigned int divisor = 100;
|
||||
float textureWidth = FontWidth * 10;
|
||||
gfx::Float opacity = 1;
|
||||
gfx::Matrix4x4 transform;
|
||||
transform.Scale(FontScaleX, FontScaleY, 1);
|
||||
|
||||
for (size_t n = 0; n < 3; ++n) {
|
||||
unsigned int digit = aValue % (divisor * 10) / divisor;
|
||||
divisor /= 10;
|
||||
|
||||
RefPtr<TexturedEffect> texturedEffect = static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
|
||||
texturedEffect->mTextureCoords = Rect(float(digit * FontWidth) / textureWidth, 0, FontWidth / textureWidth, 1.0f);
|
||||
|
||||
Rect drawRect = Rect(aOffsetX + n * FontWidth, aOffsetY, FontWidth, FontHeight);
|
||||
Rect clipRect = Rect(0, 0, 300, 100);
|
||||
aCompositor->DrawQuad(drawRect, clipRect,
|
||||
aEffectChain, opacity, transform);
|
||||
}
|
||||
}
|
||||
|
||||
void FPSState::DrawFPS(TimeStamp aNow,
|
||||
int aOffsetX, int aOffsetY,
|
||||
unsigned int aFillRatio,
|
||||
Compositor* aCompositor)
|
||||
{
|
||||
if (!mFPSTextureSource) {
|
||||
const char *text =
|
||||
" "
|
||||
" XXX XX XXX XXX X X XXX XXX XXX XXX XXX"
|
||||
" X X X X X X X X X X X X X X"
|
||||
" X X X XXX XXX XXX XXX XXX X XXX XXX"
|
||||
" X X X X X X X X X X X X X"
|
||||
" XXX XXX XXX XXX X XXX XXX X XXX X"
|
||||
" ";
|
||||
|
||||
// Convert the text encoding above to RGBA.
|
||||
int w = FontWidth * 10;
|
||||
int h = FontHeight;
|
||||
uint32_t* buf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
|
||||
for (int i = 0; i < h; i++) {
|
||||
for (int j = 0; j < w; j++) {
|
||||
uint32_t purple = 0xfff000ff;
|
||||
uint32_t white = 0xffffffff;
|
||||
buf[i * w + j] = (text[i * w + j] == ' ') ? purple : white;
|
||||
}
|
||||
}
|
||||
|
||||
int bytesPerPixel = 4;
|
||||
RefPtr<DataSourceSurface> fpsSurface = Factory::CreateWrappingDataSourceSurface(
|
||||
reinterpret_cast<uint8_t*>(buf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
|
||||
mFPSTextureSource = aCompositor->CreateDataTextureSource();
|
||||
mFPSTextureSource->Update(fpsSurface);
|
||||
}
|
||||
|
||||
EffectChain effectChain;
|
||||
effectChain.mPrimaryEffect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mFPSTextureSource, Filter::POINT);
|
||||
|
||||
unsigned int fps = unsigned(mCompositionFps.AddFrameAndGetFps(aNow));
|
||||
unsigned int txnFps = unsigned(mTransactionFps.GetFpsAt(aNow));
|
||||
|
||||
DrawDigits(fps, aOffsetX + 0, aOffsetY, aCompositor, effectChain);
|
||||
DrawDigits(txnFps, aOffsetX + FontWidth * 4, aOffsetY, aCompositor, effectChain);
|
||||
DrawDigits(aFillRatio, aOffsetX + FontWidth * 8, aOffsetY, aCompositor, effectChain);
|
||||
}
|
||||
|
||||
static uint16_t sFrameCount = 0;
|
||||
void
|
||||
LayerManagerComposite::RenderDebugOverlay(const Rect& aBounds)
|
||||
|
@ -262,6 +262,7 @@ UNIFIED_SOURCES += [
|
||||
'composite/CompositableHost.cpp',
|
||||
'composite/ContainerLayerComposite.cpp',
|
||||
'composite/ContentHost.cpp',
|
||||
'composite/FPSCounter.cpp',
|
||||
'composite/ImageHost.cpp',
|
||||
'composite/ImageLayerComposite.cpp',
|
||||
'composite/LayerManagerComposite.cpp',
|
||||
|
@ -158,6 +158,8 @@ private:
|
||||
|
||||
DECL_GFX_PREF(Once, "layers.acceleration.disabled", LayersAccelerationDisabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.acceleration.draw-fps", LayersDrawFPS, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram", FPSPrintHistogram, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
|
||||
DECL_GFX_PREF(Once, "layers.acceleration.force-enabled", LayersAccelerationForceEnabled, bool, false);
|
||||
#ifdef XP_WIN
|
||||
// On windows, ignore the preference value, forcing async video to false.
|
||||
|
Loading…
Reference in New Issue
Block a user