| Differences between
and this patch
- a/Source/WebCore/ChangeLog +32 lines
Lines 1-3 a/Source/WebCore/ChangeLog_sec1
1
2012-04-11  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; and to keep statistics on frame rate between
17
        pauses in the activity in the UI so that you can trigger a
18
        transition and read off the average and standard deviation for
19
        that transition to judge it smooth enough or not.
20
21
22
        Reviewed by NOBODY (OOPS!).
23
24
        HUD is not testable in webkit.
25
26
        * platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp:
27
        (WebCore::CCHeadsUpDisplay::CCHeadsUpDisplay):
28
        (WebCore::CCHeadsUpDisplay::onFrameBegin):
29
        (WebCore::CCHeadsUpDisplay::drawHudContents):
30
        (WebCore::CCHeadsUpDisplay::drawFPSCounter):
31
        * platform/graphics/chromium/cc/CCHeadsUpDisplay.h:
32
1
2012-04-10  Kent Tamura  <tkent@chromium.org>
33
2012-04-10  Kent Tamura  <tkent@chromium.org>
2
34
3
        Add a function to set empty clients to a PageClients
35
        Add a function to set empty clients to a PageClients
- a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp -33 / +93 lines
Lines 50-56 using namespace std; a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp_sec1
50
50
51
CCHeadsUpDisplay::CCHeadsUpDisplay(LayerRendererChromium* owner)
51
CCHeadsUpDisplay::CCHeadsUpDisplay(LayerRendererChromium* owner)
52
    : m_currentFrameNumber(1)
52
    : m_currentFrameNumber(1)
53
    , m_filteredFrameTime(0)
54
    , m_layerRenderer(owner)
53
    , m_layerRenderer(owner)
55
    , m_useMapSubForUploads(owner->contextSupportsMapSub())
54
    , m_useMapSubForUploads(owner->contextSupportsMapSub())
