Source/WebCore/ChangeLog

 12012-02-01 Alex Nicolaou <anicolao@chromium.org>
 2
 3 [chromium] When rendering goes idle, do not count that time against frame rate
 4 https://bugs.webkit.org/show_bug.cgi?id=73454
 5
 6 The FPS counter had a few issues with its reporting. The first
 7 3 swapbuffers are non-blocking and create FPS rates that are
 8 unrealistically high, throwing off the moving averages and
 9 introducing false spikes into the FPS graph. There was also no
 10 way to monitor the smoothness of the animation, or to focus in
 11 on a particular animation or transition.
 12
 13 This patch updates the FPS counter code so that bad data points
 14 are trimmed and not graphed; so that the graph itself is taller
 15 and more legible; so that there is a clear boundary between 40FPS
 16 and better; so that outlier data points are labelled with their
 17 frame rate so that you can see how slow the frame is that is
 18 causing the jank; and to keep statistics on frame rate between
 19 pauses in the activity in the UI so that you can trigger a
 20 transition and read off the average and standard deviation for
 21 that transition to judge it smooth enough or not.
 22
 23
 24 Reviewed by NOBODY (OOPS!).
 25
 26 HUD is not testable in webkit.
 27
 28 * platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp:
 29 (WebCore::CCHeadsUpDisplay::CCHeadsUpDisplay):
 30 (WebCore::CCHeadsUpDisplay::onFrameBegin):
 31 (WebCore::CCHeadsUpDisplay::drawHudContents):
 32 (WebCore::CCHeadsUpDisplay::drawFPSCounter):
 33 * platform/graphics/chromium/cc/CCHeadsUpDisplay.h:
 34
1352012-01-31 Adam Klein <adamk@chromium.org>
236
337 ProcessingInstruction should not be a ContainerNode

Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp

@@using namespace std;
4949
5050CCHeadsUpDisplay::CCHeadsUpDisplay(LayerRendererChromium* owner)
5151 : m_currentFrameNumber(1)
52  , m_filteredFrameTime(0)
 52 , m_averageFPS(0)
 53 , m_averageFPSCount(0)
 54 , m_varianceNumerator(0)
5355 , m_layerRenderer(owner)
5456 , m_useMapSubForUploads(owner->contextSupportsMapSub())
5557{

@@CCHeadsUpDisplay::CCHeadsUpDisplay(LayerRendererChromium* owner)
6062
6163 FontDescription mediumFontDesc;
6264 mediumFontDesc.setGenericFamily(FontDescription::MonospaceFamily);
63  mediumFontDesc.setComputedSize(20);
 65 mediumFontDesc.setComputedSize(12);
6466
6567 m_mediumFont = adoptPtr(new Font(mediumFontDesc, 0, 0));
6668 m_mediumFont->update(0);

@@CCHeadsUpDisplay::~CCHeadsUpDisplay()
7779{
7880}
7981
 82// SAFE_MOD works on -1, returning m-1 in that case.
 83#define SAFE_MOD(n, m) (((n) + (m)) % (m))
 84#define FRAME_INDEX(frame) SAFE_MOD(frame, kBeginFrameHistorySize)
 85
8086void CCHeadsUpDisplay::onFrameBegin(double timestamp)
8187{
82  m_beginTimeHistoryInSec[m_currentFrameNumber % kBeginFrameHistorySize] = timestamp;
 88 m_beginTimeHistoryInSec[FRAME_INDEX(m_currentFrameNumber)] = timestamp;
8389}
8490
8591void CCHeadsUpDisplay::onSwapBuffers()

@@void CCHeadsUpDisplay::draw()
161167 m_hudTexture->unreserve();
162168}
163169
 170bool CCHeadsUpDisplay::isBadFrame(int frameNumber) const
 171{
 172 double delta = m_beginTimeHistoryInSec[FRAME_INDEX(frameNumber)] -
 173 m_beginTimeHistoryInSec[FRAME_INDEX(frameNumber - 1)];
 174 bool tooSlow = delta > kFrameTooSlow;
 175 bool bSchedulerAllowsDoubleFrames = !CCProxy::hasImplThread();
 176 return (bSchedulerAllowsDoubleFrames && delta < kFrameTooFast) || tooSlow;
 177}
 178
