Modified: trunk/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp (113917 => 113918)
--- trunk/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp 2012-04-11 22:44:57 UTC (rev 113917)
+++ trunk/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp 2012-04-11 22:48:44 UTC (rev 113918)
@@ -50,7 +50,6 @@
CCHeadsUpDisplay::CCHeadsUpDisplay(LayerRendererChromium* owner)
: m_currentFrameNumber(1)
- , m_filteredFrameTime(0)
, m_layerRenderer(owner)
, m_useMapSubForUploads(owner->contextSupportsMapSub())
{
@@ -69,12 +68,15 @@
{
}
+const double CCHeadsUpDisplay::kIdleSecondsTriggersReset = 0.5;
+const double CCHeadsUpDisplay::kFrameTooFast = 1.0 / 70;
+
void CCHeadsUpDisplay::initializeFonts()
{
ASSERT(!CCProxy::implThread());
FontDescription mediumFontDesc;
mediumFontDesc.setGenericFamily(FontDescription::MonospaceFamily);
- mediumFontDesc.setComputedSize(20);
+ mediumFontDesc.setComputedSize(12);
m_mediumFont = adoptPtr(new Font(mediumFontDesc, 0, 0));
m_mediumFont->update(0);
@@ -87,9 +89,20 @@
m_smallFont->update(0);
}
+// safeMod works on -1, returning m-1 in that case.
+static inline int safeMod(int number, int modulus)
+{
+ return (number + modulus) % modulus;
+}
+
+inline int CCHeadsUpDisplay::frameIndex(int frame) const
+{
+ return safeMod(frame, kBeginFrameHistorySize);
+}
+
void CCHeadsUpDisplay::onFrameBegin(double timestamp)
{
- m_beginTimeHistoryInSec[m_currentFrameNumber % kBeginFrameHistorySize] = timestamp;
+ m_beginTimeHistoryInSec[frameIndex(m_currentFrameNumber)] = timestamp;
}
void CCHeadsUpDisplay::onSwapBuffers()
@@ -174,6 +187,15 @@
m_hudTexture->unreserve();
}
+bool CCHeadsUpDisplay::isBadFrame(int frameNumber) const
+{
+ double delta = m_beginTimeHistoryInSec[frameIndex(frameNumber)] -
+ m_beginTimeHistoryInSec[frameIndex(frameNumber - 1)];
+ bool tooSlow = delta > (kNumMissedFramesForReset / 60.0);
+ bool schedulerAllowsDoubleFrames = !CCProxy::hasImplThread();
+ return (schedulerAllowsDoubleFrames && delta < kFrameTooFast) || tooSlow;
+}
+
void CCHeadsUpDisplay::drawHudContents(GraphicsContext* context, const IntSize& hudSize)
{
if (showPlatformLayerTree()) {
@@ -181,11 +203,11 @@
context->fillRect(FloatRect(0, 0, hudSize.width(), hudSize.height()));
}
- int fpsCounterHeight = 25;
+ int fpsCounterHeight = m_mediumFont->fontMetrics().floatHeight() + 40;
int fpsCounterTop = 2;
int platformLayerTreeTop;
if (settings().showFPSCounter)
- platformLayerTreeTop = fpsCounterTop + fpsCounterHeight + 2;
+ platformLayerTreeTop = fpsCounterTop + fpsCounterHeight;
else
platformLayerTreeTop = 0;
@@ -196,36 +218,69 @@
drawPlatformLayerTree(context, platformLayerTreeTop);
}
-void CCHeadsUpDisplay::drawFPSCounter(GraphicsContext* context, int top, int height)
+void CCHeadsUpDisplay::getAverageFPSAndStandardDeviation(double *average, double *standardDeviation) const
{
- // Note that since we haven't finished the current frame, the FPS counter
- // actually reports the last frame's time.
- double secForLastFrame = m_beginTimeHistoryInSec[(m_currentFrameNumber + kBeginFrameHistorySize - 1) % kBeginFrameHistorySize] -
- m_beginTimeHistoryInSec[(m_currentFrameNumber + kBeginFrameHistorySize - 2) % kBeginFrameHistorySize];
+ double& averageFPS = *average;
- // Filter the frame times to avoid spikes.
- const float alpha = 0.1;
- if (!m_filteredFrameTime) {
- if (m_currentFrameNumber == 2)
- m_filteredFrameTime = secForLastFrame;
- } else
- m_filteredFrameTime = ((1.0 - alpha) * m_filteredFrameTime) + (alpha * secForLastFrame);
+ int frame = m_currentFrameNumber - 1;
+ averageFPS = 0;
+ int averageFPSCount = 0;
+ double fpsVarianceNumerator = 0;
+ // Walk backwards through the samples looking for a run of good frame
+ // timings from which to compute the mean and standard deviation.
+ //
+ // Slow frames occur just because the user is inactive, and should be
+ // ignored. Fast frames are ignored if the scheduler is in single-thread
+ // mode in order to represent the true frame rate in spite of the fact that
+ // the first few swapbuffers happen instantly which skews the statistics
+ // too much for short lived animations.
+ //
+ // isBadFrame encapsulates the frame too slow/frame too fast logic.
+ while (1) {
+ if (!isBadFrame(frame)) {
+ averageFPSCount++;
+ double secForLastFrame = m_beginTimeHistoryInSec[frameIndex(frame)] -
+ m_beginTimeHistoryInSec[frameIndex(frame - 1)];
+ double x = 1.0 / secForLastFrame;
+ double deltaFromAverage = x - averageFPS;
+ // Change with caution - numerics. http://en.wikipedia.org/wiki/Standard_deviation
+ averageFPS = averageFPS + deltaFromAverage / averageFPSCount;
+ fpsVarianceNumerator = fpsVarianceNumerator + deltaFromAverage * (x - averageFPS);
+ }
+ if (averageFPSCount && isBadFrame(frame)) {
+ // We've gathered a run of good samples, so stop.
+ break;
+ }
+ --frame;
+ if (frameIndex(frame) == frameIndex(m_currentFrameNumber) || frame < 0) {
+ // We've gone through all available historical data, so stop.
+ break;
+ }
+ }
+
+ *standardDeviation = sqrt(fpsVarianceNumerator / averageFPSCount);
+}
+
+void CCHeadsUpDisplay::drawFPSCounter(GraphicsContext* context, int top, int height)
+{
float textWidth = 0;
if (!CCProxy::implThread())
textWidth = drawFPSCounterText(context, top, height);
float graphWidth = kBeginFrameHistorySize;
- // Draw background for graph
- context->setFillColor(Color(0, 0, 0, 255), ColorSpaceDeviceRGB);
- context->fillRect(FloatRect(2 + textWidth, top, graphWidth, height));
-
// Draw FPS graph.
- const double loFPS = 0.0;
- const double hiFPS = 120.0;
+ const double loFPS = 0;
+ const double hiFPS = 80;
context->setStrokeStyle(SolidStroke);
+ context->setFillColor(Color(154, 205, 50), ColorSpaceDeviceRGB);
+ context->fillRect(FloatRect(2 + textWidth, top, graphWidth, height / 2));
+ context->setFillColor(Color(255, 250, 205), ColorSpaceDeviceRGB);
+ context->fillRect(FloatRect(2 + textWidth, top + height / 2, graphWidth, height / 2));
context->setStrokeColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
+ context->setFillColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
+
int graphLeft = static_cast<int>(textWidth + 3);
IntPoint prev(-1, 0);
int x = 0;
@@ -233,6 +288,10 @@
for (int i = m_currentFrameNumber % kBeginFrameHistorySize; i != (m_currentFrameNumber - 1) % kBeginFrameHistorySize; i = (i + 1) % kBeginFrameHistorySize) {
int j = (i + 1) % kBeginFrameHistorySize;
double fps = 1.0 / (m_beginTimeHistoryInSec[j] - m_beginTimeHistoryInSec[i]);
+ if (isBadFrame(j)) {
+ x += 1;
+ continue;
+ }
double p = 1 - ((fps - loFPS) / (hiFPS - loFPS));
if (p < 0)
p = 0;
@@ -251,21 +310,22 @@
ASSERT(!CCProxy::implThread());
FontCachePurgePreventer fontCachePurgePreventer;
+ double averageFPS, stdDeviation;
- // Create & measure FPS text.
- String text(String::format("FPS: %5.1f", 1.0 / m_filteredFrameTime));
- TextRun run(text);
- float textWidth = m_mediumFont->width(run) + 2.0f;
+ getAverageFPSAndStandardDeviation(&averageFPS, &stdDeviation);
+ String fps(String::format("FPS: %4.1f +/-%3.1f", averageFPS, stdDeviation));
+ TextRun text(fps);
+ float textWidth = m_mediumFont->width(text) + 2;
+
// Draw background.
context->setFillColor(Color(0, 0, 0, 255), ColorSpaceDeviceRGB);
- context->fillRect(FloatRect(2, top, textWidth, height));
+ double fontHeight = m_mediumFont->fontMetrics().floatHeight() + 2;
+ context->fillRect(FloatRect(2, top, textWidth, fontHeight));
// Draw FPS text.
- if (m_filteredFrameTime) {
- context->setFillColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
- context->drawText(*m_mediumFont, run, IntPoint(3, top + height - 6));
- }
+ context->setFillColor(Color(200, 200, 200), ColorSpaceDeviceRGB);
+ context->drawText(*m_mediumFont, text, IntPoint(3, top + fontHeight - 4));
return textWidth;
}