56
{
55
{
Lines 69-80 CCHeadsUpDisplay::~CCHeadsUpDisplay() a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp_sec2
69
{
68
{
70
}
69
}
71
70
71
const double CCHeadsUpDisplay::kIdleSecondsTriggersReset = 0.5;
72
const double CCHeadsUpDisplay::kFrameTooFast = 1.0 / 70;
73
72
void CCHeadsUpDisplay::initializeFonts()
74
void CCHeadsUpDisplay::initializeFonts()
73
{
75
{
74
    ASSERT(!CCProxy::implThread());
76
    ASSERT(!CCProxy::implThread());
75
    FontDescription mediumFontDesc;
77
    FontDescription mediumFontDesc;
76
    mediumFontDesc.setGenericFamily(FontDescription::MonospaceFamily);
78
    mediumFontDesc.setGenericFamily(FontDescription::MonospaceFamily);
77
    mediumFontDesc.setComputedSize(20);
79
    mediumFontDesc.setComputedSize(12);
78
80
79
    m_mediumFont = adoptPtr(new Font(mediumFontDesc, 0, 0));
81
    m_mediumFont = adoptPtr(new Font(mediumFontDesc, 0, 0));
80
    m_mediumFont->update(0);
82
    m_mediumFont->update(0);
Lines 87-95 void CCHeadsUpDisplay::initializeFonts() a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp_sec3
87
    m_smallFont->update(0);
89
    m_smallFont->update(0);
88
}
90
}
89
91
92
// safeMod works on -1, returning m-1 in that case.
93
static inline int safeMod(int number, int modulus)
94
{
95
    return (number + modulus) % modulus;
96
}
97
98
inline int CCHeadsUpDisplay::frameIndex(int frame) const
99
{
100
    return safeMod(frame, kBeginFrameHistorySize);
101
}
102
90
void CCHeadsUpDisplay::onFrameBegin(double timestamp)
103
void CCHeadsUpDisplay::onFrameBegin(double timestamp)
91
{
104
{
92
    m_beginTimeHistoryInSec[m_currentFrameNumber % kBeginFrameHistorySize] = timestamp;
105
    m_beginTimeHistoryInSec[frameIndex(m_currentFrameNumber)] = timestamp;
93
}
106
}
94
107
95
void CCHeadsUpDisplay::onSwapBuffers()
108
void CCHeadsUpDisplay::onSwapBuffers()
Lines 174-179 void CCHeadsUpDisplay::draw() a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp_sec4
174
    m_hudTexture->unreserve();
187
    m_hudTexture->unreserve();
175
}
188
}
176
189
190
bool CCHeadsUpDisplay::isBadFrame(int frameNumber) const
191
{
192
    double delta = m_beginTimeHistoryInSec[frameIndex(frameNumber)] -
193
                   m_beginTimeHistoryInSec[frameIndex(frameNumber - 1)];
194
    bool tooSlow = delta > (kNumMissedFramesForReset / 60.0);
195
    bool schedulerAllowsDoubleFrames = !CCProxy::hasImplThread();
196
    return (schedulerAllowsDoubleFrames && delta < kFrameTooFast) || tooSlow;
197
}
198
177
void CCHeadsUpDisplay::drawHudContents(GraphicsContext* context, const IntSize& hudSize)
199
void CCHeadsUpDisplay::drawHudContents(GraphicsContext* context, const IntSize& hudSize)
178
{
200
{
179
    if (showPlatformLayerTree()) {
201
    if (showPlatformLayerTree()) {
Lines 181-191 void CCHeadsUpDisplay::drawHudContents(GraphicsContext* context, const IntSize& a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp_sec5
181
        context->fillRect(FloatRect(0, 0, hudSize.width(), hudSize.height()));
203
        context->fillRect(FloatRect(0, 0, hudSize.width(), hudSize.height()));
182
    }
204
    }
183
205
184
    int fpsCounterHeight = 25;
206
    int fpsCounterHeight = m_mediumFont->fontMetrics().floatHeight() + 40;
185
    int fpsCounterTop = 2;
207
    int fpsCounterTop = 2;
186
    int platformLayerTreeTop;
208
    int platformLayerTreeTop;
187
    if (settings().showFPSCounter)
209
    if (settings().showFPSCounter)
188
        platformLayerTreeTop = fpsCounterTop + fpsCounterHeight + 2;
210
        platformLayerTreeTop = fpsCounterTop + fpsCounterHeight;
189
    else
211
    else
190
        platformLayerTreeTop = 0;
212
        platformLayerTreeTop = 0;
191
213
Lines 196-231 void CCHeadsUpDisplay::drawHudContents(GraphicsContext* context, const IntSize& a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp_sec6
196
        drawPlatformLayerTree(context, platformLayerTreeTop);
218
        drawPlatformLayerTree(context, platformLayerTreeTop);
197
}
219
}
198
220
199
void CCHeadsUpDisplay::drawFPSCounter(GraphicsContext* context, int top, int height)
221
void CCHeadsUpDisplay::getAverageFPSAndStandardDeviation(double *average, double *standardDeviation) const
200
{
222
{
201
    // Note that since we haven't finished the current frame, the FPS counter
223
    double& averageFPS = *average;
202
    // actually reports the last frame's time.
224
203
    double secForLastFrame = m_beginTimeHistoryInSec[(m_currentFrameNumber + kBeginFrameHistorySize - 1) % kBeginFrameHistorySize] -
225
    int frame = m_currentFrameNumber - 1; 
204
                             m_beginTimeHistoryInSec[(m_currentFrameNumber + kBeginFrameHistorySize - 2) % kBeginFrameHistorySize];
226
    averageFPS = 0;
205
227
    int averageFPSCount = 0;
206
    // Filter the frame times to avoid spikes.
228
    double fpsVarianceNumerator = 0;
207
    const float alpha = 0.1;
229
208
    if (!m_filteredFrameTime) {
230
    // Walk backwards through the samples looking for a run of good frame
209
        if (m_currentFrameNumber == 2)
231
    // timings from which to compute the mean and standard deviation.
210
            m_filteredFrameTime = secForLastFrame;
232
    //
211
    } else
233
    // Slow frames occur just because the user is inactive, and should be
212
        m_filteredFrameTime = ((1.0 - alpha) * m_filteredFrameTime) + (alpha * secForLastFrame);
234
    // ignored. Fast frames are ignored if the scheduler is in single-thread
235
    // mode in order to represent the true frame rate in spite of the fact that
236
    // the first few swapbuffers happen instantly which skews the statistics
237
    // too much for short lived animations.
238
    //
239
    // isBadFrame encapsulates the frame too slow/frame too fast logic.
240
    while (1) {
241
        if (!isBadFrame(frame)) {
242
            averageFPSCount++;
243
            double secForLastFrame = m_beginTimeHistoryInSec[frameIndex(frame)] -
244
                                     m_beginTimeHistoryInSec[frameIndex(frame - 1)];
245
            double x = 1.0 / secForLastFrame;
246
            double deltaFromAverage = x - averageFPS;
247
            // Change with caution - numerics. http://en.wikipedia.org/wiki/Standard_deviation
248
            averageFPS = averageFPS + deltaFromAverage / averageFPSCount;
249
            fpsVarianceNumerator = fpsVarianceNumerator + deltaFromAverage * (x - averageFPS);
250
        }
251
        if (averageFPSCount && isBadFrame(frame)) {
252
            // We've gathered a run of good samples, so stop.
253
            break;
254
        }
255
        --frame;
256
        if (frameIndex(frame) == frameIndex(m_currentFrameNumber) || frame < 0) {
257
            // We've gone through all available historical data, so stop.
258
            break;
259
        }
260
    }
261
262
    *standardDeviation = sqrt(fpsVarianceNumerator / averageFPSCount);
263
}
213
264
265
void CCHeadsUpDisplay::drawFPSCounter(GraphicsContext* context, int top, int height)
266
{
214
    float textWidth = 0;
267
    float textWidth = 0;
215
    if (!CCProxy::implThread())
268
    if (!CCProxy::implThread())
216
        textWidth = drawFPSCounterText(context, top, height);
269
        textWidth = drawFPSCounterText(context, top, height);
217
270
218
    float graphWidth = kBeginFrameHistorySize;
271
    float graphWidth = kBeginFrameHistorySize;
219
272
220
    // Draw background for graph
221
    context->setFillColor(Color(0, 0, 0, 255), ColorSpaceDeviceRGB);
222
    context->fillRect(FloatRect(2 + textWidth, top, graphWidth, height));
223
224
    // Draw FPS graph.
273
    // Draw FPS graph.
225
    const double loFPS = 0.0;
274
    const double loFPS = 0;
226
    const double hiFPS = 120.0;
275
    const double hiFPS = 80;
227
    context->setStrokeStyle(SolidStroke);
276
    context->setStrokeStyle(SolidStroke);
277
    context->setFillColor(Color(154, 205, 50), ColorSpaceDeviceRGB);
278
    context->fillRect(FloatRect(2 + textWidth, top, graphWidth, height / 2));
279
    context->setFillColor(Color(255, 250, 205), ColorSpaceDeviceRGB);
280
    context->fillRect(FloatRect(2 + textWidth, top + height / 2, graphWidth, height / 2));
228
    context->setStrokeColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
281
    context->setStrokeColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
282
    context->setFillColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
283
229
    int graphLeft = static_cast<int>(textWidth + 3);
284
    int graphLeft = static_cast<int>(textWidth + 3);
230
    IntPoint prev(-1, 0);
285
    IntPoint prev(-1, 0);
231
    int x = 0;
286
    int x = 0;
Lines 233-238 void CCHeadsUpDisplay::drawFPSCounter(GraphicsContext* context, int top, int hei a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp_sec7
233
    for (int i = m_currentFrameNumber % kBeginFrameHistorySize; i != (m_currentFrameNumber - 1) % kBeginFrameHistorySize; i = (i + 1) % kBeginFrameHistorySize) {
288
    for (int i = m_currentFrameNumber % kBeginFrameHistorySize; i != (m_currentFrameNumber - 1) % kBeginFrameHistorySize; i = (i + 1) % kBeginFrameHistorySize) {
234
        int j = (i + 1) % kBeginFrameHistorySize;
289
        int j = (i + 1) % kBeginFrameHistorySize;
235
        double fps = 1.0 / (m_beginTimeHistoryInSec[j] - m_beginTimeHistoryInSec[i]);
290
        double fps = 1.0 / (m_beginTimeHistoryInSec[j] - m_beginTimeHistoryInSec[i]);
291
        if (isBadFrame(j)) {
292
            x += 1;
293
            continue;
294
        }
236
        double p = 1 - ((fps - loFPS) / (hiFPS - loFPS));
295
        double p = 1 - ((fps - loFPS) / (hiFPS - loFPS));
237
        if (p < 0)
296
        if (p < 0)
238
            p = 0;
297
            p = 0;
Lines 251-271 float CCHeadsUpDisplay::drawFPSCounterText(GraphicsContext* context, int top, in a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp_sec8
251
    ASSERT(!CCProxy::implThread());
310
    ASSERT(!CCProxy::implThread());
252
311
253
    FontCachePurgePreventer fontCachePurgePreventer;
312
    FontCachePurgePreventer fontCachePurgePreventer;
313
    double averageFPS, stdDeviation;
314
315
    getAverageFPSAndStandardDeviation(&averageFPS, &stdDeviation);
254
316
255
    // Create & measure FPS text.
317
    String fps(String::format("FPS: %4.1f +/-%3.1f", averageFPS, stdDeviation));
256
    String text(String::format("FPS: %5.1f", 1.0 / m_filteredFrameTime));
318
    TextRun text(fps);
257
    TextRun run(text);
319
    float textWidth = m_mediumFont->width(text) + 2;
258
    float textWidth = m_mediumFont->width(run) + 2.0f;
259
320
260
    // Draw background.
321
    // Draw background.
261
    context->setFillColor(Color(0, 0, 0, 255), ColorSpaceDeviceRGB);
322
    context->setFillColor(Color(0, 0, 0, 255), ColorSpaceDeviceRGB);
262
    context->fillRect(FloatRect(2, top, textWidth, height));
323
    double fontHeight = m_mediumFont->fontMetrics().floatHeight() + 2;
324
    context->fillRect(FloatRect(2, top, textWidth, fontHeight));
263
325
264
    // Draw FPS text.
326
    // Draw FPS text.
265
    if (m_filteredFrameTime) {
327
    context->setFillColor(Color(200, 200, 200), ColorSpaceDeviceRGB);
266
        context->setFillColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
328
    context->drawText(*m_mediumFont, text, IntPoint(3, top + fontHeight - 4));
267
        context->drawText(*m_mediumFont, run, IntPoint(3, top + height - 6));
268
    }
269
329
270
    return textWidth;
330
    return textWidth;
271
}
331
}
- a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.h -3 / +9 lines
Lines 67-72 private: a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.h_sec1
67
    float drawFPSCounterText(GraphicsContext*, int top, int height);
67
    float drawFPSCounterText(GraphicsContext*, int top, int height);
68
    void drawPlatformLayerTree(GraphicsContext*, int top);
68
    void drawPlatformLayerTree(GraphicsContext*, int top);
69
    const CCSettings& settings() const;
69
    const CCSettings& settings() const;
70
    bool isBadFrame(int frameNumber) const;
71
    int frameIndex(int frameNumber) const;
72
    void getAverageFPSAndStandardDeviation(double *average, double *standardDeviation) const;
70
73
71
    bool showPlatformLayerTree() const;
74
    bool showPlatformLayerTree() const;
72
75
Lines 74-87 private: a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.h_sec2
74
77
75
    int m_currentFrameNumber;
78
    int m_currentFrameNumber;
76
79
77
    double m_filteredFrameTime;
78
79
    OwnPtr<ManagedTexture> m_hudTexture;
80
    OwnPtr<ManagedTexture> m_hudTexture;
80
81
81
    LayerRendererChromium* m_layerRenderer;
82
    LayerRendererChromium* m_layerRenderer;
82
83
83
    static const int kBeginFrameHistorySize = 64;
84
    static const int kBeginFrameHistorySize = 120;
85
    // The number of seconds of no frames makes us decide that the previous
86
    // animation is over.
87
    static const double kIdleSecondsTriggersReset;
84
    double m_beginTimeHistoryInSec[kBeginFrameHistorySize];
88
    double m_beginTimeHistoryInSec[kBeginFrameHistorySize];
89
    static const double kFrameTooFast;
90
    static const int kNumMissedFramesForReset = 5;
85
91
86
    OwnPtr<Font> m_smallFont;
92
    OwnPtr<Font> m_smallFont;
87
    OwnPtr<Font> m_mediumFont;
93
    OwnPtr<Font> m_mediumFont;

Return to Bug 73454