164179void CCHeadsUpDisplay::drawHudContents(GraphicsContext* ctx, const IntSize& hudSize)
165180{
166181 FontCachePurgePreventer fontCachePurgePreventer;

@@void CCHeadsUpDisplay::drawHudContents(GraphicsContext* ctx, const IntSize& hudS
170185 ctx->fillRect(FloatRect(0, 0, hudSize.width(), hudSize.height()));
171186 }
172187
173  int fpsCounterHeight = m_mediumFont->fontMetrics().floatHeight() + 2;
 188 int fpsCounterHeight = m_mediumFont->fontMetrics().floatHeight() + 40;
174189 int fpsCounterTop = 2;
175190 int platformLayerTreeTop;
176191 if (settings().showFPSCounter)
177  platformLayerTreeTop = fpsCounterTop + fpsCounterHeight + 2;
 192 platformLayerTreeTop = fpsCounterTop + fpsCounterHeight;
178193 else
179194 platformLayerTreeTop = 0;
180195

@@void CCHeadsUpDisplay::drawFPSCounter(GraphicsContext* ctx, int top, int height)
189204{
190205 // Note that since we haven't finished the current frame, the FPS counter
191206 // actually reports the last frame's time.
192  double secForLastFrame = m_beginTimeHistoryInSec[(m_currentFrameNumber + kBeginFrameHistorySize - 1) % kBeginFrameHistorySize] -
193  m_beginTimeHistoryInSec[(m_currentFrameNumber + kBeginFrameHistorySize - 2) % kBeginFrameHistorySize];
194 
195  // Filter the frame times to avoid spikes.
196  const float alpha = 0.1;
197  if (!m_filteredFrameTime) {
198  if (m_currentFrameNumber == 2)
199  m_filteredFrameTime = secForLastFrame;
200  } else
201  m_filteredFrameTime = ((1.0 - alpha) * m_filteredFrameTime) + (alpha * secForLastFrame);
 207 double secForLastFrame = m_beginTimeHistoryInSec[FRAME_INDEX(m_currentFrameNumber - 1)] -
 208 m_beginTimeHistoryInSec[FRAME_INDEX(m_currentFrameNumber - 2)];
 209 double secForThirdLastFrame = m_beginTimeHistoryInSec[FRAME_INDEX(m_currentFrameNumber - 3)] -
 210 m_beginTimeHistoryInSec[FRAME_INDEX(m_currentFrameNumber - 4)];
 211
 212 // Filter the frame times to avoid spikes. Specifically, keep a running
 213 // average frame rate and a running variance while ignoring bad frames
 214 // which are either too slow or too fast. Slow frames occur just because
 215 // the user is inactive, and should be ignored. Fast frames are ignored if
 216 // the scheduler is not active in order to represent the true frame rate in
 217 // spite of the fact that the first few swapbuffers happen instantly which
 218 // skews the statistics too much for short lived animations.
 219 if (secForThirdLastFrame > kIdleSecondsTriggersReset || m_averageFPSCount > kLongAnimationTriggersReset) {
 220 // restart the frame statistics
 221 m_averageFPS = 0;
 222 m_averageFPSCount = 0;
 223 m_varianceNumerator = 0;
 224 } else if (!isBadFrame(m_currentFrameNumber - 1)) {
 225 m_averageFPSCount++;
 226 double x = 1.0 / secForLastFrame;
 227 double deltaFromAverage = x - m_averageFPS;
 228 // Change with caution - numerics. http://en.wikipedia.org/wiki/Standard_deviation
 229 m_averageFPS = m_averageFPS + deltaFromAverage / m_averageFPSCount;
 230 m_varianceNumerator = m_varianceNumerator + deltaFromAverage * (x - m_averageFPS);
 231 }
202232
203233 // Create & measure FPS text.
204  String text(String::format("FPS: %5.1f", 1.0 / m_filteredFrameTime));
205  TextRun run(text);
206  float textWidth = m_mediumFont->width(run) + 2.0f;
 234 double stdDeviation = sqrt(m_varianceNumerator / m_averageFPSCount);
 235 String fps(String::format("FPS: %4.1f +/-%3.1f", m_averageFPS, stdDeviation));
 236 TextRun text(fps);
 237 float textWidth = m_mediumFont->width(text) + 2.0f;
207238 float graphWidth = kBeginFrameHistorySize;
208239
209240 // Draw background.
210241 ctx->setFillColor(Color(0, 0, 0, 255), ColorSpaceDeviceRGB);
211  ctx->fillRect(FloatRect(2, top, textWidth + graphWidth, height));
 242 double fontHeight = m_mediumFont->fontMetrics().floatHeight() + 2;
 243 ctx->fillRect(FloatRect(2, top, textWidth, fontHeight));
 244 ctx->fillRect(FloatRect(2 + textWidth, top, graphWidth, height));
212245
213246 // Draw FPS text.
214  if (m_filteredFrameTime) {
215  ctx->setFillColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
216  ctx->drawText(*m_mediumFont, run, IntPoint(3, top + height - 6));
217  }
 247 ctx->setFillColor(Color(200, 200, 200), ColorSpaceDeviceRGB);
 248 ctx->drawText(*m_mediumFont, text, IntPoint(3, top + fontHeight - 4));
218249
219250 // Draw FPS graph.
220251 const double loFPS = 0.0;
221  const double hiFPS = 120.0;
 252 const double hiFPS = 80.0;
222253 ctx->setStrokeStyle(SolidStroke);
 254 ctx->setFillColor(Color(154, 205, 50), ColorSpaceDeviceRGB);
 255 ctx->fillRect(FloatRect(2 + textWidth, top, graphWidth, height / 2));
 256 ctx->setFillColor(Color(255, 250, 205), ColorSpaceDeviceRGB);
 257 ctx->fillRect(FloatRect(2 + textWidth, top + height / 2, graphWidth, height / 2));
223258 ctx->setStrokeColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
 259 ctx->setFillColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
224260 int graphLeft = static_cast<int>(textWidth + 3);
225261 IntPoint prev(-1, 0);
226262 int x = 0;

@@void CCHeadsUpDisplay::drawFPSCounter(GraphicsContext* ctx, int top, int height)
228264 for (int i = m_currentFrameNumber % kBeginFrameHistorySize; i != (m_currentFrameNumber - 1) % kBeginFrameHistorySize; i = (i + 1) % kBeginFrameHistorySize) {
229265 int j = (i + 1) % kBeginFrameHistorySize;
230266 double fps = 1.0 / (m_beginTimeHistoryInSec[j] - m_beginTimeHistoryInSec[i]);
231  double p = 1 - ((fps - loFPS) / (hiFPS - loFPS));
232  if (p < 0)
233  p = 0;
234  if (p > 1)
235  p = 1;
236  IntPoint cur(graphLeft + x, 1 + top + p*h);
237  if (prev.x() != -1)
238  ctx->drawLine(prev, cur);
239  prev = cur;
 267 if (!isBadFrame(j)) {
 268 double p = 1 - ((fps - loFPS) / (hiFPS - loFPS));
 269 if (p < 0)
 270 p = 0;
 271 if (p > 1)
 272 p = 1;
 273 IntPoint cur(graphLeft + x, 1 + top + p*h);
 274 if (prev.x() != -1)
 275 ctx->drawLine(prev, cur);
 276 prev = cur;
 277 }
240278 x += 1;
241279 }
242280}

Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.h

@@private:
6666 void drawFPSCounter(GraphicsContext*, int top, int height);
6767 void drawPlatformLayerTree(GraphicsContext*, int top);
6868 const CCSettings& settings() const;
 69 bool isBadFrame(int frameNumber) const;
6970
7071 int m_currentFrameNumber;
7172
72  double m_filteredFrameTime;
 73 double m_averageFPS;
 74 int m_averageFPSCount;
 75 double m_varianceNumerator;
7376
7477 OwnPtr<ManagedTexture> m_hudTexture;
7578
7679 LayerRendererChromium* m_layerRenderer;
7780
7881 static const int kBeginFrameHistorySize = 64;
 82 // The number of seconds of no frames makes us decide that the previous
 83 // animation is over.
 84 static const double kIdleSecondsTriggersReset = 0.5;
 85 // The number of frames that cause us to reset the FPS statistics so that
 86 // the average of zillions of frames doesn't drown out a momentary jank.
 87 static const int kLongAnimationTriggersReset = 60*2;
7988 double m_beginTimeHistoryInSec[kBeginFrameHistorySize];
 89 static const double kFrameTooFast = 1.0 / 70;
 90 static const double kFrameTooSlow = 5.0 / 60;
8091
8192 OwnPtr<Font> m_smallFont;
8293 OwnPtr<Font> m_mediumFont;